linux/net/dsa/slave.c
<<
>>
Prefs
   1/*
   2 * net/dsa/slave.c - Slave device handling
   3 * Copyright (c) 2008-2009 Marvell Semiconductor
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; either version 2 of the License, or
   8 * (at your option) any later version.
   9 */
  10
  11#include <linux/list.h>
  12#include <linux/netdevice.h>
  13#include <linux/etherdevice.h>
  14#include <linux/phy.h>
  15#include "dsa_priv.h"
  16
  17/* slave mii_bus handling ***************************************************/
  18static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
  19{
  20        struct dsa_switch *ds = bus->priv;
  21
  22        if (ds->phys_port_mask & (1 << addr))
  23                return ds->drv->phy_read(ds, addr, reg);
  24
  25        return 0xffff;
  26}
  27
  28static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
  29{
  30        struct dsa_switch *ds = bus->priv;
  31
  32        if (ds->phys_port_mask & (1 << addr))
  33                return ds->drv->phy_write(ds, addr, reg, val);
  34
  35        return 0;
  36}
  37
  38void dsa_slave_mii_bus_init(struct dsa_switch *ds)
  39{
  40        ds->slave_mii_bus->priv = (void *)ds;
  41        ds->slave_mii_bus->name = "dsa slave smi";
  42        ds->slave_mii_bus->read = dsa_slave_phy_read;
  43        ds->slave_mii_bus->write = dsa_slave_phy_write;
  44        snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x",
  45                        ds->master_mii_bus->id, ds->pd->sw_addr);
  46        ds->slave_mii_bus->parent = &ds->master_mii_bus->dev;
  47}
  48
  49
  50/* slave device handling ****************************************************/
  51static int dsa_slave_init(struct net_device *dev)
  52{
  53        struct dsa_slave_priv *p = netdev_priv(dev);
  54
  55        dev->iflink = p->parent->dst->master_netdev->ifindex;
  56
  57        return 0;
  58}
  59
  60static int dsa_slave_open(struct net_device *dev)
  61{
  62        struct dsa_slave_priv *p = netdev_priv(dev);
  63        struct net_device *master = p->parent->dst->master_netdev;
  64        int err;
  65
  66        if (!(master->flags & IFF_UP))
  67                return -ENETDOWN;
  68
  69        if (compare_ether_addr(dev->dev_addr, master->dev_addr)) {
  70                err = dev_uc_add(master, dev->dev_addr);
  71                if (err < 0)
  72                        goto out;
  73        }
  74
  75        if (dev->flags & IFF_ALLMULTI) {
  76                err = dev_set_allmulti(master, 1);
  77                if (err < 0)
  78                        goto del_unicast;
  79        }
  80        if (dev->flags & IFF_PROMISC) {
  81                err = dev_set_promiscuity(master, 1);
  82                if (err < 0)
  83                        goto clear_allmulti;
  84        }
  85
  86        return 0;
  87
  88clear_allmulti:
  89        if (dev->flags & IFF_ALLMULTI)
  90                dev_set_allmulti(master, -1);
  91del_unicast:
  92        if (compare_ether_addr(dev->dev_addr, master->dev_addr))
  93                dev_uc_del(master, dev->dev_addr);
  94out:
  95        return err;
  96}
  97
  98static int dsa_slave_close(struct net_device *dev)
  99{
 100        struct dsa_slave_priv *p = netdev_priv(dev);
 101        struct net_device *master = p->parent->dst->master_netdev;
 102
 103        dev_mc_unsync(master, dev);
 104        dev_uc_unsync(master, dev);
 105        if (dev->flags & IFF_ALLMULTI)
 106                dev_set_allmulti(master, -1);
 107        if (dev->flags & IFF_PROMISC)
 108                dev_set_promiscuity(master, -1);
 109
 110        if (compare_ether_addr(dev->dev_addr, master->dev_addr))
 111                dev_uc_del(master, dev->dev_addr);
 112
 113        return 0;
 114}
 115
 116static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
 117{
 118        struct dsa_slave_priv *p = netdev_priv(dev);
 119        struct net_device *master = p->parent->dst->master_netdev;
 120
 121        if (change & IFF_ALLMULTI)
 122                dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
 123        if (change & IFF_PROMISC)
 124                dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
 125}
 126
 127static void dsa_slave_set_rx_mode(struct net_device *dev)
 128{
 129        struct dsa_slave_priv *p = netdev_priv(dev);
 130        struct net_device *master = p->parent->dst->master_netdev;
 131
 132        dev_mc_sync(master, dev);
 133        dev_uc_sync(master, dev);
 134}
 135
 136static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
 137{
 138        struct dsa_slave_priv *p = netdev_priv(dev);
 139        struct net_device *master = p->parent->dst->master_netdev;
 140        struct sockaddr *addr = a;
 141        int err;
 142
 143        if (!is_valid_ether_addr(addr->sa_data))
 144                return -EADDRNOTAVAIL;
 145
 146        if (!(dev->flags & IFF_UP))
 147                goto out;
 148
 149        if (compare_ether_addr(addr->sa_data, master->dev_addr)) {
 150                err = dev_uc_add(master, addr->sa_data);
 151                if (err < 0)
 152                        return err;
 153        }
 154
 155        if (compare_ether_addr(dev->dev_addr, master->dev_addr))
 156                dev_uc_del(master, dev->dev_addr);
 157
 158out:
 159        memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
 160
 161        return 0;
 162}
 163
 164static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 165{
 166        struct dsa_slave_priv *p = netdev_priv(dev);
 167
 168        if (p->phy != NULL)
 169                return phy_mii_ioctl(p->phy, ifr, cmd);
 170
 171        return -EOPNOTSUPP;
 172}
 173
 174
 175/* ethtool operations *******************************************************/
 176static int
 177dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 178{
 179        struct dsa_slave_priv *p = netdev_priv(dev);
 180        int err;
 181
 182        err = -EOPNOTSUPP;
 183        if (p->phy != NULL) {
 184                err = phy_read_status(p->phy);
 185                if (err == 0)
 186                        err = phy_ethtool_gset(p->phy, cmd);
 187        }
 188
 189        return err;
 190}
 191
 192static int
 193dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 194{
 195        struct dsa_slave_priv *p = netdev_priv(dev);
 196
 197        if (p->phy != NULL)
 198                return phy_ethtool_sset(p->phy, cmd);
 199
 200        return -EOPNOTSUPP;
 201}
 202
 203static void dsa_slave_get_drvinfo(struct net_device *dev,
 204                                  struct ethtool_drvinfo *drvinfo)
 205{
 206        strncpy(drvinfo->driver, "dsa", 32);
 207        strncpy(drvinfo->version, dsa_driver_version, 32);
 208        strncpy(drvinfo->fw_version, "N/A", 32);
 209        strncpy(drvinfo->bus_info, "platform", 32);
 210}
 211
 212static int dsa_slave_nway_reset(struct net_device *dev)
 213{
 214        struct dsa_slave_priv *p = netdev_priv(dev);
 215
 216        if (p->phy != NULL)
 217                return genphy_restart_aneg(p->phy);
 218
 219        return -EOPNOTSUPP;
 220}
 221
 222static u32 dsa_slave_get_link(struct net_device *dev)
 223{
 224        struct dsa_slave_priv *p = netdev_priv(dev);
 225
 226        if (p->phy != NULL) {
 227                genphy_update_link(p->phy);
 228                return p->phy->link;
 229        }
 230
 231        return -EOPNOTSUPP;
 232}
 233
 234static void dsa_slave_get_strings(struct net_device *dev,
 235                                  uint32_t stringset, uint8_t *data)
 236{
 237        struct dsa_slave_priv *p = netdev_priv(dev);
 238        struct dsa_switch *ds = p->parent;
 239
 240        if (stringset == ETH_SS_STATS) {
 241                int len = ETH_GSTRING_LEN;
 242
 243                strncpy(data, "tx_packets", len);
 244                strncpy(data + len, "tx_bytes", len);
 245                strncpy(data + 2 * len, "rx_packets", len);
 246                strncpy(data + 3 * len, "rx_bytes", len);
 247                if (ds->drv->get_strings != NULL)
 248                        ds->drv->get_strings(ds, p->port, data + 4 * len);
 249        }
 250}
 251
 252static void dsa_slave_get_ethtool_stats(struct net_device *dev,
 253                                        struct ethtool_stats *stats,
 254                                        uint64_t *data)
 255{
 256        struct dsa_slave_priv *p = netdev_priv(dev);
 257        struct dsa_switch *ds = p->parent;
 258
 259        data[0] = p->dev->stats.tx_packets;
 260        data[1] = p->dev->stats.tx_bytes;
 261        data[2] = p->dev->stats.rx_packets;
 262        data[3] = p->dev->stats.rx_bytes;
 263        if (ds->drv->get_ethtool_stats != NULL)
 264                ds->drv->get_ethtool_stats(ds, p->port, data + 4);
 265}
 266
 267static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
 268{
 269        struct dsa_slave_priv *p = netdev_priv(dev);
 270        struct dsa_switch *ds = p->parent;
 271
 272        if (sset == ETH_SS_STATS) {
 273                int count;
 274
 275                count = 4;
 276                if (ds->drv->get_sset_count != NULL)
 277                        count += ds->drv->get_sset_count(ds);
 278
 279                return count;
 280        }
 281
 282        return -EOPNOTSUPP;
 283}
 284
 285static const struct ethtool_ops dsa_slave_ethtool_ops = {
 286        .get_settings           = dsa_slave_get_settings,
 287        .set_settings           = dsa_slave_set_settings,
 288        .get_drvinfo            = dsa_slave_get_drvinfo,
 289        .nway_reset             = dsa_slave_nway_reset,
 290        .get_link               = dsa_slave_get_link,
 291        .get_strings            = dsa_slave_get_strings,
 292        .get_ethtool_stats      = dsa_slave_get_ethtool_stats,
 293        .get_sset_count         = dsa_slave_get_sset_count,
 294};
 295
 296#ifdef CONFIG_NET_DSA_TAG_DSA
 297static const struct net_device_ops dsa_netdev_ops = {
 298        .ndo_init               = dsa_slave_init,
 299        .ndo_open               = dsa_slave_open,
 300        .ndo_stop               = dsa_slave_close,
 301        .ndo_start_xmit         = dsa_xmit,
 302        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 303        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 304        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 305        .ndo_do_ioctl           = dsa_slave_ioctl,
 306};
 307#endif
 308#ifdef CONFIG_NET_DSA_TAG_EDSA
 309static const struct net_device_ops edsa_netdev_ops = {
 310        .ndo_init               = dsa_slave_init,
 311        .ndo_open               = dsa_slave_open,
 312        .ndo_stop               = dsa_slave_close,
 313        .ndo_start_xmit         = edsa_xmit,
 314        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 315        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 316        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 317        .ndo_do_ioctl           = dsa_slave_ioctl,
 318};
 319#endif
 320#ifdef CONFIG_NET_DSA_TAG_TRAILER
 321static const struct net_device_ops trailer_netdev_ops = {
 322        .ndo_init               = dsa_slave_init,
 323        .ndo_open               = dsa_slave_open,
 324        .ndo_stop               = dsa_slave_close,
 325        .ndo_start_xmit         = trailer_xmit,
 326        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 327        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 328        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 329        .ndo_do_ioctl           = dsa_slave_ioctl,
 330};
 331#endif
 332
 333/* slave device setup *******************************************************/
 334struct net_device *
 335dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 336                 int port, char *name)
 337{
 338        struct net_device *master = ds->dst->master_netdev;
 339        struct net_device *slave_dev;
 340        struct dsa_slave_priv *p;
 341        int ret;
 342
 343        slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
 344                                 name, ether_setup);
 345        if (slave_dev == NULL)
 346                return slave_dev;
 347
 348        slave_dev->features = master->vlan_features;
 349        SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
 350        memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
 351        slave_dev->tx_queue_len = 0;
 352
 353        switch (ds->dst->tag_protocol) {
 354#ifdef CONFIG_NET_DSA_TAG_DSA
 355        case htons(ETH_P_DSA):
 356                slave_dev->netdev_ops = &dsa_netdev_ops;
 357                break;
 358#endif
 359#ifdef CONFIG_NET_DSA_TAG_EDSA
 360        case htons(ETH_P_EDSA):
 361                slave_dev->netdev_ops = &edsa_netdev_ops;
 362                break;
 363#endif
 364#ifdef CONFIG_NET_DSA_TAG_TRAILER
 365        case htons(ETH_P_TRAILER):
 366                slave_dev->netdev_ops = &trailer_netdev_ops;
 367                break;
 368#endif
 369        default:
 370                BUG();
 371        }
 372
 373        SET_NETDEV_DEV(slave_dev, parent);
 374        slave_dev->vlan_features = master->vlan_features;
 375
 376        p = netdev_priv(slave_dev);
 377        p->dev = slave_dev;
 378        p->parent = ds;
 379        p->port = port;
 380        p->phy = ds->slave_mii_bus->phy_map[port];
 381
 382        ret = register_netdev(slave_dev);
 383        if (ret) {
 384                printk(KERN_ERR "%s: error %d registering interface %s\n",
 385                                master->name, ret, slave_dev->name);
 386                free_netdev(slave_dev);
 387                return NULL;
 388        }
 389
 390        netif_carrier_off(slave_dev);
 391
 392        if (p->phy != NULL) {
 393                phy_attach(slave_dev, dev_name(&p->phy->dev),
 394                           0, PHY_INTERFACE_MODE_GMII);
 395
 396                p->phy->autoneg = AUTONEG_ENABLE;
 397                p->phy->speed = 0;
 398                p->phy->duplex = 0;
 399                p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
 400                phy_start_aneg(p->phy);
 401        }
 402
 403        return slave_dev;
 404}
 405