1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* |
3 | * Microsemi Ocelot Switch driver |
4 | * |
5 | * Copyright (c) 2017 Microsemi Corporation |
6 | */ |
7 | #include <linux/dsa/ocelot.h> |
8 | #include <linux/interrupt.h> |
9 | #include <linux/module.h> |
10 | #include <linux/of_net.h> |
11 | #include <linux/netdevice.h> |
12 | #include <linux/phylink.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_mdio.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/skbuff.h> |
18 | #include <net/switchdev.h> |
19 | |
20 | #include <soc/mscc/ocelot.h> |
21 | #include <soc/mscc/ocelot_vcap.h> |
22 | #include <soc/mscc/vsc7514_regs.h> |
23 | #include "ocelot_fdma.h" |
24 | #include "ocelot.h" |
25 | |
26 | #define VSC7514_VCAP_POLICER_BASE 128 |
27 | #define VSC7514_VCAP_POLICER_MAX 191 |
28 | |
29 | static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops) |
30 | { |
31 | int ret; |
32 | |
33 | ocelot->map = vsc7514_regmap; |
34 | ocelot->num_mact_rows = 1024; |
35 | ocelot->ops = ops; |
36 | |
37 | ret = ocelot_regfields_init(ocelot, regfields: vsc7514_regfields); |
38 | if (ret) |
39 | return ret; |
40 | |
41 | ocelot_pll5_init(ocelot); |
42 | |
43 | eth_random_addr(addr: ocelot->base_mac); |
44 | ocelot->base_mac[5] &= 0xf0; |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) |
50 | { |
51 | struct ocelot *ocelot = arg; |
52 | int grp = 0, err; |
53 | |
54 | while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) { |
55 | struct sk_buff *skb; |
56 | |
57 | err = ocelot_xtr_poll_frame(ocelot, grp, skb: &skb); |
58 | if (err) |
59 | goto out; |
60 | |
61 | skb->dev->stats.rx_bytes += skb->len; |
62 | skb->dev->stats.rx_packets++; |
63 | |
64 | if (!skb_defer_rx_timestamp(skb)) |
65 | netif_rx(skb); |
66 | } |
67 | |
68 | out: |
69 | if (err < 0) |
70 | ocelot_drain_cpu_queue(ocelot, grp: 0); |
71 | |
72 | return IRQ_HANDLED; |
73 | } |
74 | |
75 | static irqreturn_t ocelot_ptp_rdy_irq_handler(int irq, void *arg) |
76 | { |
77 | struct ocelot *ocelot = arg; |
78 | |
79 | ocelot_get_txtstamp(ocelot); |
80 | |
81 | return IRQ_HANDLED; |
82 | } |
83 | |
84 | static const struct of_device_id mscc_ocelot_match[] = { |
85 | { .compatible = "mscc,vsc7514-switch" }, |
86 | { } |
87 | }; |
88 | MODULE_DEVICE_TABLE(of, mscc_ocelot_match); |
89 | |
90 | static const struct ocelot_ops ocelot_ops = { |
91 | .reset = ocelot_reset, |
92 | .wm_enc = ocelot_wm_enc, |
93 | .wm_dec = ocelot_wm_dec, |
94 | .wm_stat = ocelot_wm_stat, |
95 | .port_to_netdev = ocelot_port_to_netdev, |
96 | .netdev_to_port = ocelot_netdev_to_port, |
97 | }; |
98 | |
99 | static struct ptp_clock_info ocelot_ptp_clock_info = { |
100 | .owner = THIS_MODULE, |
101 | .name = "ocelot ptp" , |
102 | .max_adj = 0x7fffffff, |
103 | .n_alarm = 0, |
104 | .n_ext_ts = 0, |
105 | .n_per_out = OCELOT_PTP_PINS_NUM, |
106 | .n_pins = OCELOT_PTP_PINS_NUM, |
107 | .pps = 0, |
108 | .gettime64 = ocelot_ptp_gettime64, |
109 | .settime64 = ocelot_ptp_settime64, |
110 | .adjtime = ocelot_ptp_adjtime, |
111 | .adjfine = ocelot_ptp_adjfine, |
112 | .verify = ocelot_ptp_verify, |
113 | .enable = ocelot_ptp_enable, |
114 | }; |
115 | |
116 | static void mscc_ocelot_teardown_devlink_ports(struct ocelot *ocelot) |
117 | { |
118 | int port; |
119 | |
120 | for (port = 0; port < ocelot->num_phys_ports; port++) |
121 | ocelot_port_devlink_teardown(ocelot, port); |
122 | } |
123 | |
124 | static void mscc_ocelot_release_ports(struct ocelot *ocelot) |
125 | { |
126 | int port; |
127 | |
128 | for (port = 0; port < ocelot->num_phys_ports; port++) { |
129 | struct ocelot_port *ocelot_port; |
130 | |
131 | ocelot_port = ocelot->ports[port]; |
132 | if (!ocelot_port) |
133 | continue; |
134 | |
135 | ocelot_deinit_port(ocelot, port); |
136 | ocelot_release_port(ocelot_port); |
137 | } |
138 | } |
139 | |
140 | static int mscc_ocelot_init_ports(struct platform_device *pdev, |
141 | struct device_node *ports) |
142 | { |
143 | struct ocelot *ocelot = platform_get_drvdata(pdev); |
144 | u32 devlink_ports_registered = 0; |
145 | struct device_node *portnp; |
146 | int port, err; |
147 | u32 reg; |
148 | |
149 | ocelot->ports = devm_kcalloc(dev: ocelot->dev, n: ocelot->num_phys_ports, |
150 | size: sizeof(struct ocelot_port *), GFP_KERNEL); |
151 | if (!ocelot->ports) |
152 | return -ENOMEM; |
153 | |
154 | ocelot->devlink_ports = devm_kcalloc(dev: ocelot->dev, |
155 | n: ocelot->num_phys_ports, |
156 | size: sizeof(*ocelot->devlink_ports), |
157 | GFP_KERNEL); |
158 | if (!ocelot->devlink_ports) |
159 | return -ENOMEM; |
160 | |
161 | for_each_available_child_of_node(ports, portnp) { |
162 | struct regmap *target; |
163 | struct resource *res; |
164 | char res_name[8]; |
165 | |
166 | if (of_property_read_u32(np: portnp, propname: "reg" , out_value: ®)) |
167 | continue; |
168 | |
169 | port = reg; |
170 | if (port < 0 || port >= ocelot->num_phys_ports) { |
171 | dev_err(ocelot->dev, |
172 | "invalid port number: %d >= %d\n" , port, |
173 | ocelot->num_phys_ports); |
174 | continue; |
175 | } |
176 | |
177 | snprintf(buf: res_name, size: sizeof(res_name), fmt: "port%d" , port); |
178 | |
179 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
180 | res_name); |
181 | target = ocelot_regmap_init(ocelot, res); |
182 | if (IS_ERR(ptr: target)) { |
183 | err = PTR_ERR(ptr: target); |
184 | of_node_put(node: portnp); |
185 | goto out_teardown; |
186 | } |
187 | |
188 | err = ocelot_port_devlink_init(ocelot, port, |
189 | flavour: DEVLINK_PORT_FLAVOUR_PHYSICAL); |
190 | if (err) { |
191 | of_node_put(node: portnp); |
192 | goto out_teardown; |
193 | } |
194 | |
195 | err = ocelot_probe_port(ocelot, port, target, portnp); |
196 | if (err) { |
197 | ocelot_port_devlink_teardown(ocelot, port); |
198 | continue; |
199 | } |
200 | |
201 | devlink_ports_registered |= BIT(port); |
202 | } |
203 | |
204 | /* Initialize unused devlink ports at the end */ |
205 | for (port = 0; port < ocelot->num_phys_ports; port++) { |
206 | if (devlink_ports_registered & BIT(port)) |
207 | continue; |
208 | |
209 | err = ocelot_port_devlink_init(ocelot, port, |
210 | flavour: DEVLINK_PORT_FLAVOUR_UNUSED); |
211 | if (err) |
212 | goto out_teardown; |
213 | |
214 | devlink_ports_registered |= BIT(port); |
215 | } |
216 | |
217 | return 0; |
218 | |
219 | out_teardown: |
220 | /* Unregister the network interfaces */ |
221 | mscc_ocelot_release_ports(ocelot); |
222 | /* Tear down devlink ports for the registered network interfaces */ |
223 | for (port = 0; port < ocelot->num_phys_ports; port++) { |
224 | if (devlink_ports_registered & BIT(port)) |
225 | ocelot_port_devlink_teardown(ocelot, port); |
226 | } |
227 | return err; |
228 | } |
229 | |
230 | static int mscc_ocelot_probe(struct platform_device *pdev) |
231 | { |
232 | struct device_node *np = pdev->dev.of_node; |
233 | int err, irq_xtr, irq_ptp_rdy; |
234 | struct device_node *ports; |
235 | struct devlink *devlink; |
236 | struct ocelot *ocelot; |
237 | struct regmap *hsio; |
238 | unsigned int i; |
239 | |
240 | struct { |
241 | enum ocelot_target id; |
242 | char *name; |
243 | u8 optional:1; |
244 | } io_target[] = { |
245 | { SYS, "sys" }, |
246 | { REW, "rew" }, |
247 | { QSYS, "qsys" }, |
248 | { ANA, "ana" }, |
249 | { QS, "qs" }, |
250 | { S0, "s0" }, |
251 | { S1, "s1" }, |
252 | { S2, "s2" }, |
253 | { PTP, "ptp" , 1 }, |
254 | { FDMA, "fdma" , 1 }, |
255 | }; |
256 | |
257 | if (!np && !pdev->dev.platform_data) |
258 | return -ENODEV; |
259 | |
260 | devlink = |
261 | devlink_alloc(ops: &ocelot_devlink_ops, priv_size: sizeof(*ocelot), dev: &pdev->dev); |
262 | if (!devlink) |
263 | return -ENOMEM; |
264 | |
265 | ocelot = devlink_priv(devlink); |
266 | ocelot->devlink = priv_to_devlink(priv: ocelot); |
267 | platform_set_drvdata(pdev, data: ocelot); |
268 | ocelot->dev = &pdev->dev; |
269 | |
270 | for (i = 0; i < ARRAY_SIZE(io_target); i++) { |
271 | struct regmap *target; |
272 | struct resource *res; |
273 | |
274 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
275 | io_target[i].name); |
276 | |
277 | target = ocelot_regmap_init(ocelot, res); |
278 | if (IS_ERR(ptr: target)) { |
279 | if (io_target[i].optional) { |
280 | ocelot->targets[io_target[i].id] = NULL; |
281 | continue; |
282 | } |
283 | err = PTR_ERR(ptr: target); |
284 | goto out_free_devlink; |
285 | } |
286 | |
287 | ocelot->targets[io_target[i].id] = target; |
288 | } |
289 | |
290 | if (ocelot->targets[FDMA]) |
291 | ocelot_fdma_init(pdev, ocelot); |
292 | |
293 | hsio = syscon_regmap_lookup_by_compatible(s: "mscc,ocelot-hsio" ); |
294 | if (IS_ERR(ptr: hsio)) { |
295 | dev_err(&pdev->dev, "missing hsio syscon\n" ); |
296 | err = PTR_ERR(ptr: hsio); |
297 | goto out_free_devlink; |
298 | } |
299 | |
300 | ocelot->targets[HSIO] = hsio; |
301 | |
302 | err = ocelot_chip_init(ocelot, ops: &ocelot_ops); |
303 | if (err) |
304 | goto out_free_devlink; |
305 | |
306 | irq_xtr = platform_get_irq_byname(pdev, "xtr" ); |
307 | if (irq_xtr < 0) { |
308 | err = irq_xtr; |
309 | goto out_free_devlink; |
310 | } |
311 | |
312 | err = devm_request_threaded_irq(dev: &pdev->dev, irq: irq_xtr, NULL, |
313 | thread_fn: ocelot_xtr_irq_handler, IRQF_ONESHOT, |
314 | devname: "frame extraction" , dev_id: ocelot); |
315 | if (err) |
316 | goto out_free_devlink; |
317 | |
318 | irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy" ); |
319 | if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) { |
320 | err = devm_request_threaded_irq(dev: &pdev->dev, irq: irq_ptp_rdy, NULL, |
321 | thread_fn: ocelot_ptp_rdy_irq_handler, |
322 | IRQF_ONESHOT, devname: "ptp ready" , |
323 | dev_id: ocelot); |
324 | if (err) |
325 | goto out_free_devlink; |
326 | |
327 | /* Both the PTP interrupt and the PTP bank are available */ |
328 | ocelot->ptp = 1; |
329 | } |
330 | |
331 | ports = of_get_child_by_name(node: np, name: "ethernet-ports" ); |
332 | if (!ports) { |
333 | dev_err(ocelot->dev, "no ethernet-ports child node found\n" ); |
334 | err = -ENODEV; |
335 | goto out_free_devlink; |
336 | } |
337 | |
338 | ocelot->num_phys_ports = of_get_child_count(np: ports); |
339 | ocelot->num_flooding_pgids = 1; |
340 | |
341 | ocelot->vcap = vsc7514_vcap_props; |
342 | |
343 | ocelot->vcap_pol.base = VSC7514_VCAP_POLICER_BASE; |
344 | ocelot->vcap_pol.max = VSC7514_VCAP_POLICER_MAX; |
345 | |
346 | ocelot->npi = -1; |
347 | |
348 | err = ocelot_init(ocelot); |
349 | if (err) |
350 | goto out_put_ports; |
351 | |
352 | err = mscc_ocelot_init_ports(pdev, ports); |
353 | if (err) |
354 | goto out_ocelot_devlink_unregister; |
355 | |
356 | if (ocelot->fdma) |
357 | ocelot_fdma_start(ocelot); |
358 | |
359 | err = ocelot_devlink_sb_register(ocelot); |
360 | if (err) |
361 | goto out_ocelot_release_ports; |
362 | |
363 | if (ocelot->ptp) { |
364 | err = ocelot_init_timestamp(ocelot, info: &ocelot_ptp_clock_info); |
365 | if (err) { |
366 | dev_err(ocelot->dev, |
367 | "Timestamp initialization failed\n" ); |
368 | ocelot->ptp = 0; |
369 | } |
370 | } |
371 | |
372 | register_netdevice_notifier(nb: &ocelot_netdevice_nb); |
373 | register_switchdev_notifier(nb: &ocelot_switchdev_nb); |
374 | register_switchdev_blocking_notifier(nb: &ocelot_switchdev_blocking_nb); |
375 | |
376 | of_node_put(node: ports); |
377 | devlink_register(devlink); |
378 | |
379 | dev_info(&pdev->dev, "Ocelot switch probed\n" ); |
380 | |
381 | return 0; |
382 | |
383 | out_ocelot_release_ports: |
384 | mscc_ocelot_release_ports(ocelot); |
385 | mscc_ocelot_teardown_devlink_ports(ocelot); |
386 | out_ocelot_devlink_unregister: |
387 | ocelot_deinit(ocelot); |
388 | out_put_ports: |
389 | of_node_put(node: ports); |
390 | out_free_devlink: |
391 | devlink_free(devlink); |
392 | return err; |
393 | } |
394 | |
395 | static void mscc_ocelot_remove(struct platform_device *pdev) |
396 | { |
397 | struct ocelot *ocelot = platform_get_drvdata(pdev); |
398 | |
399 | if (ocelot->fdma) |
400 | ocelot_fdma_deinit(ocelot); |
401 | devlink_unregister(devlink: ocelot->devlink); |
402 | ocelot_deinit_timestamp(ocelot); |
403 | ocelot_devlink_sb_unregister(ocelot); |
404 | mscc_ocelot_release_ports(ocelot); |
405 | mscc_ocelot_teardown_devlink_ports(ocelot); |
406 | ocelot_deinit(ocelot); |
407 | unregister_switchdev_blocking_notifier(nb: &ocelot_switchdev_blocking_nb); |
408 | unregister_switchdev_notifier(nb: &ocelot_switchdev_nb); |
409 | unregister_netdevice_notifier(nb: &ocelot_netdevice_nb); |
410 | devlink_free(devlink: ocelot->devlink); |
411 | } |
412 | |
413 | static struct platform_driver mscc_ocelot_driver = { |
414 | .probe = mscc_ocelot_probe, |
415 | .remove_new = mscc_ocelot_remove, |
416 | .driver = { |
417 | .name = "ocelot-switch" , |
418 | .of_match_table = mscc_ocelot_match, |
419 | }, |
420 | }; |
421 | |
422 | module_platform_driver(mscc_ocelot_driver); |
423 | |
424 | MODULE_DESCRIPTION("Microsemi Ocelot switch driver" ); |
425 | MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>" ); |
426 | MODULE_LICENSE("Dual MIT/GPL" ); |
427 | |