1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip lan969x Switch driver
4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
9 #define LAN969X_SDLB_GRP_CNT 5
10 #define LAN969X_HSCH_LEAK_GRP_CNT 4
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_DSM
, 0x30ec000, 1 }, /* 0xe30ec000 */
94 { TARGET_PORT_CONF
, 0x30f0000, 1 }, /* 0xe30f0000 */
95 { TARGET_ASM
, 0x3200000, 1 }, /* 0xe3200000 */
98 static struct sparx5_sdlb_group lan969x_sdlb_groups
[LAN969X_SDLB_GRP_CNT
] = {
99 { 1000000000, 8192 / 2, 64 }, /* 1 G */
100 { 500000000, 8192 / 2, 64 }, /* 500 M */
101 { 100000000, 8192 / 4, 64 }, /* 100 M */
102 { 50000000, 8192 / 4, 64 }, /* 50 M */
103 { 5000000, 8192 / 8, 64 }, /* 10 M */
106 static u32 lan969x_hsch_max_group_rate
[LAN969X_HSCH_LEAK_GRP_CNT
] = {
107 655355, 1048568, 6553550, 10485680
110 static struct sparx5_sdlb_group
*lan969x_get_sdlb_group(int idx
)
112 return &lan969x_sdlb_groups
[idx
];
115 static u32
lan969x_get_hsch_max_group_rate(int grp
)
117 return lan969x_hsch_max_group_rate
[grp
];
120 static u32
lan969x_get_dev_mode_bit(struct sparx5
*sparx5
, int port
)
122 if (lan969x_port_is_2g5(port
) || lan969x_port_is_5g(port
))
140 static u32
lan969x_port_dev_mapping(struct sparx5
*sparx5
, int port
)
142 if (lan969x_port_is_5g(port
)) {
155 if (lan969x_port_is_10g(port
)) {
184 static int lan969x_port_mux_set(struct sparx5
*sparx5
, struct sparx5_port
*port
,
185 struct sparx5_port_config
*conf
)
187 u32 portno
= port
->portno
;
190 if (port
->conf
.portmode
== conf
->portmode
)
191 return 0; /* Nothing to do */
193 switch (conf
->portmode
) {
194 case PHY_INTERFACE_MODE_QSGMII
: /* QSGMII: 4x2G5 devices. Mode Q' */
195 inst
= (portno
- portno
% 4) / 4;
196 spx5_rmw(BIT(inst
), BIT(inst
), sparx5
, PORT_CONF_QSGMII_ENA
);
204 static irqreturn_t
lan969x_ptp_irq_handler(int irq
, void *args
)
206 int budget
= SPARX5_MAX_PTP_ID
;
207 struct sparx5
*sparx5
= args
;
210 struct sk_buff
*skb
, *skb_tmp
, *skb_match
= NULL
;
211 struct skb_shared_hwtstamps shhwtstamps
;
212 struct sparx5_port
*port
;
213 struct timespec64 ts
;
218 val
= spx5_rd(sparx5
, PTP_TWOSTEP_CTRL
);
220 /* Check if a timestamp can be retrieved */
221 if (!(val
& PTP_TWOSTEP_CTRL_PTP_VLD
))
224 WARN_ON(val
& PTP_TWOSTEP_CTRL_PTP_OVFL
);
226 if (!(val
& PTP_TWOSTEP_CTRL_STAMP_TX
))
229 /* Retrieve the ts Tx port */
230 txport
= PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val
);
232 /* Retrieve its associated skb */
233 port
= sparx5
->ports
[txport
];
235 /* Retrieve the delay */
236 delay
= spx5_rd(sparx5
, PTP_TWOSTEP_STAMP_NSEC
);
237 delay
= PTP_TWOSTEP_STAMP_NSEC_NS_GET(delay
);
239 /* Get next timestamp from fifo, which needs to be the
240 * rx timestamp which represents the id of the frame
242 spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1),
243 PTP_TWOSTEP_CTRL_PTP_NXT
,
244 sparx5
, PTP_TWOSTEP_CTRL
);
246 val
= spx5_rd(sparx5
, PTP_TWOSTEP_CTRL
);
248 /* Check if a timestamp can be retrieved */
249 if (!(val
& PTP_TWOSTEP_CTRL_PTP_VLD
))
252 /* Read RX timestamping to get the ID */
253 id
= spx5_rd(sparx5
, PTP_TWOSTEP_STAMP_NSEC
);
255 id
|= spx5_rd(sparx5
, PTP_TWOSTEP_STAMP_SUBNS
);
257 spin_lock_irqsave(&port
->tx_skbs
.lock
, flags
);
258 skb_queue_walk_safe(&port
->tx_skbs
, skb
, skb_tmp
) {
259 if (SPARX5_SKB_CB(skb
)->ts_id
!= id
)
262 __skb_unlink(skb
, &port
->tx_skbs
);
266 spin_unlock_irqrestore(&port
->tx_skbs
.lock
, flags
);
269 spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1),
270 PTP_TWOSTEP_CTRL_PTP_NXT
,
271 sparx5
, PTP_TWOSTEP_CTRL
);
273 if (WARN_ON(!skb_match
))
276 spin_lock(&sparx5
->ptp_ts_id_lock
);
278 spin_unlock(&sparx5
->ptp_ts_id_lock
);
280 /* Get the h/w timestamp */
281 sparx5_get_hwtimestamp(sparx5
, &ts
, delay
);
283 /* Set the timestamp in the skb */
284 shhwtstamps
.hwtstamp
= ktime_set(ts
.tv_sec
, ts
.tv_nsec
);
285 skb_tstamp_tx(skb_match
, &shhwtstamps
);
287 dev_kfree_skb_any(skb_match
);
293 static const struct sparx5_regs lan969x_regs
= {
294 .tsize
= lan969x_tsize
,
295 .gaddr
= lan969x_gaddr
,
296 .gcnt
= lan969x_gcnt
,
297 .gsize
= lan969x_gsize
,
298 .raddr
= lan969x_raddr
,
299 .rcnt
= lan969x_rcnt
,
300 .fpos
= lan969x_fpos
,
301 .fsize
= lan969x_fsize
,
304 static const struct sparx5_consts lan969x_consts
= {
307 .n_hsch_l1_elems
= 32,
310 .n_pgids
= 1054, /* (1024 + n_ports) */
317 .n_dsm_cal_taxis
= 5,
319 .qres_max_prio_idx
= 315,
320 .qres_max_colour_idx
= 323,
322 .vcaps
= lan969x_vcaps
,
323 .vcap_stats
= &lan969x_vcap_stats
,
324 .vcaps_cfg
= lan969x_vcap_inst_cfg
,
327 static const struct sparx5_ops lan969x_ops
= {
328 .is_port_2g5
= &lan969x_port_is_2g5
,
329 .is_port_5g
= &lan969x_port_is_5g
,
330 .is_port_10g
= &lan969x_port_is_10g
,
331 .is_port_25g
= &lan969x_port_is_25g
,
332 .get_port_dev_index
= &lan969x_port_dev_mapping
,
333 .get_port_dev_bit
= &lan969x_get_dev_mode_bit
,
334 .get_hsch_max_group_rate
= &lan969x_get_hsch_max_group_rate
,
335 .get_sdlb_group
= &lan969x_get_sdlb_group
,
336 .set_port_mux
= &lan969x_port_mux_set
,
337 .ptp_irq_handler
= &lan969x_ptp_irq_handler
,
338 .dsm_calendar_calc
= &lan969x_dsm_calendar_calc
,
341 const struct sparx5_match_data lan969x_desc
= {
342 .iomap
= lan969x_main_iomap
,
343 .iomap_size
= ARRAY_SIZE(lan969x_main_iomap
),
345 .regs
= &lan969x_regs
,
346 .consts
= &lan969x_consts
,
349 EXPORT_SYMBOL_GPL(lan969x_desc
);
351 MODULE_DESCRIPTION("Microchip lan969x switch driver");
352 MODULE_AUTHOR("Daniel Machon <daniel.machon@microchip.com>");
353 MODULE_LICENSE("Dual MIT/GPL");