| 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* Microchip lan969x Switch driver |
| 3 | * |
| 4 | * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries. |
| 5 | */ |
| 6 | |
| 7 | #include "lan969x.h" |
| 8 | |
| 9 | #define LAN969X_SDLB_GRP_CNT 5 |
| 10 | #define LAN969X_HSCH_LEAK_GRP_CNT 4 |
| 11 | |
| 12 | static const struct sparx5_main_io_resource lan969x_main_iomap[] = { |
| 13 | { TARGET_CPU, 0xc0000, 0 }, /* 0xe00c0000 */ |
| 14 | { TARGET_FDMA, 0xc0400, 0 }, /* 0xe00c0400 */ |
| 15 | { TARGET_GCB, 0x2010000, 1 }, /* 0xe2010000 */ |
| 16 | { TARGET_QS, 0x2030000, 1 }, /* 0xe2030000 */ |
| 17 | { TARGET_PTP, 0x2040000, 1 }, /* 0xe2040000 */ |
| 18 | { TARGET_ANA_ACL, 0x2050000, 1 }, /* 0xe2050000 */ |
| 19 | { TARGET_LRN, 0x2060000, 1 }, /* 0xe2060000 */ |
| 20 | { TARGET_VCAP_SUPER, 0x2080000, 1 }, /* 0xe2080000 */ |
| 21 | { TARGET_QSYS, 0x20a0000, 1 }, /* 0xe20a0000 */ |
| 22 | { TARGET_QFWD, 0x20b0000, 1 }, /* 0xe20b0000 */ |
| 23 | { TARGET_XQS, 0x20c0000, 1 }, /* 0xe20c0000 */ |
| 24 | { TARGET_VCAP_ES2, 0x20d0000, 1 }, /* 0xe20d0000 */ |
| 25 | { TARGET_VCAP_ES0, 0x20e0000, 1 }, /* 0xe20e0000 */ |
| 26 | { TARGET_ANA_AC_POL, 0x2200000, 1 }, /* 0xe2200000 */ |
| 27 | { TARGET_QRES, 0x2280000, 1 }, /* 0xe2280000 */ |
| 28 | { TARGET_EACL, 0x22c0000, 1 }, /* 0xe22c0000 */ |
| 29 | { TARGET_ANA_CL, 0x2400000, 1 }, /* 0xe2400000 */ |
| 30 | { TARGET_ANA_L3, 0x2480000, 1 }, /* 0xe2480000 */ |
| 31 | { TARGET_ANA_AC_SDLB, 0x2500000, 1 }, /* 0xe2500000 */ |
| 32 | { TARGET_HSCH, 0x2580000, 1 }, /* 0xe2580000 */ |
| 33 | { TARGET_REW, 0x2600000, 1 }, /* 0xe2600000 */ |
| 34 | { TARGET_ANA_L2, 0x2800000, 1 }, /* 0xe2800000 */ |
| 35 | { TARGET_ANA_AC, 0x2900000, 1 }, /* 0xe2900000 */ |
| 36 | { TARGET_VOP, 0x2a00000, 1 }, /* 0xe2a00000 */ |
| 37 | { TARGET_DEV2G5, 0x3004000, 1 }, /* 0xe3004000 */ |
| 38 | { TARGET_DEV10G, 0x3008000, 1 }, /* 0xe3008000 */ |
| 39 | { TARGET_PCS10G_BR, 0x300c000, 1 }, /* 0xe300c000 */ |
| 40 | { TARGET_DEV2G5 + 1, 0x3010000, 1 }, /* 0xe3010000 */ |
| 41 | { TARGET_DEV2G5 + 2, 0x3014000, 1 }, /* 0xe3014000 */ |
| 42 | { TARGET_DEV2G5 + 3, 0x3018000, 1 }, /* 0xe3018000 */ |
| 43 | { TARGET_DEV2G5 + 4, 0x301c000, 1 }, /* 0xe301c000 */ |
| 44 | { TARGET_DEV10G + 1, 0x3020000, 1 }, /* 0xe3020000 */ |
| 45 | { TARGET_PCS10G_BR + 1, 0x3024000, 1 }, /* 0xe3024000 */ |
| 46 | { TARGET_DEV2G5 + 5, 0x3028000, 1 }, /* 0xe3028000 */ |
| 47 | { TARGET_DEV2G5 + 6, 0x302c000, 1 }, /* 0xe302c000 */ |
| 48 | { TARGET_DEV2G5 + 7, 0x3030000, 1 }, /* 0xe3030000 */ |
| 49 | { TARGET_DEV2G5 + 8, 0x3034000, 1 }, /* 0xe3034000 */ |
| 50 | { TARGET_DEV10G + 2, 0x3038000, 1 }, /* 0xe3038000 */ |
| 51 | { TARGET_PCS10G_BR + 2, 0x303c000, 1 }, /* 0xe303c000 */ |
| 52 | { TARGET_DEV2G5 + 9, 0x3040000, 1 }, /* 0xe3040000 */ |
| 53 | { TARGET_DEV5G, 0x3044000, 1 }, /* 0xe3044000 */ |
| 54 | { TARGET_PCS5G_BR, 0x3048000, 1 }, /* 0xe3048000 */ |
| 55 | { TARGET_DEV2G5 + 10, 0x304c000, 1 }, /* 0xe304c000 */ |
| 56 | { TARGET_DEV2G5 + 11, 0x3050000, 1 }, /* 0xe3050000 */ |
| 57 | { TARGET_DEV2G5 + 12, 0x3054000, 1 }, /* 0xe3054000 */ |
| 58 | { TARGET_DEV10G + 3, 0x3058000, 1 }, /* 0xe3058000 */ |
| 59 | { TARGET_PCS10G_BR + 3, 0x305c000, 1 }, /* 0xe305c000 */ |
| 60 | { TARGET_DEV2G5 + 13, 0x3060000, 1 }, /* 0xe3060000 */ |
| 61 | { TARGET_DEV5G + 1, 0x3064000, 1 }, /* 0xe3064000 */ |
| 62 | { TARGET_PCS5G_BR + 1, 0x3068000, 1 }, /* 0xe3068000 */ |
| 63 | { TARGET_DEV2G5 + 14, 0x306c000, 1 }, /* 0xe306c000 */ |
| 64 | { TARGET_DEV2G5 + 15, 0x3070000, 1 }, /* 0xe3070000 */ |
| 65 | { TARGET_DEV2G5 + 16, 0x3074000, 1 }, /* 0xe3074000 */ |
| 66 | { TARGET_DEV10G + 4, 0x3078000, 1 }, /* 0xe3078000 */ |
| 67 | { TARGET_PCS10G_BR + 4, 0x307c000, 1 }, /* 0xe307c000 */ |
| 68 | { TARGET_DEV2G5 + 17, 0x3080000, 1 }, /* 0xe3080000 */ |
| 69 | { TARGET_DEV5G + 2, 0x3084000, 1 }, /* 0xe3084000 */ |
| 70 | { TARGET_PCS5G_BR + 2, 0x3088000, 1 }, /* 0xe3088000 */ |
| 71 | { TARGET_DEV2G5 + 18, 0x308c000, 1 }, /* 0xe308c000 */ |
| 72 | { TARGET_DEV2G5 + 19, 0x3090000, 1 }, /* 0xe3090000 */ |
| 73 | { TARGET_DEV2G5 + 20, 0x3094000, 1 }, /* 0xe3094000 */ |
| 74 | { TARGET_DEV10G + 5, 0x3098000, 1 }, /* 0xe3098000 */ |
| 75 | { TARGET_PCS10G_BR + 5, 0x309c000, 1 }, /* 0xe309c000 */ |
| 76 | { TARGET_DEV2G5 + 21, 0x30a0000, 1 }, /* 0xe30a0000 */ |
| 77 | { TARGET_DEV5G + 3, 0x30a4000, 1 }, /* 0xe30a4000 */ |
| 78 | { TARGET_PCS5G_BR + 3, 0x30a8000, 1 }, /* 0xe30a8000 */ |
| 79 | { TARGET_DEV2G5 + 22, 0x30ac000, 1 }, /* 0xe30ac000 */ |
| 80 | { TARGET_DEV2G5 + 23, 0x30b0000, 1 }, /* 0xe30b0000 */ |
| 81 | { TARGET_DEV2G5 + 24, 0x30b4000, 1 }, /* 0xe30b4000 */ |
| 82 | { TARGET_DEV10G + 6, 0x30b8000, 1 }, /* 0xe30b8000 */ |
| 83 | { TARGET_PCS10G_BR + 6, 0x30bc000, 1 }, /* 0xe30bc000 */ |
| 84 | { TARGET_DEV2G5 + 25, 0x30c0000, 1 }, /* 0xe30c0000 */ |
| 85 | { TARGET_DEV10G + 7, 0x30c4000, 1 }, /* 0xe30c4000 */ |
| 86 | { TARGET_PCS10G_BR + 7, 0x30c8000, 1 }, /* 0xe30c8000 */ |
| 87 | { TARGET_DEV2G5 + 26, 0x30cc000, 1 }, /* 0xe30cc000 */ |
| 88 | { TARGET_DEV10G + 8, 0x30d0000, 1 }, /* 0xe30d0000 */ |
| 89 | { TARGET_PCS10G_BR + 8, 0x30d4000, 1 }, /* 0xe30d4000 */ |
| 90 | { TARGET_DEV2G5 + 27, 0x30d8000, 1 }, /* 0xe30d8000 */ |
| 91 | { TARGET_DEV10G + 9, 0x30dc000, 1 }, /* 0xe30dc000 */ |
| 92 | { TARGET_PCS10G_BR + 9, 0x30e0000, 1 }, /* 0xe30e0000 */ |
| 93 | { TARGET_DEVRGMII, 0x30e4000, 1 }, /* 0xe30e4000 */ |
| 94 | { TARGET_DEVRGMII + 1, 0x30e8000, 1 }, /* 0xe30e8000 */ |
| 95 | { TARGET_DSM, 0x30ec000, 1 }, /* 0xe30ec000 */ |
| 96 | { TARGET_PORT_CONF, 0x30f0000, 1 }, /* 0xe30f0000 */ |
| 97 | { TARGET_ASM, 0x3200000, 1 }, /* 0xe3200000 */ |
| 98 | { TARGET_HSIO_WRAP, 0x3408000, 1 }, /* 0xe3408000 */ |
| 99 | }; |
| 100 | |
| 101 | static struct sparx5_sdlb_group lan969x_sdlb_groups[LAN969X_SDLB_GRP_CNT] = { |
| 102 | { 1000000000, 8192 / 2, 64 }, /* 1 G */ |
| 103 | { 500000000, 8192 / 2, 64 }, /* 500 M */ |
| 104 | { 100000000, 8192 / 4, 64 }, /* 100 M */ |
| 105 | { 50000000, 8192 / 4, 64 }, /* 50 M */ |
| 106 | { 5000000, 8192 / 8, 64 }, /* 10 M */ |
| 107 | }; |
| 108 | |
| 109 | static u32 lan969x_hsch_max_group_rate[LAN969X_HSCH_LEAK_GRP_CNT] = { |
| 110 | 655355, 1048568, 6553550, 10485680 |
| 111 | }; |
| 112 | |
| 113 | static struct sparx5_sdlb_group *lan969x_get_sdlb_group(int idx) |
| 114 | { |
| 115 | return &lan969x_sdlb_groups[idx]; |
| 116 | } |
| 117 | |
| 118 | static u32 lan969x_get_hsch_max_group_rate(int grp) |
| 119 | { |
| 120 | return lan969x_hsch_max_group_rate[grp]; |
| 121 | } |
| 122 | |
| 123 | static u32 lan969x_get_dev_mode_bit(struct sparx5 *sparx5, int port) |
| 124 | { |
| 125 | if (lan969x_port_is_2g5(portno: port) || lan969x_port_is_5g(portno: port)) |
| 126 | return port; |
| 127 | |
| 128 | /* 10G */ |
| 129 | switch (port) { |
| 130 | case 0: |
| 131 | return 12; |
| 132 | case 4: |
| 133 | return 13; |
| 134 | case 8: |
| 135 | return 14; |
| 136 | case 12: |
| 137 | return 0; |
| 138 | default: |
| 139 | return port; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | static u32 lan969x_port_dev_mapping(struct sparx5 *sparx5, int port) |
| 144 | { |
| 145 | if (lan969x_port_is_5g(portno: port)) { |
| 146 | switch (port) { |
| 147 | case 9: |
| 148 | return 0; |
| 149 | case 13: |
| 150 | return 1; |
| 151 | case 17: |
| 152 | return 2; |
| 153 | case 21: |
| 154 | return 3; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | if (lan969x_port_is_10g(portno: port)) { |
| 159 | switch (port) { |
| 160 | case 0: |
| 161 | return 0; |
| 162 | case 4: |
| 163 | return 1; |
| 164 | case 8: |
| 165 | return 2; |
| 166 | case 12: |
| 167 | return 3; |
| 168 | case 16: |
| 169 | return 4; |
| 170 | case 20: |
| 171 | return 5; |
| 172 | case 24: |
| 173 | return 6; |
| 174 | case 25: |
| 175 | return 7; |
| 176 | case 26: |
| 177 | return 8; |
| 178 | case 27: |
| 179 | return 9; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | /* 2g5 port */ |
| 184 | return port; |
| 185 | } |
| 186 | |
| 187 | static int lan969x_port_mux_set(struct sparx5 *sparx5, struct sparx5_port *port, |
| 188 | struct sparx5_port_config *conf) |
| 189 | { |
| 190 | u32 portno = port->portno; |
| 191 | u32 inst; |
| 192 | |
| 193 | if (port->conf.portmode == conf->portmode) |
| 194 | return 0; /* Nothing to do */ |
| 195 | |
| 196 | switch (conf->portmode) { |
| 197 | case PHY_INTERFACE_MODE_QSGMII: /* QSGMII: 4x2G5 devices. Mode Q' */ |
| 198 | inst = (portno - portno % 4) / 4; |
| 199 | spx5_rmw(BIT(inst), BIT(inst), sparx5, PORT_CONF_QSGMII_ENA); |
| 200 | break; |
| 201 | default: |
| 202 | break; |
| 203 | } |
| 204 | return 0; |
| 205 | } |
| 206 | |
| 207 | static irqreturn_t lan969x_ptp_irq_handler(int irq, void *args) |
| 208 | { |
| 209 | int budget = SPARX5_MAX_PTP_ID; |
| 210 | struct sparx5 *sparx5 = args; |
| 211 | |
| 212 | while (budget--) { |
| 213 | struct sk_buff *skb, *skb_tmp, *skb_match = NULL; |
| 214 | struct skb_shared_hwtstamps shhwtstamps; |
| 215 | struct sparx5_port *port; |
| 216 | struct timespec64 ts; |
| 217 | unsigned long flags; |
| 218 | u32 val, id, txport; |
| 219 | u32 delay; |
| 220 | |
| 221 | val = spx5_rd(sparx5, PTP_TWOSTEP_CTRL); |
| 222 | |
| 223 | /* Check if a timestamp can be retrieved */ |
| 224 | if (!(val & PTP_TWOSTEP_CTRL_PTP_VLD)) |
| 225 | break; |
| 226 | |
| 227 | WARN_ON(val & PTP_TWOSTEP_CTRL_PTP_OVFL); |
| 228 | |
| 229 | if (!(val & PTP_TWOSTEP_CTRL_STAMP_TX)) |
| 230 | continue; |
| 231 | |
| 232 | /* Retrieve the ts Tx port */ |
| 233 | txport = PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val); |
| 234 | |
| 235 | /* Retrieve its associated skb */ |
| 236 | port = sparx5->ports[txport]; |
| 237 | |
| 238 | /* Retrieve the delay */ |
| 239 | delay = spx5_rd(sparx5, PTP_TWOSTEP_STAMP_NSEC); |
| 240 | delay = PTP_TWOSTEP_STAMP_NSEC_NS_GET(delay); |
| 241 | |
| 242 | /* Get next timestamp from fifo, which needs to be the |
| 243 | * rx timestamp which represents the id of the frame |
| 244 | */ |
| 245 | spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), |
| 246 | PTP_TWOSTEP_CTRL_PTP_NXT, |
| 247 | sparx5, PTP_TWOSTEP_CTRL); |
| 248 | |
| 249 | val = spx5_rd(sparx5, PTP_TWOSTEP_CTRL); |
| 250 | |
| 251 | /* Check if a timestamp can be retrieved */ |
| 252 | if (!(val & PTP_TWOSTEP_CTRL_PTP_VLD)) |
| 253 | break; |
| 254 | |
| 255 | /* Read RX timestamping to get the ID */ |
| 256 | id = spx5_rd(sparx5, PTP_TWOSTEP_STAMP_NSEC); |
| 257 | id <<= 8; |
| 258 | id |= spx5_rd(sparx5, PTP_TWOSTEP_STAMP_SUBNS); |
| 259 | |
| 260 | spin_lock_irqsave(&port->tx_skbs.lock, flags); |
| 261 | skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { |
| 262 | if (SPARX5_SKB_CB(skb)->ts_id != id) |
| 263 | continue; |
| 264 | |
| 265 | __skb_unlink(skb, list: &port->tx_skbs); |
| 266 | skb_match = skb; |
| 267 | break; |
| 268 | } |
| 269 | spin_unlock_irqrestore(lock: &port->tx_skbs.lock, flags); |
| 270 | |
| 271 | /* Next ts */ |
| 272 | spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), |
| 273 | PTP_TWOSTEP_CTRL_PTP_NXT, |
| 274 | sparx5, PTP_TWOSTEP_CTRL); |
| 275 | |
| 276 | if (WARN_ON(!skb_match)) |
| 277 | continue; |
| 278 | |
| 279 | spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); |
| 280 | sparx5->ptp_skbs--; |
| 281 | spin_unlock_irqrestore(lock: &sparx5->ptp_ts_id_lock, flags); |
| 282 | |
| 283 | /* Get the h/w timestamp */ |
| 284 | sparx5_get_hwtimestamp(sparx5, ts: &ts, nsec: delay); |
| 285 | |
| 286 | /* Set the timestamp in the skb */ |
| 287 | shhwtstamps.hwtstamp = ktime_set(secs: ts.tv_sec, nsecs: ts.tv_nsec); |
| 288 | skb_tstamp_tx(orig_skb: skb_match, hwtstamps: &shhwtstamps); |
| 289 | |
| 290 | dev_kfree_skb_any(skb: skb_match); |
| 291 | } |
| 292 | |
| 293 | return IRQ_HANDLED; |
| 294 | } |
| 295 | |
| 296 | static const struct sparx5_regs lan969x_regs = { |
| 297 | .tsize = lan969x_tsize, |
| 298 | .gaddr = lan969x_gaddr, |
| 299 | .gcnt = lan969x_gcnt, |
| 300 | .gsize = lan969x_gsize, |
| 301 | .raddr = lan969x_raddr, |
| 302 | .rcnt = lan969x_rcnt, |
| 303 | .fpos = lan969x_fpos, |
| 304 | .fsize = lan969x_fsize, |
| 305 | }; |
| 306 | |
| 307 | static const struct sparx5_consts lan969x_consts = { |
| 308 | .n_ports = 30, |
| 309 | .n_ports_all = 35, |
| 310 | .n_hsch_l1_elems = 32, |
| 311 | .n_hsch_queues = 4, |
| 312 | .n_lb_groups = 5, |
| 313 | .n_pgids = 1054, /* (1024 + n_ports) */ |
| 314 | .n_sio_clks = 1, |
| 315 | .n_own_upsids = 1, |
| 316 | .n_auto_cals = 4, |
| 317 | .n_filters = 256, |
| 318 | .n_gates = 256, |
| 319 | .n_sdlbs = 496, |
| 320 | .n_dsm_cal_taxis = 5, |
| 321 | .buf_size = 1572864, |
| 322 | .qres_max_prio_idx = 315, |
| 323 | .qres_max_colour_idx = 323, |
| 324 | .tod_pin = 4, |
| 325 | .vcaps = lan969x_vcaps, |
| 326 | .vcap_stats = &lan969x_vcap_stats, |
| 327 | .vcaps_cfg = lan969x_vcap_inst_cfg, |
| 328 | }; |
| 329 | |
| 330 | static const struct sparx5_ops lan969x_ops = { |
| 331 | .is_port_2g5 = &lan969x_port_is_2g5, |
| 332 | .is_port_5g = &lan969x_port_is_5g, |
| 333 | .is_port_10g = &lan969x_port_is_10g, |
| 334 | .is_port_25g = &lan969x_port_is_25g, |
| 335 | .is_port_rgmii = &lan969x_port_is_rgmii, |
| 336 | .get_port_dev_index = &lan969x_port_dev_mapping, |
| 337 | .get_port_dev_bit = &lan969x_get_dev_mode_bit, |
| 338 | .get_hsch_max_group_rate = &lan969x_get_hsch_max_group_rate, |
| 339 | .get_sdlb_group = &lan969x_get_sdlb_group, |
| 340 | .set_port_mux = &lan969x_port_mux_set, |
| 341 | .ptp_irq_handler = &lan969x_ptp_irq_handler, |
| 342 | .dsm_calendar_calc = &lan969x_dsm_calendar_calc, |
| 343 | .port_config_rgmii = &lan969x_port_config_rgmii, |
| 344 | .fdma_init = &lan969x_fdma_init, |
| 345 | .fdma_deinit = &lan969x_fdma_deinit, |
| 346 | .fdma_poll = &lan969x_fdma_napi_poll, |
| 347 | .fdma_xmit = &lan969x_fdma_xmit, |
| 348 | }; |
| 349 | |
| 350 | const struct sparx5_match_data lan969x_desc = { |
| 351 | .iomap = lan969x_main_iomap, |
| 352 | .iomap_size = ARRAY_SIZE(lan969x_main_iomap), |
| 353 | .ioranges = 2, |
| 354 | .regs = &lan969x_regs, |
| 355 | .consts = &lan969x_consts, |
| 356 | .ops = &lan969x_ops, |
| 357 | }; |
| 358 | |