linux/drivers/net/dsa/dsa_loop.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Distributed Switch Architecture loopback driver
   4 *
   5 * Copyright (C) 2016, Florian Fainelli <f.fainelli@gmail.com>
   6 */
   7
   8#include <linux/platform_device.h>
   9#include <linux/netdevice.h>
  10#include <linux/phy.h>
  11#include <linux/phy_fixed.h>
  12#include <linux/export.h>
  13#include <linux/ethtool.h>
  14#include <linux/workqueue.h>
  15#include <linux/module.h>
  16#include <linux/if_bridge.h>
  17#include <net/dsa.h>
  18
  19#include "dsa_loop.h"
  20
  21struct dsa_loop_vlan {
  22        u16 members;
  23        u16 untagged;
  24};
  25
  26struct dsa_loop_mib_entry {
  27        char name[ETH_GSTRING_LEN];
  28        unsigned long val;
  29};
  30
  31enum dsa_loop_mib_counters {
  32        DSA_LOOP_PHY_READ_OK,
  33        DSA_LOOP_PHY_READ_ERR,
  34        DSA_LOOP_PHY_WRITE_OK,
  35        DSA_LOOP_PHY_WRITE_ERR,
  36        __DSA_LOOP_CNT_MAX,
  37};
  38
  39static struct dsa_loop_mib_entry dsa_loop_mibs[] = {
  40        [DSA_LOOP_PHY_READ_OK]  = { "phy_read_ok", },
  41        [DSA_LOOP_PHY_READ_ERR] = { "phy_read_err", },
  42        [DSA_LOOP_PHY_WRITE_OK] = { "phy_write_ok", },
  43        [DSA_LOOP_PHY_WRITE_ERR] = { "phy_write_err", },
  44};
  45
  46struct dsa_loop_port {
  47        struct dsa_loop_mib_entry mib[__DSA_LOOP_CNT_MAX];
  48};
  49
  50#define DSA_LOOP_VLANS  5
  51
  52struct dsa_loop_priv {
  53        struct mii_bus  *bus;
  54        unsigned int    port_base;
  55        struct dsa_loop_vlan vlans[DSA_LOOP_VLANS];
  56        struct net_device *netdev;
  57        struct dsa_loop_port ports[DSA_MAX_PORTS];
  58        u16 pvid;
  59};
  60
  61static struct phy_device *phydevs[PHY_MAX_ADDR];
  62
  63static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds,
  64                                                   int port)
  65{
  66        dev_dbg(ds->dev, "%s: port: %d\n", __func__, port);
  67
  68        return DSA_TAG_PROTO_NONE;
  69}
  70
  71static int dsa_loop_setup(struct dsa_switch *ds)
  72{
  73        struct dsa_loop_priv *ps = ds->priv;
  74        unsigned int i;
  75
  76        for (i = 0; i < ds->num_ports; i++)
  77                memcpy(ps->ports[i].mib, dsa_loop_mibs,
  78                       sizeof(dsa_loop_mibs));
  79
  80        dev_dbg(ds->dev, "%s\n", __func__);
  81
  82        return 0;
  83}
  84
  85static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port, int sset)
  86{
  87        if (sset != ETH_SS_STATS && sset != ETH_SS_PHY_STATS)
  88                return 0;
  89
  90        return __DSA_LOOP_CNT_MAX;
  91}
  92
  93static void dsa_loop_get_strings(struct dsa_switch *ds, int port,
  94                                 u32 stringset, uint8_t *data)
  95{
  96        struct dsa_loop_priv *ps = ds->priv;
  97        unsigned int i;
  98
  99        if (stringset != ETH_SS_STATS && stringset != ETH_SS_PHY_STATS)
 100                return;
 101
 102        for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
 103                memcpy(data + i * ETH_GSTRING_LEN,
 104                       ps->ports[port].mib[i].name, ETH_GSTRING_LEN);
 105}
 106
 107static void dsa_loop_get_ethtool_stats(struct dsa_switch *ds, int port,
 108                                       uint64_t *data)
 109{
 110        struct dsa_loop_priv *ps = ds->priv;
 111        unsigned int i;
 112
 113        for (i = 0; i < __DSA_LOOP_CNT_MAX; i++)
 114                data[i] = ps->ports[port].mib[i].val;
 115}
 116
 117static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum)
 118{
 119        struct dsa_loop_priv *ps = ds->priv;
 120        struct mii_bus *bus = ps->bus;
 121        int ret;
 122
 123        ret = mdiobus_read_nested(bus, ps->port_base + port, regnum);
 124        if (ret < 0)
 125                ps->ports[port].mib[DSA_LOOP_PHY_READ_ERR].val++;
 126        else
 127                ps->ports[port].mib[DSA_LOOP_PHY_READ_OK].val++;
 128
 129        return ret;
 130}
 131
 132static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
 133                              int regnum, u16 value)
 134{
 135        struct dsa_loop_priv *ps = ds->priv;
 136        struct mii_bus *bus = ps->bus;
 137        int ret;
 138
 139        ret = mdiobus_write_nested(bus, ps->port_base + port, regnum, value);
 140        if (ret < 0)
 141                ps->ports[port].mib[DSA_LOOP_PHY_WRITE_ERR].val++;
 142        else
 143                ps->ports[port].mib[DSA_LOOP_PHY_WRITE_OK].val++;
 144
 145        return ret;
 146}
 147
 148static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
 149                                     struct net_device *bridge)
 150{
 151        dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n",
 152                __func__, port, bridge->name);
 153
 154        return 0;
 155}
 156
 157static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,
 158                                       struct net_device *bridge)
 159{
 160        dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n",
 161                __func__, port, bridge->name);
 162}
 163
 164static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
 165                                        u8 state)
 166{
 167        dev_dbg(ds->dev, "%s: port: %d, state: %d\n",
 168                __func__, port, state);
 169}
 170
 171static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
 172                                        bool vlan_filtering)
 173{
 174        dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n",
 175                __func__, port, vlan_filtering);
 176
 177        return 0;
 178}
 179
 180static int
 181dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,
 182                           const struct switchdev_obj_port_vlan *vlan)
 183{
 184        struct dsa_loop_priv *ps = ds->priv;
 185        struct mii_bus *bus = ps->bus;
 186
 187        dev_dbg(ds->dev, "%s: port: %d, vlan: %d-%d",
 188                __func__, port, vlan->vid_begin, vlan->vid_end);
 189
 190        /* Just do a sleeping operation to make lockdep checks effective */
 191        mdiobus_read(bus, ps->port_base + port, MII_BMSR);
 192
 193        if (vlan->vid_end > DSA_LOOP_VLANS)
 194                return -ERANGE;
 195
 196        return 0;
 197}
 198
 199static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
 200                                   const struct switchdev_obj_port_vlan *vlan)
 201{
 202        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
 203        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
 204        struct dsa_loop_priv *ps = ds->priv;
 205        struct mii_bus *bus = ps->bus;
 206        struct dsa_loop_vlan *vl;
 207        u16 vid;
 208
 209        /* Just do a sleeping operation to make lockdep checks effective */
 210        mdiobus_read(bus, ps->port_base + port, MII_BMSR);
 211
 212        for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
 213                vl = &ps->vlans[vid];
 214
 215                vl->members |= BIT(port);
 216                if (untagged)
 217                        vl->untagged |= BIT(port);
 218                else
 219                        vl->untagged &= ~BIT(port);
 220
 221                dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
 222                        __func__, port, vid, untagged ? "un" : "", pvid);
 223        }
 224
 225        if (pvid)
 226                ps->pvid = vid;
 227}
 228
 229static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
 230                                  const struct switchdev_obj_port_vlan *vlan)
 231{
 232        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
 233        struct dsa_loop_priv *ps = ds->priv;
 234        struct mii_bus *bus = ps->bus;
 235        struct dsa_loop_vlan *vl;
 236        u16 vid, pvid = ps->pvid;
 237
 238        /* Just do a sleeping operation to make lockdep checks effective */
 239        mdiobus_read(bus, ps->port_base + port, MII_BMSR);
 240
 241        for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
 242                vl = &ps->vlans[vid];
 243
 244                vl->members &= ~BIT(port);
 245                if (untagged)
 246                        vl->untagged &= ~BIT(port);
 247
 248                if (pvid == vid)
 249                        pvid = 1;
 250
 251                dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
 252                        __func__, port, vid, untagged ? "un" : "", pvid);
 253        }
 254        ps->pvid = pvid;
 255
 256        return 0;
 257}
 258
 259static const struct dsa_switch_ops dsa_loop_driver = {
 260        .get_tag_protocol       = dsa_loop_get_protocol,
 261        .setup                  = dsa_loop_setup,
 262        .get_strings            = dsa_loop_get_strings,
 263        .get_ethtool_stats      = dsa_loop_get_ethtool_stats,
 264        .get_sset_count         = dsa_loop_get_sset_count,
 265        .get_ethtool_phy_stats  = dsa_loop_get_ethtool_stats,
 266        .phy_read               = dsa_loop_phy_read,
 267        .phy_write              = dsa_loop_phy_write,
 268        .port_bridge_join       = dsa_loop_port_bridge_join,
 269        .port_bridge_leave      = dsa_loop_port_bridge_leave,
 270        .port_stp_state_set     = dsa_loop_port_stp_state_set,
 271        .port_vlan_filtering    = dsa_loop_port_vlan_filtering,
 272        .port_vlan_prepare      = dsa_loop_port_vlan_prepare,
 273        .port_vlan_add          = dsa_loop_port_vlan_add,
 274        .port_vlan_del          = dsa_loop_port_vlan_del,
 275};
 276
 277static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
 278{
 279        struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data;
 280        struct dsa_loop_priv *ps;
 281        struct dsa_switch *ds;
 282
 283        if (!pdata)
 284                return -ENODEV;
 285
 286        dev_info(&mdiodev->dev, "%s: 0x%0x\n",
 287                 pdata->name, pdata->enabled_ports);
 288
 289        ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
 290        if (!ds)
 291                return -ENOMEM;
 292
 293        ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL);
 294        if (!ps)
 295                return -ENOMEM;
 296
 297        ps->netdev = dev_get_by_name(&init_net, pdata->netdev);
 298        if (!ps->netdev)
 299                return -EPROBE_DEFER;
 300
 301        pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev;
 302
 303        ds->dev = &mdiodev->dev;
 304        ds->ops = &dsa_loop_driver;
 305        ds->priv = ps;
 306        ps->bus = mdiodev->bus;
 307
 308        dev_set_drvdata(&mdiodev->dev, ds);
 309
 310        return dsa_register_switch(ds);
 311}
 312
 313static void dsa_loop_drv_remove(struct mdio_device *mdiodev)
 314{
 315        struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
 316        struct dsa_loop_priv *ps = ds->priv;
 317
 318        dsa_unregister_switch(ds);
 319        dev_put(ps->netdev);
 320}
 321
 322static struct mdio_driver dsa_loop_drv = {
 323        .mdiodrv.driver = {
 324                .name   = "dsa-loop",
 325        },
 326        .probe  = dsa_loop_drv_probe,
 327        .remove = dsa_loop_drv_remove,
 328};
 329
 330#define NUM_FIXED_PHYS  (DSA_LOOP_NUM_PORTS - 2)
 331
 332static int __init dsa_loop_init(void)
 333{
 334        struct fixed_phy_status status = {
 335                .link = 1,
 336                .speed = SPEED_100,
 337                .duplex = DUPLEX_FULL,
 338        };
 339        unsigned int i;
 340
 341        for (i = 0; i < NUM_FIXED_PHYS; i++)
 342                phydevs[i] = fixed_phy_register(PHY_POLL, &status, NULL);
 343
 344        return mdio_driver_register(&dsa_loop_drv);
 345}
 346module_init(dsa_loop_init);
 347
 348static void __exit dsa_loop_exit(void)
 349{
 350        unsigned int i;
 351
 352        mdio_driver_unregister(&dsa_loop_drv);
 353        for (i = 0; i < NUM_FIXED_PHYS; i++)
 354                if (!IS_ERR(phydevs[i]))
 355                        fixed_phy_unregister(phydevs[i]);
 356}
 357module_exit(dsa_loop_exit);
 358
 359MODULE_LICENSE("GPL");
 360MODULE_AUTHOR("Florian Fainelli");
 361MODULE_DESCRIPTION("DSA loopback driver");
 362