]>
Commit | Line | Data |
---|---|---|
91da11f8 LB |
1 | /* |
2 | * net/dsa/dsa.c - Hardware switch handling | |
e84665c9 | 3 | * Copyright (c) 2008-2009 Marvell Semiconductor |
5e95329b | 4 | * Copyright (c) 2013 Florian Fainelli <[email protected]> |
91da11f8 LB |
5 | * |
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 | ||
51579c3f | 12 | #include <linux/device.h> |
91da11f8 | 13 | #include <linux/list.h> |
91da11f8 | 14 | #include <linux/platform_device.h> |
5a0e3ad6 | 15 | #include <linux/slab.h> |
3a9a231d | 16 | #include <linux/module.h> |
60724d4b | 17 | #include <linux/notifier.h> |
5e95329b FF |
18 | #include <linux/of.h> |
19 | #include <linux/of_mdio.h> | |
20 | #include <linux/of_platform.h> | |
769a0202 | 21 | #include <linux/of_net.h> |
cc30c163 | 22 | #include <linux/of_gpio.h> |
c6e970a0 | 23 | #include <linux/netdevice.h> |
51579c3f | 24 | #include <linux/sysfs.h> |
cbc5d90b | 25 | #include <linux/phy_fixed.h> |
90af1059 | 26 | #include <linux/ptp_classify.h> |
85beabfe | 27 | #include <linux/gpio/consumer.h> |
a86d8bec | 28 | #include <linux/etherdevice.h> |
ea5dd34b | 29 | |
91da11f8 LB |
30 | #include "dsa_priv.h" |
31 | ||
39a7f2a4 AL |
32 | static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb, |
33 | struct net_device *dev) | |
34 | { | |
35 | /* Just return the original SKB */ | |
36 | return skb; | |
37 | } | |
38 | ||
39 | static const struct dsa_device_ops none_ops = { | |
40 | .xmit = dsa_slave_notag_xmit, | |
41 | .rcv = NULL, | |
42 | }; | |
43 | ||
44 | const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { | |
eb7b7211 AL |
45 | #ifdef CONFIG_NET_DSA_TAG_BRCM |
46 | [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops, | |
47 | #endif | |
b74b70c4 FF |
48 | #ifdef CONFIG_NET_DSA_TAG_BRCM_PREPEND |
49 | [DSA_TAG_PROTO_BRCM_PREPEND] = &brcm_prepend_netdev_ops, | |
50 | #endif | |
39a7f2a4 AL |
51 | #ifdef CONFIG_NET_DSA_TAG_DSA |
52 | [DSA_TAG_PROTO_DSA] = &dsa_netdev_ops, | |
53 | #endif | |
54 | #ifdef CONFIG_NET_DSA_TAG_EDSA | |
55 | [DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops, | |
56 | #endif | |
8b8010fb WH |
57 | #ifdef CONFIG_NET_DSA_TAG_KSZ |
58 | [DSA_TAG_PROTO_KSZ] = &ksz_netdev_ops, | |
59 | #endif | |
eb7b7211 AL |
60 | #ifdef CONFIG_NET_DSA_TAG_LAN9303 |
61 | [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops, | |
39a7f2a4 | 62 | #endif |
eb7b7211 AL |
63 | #ifdef CONFIG_NET_DSA_TAG_MTK |
64 | [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops, | |
cafdc45c JC |
65 | #endif |
66 | #ifdef CONFIG_NET_DSA_TAG_QCA | |
67 | [DSA_TAG_PROTO_QCA] = &qca_netdev_ops, | |
5cd8985a | 68 | #endif |
eb7b7211 AL |
69 | #ifdef CONFIG_NET_DSA_TAG_TRAILER |
70 | [DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops, | |
39a7f2a4 AL |
71 | #endif |
72 | [DSA_TAG_PROTO_NONE] = &none_ops, | |
73 | }; | |
91da11f8 | 74 | |
39a7f2a4 AL |
75 | const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol) |
76 | { | |
77 | const struct dsa_device_ops *ops; | |
78 | ||
79 | if (tag_protocol >= DSA_TAG_LAST) | |
80 | return ERR_PTR(-EINVAL); | |
81 | ops = dsa_device_ops[tag_protocol]; | |
82 | ||
83 | if (!ops) | |
84 | return ERR_PTR(-ENOPROTOOPT); | |
85 | ||
86 | return ops; | |
87 | } | |
88 | ||
91da11f8 LB |
89 | static int dev_is_class(struct device *dev, void *class) |
90 | { | |
91 | if (dev->class != NULL && !strcmp(dev->class->name, class)) | |
92 | return 1; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static struct device *dev_find_class(struct device *parent, char *class) | |
98 | { | |
99 | if (dev_is_class(parent, class)) { | |
100 | get_device(parent); | |
101 | return parent; | |
102 | } | |
103 | ||
104 | return device_find_child(parent, class, dev_is_class); | |
105 | } | |
106 | ||
14b89f36 | 107 | struct net_device *dsa_dev_to_net_device(struct device *dev) |
91da11f8 LB |
108 | { |
109 | struct device *d; | |
110 | ||
111 | d = dev_find_class(dev, "net"); | |
112 | if (d != NULL) { | |
113 | struct net_device *nd; | |
114 | ||
115 | nd = to_net_dev(d); | |
116 | dev_hold(nd); | |
117 | put_device(d); | |
118 | ||
119 | return nd; | |
120 | } | |
121 | ||
122 | return NULL; | |
123 | } | |
14b89f36 | 124 | EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); |
91da11f8 | 125 | |
90af1059 BS |
126 | /* Determine if we should defer delivery of skb until we have a rx timestamp. |
127 | * | |
128 | * Called from dsa_switch_rcv. For now, this will only work if tagging is | |
129 | * enabled on the switch. Normally the MAC driver would retrieve the hardware | |
130 | * timestamp when it reads the packet out of the hardware. However in a DSA | |
131 | * switch, the DSA driver owning the interface to which the packet is | |
132 | * delivered is never notified unless we do so here. | |
133 | */ | |
134 | static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p, | |
135 | struct sk_buff *skb) | |
136 | { | |
137 | struct dsa_switch *ds = p->dp->ds; | |
138 | unsigned int type; | |
139 | ||
140 | if (skb_headroom(skb) < ETH_HLEN) | |
141 | return false; | |
142 | ||
143 | __skb_push(skb, ETH_HLEN); | |
144 | ||
145 | type = ptp_classify_raw(skb); | |
146 | ||
147 | __skb_pull(skb, ETH_HLEN); | |
148 | ||
149 | if (type == PTP_CLASS_NONE) | |
150 | return false; | |
151 | ||
152 | if (likely(ds->ops->port_rxtstamp)) | |
153 | return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); | |
154 | ||
155 | return false; | |
156 | } | |
157 | ||
3e8a72d1 | 158 | static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, |
89e49506 | 159 | struct packet_type *pt, struct net_device *unused) |
3e8a72d1 | 160 | { |
2f657a60 | 161 | struct dsa_port *cpu_dp = dev->dsa_ptr; |
a86d8bec | 162 | struct sk_buff *nskb = NULL; |
5f6b4e14 | 163 | struct pcpu_sw_netstats *s; |
f613ed66 | 164 | struct dsa_slave_priv *p; |
3e8a72d1 | 165 | |
2f657a60 | 166 | if (unlikely(!cpu_dp)) { |
3e8a72d1 FF |
167 | kfree_skb(skb); |
168 | return 0; | |
169 | } | |
170 | ||
16c5dcb1 FF |
171 | skb = skb_unshare(skb, GFP_ATOMIC); |
172 | if (!skb) | |
173 | return 0; | |
174 | ||
2f657a60 | 175 | nskb = cpu_dp->rcv(skb, dev, pt); |
a86d8bec FF |
176 | if (!nskb) { |
177 | kfree_skb(skb); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | skb = nskb; | |
f613ed66 | 182 | p = netdev_priv(skb->dev); |
a86d8bec FF |
183 | skb_push(skb, ETH_HLEN); |
184 | skb->pkt_type = PACKET_HOST; | |
185 | skb->protocol = eth_type_trans(skb, skb->dev); | |
186 | ||
5f6b4e14 FF |
187 | s = this_cpu_ptr(p->stats64); |
188 | u64_stats_update_begin(&s->syncp); | |
189 | s->rx_packets++; | |
190 | s->rx_bytes += skb->len; | |
191 | u64_stats_update_end(&s->syncp); | |
a86d8bec | 192 | |
90af1059 BS |
193 | if (dsa_skb_defer_rx_timestamp(p, skb)) |
194 | return 0; | |
195 | ||
a86d8bec FF |
196 | netif_receive_skb(skb); |
197 | ||
198 | return 0; | |
3e8a72d1 FF |
199 | } |
200 | ||
ac2629a4 | 201 | #ifdef CONFIG_PM_SLEEP |
e7d53ad3 VD |
202 | static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) |
203 | { | |
4a5b85ff | 204 | return dsa_is_user_port(ds, p) && ds->ports[p].slave; |
e7d53ad3 VD |
205 | } |
206 | ||
ac2629a4 FF |
207 | int dsa_switch_suspend(struct dsa_switch *ds) |
208 | { | |
209 | int i, ret = 0; | |
210 | ||
211 | /* Suspend slave network devices */ | |
212 | for (i = 0; i < ds->num_ports; i++) { | |
213 | if (!dsa_is_port_initialized(ds, i)) | |
214 | continue; | |
215 | ||
f8b8b1cd | 216 | ret = dsa_slave_suspend(ds->ports[i].slave); |
ac2629a4 FF |
217 | if (ret) |
218 | return ret; | |
219 | } | |
220 | ||
221 | if (ds->ops->suspend) | |
222 | ret = ds->ops->suspend(ds); | |
223 | ||
224 | return ret; | |
225 | } | |
226 | EXPORT_SYMBOL_GPL(dsa_switch_suspend); | |
227 | ||
228 | int dsa_switch_resume(struct dsa_switch *ds) | |
229 | { | |
230 | int i, ret = 0; | |
231 | ||
232 | if (ds->ops->resume) | |
233 | ret = ds->ops->resume(ds); | |
234 | ||
235 | if (ret) | |
236 | return ret; | |
237 | ||
238 | /* Resume slave network devices */ | |
239 | for (i = 0; i < ds->num_ports; i++) { | |
240 | if (!dsa_is_port_initialized(ds, i)) | |
241 | continue; | |
242 | ||
f8b8b1cd | 243 | ret = dsa_slave_resume(ds->ports[i].slave); |
ac2629a4 FF |
244 | if (ret) |
245 | return ret; | |
246 | } | |
247 | ||
248 | return 0; | |
249 | } | |
250 | EXPORT_SYMBOL_GPL(dsa_switch_resume); | |
251 | #endif | |
252 | ||
61b7363f | 253 | static struct packet_type dsa_pack_type __read_mostly = { |
3e8a72d1 FF |
254 | .type = cpu_to_be16(ETH_P_XDSA), |
255 | .func = dsa_switch_rcv, | |
256 | }; | |
257 | ||
c9eb3e0f AS |
258 | static struct workqueue_struct *dsa_owq; |
259 | ||
260 | bool dsa_schedule_work(struct work_struct *work) | |
261 | { | |
262 | return queue_work(dsa_owq, work); | |
263 | } | |
264 | ||
60724d4b FF |
265 | static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain); |
266 | ||
267 | int register_dsa_notifier(struct notifier_block *nb) | |
268 | { | |
269 | return atomic_notifier_chain_register(&dsa_notif_chain, nb); | |
270 | } | |
271 | EXPORT_SYMBOL_GPL(register_dsa_notifier); | |
272 | ||
273 | int unregister_dsa_notifier(struct notifier_block *nb) | |
274 | { | |
275 | return atomic_notifier_chain_unregister(&dsa_notif_chain, nb); | |
276 | } | |
277 | EXPORT_SYMBOL_GPL(unregister_dsa_notifier); | |
278 | ||
279 | int call_dsa_notifiers(unsigned long val, struct net_device *dev, | |
280 | struct dsa_notifier_info *info) | |
281 | { | |
282 | info->dev = dev; | |
283 | return atomic_notifier_call_chain(&dsa_notif_chain, val, info); | |
284 | } | |
285 | EXPORT_SYMBOL_GPL(call_dsa_notifiers); | |
286 | ||
91da11f8 LB |
287 | static int __init dsa_init_module(void) |
288 | { | |
7df899c3 BH |
289 | int rc; |
290 | ||
c9eb3e0f AS |
291 | dsa_owq = alloc_ordered_workqueue("dsa_ordered", |
292 | WQ_MEM_RECLAIM); | |
293 | if (!dsa_owq) | |
294 | return -ENOMEM; | |
295 | ||
88e4f0ca VD |
296 | rc = dsa_slave_register_notifier(); |
297 | if (rc) | |
298 | return rc; | |
b73adef6 | 299 | |
a6a71f19 | 300 | rc = dsa_legacy_register(); |
7df899c3 BH |
301 | if (rc) |
302 | return rc; | |
303 | ||
3e8a72d1 FF |
304 | dev_add_pack(&dsa_pack_type); |
305 | ||
7df899c3 | 306 | return 0; |
91da11f8 LB |
307 | } |
308 | module_init(dsa_init_module); | |
309 | ||
310 | static void __exit dsa_cleanup_module(void) | |
311 | { | |
88e4f0ca | 312 | dsa_slave_unregister_notifier(); |
3e8a72d1 | 313 | dev_remove_pack(&dsa_pack_type); |
a6a71f19 | 314 | dsa_legacy_unregister(); |
c9eb3e0f | 315 | destroy_workqueue(dsa_owq); |
91da11f8 LB |
316 | } |
317 | module_exit(dsa_cleanup_module); | |
318 | ||
577d6a7c | 319 | MODULE_AUTHOR("Lennert Buytenhek <[email protected]>"); |
91da11f8 LB |
320 | MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); |
321 | MODULE_LICENSE("GPL"); | |
322 | MODULE_ALIAS("platform:dsa"); |