blob: 71907acd8f82f2a96b47f8595cec052a54c34a3e [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
2 * net/dsa/dsa.c - Hardware switch handling
Lennert Buytenheke84665c2009-03-20 09:52:09 +00003 * Copyright (c) 2008-2009 Marvell Semiconductor
Florian Fainelli5e95329b2013-03-22 10:50:50 +00004 * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
Guenter Roeck51579c32014-10-29 10:44:58 -070012#include <linux/device.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000013#include <linux/list.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000014#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040016#include <linux/module.h>
Florian Fainelli60724d42017-10-11 10:57:48 -070017#include <linux/notifier.h>
Florian Fainelli5e95329b2013-03-22 10:50:50 +000018#include <linux/of.h>
19#include <linux/of_mdio.h>
20#include <linux/of_platform.h>
Florian Fainelli769a0202015-03-09 14:31:21 -070021#include <linux/of_net.h>
Andrew Lunnc6e970a2017-03-28 23:45:06 +020022#include <linux/netdevice.h>
Guenter Roeck51579c32014-10-29 10:44:58 -070023#include <linux/sysfs.h>
Neil Armstrongcbc5d902015-10-06 15:40:32 +010024#include <linux/phy_fixed.h>
Brandon Streiff90af1052018-02-14 01:07:49 +010025#include <linux/ptp_classify.h>
Florian Fainellia86d8be2017-04-08 08:55:23 -070026#include <linux/etherdevice.h>
Vivien Didelotea5dd342017-05-17 15:46:03 -040027
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000028#include "dsa_priv.h"
29
Andrew Lunnbdc6fe52019-04-28 19:37:16 +020030static LIST_HEAD(dsa_tag_drivers_list);
31static DEFINE_MUTEX(dsa_tag_drivers_lock);
32
Andrew Lunn39a7f2a2016-06-04 21:17:03 +020033static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
34 struct net_device *dev)
35{
36 /* Just return the original SKB */
37 return skb;
38}
39
40static const struct dsa_device_ops none_ops = {
Andrew Lunn875138f2019-04-28 19:37:11 +020041 .name = "none",
Andrew Lunn056eed22019-04-28 19:37:14 +020042 .proto = DSA_TAG_PROTO_NONE,
Andrew Lunn39a7f2a2016-06-04 21:17:03 +020043 .xmit = dsa_slave_notag_xmit,
44 .rcv = NULL,
45};
46
Andrew Lunn409065b2019-04-28 19:37:17 +020047DSA_TAG_DRIVER(none_ops);
48
Andrew Lunn39a7f2a2016-06-04 21:17:03 +020049const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
Andrew Lunneb7b7212017-05-16 22:40:07 +020050#ifdef CONFIG_NET_DSA_TAG_BRCM
51 [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops,
52#endif
Florian Fainellib74b70c2017-11-10 15:22:54 -080053#ifdef CONFIG_NET_DSA_TAG_BRCM_PREPEND
54 [DSA_TAG_PROTO_BRCM_PREPEND] = &brcm_prepend_netdev_ops,
55#endif
Andrew Lunn39a7f2a2016-06-04 21:17:03 +020056#ifdef CONFIG_NET_DSA_TAG_DSA
57 [DSA_TAG_PROTO_DSA] = &dsa_netdev_ops,
58#endif
59#ifdef CONFIG_NET_DSA_TAG_EDSA
60 [DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops,
61#endif
Hauke Mehrtens79691192018-09-09 22:16:43 +020062#ifdef CONFIG_NET_DSA_TAG_GSWIP
63 [DSA_TAG_PROTO_GSWIP] = &gswip_netdev_ops,
64#endif
Tristram Ha39d6b962018-12-15 01:58:04 +010065#ifdef CONFIG_NET_DSA_TAG_KSZ9477
66 [DSA_TAG_PROTO_KSZ9477] = &ksz9477_netdev_ops,
Tristram Ha88b573a2019-02-28 19:57:23 -080067 [DSA_TAG_PROTO_KSZ9893] = &ksz9893_netdev_ops,
Woojung Huh8b8010f2017-05-31 20:19:06 +000068#endif
Andrew Lunneb7b7212017-05-16 22:40:07 +020069#ifdef CONFIG_NET_DSA_TAG_LAN9303
70 [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops,
John Crispincafdc452016-09-15 16:26:40 +020071#endif
Sean Wang5cd89852017-04-07 16:45:06 +080072#ifdef CONFIG_NET_DSA_TAG_MTK
73 [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops,
74#endif
Andrew Lunneb7b7212017-05-16 22:40:07 +020075#ifdef CONFIG_NET_DSA_TAG_QCA
76 [DSA_TAG_PROTO_QCA] = &qca_netdev_ops,
77#endif
78#ifdef CONFIG_NET_DSA_TAG_TRAILER
79 [DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops,
Juergen Beiserte8fe1772017-04-18 10:48:24 +020080#endif
Andrew Lunn39a7f2a2016-06-04 21:17:03 +020081 [DSA_TAG_PROTO_NONE] = &none_ops,
82};
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000083
Andrew Lunnbdc6fe52019-04-28 19:37:16 +020084static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver,
85 struct module *owner)
86{
87 dsa_tag_driver->owner = owner;
88
89 mutex_lock(&dsa_tag_drivers_lock);
90 list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list);
91 mutex_unlock(&dsa_tag_drivers_lock);
92}
93
Andrew Lunnd3b8c042019-04-28 19:37:15 +020094void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
95 unsigned int count, struct module *owner)
96{
Andrew Lunnbdc6fe52019-04-28 19:37:16 +020097 unsigned int i;
98
99 for (i = 0; i < count; i++)
100 dsa_tag_driver_register(dsa_tag_driver_array[i], owner);
101}
102
103static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver)
104{
105 mutex_lock(&dsa_tag_drivers_lock);
106 list_del(&dsa_tag_driver->list);
107 mutex_unlock(&dsa_tag_drivers_lock);
Andrew Lunnd3b8c042019-04-28 19:37:15 +0200108}
109EXPORT_SYMBOL_GPL(dsa_tag_drivers_register);
110
111void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
112 unsigned int count)
113{
Andrew Lunnbdc6fe52019-04-28 19:37:16 +0200114 unsigned int i;
115
116 for (i = 0; i < count; i++)
117 dsa_tag_driver_unregister(dsa_tag_driver_array[i]);
Andrew Lunnd3b8c042019-04-28 19:37:15 +0200118}
119EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister);
120
Florian Fainelli98cdb482018-09-07 11:09:02 -0700121const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
122{
Andrew Lunn875138f2019-04-28 19:37:11 +0200123 return ops->name;
Florian Fainelli98cdb482018-09-07 11:09:02 -0700124};
125
Andrew Lunn39a7f2a2016-06-04 21:17:03 +0200126const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
127{
128 const struct dsa_device_ops *ops;
129
130 if (tag_protocol >= DSA_TAG_LAST)
131 return ERR_PTR(-EINVAL);
132 ops = dsa_device_ops[tag_protocol];
133
134 if (!ops)
135 return ERR_PTR(-ENOPROTOOPT);
136
137 return ops;
138}
139
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000140static int dev_is_class(struct device *dev, void *class)
141{
142 if (dev->class != NULL && !strcmp(dev->class->name, class))
143 return 1;
144
145 return 0;
146}
147
148static struct device *dev_find_class(struct device *parent, char *class)
149{
150 if (dev_is_class(parent, class)) {
151 get_device(parent);
152 return parent;
153 }
154
155 return device_find_child(parent, class, dev_is_class);
156}
157
Florian Fainelli14b89f32017-02-04 13:02:42 -0800158struct net_device *dsa_dev_to_net_device(struct device *dev)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000159{
160 struct device *d;
161
162 d = dev_find_class(dev, "net");
163 if (d != NULL) {
164 struct net_device *nd;
165
166 nd = to_net_dev(d);
167 dev_hold(nd);
168 put_device(d);
169
170 return nd;
171 }
172
173 return NULL;
174}
Florian Fainelli14b89f32017-02-04 13:02:42 -0800175EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000176
Brandon Streiff90af1052018-02-14 01:07:49 +0100177/* Determine if we should defer delivery of skb until we have a rx timestamp.
178 *
179 * Called from dsa_switch_rcv. For now, this will only work if tagging is
180 * enabled on the switch. Normally the MAC driver would retrieve the hardware
181 * timestamp when it reads the packet out of the hardware. However in a DSA
182 * switch, the DSA driver owning the interface to which the packet is
183 * delivered is never notified unless we do so here.
184 */
185static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p,
186 struct sk_buff *skb)
187{
188 struct dsa_switch *ds = p->dp->ds;
189 unsigned int type;
190
191 if (skb_headroom(skb) < ETH_HLEN)
192 return false;
193
194 __skb_push(skb, ETH_HLEN);
195
196 type = ptp_classify_raw(skb);
197
198 __skb_pull(skb, ETH_HLEN);
199
200 if (type == PTP_CLASS_NONE)
201 return false;
202
203 if (likely(ds->ops->port_rxtstamp))
204 return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type);
205
206 return false;
207}
208
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700209static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
Florian Westphal89e49502017-08-17 16:47:00 +0200210 struct packet_type *pt, struct net_device *unused)
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700211{
Vivien Didelot2f657a62017-09-29 17:19:20 -0400212 struct dsa_port *cpu_dp = dev->dsa_ptr;
Florian Fainellia86d8be2017-04-08 08:55:23 -0700213 struct sk_buff *nskb = NULL;
Florian Fainelli5f6b4e142017-08-03 21:33:27 -0700214 struct pcpu_sw_netstats *s;
Florian Fainellif613ed62017-08-01 15:00:36 -0700215 struct dsa_slave_priv *p;
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700216
Vivien Didelot2f657a62017-09-29 17:19:20 -0400217 if (unlikely(!cpu_dp)) {
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700218 kfree_skb(skb);
219 return 0;
220 }
221
Florian Fainelli16c5dcb2017-04-08 08:55:22 -0700222 skb = skb_unshare(skb, GFP_ATOMIC);
223 if (!skb)
224 return 0;
225
Vivien Didelot2f657a62017-09-29 17:19:20 -0400226 nskb = cpu_dp->rcv(skb, dev, pt);
Florian Fainellia86d8be2017-04-08 08:55:23 -0700227 if (!nskb) {
228 kfree_skb(skb);
229 return 0;
230 }
231
232 skb = nskb;
Florian Fainellif613ed62017-08-01 15:00:36 -0700233 p = netdev_priv(skb->dev);
Florian Fainellia86d8be2017-04-08 08:55:23 -0700234 skb_push(skb, ETH_HLEN);
235 skb->pkt_type = PACKET_HOST;
236 skb->protocol = eth_type_trans(skb, skb->dev);
237
Florian Fainelli5f6b4e142017-08-03 21:33:27 -0700238 s = this_cpu_ptr(p->stats64);
239 u64_stats_update_begin(&s->syncp);
240 s->rx_packets++;
241 s->rx_bytes += skb->len;
242 u64_stats_update_end(&s->syncp);
Florian Fainellia86d8be2017-04-08 08:55:23 -0700243
Brandon Streiff90af1052018-02-14 01:07:49 +0100244 if (dsa_skb_defer_rx_timestamp(p, skb))
245 return 0;
246
Florian Fainellia86d8be2017-04-08 08:55:23 -0700247 netif_receive_skb(skb);
248
249 return 0;
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700250}
251
Florian Fainelliac2629a2017-06-01 19:53:04 -0700252#ifdef CONFIG_PM_SLEEP
Vivien Didelote7d53ad2017-07-18 16:23:56 -0400253static bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
254{
Vivien Didelot4a5b85f2017-10-26 11:22:55 -0400255 return dsa_is_user_port(ds, p) && ds->ports[p].slave;
Vivien Didelote7d53ad2017-07-18 16:23:56 -0400256}
257
Florian Fainelliac2629a2017-06-01 19:53:04 -0700258int dsa_switch_suspend(struct dsa_switch *ds)
259{
260 int i, ret = 0;
261
262 /* Suspend slave network devices */
263 for (i = 0; i < ds->num_ports; i++) {
264 if (!dsa_is_port_initialized(ds, i))
265 continue;
266
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400267 ret = dsa_slave_suspend(ds->ports[i].slave);
Florian Fainelliac2629a2017-06-01 19:53:04 -0700268 if (ret)
269 return ret;
270 }
271
272 if (ds->ops->suspend)
273 ret = ds->ops->suspend(ds);
274
275 return ret;
276}
277EXPORT_SYMBOL_GPL(dsa_switch_suspend);
278
279int dsa_switch_resume(struct dsa_switch *ds)
280{
281 int i, ret = 0;
282
283 if (ds->ops->resume)
284 ret = ds->ops->resume(ds);
285
286 if (ret)
287 return ret;
288
289 /* Resume slave network devices */
290 for (i = 0; i < ds->num_ports; i++) {
291 if (!dsa_is_port_initialized(ds, i))
292 continue;
293
Vivien Didelotf8b8b1c2017-10-16 11:12:18 -0400294 ret = dsa_slave_resume(ds->ports[i].slave);
Florian Fainelliac2629a2017-06-01 19:53:04 -0700295 if (ret)
296 return ret;
297 }
298
299 return 0;
300}
301EXPORT_SYMBOL_GPL(dsa_switch_resume);
302#endif
303
Florian Fainelli61b73632014-08-29 12:42:07 -0700304static struct packet_type dsa_pack_type __read_mostly = {
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700305 .type = cpu_to_be16(ETH_P_XDSA),
306 .func = dsa_switch_rcv,
307};
308
Arkadi Sharshevskyc9eb3e02017-08-06 16:15:42 +0300309static struct workqueue_struct *dsa_owq;
310
311bool dsa_schedule_work(struct work_struct *work)
312{
313 return queue_work(dsa_owq, work);
314}
315
Florian Fainelli60724d42017-10-11 10:57:48 -0700316static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain);
317
318int register_dsa_notifier(struct notifier_block *nb)
319{
320 return atomic_notifier_chain_register(&dsa_notif_chain, nb);
321}
322EXPORT_SYMBOL_GPL(register_dsa_notifier);
323
324int unregister_dsa_notifier(struct notifier_block *nb)
325{
326 return atomic_notifier_chain_unregister(&dsa_notif_chain, nb);
327}
328EXPORT_SYMBOL_GPL(unregister_dsa_notifier);
329
330int call_dsa_notifiers(unsigned long val, struct net_device *dev,
331 struct dsa_notifier_info *info)
332{
333 info->dev = dev;
334 return atomic_notifier_call_chain(&dsa_notif_chain, val, info);
335}
336EXPORT_SYMBOL_GPL(call_dsa_notifiers);
337
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000338static int __init dsa_init_module(void)
339{
Ben Hutchings7df899c2011-11-25 14:35:02 +0000340 int rc;
341
Arkadi Sharshevskyc9eb3e02017-08-06 16:15:42 +0300342 dsa_owq = alloc_ordered_workqueue("dsa_ordered",
343 WQ_MEM_RECLAIM);
344 if (!dsa_owq)
345 return -ENOMEM;
346
Vivien Didelot88e4f0c2017-02-03 13:20:16 -0500347 rc = dsa_slave_register_notifier();
348 if (rc)
349 return rc;
Florian Fainellib73adef2015-02-24 13:15:33 -0800350
Vivien Didelota6a71f12017-04-12 12:45:03 -0400351 rc = dsa_legacy_register();
Ben Hutchings7df899c2011-11-25 14:35:02 +0000352 if (rc)
353 return rc;
354
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700355 dev_add_pack(&dsa_pack_type);
356
Andrew Lunn409065b2019-04-28 19:37:17 +0200357 dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops),
358 THIS_MODULE);
359
Ben Hutchings7df899c2011-11-25 14:35:02 +0000360 return 0;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000361}
362module_init(dsa_init_module);
363
364static void __exit dsa_cleanup_module(void)
365{
Andrew Lunn409065b2019-04-28 19:37:17 +0200366 dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops));
367
Vivien Didelot88e4f0c2017-02-03 13:20:16 -0500368 dsa_slave_unregister_notifier();
Florian Fainelli3e8a72d2014-08-27 17:04:46 -0700369 dev_remove_pack(&dsa_pack_type);
Vivien Didelota6a71f12017-04-12 12:45:03 -0400370 dsa_legacy_unregister();
Arkadi Sharshevskyc9eb3e02017-08-06 16:15:42 +0300371 destroy_workqueue(dsa_owq);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000372}
373module_exit(dsa_cleanup_module);
374
Rusty Russell577d6a72011-01-24 14:32:52 -0600375MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000376MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
377MODULE_LICENSE("GPL");
378MODULE_ALIAS("platform:dsa");