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_unicast_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_unicast_delete(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_unicast_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_unicast_delete(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_unicast_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_unicast_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_unicast_delete(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        struct mii_ioctl_data *mii_data = if_mii(ifr);
 168
 169        if (p->phy != NULL)
 170                return phy_mii_ioctl(p->phy, mii_data, cmd);
 171
 172        return -EOPNOTSUPP;
 173}
 174
 175
 176/* ethtool operations *******************************************************/
 177static int
 178dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 179{
 180        struct dsa_slave_priv *p = netdev_priv(dev);
 181        int err;
 182
 183        err = -EOPNOTSUPP;
 184        if (p->phy != NULL) {
 185                err = phy_read_status(p->phy);
 186                if (err == 0)
 187                        err = phy_ethtool_gset(p->phy, cmd);
 188        }
 189
 190        return err;
 191}
 192
 193static int
 194dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 195{
 196        struct dsa_slave_priv *p = netdev_priv(dev);
 197
 198        if (p->phy != NULL)
 199                return phy_ethtool_sset(p->phy, cmd);
 200
 201        return -EOPNOTSUPP;
 202}
 203
 204static void dsa_slave_get_drvinfo(struct net_device *dev,
 205                                  struct ethtool_drvinfo *drvinfo)
 206{
 207        strncpy(drvinfo->driver, "dsa", 32);
 208        strncpy(drvinfo->version, dsa_driver_version, 32);
 209        strncpy(drvinfo->fw_version, "N/A", 32);
 210        strncpy(drvinfo->bus_info, "platform", 32);
 211}
 212
 213static int dsa_slave_nway_reset(struct net_device *dev)
 214{
 215        struct dsa_slave_priv *p = netdev_priv(dev);
 216
 217        if (p->phy != NULL)
 218                return genphy_restart_aneg(p->phy);
 219
 220        return -EOPNOTSUPP;
 221}
 222
 223static u32 dsa_slave_get_link(struct net_device *dev)
 224{
 225        struct dsa_slave_priv *p = netdev_priv(dev);
 226
 227        if (p->phy != NULL) {
 228                genphy_update_link(p->phy);
 229                return p->phy->link;
 230        }
 231
 232        return -EOPNOTSUPP;
 233}
 234
 235static void dsa_slave_get_strings(struct net_device *dev,
 236                                  uint32_t stringset, uint8_t *data)
 237{
 238        struct dsa_slave_priv *p = netdev_priv(dev);
 239        struct dsa_switch *ds = p->parent;
 240
 241        if (stringset == ETH_SS_STATS) {
 242                int len = ETH_GSTRING_LEN;
 243
 244                strncpy(data, "tx_packets", len);
 245                strncpy(data + len, "tx_bytes", len);
 246                strncpy(data + 2 * len, "rx_packets", len);
 247                strncpy(data + 3 * len, "rx_bytes", len);
 248                if (ds->drv->get_strings != NULL)
 249                        ds->drv->get_strings(ds, p->port, data + 4 * len);
 250        }
 251}
 252
 253static void dsa_slave_get_ethtool_stats(struct net_device *dev,
 254                                        struct ethtool_stats *stats,
 255                                        uint64_t *data)
 256{
 257        struct dsa_slave_priv *p = netdev_priv(dev);
 258        struct dsa_switch *ds = p->parent;
 259
 260        data[0] = p->dev->stats.tx_packets;
 261        data[1] = p->dev->stats.tx_bytes;
 262        data[2] = p->dev->stats.rx_packets;
 263        data[3] = p->dev->stats.rx_bytes;
 264        if (ds->drv->get_ethtool_stats != NULL)
 265                ds->drv->get_ethtool_stats(ds, p->port, data + 4);
 266}
 267
 268static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
 269{
 270        struct dsa_slave_priv *p = netdev_priv(dev);
 271        struct dsa_switch *ds = p->parent;
 272
 273        if (sset == ETH_SS_STATS) {
 274                int count;
 275
 276                count = 4;
 277                if (ds->drv->get_sset_count != NULL)
 278                        count += ds->drv->get_sset_count(ds);
 279
 280                return count;
 281        }
 282
 283        return -EOPNOTSUPP;
 284}
 285
 286static const struct ethtool_ops dsa_slave_ethtool_ops = {
 287        .get_settings           = dsa_slave_get_settings,
 288        .set_settings           = dsa_slave_set_settings,
 289        .get_drvinfo            = dsa_slave_get_drvinfo,
 290        .nway_reset             = dsa_slave_nway_reset,
 291        .get_link               = dsa_slave_get_link,
 292        .set_sg                 = ethtool_op_set_sg,
 293        .get_strings            = dsa_slave_get_strings,
 294        .get_ethtool_stats      = dsa_slave_get_ethtool_stats,
 295        .get_sset_count         = dsa_slave_get_sset_count,
 296};
 297
 298#ifdef CONFIG_NET_DSA_TAG_DSA
 299static const struct net_device_ops dsa_netdev_ops = {
 300        .ndo_init               = dsa_slave_init,
 301        .ndo_open               = dsa_slave_open,
 302        .ndo_stop               = dsa_slave_close,
 303        .ndo_start_xmit         = dsa_xmit,
 304        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 305        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 306        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 307        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 308        .ndo_do_ioctl           = dsa_slave_ioctl,
 309};
 310#endif
 311#ifdef CONFIG_NET_DSA_TAG_EDSA
 312static const struct net_device_ops edsa_netdev_ops = {
 313        .ndo_init               = dsa_slave_init,
 314        .ndo_open               = dsa_slave_open,
 315        .ndo_stop               = dsa_slave_close,
 316        .ndo_start_xmit         = edsa_xmit,
 317        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 318        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 319        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 320        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 321        .ndo_do_ioctl           = dsa_slave_ioctl,
 322};
 323#endif
 324#ifdef CONFIG_NET_DSA_TAG_TRAILER
 325static const struct net_device_ops trailer_netdev_ops = {
 326        .ndo_init               = dsa_slave_init,
 327        .ndo_open               = dsa_slave_open,
 328        .ndo_stop               = dsa_slave_close,
 329        .ndo_start_xmit         = trailer_xmit,
 330        .ndo_change_rx_flags    = dsa_slave_change_rx_flags,
 331        .ndo_set_rx_mode        = dsa_slave_set_rx_mode,
 332        .ndo_set_multicast_list = dsa_slave_set_rx_mode,
 333        .ndo_set_mac_address    = dsa_slave_set_mac_address,
 334        .ndo_do_ioctl           = dsa_slave_ioctl,
 335};
 336#endif
 337
 338/* slave device setup *******************************************************/
 339struct net_device *
 340dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 341                 int port, char *name)
 342{
 343        struct net_device *master = ds->dst->master_netdev;
 344        struct net_device *slave_dev;
 345        struct dsa_slave_priv *p;
 346        int ret;
 347
 348        slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
 349                                 name, ether_setup);
 350        if (slave_dev == NULL)
 351                return slave_dev;
 352
 353        slave_dev->features = master->vlan_features;
 354        SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
 355        memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
 356        slave_dev->tx_queue_len = 0;
 357
 358        switch (ds->dst->tag_protocol) {
 359#ifdef CONFIG_NET_DSA_TAG_DSA
 360        case htons(ETH_P_DSA):
 361                slave_dev->netdev_ops = &dsa_netdev_ops;
 362                break;
 363#endif
 364#ifdef CONFIG_NET_DSA_TAG_EDSA
 365        case htons(ETH_P_EDSA):
 366                slave_dev->netdev_ops = &edsa_netdev_ops;
 367                break;
 368#endif
 369#ifdef CONFIG_NET_DSA_TAG_TRAILER
 370        case htons(ETH_P_TRAILER):
 371                slave_dev->netdev_ops = &trailer_netdev_ops;
 372                break;
 373#endif
 374        default:
 375                BUG();
 376        }
 377
 378        SET_NETDEV_DEV(slave_dev, parent);
 379        slave_dev->vlan_features = master->vlan_features;
 380
 381        p = netdev_priv(slave_dev);
 382        p->dev = slave_dev;
 383        p->parent = ds;
 384        p->port = port;
 385        p->phy = ds->slave_mii_bus->phy_map[port];
 386
 387        ret = register_netdev(slave_dev);
 388        if (ret) {
 389                printk(KERN_ERR "%s: error %d registering interface %s\n",
 390                                master->name, ret, slave_dev->name);
 391                free_netdev(slave_dev);
 392                return NULL;
 393        }
 394
 395        netif_carrier_off(slave_dev);
 396
 397        if (p->phy != NULL) {
 398                phy_attach(slave_dev, dev_name(&p->phy->dev),
 399                           0, PHY_INTERFACE_MODE_GMII);
 400
 401                p->phy->autoneg = AUTONEG_ENABLE;
 402                p->phy->speed = 0;
 403                p->phy->duplex = 0;
 404                p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
 405                phy_start_aneg(p->phy);
 406        }
 407
 408        return slave_dev;
 409}
 410