.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Distributed Switch Architecture loopback driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2016, Florian Fainelli <f.fainelli@gmail.com> |
---|
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 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #include <linux/platform_device.h> |
---|
.. | .. |
---|
18 | 14 | #include <linux/workqueue.h> |
---|
19 | 15 | #include <linux/module.h> |
---|
20 | 16 | #include <linux/if_bridge.h> |
---|
| 17 | +#include <linux/dsa/loop.h> |
---|
21 | 18 | #include <net/dsa.h> |
---|
22 | 19 | |
---|
23 | 20 | #include "dsa_loop.h" |
---|
24 | | - |
---|
25 | | -struct dsa_loop_vlan { |
---|
26 | | - u16 members; |
---|
27 | | - u16 untagged; |
---|
28 | | -}; |
---|
29 | | - |
---|
30 | | -struct dsa_loop_mib_entry { |
---|
31 | | - char name[ETH_GSTRING_LEN]; |
---|
32 | | - unsigned long val; |
---|
33 | | -}; |
---|
34 | | - |
---|
35 | | -enum dsa_loop_mib_counters { |
---|
36 | | - DSA_LOOP_PHY_READ_OK, |
---|
37 | | - DSA_LOOP_PHY_READ_ERR, |
---|
38 | | - DSA_LOOP_PHY_WRITE_OK, |
---|
39 | | - DSA_LOOP_PHY_WRITE_ERR, |
---|
40 | | - __DSA_LOOP_CNT_MAX, |
---|
41 | | -}; |
---|
42 | 21 | |
---|
43 | 22 | static struct dsa_loop_mib_entry dsa_loop_mibs[] = { |
---|
44 | 23 | [DSA_LOOP_PHY_READ_OK] = { "phy_read_ok", }, |
---|
.. | .. |
---|
47 | 26 | [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", }, |
---|
48 | 27 | }; |
---|
49 | 28 | |
---|
50 | | -struct dsa_loop_port { |
---|
51 | | - struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX]; |
---|
52 | | -}; |
---|
53 | | - |
---|
54 | | -#define DSA_LOOP_VLANS 5 |
---|
55 | | - |
---|
56 | | -struct dsa_loop_priv { |
---|
57 | | - struct mii_bus *bus; |
---|
58 | | - unsigned int port_base; |
---|
59 | | - struct dsa_loop_vlan vlans[DSA_LOOP_VLANS]; |
---|
60 | | - struct net_device *netdev; |
---|
61 | | - struct dsa_loop_port ports[DSA_MAX_PORTS]; |
---|
62 | | - u16 pvid; |
---|
63 | | -}; |
---|
64 | | - |
---|
65 | 29 | static struct phy_device *phydevs[PHY_MAX_ADDR]; |
---|
66 | 30 | |
---|
| 31 | +enum dsa_loop_devlink_resource_id { |
---|
| 32 | + DSA_LOOP_DEVLINK_PARAM_ID_VTU, |
---|
| 33 | +}; |
---|
| 34 | + |
---|
| 35 | +static u64 dsa_loop_devlink_vtu_get(void *priv) |
---|
| 36 | +{ |
---|
| 37 | + struct dsa_loop_priv *ps = priv; |
---|
| 38 | + unsigned int i, count = 0; |
---|
| 39 | + struct dsa_loop_vlan *vl; |
---|
| 40 | + |
---|
| 41 | + for (i = 0; i < ARRAY_SIZE(ps->vlans); i++) { |
---|
| 42 | + vl = &ps->vlans[i]; |
---|
| 43 | + if (vl->members) |
---|
| 44 | + count++; |
---|
| 45 | + } |
---|
| 46 | + |
---|
| 47 | + return count; |
---|
| 48 | +} |
---|
| 49 | + |
---|
| 50 | +static int dsa_loop_setup_devlink_resources(struct dsa_switch *ds) |
---|
| 51 | +{ |
---|
| 52 | + struct devlink_resource_size_params size_params; |
---|
| 53 | + struct dsa_loop_priv *ps = ds->priv; |
---|
| 54 | + int err; |
---|
| 55 | + |
---|
| 56 | + devlink_resource_size_params_init(&size_params, ARRAY_SIZE(ps->vlans), |
---|
| 57 | + ARRAY_SIZE(ps->vlans), |
---|
| 58 | + 1, DEVLINK_RESOURCE_UNIT_ENTRY); |
---|
| 59 | + |
---|
| 60 | + err = dsa_devlink_resource_register(ds, "VTU", ARRAY_SIZE(ps->vlans), |
---|
| 61 | + DSA_LOOP_DEVLINK_PARAM_ID_VTU, |
---|
| 62 | + DEVLINK_RESOURCE_ID_PARENT_TOP, |
---|
| 63 | + &size_params); |
---|
| 64 | + if (err) |
---|
| 65 | + goto out; |
---|
| 66 | + |
---|
| 67 | + dsa_devlink_resource_occ_get_register(ds, |
---|
| 68 | + DSA_LOOP_DEVLINK_PARAM_ID_VTU, |
---|
| 69 | + dsa_loop_devlink_vtu_get, ps); |
---|
| 70 | + |
---|
| 71 | + return 0; |
---|
| 72 | + |
---|
| 73 | +out: |
---|
| 74 | + dsa_devlink_resources_unregister(ds); |
---|
| 75 | + return err; |
---|
| 76 | +} |
---|
| 77 | + |
---|
67 | 78 | static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds, |
---|
68 | | - int port) |
---|
| 79 | + int port, |
---|
| 80 | + enum dsa_tag_protocol mp) |
---|
69 | 81 | { |
---|
70 | 82 | dev_dbg(ds->dev, "%s: port: %d\n", __func__, port); |
---|
71 | 83 | |
---|
.. | .. |
---|
83 | 95 | |
---|
84 | 96 | dev_dbg(ds->dev, "%s\n", __func__); |
---|
85 | 97 | |
---|
86 | | - return 0; |
---|
| 98 | + return dsa_loop_setup_devlink_resources(ds); |
---|
| 99 | +} |
---|
| 100 | + |
---|
| 101 | +static void dsa_loop_teardown(struct dsa_switch *ds) |
---|
| 102 | +{ |
---|
| 103 | + dsa_devlink_resources_unregister(ds); |
---|
87 | 104 | } |
---|
88 | 105 | |
---|
89 | 106 | static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port, int sset) |
---|
.. | .. |
---|
173 | 190 | } |
---|
174 | 191 | |
---|
175 | 192 | static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, |
---|
176 | | - bool vlan_filtering) |
---|
| 193 | + bool vlan_filtering, |
---|
| 194 | + struct switchdev_trans *trans) |
---|
177 | 195 | { |
---|
178 | 196 | dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n", |
---|
179 | 197 | __func__, port, vlan_filtering); |
---|
.. | .. |
---|
194 | 212 | /* Just do a sleeping operation to make lockdep checks effective */ |
---|
195 | 213 | mdiobus_read(bus, ps->port_base + port, MII_BMSR); |
---|
196 | 214 | |
---|
197 | | - if (vlan->vid_end > DSA_LOOP_VLANS) |
---|
| 215 | + if (vlan->vid_end > ARRAY_SIZE(ps->vlans)) |
---|
198 | 216 | return -ERANGE; |
---|
199 | 217 | |
---|
200 | 218 | return 0; |
---|
.. | .. |
---|
227 | 245 | } |
---|
228 | 246 | |
---|
229 | 247 | if (pvid) |
---|
230 | | - ps->pvid = vid; |
---|
| 248 | + ps->ports[port].pvid = vid; |
---|
231 | 249 | } |
---|
232 | 250 | |
---|
233 | 251 | static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, |
---|
.. | .. |
---|
237 | 255 | struct dsa_loop_priv *ps = ds->priv; |
---|
238 | 256 | struct mii_bus *bus = ps->bus; |
---|
239 | 257 | struct dsa_loop_vlan *vl; |
---|
240 | | - u16 vid, pvid = ps->pvid; |
---|
| 258 | + u16 vid, pvid = ps->ports[port].pvid; |
---|
241 | 259 | |
---|
242 | 260 | /* Just do a sleeping operation to make lockdep checks effective */ |
---|
243 | 261 | mdiobus_read(bus, ps->port_base + port, MII_BMSR); |
---|
.. | .. |
---|
255 | 273 | dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n", |
---|
256 | 274 | __func__, port, vid, untagged ? "un" : "", pvid); |
---|
257 | 275 | } |
---|
258 | | - ps->pvid = pvid; |
---|
| 276 | + ps->ports[port].pvid = pvid; |
---|
259 | 277 | |
---|
260 | 278 | return 0; |
---|
| 279 | +} |
---|
| 280 | + |
---|
| 281 | +static int dsa_loop_port_change_mtu(struct dsa_switch *ds, int port, |
---|
| 282 | + int new_mtu) |
---|
| 283 | +{ |
---|
| 284 | + struct dsa_loop_priv *priv = ds->priv; |
---|
| 285 | + |
---|
| 286 | + priv->ports[port].mtu = new_mtu; |
---|
| 287 | + |
---|
| 288 | + return 0; |
---|
| 289 | +} |
---|
| 290 | + |
---|
| 291 | +static int dsa_loop_port_max_mtu(struct dsa_switch *ds, int port) |
---|
| 292 | +{ |
---|
| 293 | + return ETH_MAX_MTU; |
---|
261 | 294 | } |
---|
262 | 295 | |
---|
263 | 296 | static const struct dsa_switch_ops dsa_loop_driver = { |
---|
264 | 297 | .get_tag_protocol = dsa_loop_get_protocol, |
---|
265 | 298 | .setup = dsa_loop_setup, |
---|
| 299 | + .teardown = dsa_loop_teardown, |
---|
266 | 300 | .get_strings = dsa_loop_get_strings, |
---|
267 | 301 | .get_ethtool_stats = dsa_loop_get_ethtool_stats, |
---|
268 | 302 | .get_sset_count = dsa_loop_get_sset_count, |
---|
.. | .. |
---|
276 | 310 | .port_vlan_prepare = dsa_loop_port_vlan_prepare, |
---|
277 | 311 | .port_vlan_add = dsa_loop_port_vlan_add, |
---|
278 | 312 | .port_vlan_del = dsa_loop_port_vlan_del, |
---|
| 313 | + .port_change_mtu = dsa_loop_port_change_mtu, |
---|
| 314 | + .port_max_mtu = dsa_loop_port_max_mtu, |
---|
279 | 315 | }; |
---|
280 | 316 | |
---|
281 | 317 | static int dsa_loop_drv_probe(struct mdio_device *mdiodev) |
---|
.. | .. |
---|
283 | 319 | struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data; |
---|
284 | 320 | struct dsa_loop_priv *ps; |
---|
285 | 321 | struct dsa_switch *ds; |
---|
| 322 | + int ret; |
---|
286 | 323 | |
---|
287 | 324 | if (!pdata) |
---|
288 | 325 | return -ENODEV; |
---|
289 | 326 | |
---|
290 | | - dev_info(&mdiodev->dev, "%s: 0x%0x\n", |
---|
291 | | - pdata->name, pdata->enabled_ports); |
---|
292 | | - |
---|
293 | | - ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); |
---|
| 327 | + ds = devm_kzalloc(&mdiodev->dev, sizeof(*ds), GFP_KERNEL); |
---|
294 | 328 | if (!ds) |
---|
295 | 329 | return -ENOMEM; |
---|
| 330 | + |
---|
| 331 | + ds->dev = &mdiodev->dev; |
---|
| 332 | + ds->num_ports = DSA_LOOP_NUM_PORTS; |
---|
296 | 333 | |
---|
297 | 334 | ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); |
---|
298 | 335 | if (!ps) |
---|
.. | .. |
---|
307 | 344 | ds->dev = &mdiodev->dev; |
---|
308 | 345 | ds->ops = &dsa_loop_driver; |
---|
309 | 346 | ds->priv = ps; |
---|
| 347 | + ds->configure_vlan_while_not_filtering = true; |
---|
310 | 348 | ps->bus = mdiodev->bus; |
---|
311 | 349 | |
---|
312 | 350 | dev_set_drvdata(&mdiodev->dev, ds); |
---|
313 | 351 | |
---|
314 | | - return dsa_register_switch(ds); |
---|
| 352 | + ret = dsa_register_switch(ds); |
---|
| 353 | + if (!ret) |
---|
| 354 | + dev_info(&mdiodev->dev, "%s: 0x%0x\n", |
---|
| 355 | + pdata->name, pdata->enabled_ports); |
---|
| 356 | + |
---|
| 357 | + return ret; |
---|
315 | 358 | } |
---|
316 | 359 | |
---|
317 | 360 | static void dsa_loop_drv_remove(struct mdio_device *mdiodev) |
---|
.. | .. |
---|
333 | 376 | |
---|
334 | 377 | #define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2) |
---|
335 | 378 | |
---|
| 379 | +static void dsa_loop_phydevs_unregister(void) |
---|
| 380 | +{ |
---|
| 381 | + unsigned int i; |
---|
| 382 | + |
---|
| 383 | + for (i = 0; i < NUM_FIXED_PHYS; i++) |
---|
| 384 | + if (!IS_ERR(phydevs[i])) { |
---|
| 385 | + fixed_phy_unregister(phydevs[i]); |
---|
| 386 | + phy_device_free(phydevs[i]); |
---|
| 387 | + } |
---|
| 388 | +} |
---|
| 389 | + |
---|
336 | 390 | static int __init dsa_loop_init(void) |
---|
337 | 391 | { |
---|
338 | 392 | struct fixed_phy_status status = { |
---|
.. | .. |
---|
340 | 394 | .speed = SPEED_100, |
---|
341 | 395 | .duplex = DUPLEX_FULL, |
---|
342 | 396 | }; |
---|
343 | | - unsigned int i; |
---|
| 397 | + unsigned int i, ret; |
---|
344 | 398 | |
---|
345 | 399 | for (i = 0; i < NUM_FIXED_PHYS; i++) |
---|
346 | | - phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL); |
---|
| 400 | + phydevs[i] = fixed_phy_register(PHY_POLL, &status, NULL); |
---|
347 | 401 | |
---|
348 | | - return mdio_driver_register(&dsa_loop_drv); |
---|
| 402 | + ret = mdio_driver_register(&dsa_loop_drv); |
---|
| 403 | + if (ret) |
---|
| 404 | + dsa_loop_phydevs_unregister(); |
---|
| 405 | + |
---|
| 406 | + return ret; |
---|
349 | 407 | } |
---|
350 | 408 | module_init(dsa_loop_init); |
---|
351 | 409 | |
---|
352 | 410 | static void __exit dsa_loop_exit(void) |
---|
353 | 411 | { |
---|
354 | | - unsigned int i; |
---|
355 | | - |
---|
356 | 412 | mdio_driver_unregister(&dsa_loop_drv); |
---|
357 | | - for (i = 0; i < NUM_FIXED_PHYS; i++) |
---|
358 | | - if (!IS_ERR(phydevs[i])) |
---|
359 | | - fixed_phy_unregister(phydevs[i]); |
---|
| 413 | + dsa_loop_phydevs_unregister(); |
---|
360 | 414 | } |
---|
361 | 415 | module_exit(dsa_loop_exit); |
---|
362 | 416 | |
---|