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        .set_sg                 = ethtool_op_set_sg,
 292        .get_strings            = dsa_slave_get_strings,
 293        .get_ethtool_stats      = dsa_slave_get_ethtool_stats,
 294        .get_sset_count         = dsa_slave_get_sset_count,
 295};
 296
 297#ifdef CONFIG_NET_DSA_TAG_DSA
 298static const struct net_device_ops dsa_netdev_ops = {
 299        .ndo_init               = dsa_slave_init,
 300        .ndo_open               = dsa_slave_open,
 301        .ndo_stop               = dsa_slave_close,
 302        .ndo_start_xmit         = dsa_xmit,
 303        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 304        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 305        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 306        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 307        .ndo_do_ioctl           = dsa_slave_ioctl,
 308};
 309#endif
 310#ifdef CONFIG_NET_DSA_TAG_EDSA
 311static const struct net_device_ops edsa_netdev_ops = {
 312        .ndo_init               = dsa_slave_init,
 313        .ndo_open               = dsa_slave_open,
 314        .ndo_stop               = dsa_slave_close,
 315        .ndo_start_xmit         = edsa_xmit,
 316        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 317        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 318        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 319        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 320        .ndo_do_ioctl           = dsa_slave_ioctl,
 321};
 322#endif
 323#ifdef CONFIG_NET_DSA_TAG_TRAILER
 324static const struct net_device_ops trailer_netdev_ops = {
 325        .ndo_init               = dsa_slave_init,
 326        .ndo_open               = dsa_slave_open,
 327        .ndo_stop               = dsa_slave_close,
 328        .ndo_start_xmit         = trailer_xmit,
 329        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 330        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 331        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 332        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 333        .ndo_do_ioctl           = dsa_slave_ioctl,
 334};
 335#endif
 336
 337/* slave device setup *******************************************************/
 338struct net_device *
 339dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 340                 int port, char *name)
 341{
 342        struct net_device *master = ds->dst->master_netdev;
 343        struct net_device *slave_dev;
 344        struct dsa_slave_priv *p;
 345        int ret;
 346
 347        slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
 348                                 name, ether_setup);
 349        if (slave_dev == NULL)
 350                return slave_dev;
 351
 352        slave_dev->features = master->vlan_features;
 353        SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
 354        memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
 355        slave_dev->tx_queue_len = 0;
 356
 357        switch (ds->dst->tag_protocol) {
 358#ifdef CONFIG_NET_DSA_TAG_DSA
 359        case htons(ETH_P_DSA):
 360                slave_dev->netdev_ops = &dsa_netdev_ops;
 361                break;
 362#endif
 363#ifdef CONFIG_NET_DSA_TAG_EDSA
 364        case htons(ETH_P_EDSA):
 365                slave_dev->netdev_ops = &edsa_netdev_ops;
 366                break;
 367#endif
 368#ifdef CONFIG_NET_DSA_TAG_TRAILER
 369        case htons(ETH_P_TRAILER):
 370                slave_dev->netdev_ops = &trailer_netdev_ops;
 371                break;
 372#endif
 373        default:
 374                BUG();
 375        }
 376
 377        SET_NETDEV_DEV(slave_dev, parent);
 378        slave_dev->vlan_features = master->vlan_features;
 379
 380        p = netdev_priv(slave_dev);
 381        p->dev = slave_dev;
 382        p->parent = ds;
 383        p->port = port;
 384        p->phy = ds->slave_mii_bus->phy_map[port];
 385
 386        ret = register_netdev(slave_dev);
 387        if (ret) {
 388                printk(KERN_ERR "%s: error %d registering interface %s\n",
 389                                master->name, ret, slave_dev->name);
 390                free_netdev(slave_dev);
 391                return NULL;
 392        }
 393
 394        netif_carrier_off(slave_dev);
 395
 396        if (p->phy != NULL) {
 397                phy_attach(slave_dev, dev_name(&p->phy->dev),
 398                           0, PHY_INTERFACE_MODE_GMII);
 399
 400                p->phy->autoneg = AUTONEG_ENABLE;
 401                p->phy->speed = 0;
 402                p->phy->duplex = 0;
 403                p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
 404                phy_start_aneg(p->phy);
 405        }
 406
 407        return slave_dev;
 408}
 409