xref: /openbmc/linux/net/dsa/slave.c (revision cf85d08fdf4548ee46657ccfb7f9949a85145db5)
191da11f8SLennert Buytenhek /*
291da11f8SLennert Buytenhek  * net/dsa/slave.c - Slave device handling
391da11f8SLennert Buytenhek  * Copyright (c) 2008 Marvell Semiconductor
491da11f8SLennert Buytenhek  *
591da11f8SLennert Buytenhek  * This program is free software; you can redistribute it and/or modify
691da11f8SLennert Buytenhek  * it under the terms of the GNU General Public License as published by
791da11f8SLennert Buytenhek  * the Free Software Foundation; either version 2 of the License, or
891da11f8SLennert Buytenhek  * (at your option) any later version.
991da11f8SLennert Buytenhek  */
1091da11f8SLennert Buytenhek 
1191da11f8SLennert Buytenhek #include <linux/list.h>
1291da11f8SLennert Buytenhek #include <linux/netdevice.h>
1391da11f8SLennert Buytenhek #include <linux/phy.h>
1491da11f8SLennert Buytenhek #include "dsa_priv.h"
1591da11f8SLennert Buytenhek 
1691da11f8SLennert Buytenhek /* slave mii_bus handling ***************************************************/
1791da11f8SLennert Buytenhek static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
1891da11f8SLennert Buytenhek {
1991da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
2091da11f8SLennert Buytenhek 
2191da11f8SLennert Buytenhek 	if (ds->valid_port_mask & (1 << addr))
2291da11f8SLennert Buytenhek 		return ds->drv->phy_read(ds, addr, reg);
2391da11f8SLennert Buytenhek 
2491da11f8SLennert Buytenhek 	return 0xffff;
2591da11f8SLennert Buytenhek }
2691da11f8SLennert Buytenhek 
2791da11f8SLennert Buytenhek static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
2891da11f8SLennert Buytenhek {
2991da11f8SLennert Buytenhek 	struct dsa_switch *ds = bus->priv;
3091da11f8SLennert Buytenhek 
3191da11f8SLennert Buytenhek 	if (ds->valid_port_mask & (1 << addr))
3291da11f8SLennert Buytenhek 		return ds->drv->phy_write(ds, addr, reg, val);
3391da11f8SLennert Buytenhek 
3491da11f8SLennert Buytenhek 	return 0;
3591da11f8SLennert Buytenhek }
3691da11f8SLennert Buytenhek 
3791da11f8SLennert Buytenhek void dsa_slave_mii_bus_init(struct dsa_switch *ds)
3891da11f8SLennert Buytenhek {
3991da11f8SLennert Buytenhek 	ds->slave_mii_bus->priv = (void *)ds;
4091da11f8SLennert Buytenhek 	ds->slave_mii_bus->name = "dsa slave smi";
4191da11f8SLennert Buytenhek 	ds->slave_mii_bus->read = dsa_slave_phy_read;
4291da11f8SLennert Buytenhek 	ds->slave_mii_bus->write = dsa_slave_phy_write;
4391da11f8SLennert Buytenhek 	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x",
4491da11f8SLennert Buytenhek 			ds->master_mii_bus->id, ds->pd->sw_addr);
4591da11f8SLennert Buytenhek 	ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev);
4691da11f8SLennert Buytenhek }
4791da11f8SLennert Buytenhek 
4891da11f8SLennert Buytenhek 
4991da11f8SLennert Buytenhek /* slave device handling ****************************************************/
5091da11f8SLennert Buytenhek static int dsa_slave_open(struct net_device *dev)
5191da11f8SLennert Buytenhek {
5291da11f8SLennert Buytenhek 	return 0;
5391da11f8SLennert Buytenhek }
5491da11f8SLennert Buytenhek 
5591da11f8SLennert Buytenhek static int dsa_slave_close(struct net_device *dev)
5691da11f8SLennert Buytenhek {
5791da11f8SLennert Buytenhek 	return 0;
5891da11f8SLennert Buytenhek }
5991da11f8SLennert Buytenhek 
6091da11f8SLennert Buytenhek static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
6191da11f8SLennert Buytenhek {
6291da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
6391da11f8SLennert Buytenhek 	struct net_device *master = p->parent->master_netdev;
6491da11f8SLennert Buytenhek 
6591da11f8SLennert Buytenhek 	if (change & IFF_ALLMULTI)
6691da11f8SLennert Buytenhek 		dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
6791da11f8SLennert Buytenhek 	if (change & IFF_PROMISC)
6891da11f8SLennert Buytenhek 		dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
6991da11f8SLennert Buytenhek }
7091da11f8SLennert Buytenhek 
7191da11f8SLennert Buytenhek static void dsa_slave_set_rx_mode(struct net_device *dev)
7291da11f8SLennert Buytenhek {
7391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
7491da11f8SLennert Buytenhek 	struct net_device *master = p->parent->master_netdev;
7591da11f8SLennert Buytenhek 
7691da11f8SLennert Buytenhek 	dev_mc_sync(master, dev);
7791da11f8SLennert Buytenhek 	dev_unicast_sync(master, dev);
7891da11f8SLennert Buytenhek }
7991da11f8SLennert Buytenhek 
8091da11f8SLennert Buytenhek static int dsa_slave_set_mac_address(struct net_device *dev, void *addr)
8191da11f8SLennert Buytenhek {
8291da11f8SLennert Buytenhek 	memcpy(dev->dev_addr, addr + 2, 6);
8391da11f8SLennert Buytenhek 
8491da11f8SLennert Buytenhek 	return 0;
8591da11f8SLennert Buytenhek }
8691da11f8SLennert Buytenhek 
8791da11f8SLennert Buytenhek static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
8891da11f8SLennert Buytenhek {
8991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
9091da11f8SLennert Buytenhek 	struct mii_ioctl_data *mii_data = if_mii(ifr);
9191da11f8SLennert Buytenhek 
9291da11f8SLennert Buytenhek 	if (p->phy != NULL)
9391da11f8SLennert Buytenhek 		return phy_mii_ioctl(p->phy, mii_data, cmd);
9491da11f8SLennert Buytenhek 
9591da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
9691da11f8SLennert Buytenhek }
9791da11f8SLennert Buytenhek 
9891da11f8SLennert Buytenhek 
9991da11f8SLennert Buytenhek /* ethtool operations *******************************************************/
10091da11f8SLennert Buytenhek static int
10191da11f8SLennert Buytenhek dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
10291da11f8SLennert Buytenhek {
10391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
10491da11f8SLennert Buytenhek 	int err;
10591da11f8SLennert Buytenhek 
10691da11f8SLennert Buytenhek 	err = -EOPNOTSUPP;
10791da11f8SLennert Buytenhek 	if (p->phy != NULL) {
10891da11f8SLennert Buytenhek 		err = phy_read_status(p->phy);
10991da11f8SLennert Buytenhek 		if (err == 0)
11091da11f8SLennert Buytenhek 			err = phy_ethtool_gset(p->phy, cmd);
11191da11f8SLennert Buytenhek 	}
11291da11f8SLennert Buytenhek 
11391da11f8SLennert Buytenhek 	return err;
11491da11f8SLennert Buytenhek }
11591da11f8SLennert Buytenhek 
11691da11f8SLennert Buytenhek static int
11791da11f8SLennert Buytenhek dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
11891da11f8SLennert Buytenhek {
11991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
12091da11f8SLennert Buytenhek 
12191da11f8SLennert Buytenhek 	if (p->phy != NULL)
12291da11f8SLennert Buytenhek 		return phy_ethtool_sset(p->phy, cmd);
12391da11f8SLennert Buytenhek 
12491da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
12591da11f8SLennert Buytenhek }
12691da11f8SLennert Buytenhek 
12791da11f8SLennert Buytenhek static void dsa_slave_get_drvinfo(struct net_device *dev,
12891da11f8SLennert Buytenhek 				  struct ethtool_drvinfo *drvinfo)
12991da11f8SLennert Buytenhek {
13091da11f8SLennert Buytenhek 	strncpy(drvinfo->driver, "dsa", 32);
13191da11f8SLennert Buytenhek 	strncpy(drvinfo->version, dsa_driver_version, 32);
13291da11f8SLennert Buytenhek 	strncpy(drvinfo->fw_version, "N/A", 32);
13391da11f8SLennert Buytenhek 	strncpy(drvinfo->bus_info, "platform", 32);
13491da11f8SLennert Buytenhek }
13591da11f8SLennert Buytenhek 
13691da11f8SLennert Buytenhek static int dsa_slave_nway_reset(struct net_device *dev)
13791da11f8SLennert Buytenhek {
13891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
13991da11f8SLennert Buytenhek 
14091da11f8SLennert Buytenhek 	if (p->phy != NULL)
14191da11f8SLennert Buytenhek 		return genphy_restart_aneg(p->phy);
14291da11f8SLennert Buytenhek 
14391da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
14491da11f8SLennert Buytenhek }
14591da11f8SLennert Buytenhek 
14691da11f8SLennert Buytenhek static u32 dsa_slave_get_link(struct net_device *dev)
14791da11f8SLennert Buytenhek {
14891da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
14991da11f8SLennert Buytenhek 
15091da11f8SLennert Buytenhek 	if (p->phy != NULL) {
15191da11f8SLennert Buytenhek 		genphy_update_link(p->phy);
15291da11f8SLennert Buytenhek 		return p->phy->link;
15391da11f8SLennert Buytenhek 	}
15491da11f8SLennert Buytenhek 
15591da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
15691da11f8SLennert Buytenhek }
15791da11f8SLennert Buytenhek 
15891da11f8SLennert Buytenhek static void dsa_slave_get_strings(struct net_device *dev,
15991da11f8SLennert Buytenhek 				  uint32_t stringset, uint8_t *data)
16091da11f8SLennert Buytenhek {
16191da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
16291da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
16391da11f8SLennert Buytenhek 
16491da11f8SLennert Buytenhek 	if (stringset == ETH_SS_STATS) {
16591da11f8SLennert Buytenhek 		int len = ETH_GSTRING_LEN;
16691da11f8SLennert Buytenhek 
16791da11f8SLennert Buytenhek 		strncpy(data, "tx_packets", len);
16891da11f8SLennert Buytenhek 		strncpy(data + len, "tx_bytes", len);
16991da11f8SLennert Buytenhek 		strncpy(data + 2 * len, "rx_packets", len);
17091da11f8SLennert Buytenhek 		strncpy(data + 3 * len, "rx_bytes", len);
17191da11f8SLennert Buytenhek 		if (ds->drv->get_strings != NULL)
17291da11f8SLennert Buytenhek 			ds->drv->get_strings(ds, p->port, data + 4 * len);
17391da11f8SLennert Buytenhek 	}
17491da11f8SLennert Buytenhek }
17591da11f8SLennert Buytenhek 
17691da11f8SLennert Buytenhek static void dsa_slave_get_ethtool_stats(struct net_device *dev,
17791da11f8SLennert Buytenhek 					struct ethtool_stats *stats,
17891da11f8SLennert Buytenhek 					uint64_t *data)
17991da11f8SLennert Buytenhek {
18091da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
18191da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
18291da11f8SLennert Buytenhek 
18391da11f8SLennert Buytenhek 	data[0] = p->dev->stats.tx_packets;
18491da11f8SLennert Buytenhek 	data[1] = p->dev->stats.tx_bytes;
18591da11f8SLennert Buytenhek 	data[2] = p->dev->stats.rx_packets;
18691da11f8SLennert Buytenhek 	data[3] = p->dev->stats.rx_bytes;
18791da11f8SLennert Buytenhek 	if (ds->drv->get_ethtool_stats != NULL)
18891da11f8SLennert Buytenhek 		ds->drv->get_ethtool_stats(ds, p->port, data + 4);
18991da11f8SLennert Buytenhek }
19091da11f8SLennert Buytenhek 
19191da11f8SLennert Buytenhek static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
19291da11f8SLennert Buytenhek {
19391da11f8SLennert Buytenhek 	struct dsa_slave_priv *p = netdev_priv(dev);
19491da11f8SLennert Buytenhek 	struct dsa_switch *ds = p->parent;
19591da11f8SLennert Buytenhek 
19691da11f8SLennert Buytenhek 	if (sset == ETH_SS_STATS) {
19791da11f8SLennert Buytenhek 		int count;
19891da11f8SLennert Buytenhek 
19991da11f8SLennert Buytenhek 		count = 4;
20091da11f8SLennert Buytenhek 		if (ds->drv->get_sset_count != NULL)
20191da11f8SLennert Buytenhek 			count += ds->drv->get_sset_count(ds);
20291da11f8SLennert Buytenhek 
20391da11f8SLennert Buytenhek 		return count;
20491da11f8SLennert Buytenhek 	}
20591da11f8SLennert Buytenhek 
20691da11f8SLennert Buytenhek 	return -EOPNOTSUPP;
20791da11f8SLennert Buytenhek }
20891da11f8SLennert Buytenhek 
20991da11f8SLennert Buytenhek static const struct ethtool_ops dsa_slave_ethtool_ops = {
21091da11f8SLennert Buytenhek 	.get_settings		= dsa_slave_get_settings,
21191da11f8SLennert Buytenhek 	.set_settings		= dsa_slave_set_settings,
21291da11f8SLennert Buytenhek 	.get_drvinfo		= dsa_slave_get_drvinfo,
21391da11f8SLennert Buytenhek 	.nway_reset		= dsa_slave_nway_reset,
21491da11f8SLennert Buytenhek 	.get_link		= dsa_slave_get_link,
21591da11f8SLennert Buytenhek 	.set_sg			= ethtool_op_set_sg,
21691da11f8SLennert Buytenhek 	.get_strings		= dsa_slave_get_strings,
21791da11f8SLennert Buytenhek 	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
21891da11f8SLennert Buytenhek 	.get_sset_count		= dsa_slave_get_sset_count,
21991da11f8SLennert Buytenhek };
22091da11f8SLennert Buytenhek 
22191da11f8SLennert Buytenhek 
22291da11f8SLennert Buytenhek /* slave device setup *******************************************************/
22391da11f8SLennert Buytenhek struct net_device *
22491da11f8SLennert Buytenhek dsa_slave_create(struct dsa_switch *ds, struct device *parent,
22591da11f8SLennert Buytenhek 		 int port, char *name)
22691da11f8SLennert Buytenhek {
22791da11f8SLennert Buytenhek 	struct net_device *master = ds->master_netdev;
22891da11f8SLennert Buytenhek 	struct net_device *slave_dev;
22991da11f8SLennert Buytenhek 	struct dsa_slave_priv *p;
23091da11f8SLennert Buytenhek 	int ret;
23191da11f8SLennert Buytenhek 
23291da11f8SLennert Buytenhek 	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
23391da11f8SLennert Buytenhek 				 name, ether_setup);
23491da11f8SLennert Buytenhek 	if (slave_dev == NULL)
23591da11f8SLennert Buytenhek 		return slave_dev;
23691da11f8SLennert Buytenhek 
23791da11f8SLennert Buytenhek 	slave_dev->features = master->vlan_features;
23891da11f8SLennert Buytenhek 	SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
23991da11f8SLennert Buytenhek 	memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
24091da11f8SLennert Buytenhek 	slave_dev->tx_queue_len = 0;
24191da11f8SLennert Buytenhek 	switch (ds->tag_protocol) {
242*cf85d08fSLennert Buytenhek #ifdef CONFIG_NET_DSA_TAG_DSA
243*cf85d08fSLennert Buytenhek 	case htons(ETH_P_DSA):
244*cf85d08fSLennert Buytenhek 		slave_dev->hard_start_xmit = dsa_xmit;
245*cf85d08fSLennert Buytenhek 		break;
246*cf85d08fSLennert Buytenhek #endif
24791da11f8SLennert Buytenhek #ifdef CONFIG_NET_DSA_TAG_EDSA
24891da11f8SLennert Buytenhek 	case htons(ETH_P_EDSA):
24991da11f8SLennert Buytenhek 		slave_dev->hard_start_xmit = edsa_xmit;
25091da11f8SLennert Buytenhek 		break;
25191da11f8SLennert Buytenhek #endif
25291da11f8SLennert Buytenhek 	default:
25391da11f8SLennert Buytenhek 		BUG();
25491da11f8SLennert Buytenhek 	}
25591da11f8SLennert Buytenhek 	slave_dev->open = dsa_slave_open;
25691da11f8SLennert Buytenhek 	slave_dev->stop = dsa_slave_close;
25791da11f8SLennert Buytenhek 	slave_dev->change_rx_flags = dsa_slave_change_rx_flags;
25891da11f8SLennert Buytenhek 	slave_dev->set_rx_mode = dsa_slave_set_rx_mode;
25991da11f8SLennert Buytenhek 	slave_dev->set_multicast_list = dsa_slave_set_rx_mode;
26091da11f8SLennert Buytenhek 	slave_dev->set_mac_address = dsa_slave_set_mac_address;
26191da11f8SLennert Buytenhek 	slave_dev->do_ioctl = dsa_slave_ioctl;
26291da11f8SLennert Buytenhek 	SET_NETDEV_DEV(slave_dev, parent);
26391da11f8SLennert Buytenhek 	slave_dev->vlan_features = master->vlan_features;
26491da11f8SLennert Buytenhek 
26591da11f8SLennert Buytenhek 	p = netdev_priv(slave_dev);
26691da11f8SLennert Buytenhek 	p->dev = slave_dev;
26791da11f8SLennert Buytenhek 	p->parent = ds;
26891da11f8SLennert Buytenhek 	p->port = port;
26991da11f8SLennert Buytenhek 	p->phy = ds->slave_mii_bus->phy_map[port];
27091da11f8SLennert Buytenhek 
27191da11f8SLennert Buytenhek 	ret = register_netdev(slave_dev);
27291da11f8SLennert Buytenhek 	if (ret) {
27391da11f8SLennert Buytenhek 		printk(KERN_ERR "%s: error %d registering interface %s\n",
27491da11f8SLennert Buytenhek 				master->name, ret, slave_dev->name);
27591da11f8SLennert Buytenhek 		free_netdev(slave_dev);
27691da11f8SLennert Buytenhek 		return NULL;
27791da11f8SLennert Buytenhek 	}
27891da11f8SLennert Buytenhek 
27991da11f8SLennert Buytenhek 	netif_carrier_off(slave_dev);
28091da11f8SLennert Buytenhek 
28191da11f8SLennert Buytenhek 	if (p->phy != NULL) {
28291da11f8SLennert Buytenhek 		phy_attach(slave_dev, p->phy->dev.bus_id,
28391da11f8SLennert Buytenhek 			   0, PHY_INTERFACE_MODE_GMII);
28491da11f8SLennert Buytenhek 
28591da11f8SLennert Buytenhek 		p->phy->autoneg = AUTONEG_ENABLE;
28691da11f8SLennert Buytenhek 		p->phy->speed = 0;
28791da11f8SLennert Buytenhek 		p->phy->duplex = 0;
28891da11f8SLennert Buytenhek 		p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
28991da11f8SLennert Buytenhek 		phy_start_aneg(p->phy);
29091da11f8SLennert Buytenhek 	}
29191da11f8SLennert Buytenhek 
29291da11f8SLennert Buytenhek 	return slave_dev;
29391da11f8SLennert Buytenhek }
294