Merge branch 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / net / dsa / slave.c
1 /*
2  * net/dsa/slave.c - Slave device handling
3  * Copyright (c) 2008 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/phy.h>
14 #include "dsa_priv.h"
15
16 /* slave mii_bus handling ***************************************************/
17 static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
18 {
19         struct dsa_switch *ds = bus->priv;
20
21         if (ds->valid_port_mask & (1 << addr))
22                 return ds->drv->phy_read(ds, addr, reg);
23
24         return 0xffff;
25 }
26
27 static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
28 {
29         struct dsa_switch *ds = bus->priv;
30
31         if (ds->valid_port_mask & (1 << addr))
32                 return ds->drv->phy_write(ds, addr, reg, val);
33
34         return 0;
35 }
36
37 void dsa_slave_mii_bus_init(struct dsa_switch *ds)
38 {
39         ds->slave_mii_bus->priv = (void *)ds;
40         ds->slave_mii_bus->name = "dsa slave smi";
41         ds->slave_mii_bus->read = dsa_slave_phy_read;
42         ds->slave_mii_bus->write = dsa_slave_phy_write;
43         snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x",
44                         ds->master_mii_bus->id, ds->pd->sw_addr);
45         ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev);
46 }
47
48
49 /* slave device handling ****************************************************/
50 static int dsa_slave_open(struct net_device *dev)
51 {
52         return 0;
53 }
54
55 static int dsa_slave_close(struct net_device *dev)
56 {
57         return 0;
58 }
59
60 static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
61 {
62         struct dsa_slave_priv *p = netdev_priv(dev);
63         struct net_device *master = p->parent->master_netdev;
64
65         if (change & IFF_ALLMULTI)
66                 dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
67         if (change & IFF_PROMISC)
68                 dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
69 }
70
71 static void dsa_slave_set_rx_mode(struct net_device *dev)
72 {
73         struct dsa_slave_priv *p = netdev_priv(dev);
74         struct net_device *master = p->parent->master_netdev;
75
76         dev_mc_sync(master, dev);
77         dev_unicast_sync(master, dev);
78 }
79
80 static int dsa_slave_set_mac_address(struct net_device *dev, void *addr)
81 {
82         memcpy(dev->dev_addr, addr + 2, 6);
83
84         return 0;
85 }
86
87 static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
88 {
89         struct dsa_slave_priv *p = netdev_priv(dev);
90         struct mii_ioctl_data *mii_data = if_mii(ifr);
91
92         if (p->phy != NULL)
93                 return phy_mii_ioctl(p->phy, mii_data, cmd);
94
95         return -EOPNOTSUPP;
96 }
97
98
99 /* ethtool operations *******************************************************/
100 static int
101 dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
102 {
103         struct dsa_slave_priv *p = netdev_priv(dev);
104         int err;
105
106         err = -EOPNOTSUPP;
107         if (p->phy != NULL) {
108                 err = phy_read_status(p->phy);
109                 if (err == 0)
110                         err = phy_ethtool_gset(p->phy, cmd);
111         }
112
113         return err;
114 }
115
116 static int
117 dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
118 {
119         struct dsa_slave_priv *p = netdev_priv(dev);
120
121         if (p->phy != NULL)
122                 return phy_ethtool_sset(p->phy, cmd);
123
124         return -EOPNOTSUPP;
125 }
126
127 static void dsa_slave_get_drvinfo(struct net_device *dev,
128                                   struct ethtool_drvinfo *drvinfo)
129 {
130         strncpy(drvinfo->driver, "dsa", 32);
131         strncpy(drvinfo->version, dsa_driver_version, 32);
132         strncpy(drvinfo->fw_version, "N/A", 32);
133         strncpy(drvinfo->bus_info, "platform", 32);
134 }
135
136 static int dsa_slave_nway_reset(struct net_device *dev)
137 {
138         struct dsa_slave_priv *p = netdev_priv(dev);
139
140         if (p->phy != NULL)
141                 return genphy_restart_aneg(p->phy);
142
143         return -EOPNOTSUPP;
144 }
145
146 static u32 dsa_slave_get_link(struct net_device *dev)
147 {
148         struct dsa_slave_priv *p = netdev_priv(dev);
149
150         if (p->phy != NULL) {
151                 genphy_update_link(p->phy);
152                 return p->phy->link;
153         }
154
155         return -EOPNOTSUPP;
156 }
157
158 static void dsa_slave_get_strings(struct net_device *dev,
159                                   uint32_t stringset, uint8_t *data)
160 {
161         struct dsa_slave_priv *p = netdev_priv(dev);
162         struct dsa_switch *ds = p->parent;
163
164         if (stringset == ETH_SS_STATS) {
165                 int len = ETH_GSTRING_LEN;
166
167                 strncpy(data, "tx_packets", len);
168                 strncpy(data + len, "tx_bytes", len);
169                 strncpy(data + 2 * len, "rx_packets", len);
170                 strncpy(data + 3 * len, "rx_bytes", len);
171                 if (ds->drv->get_strings != NULL)
172                         ds->drv->get_strings(ds, p->port, data + 4 * len);
173         }
174 }
175
176 static void dsa_slave_get_ethtool_stats(struct net_device *dev,
177                                         struct ethtool_stats *stats,
178                                         uint64_t *data)
179 {
180         struct dsa_slave_priv *p = netdev_priv(dev);
181         struct dsa_switch *ds = p->parent;
182
183         data[0] = p->dev->stats.tx_packets;
184         data[1] = p->dev->stats.tx_bytes;
185         data[2] = p->dev->stats.rx_packets;
186         data[3] = p->dev->stats.rx_bytes;
187         if (ds->drv->get_ethtool_stats != NULL)
188                 ds->drv->get_ethtool_stats(ds, p->port, data + 4);
189 }
190
191 static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
192 {
193         struct dsa_slave_priv *p = netdev_priv(dev);
194         struct dsa_switch *ds = p->parent;
195
196         if (sset == ETH_SS_STATS) {
197                 int count;
198
199                 count = 4;
200                 if (ds->drv->get_sset_count != NULL)
201                         count += ds->drv->get_sset_count(ds);
202
203                 return count;
204         }
205
206         return -EOPNOTSUPP;
207 }
208
209 static const struct ethtool_ops dsa_slave_ethtool_ops = {
210         .get_settings           = dsa_slave_get_settings,
211         .set_settings           = dsa_slave_set_settings,
212         .get_drvinfo            = dsa_slave_get_drvinfo,
213         .nway_reset             = dsa_slave_nway_reset,
214         .get_link               = dsa_slave_get_link,
215         .set_sg                 = ethtool_op_set_sg,
216         .get_strings            = dsa_slave_get_strings,
217         .get_ethtool_stats      = dsa_slave_get_ethtool_stats,
218         .get_sset_count         = dsa_slave_get_sset_count,
219 };
220
221
222 /* slave device setup *******************************************************/
223 struct net_device *
224 dsa_slave_create(struct dsa_switch *ds, struct device *parent,
225                  int port, char *name)
226 {
227         struct net_device *master = ds->master_netdev;
228         struct net_device *slave_dev;
229         struct dsa_slave_priv *p;
230         int ret;
231
232         slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
233                                  name, ether_setup);
234         if (slave_dev == NULL)
235                 return slave_dev;
236
237         slave_dev->features = master->vlan_features;
238         SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
239         memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
240         slave_dev->tx_queue_len = 0;
241         switch (ds->tag_protocol) {
242 #ifdef CONFIG_NET_DSA_TAG_DSA
243         case htons(ETH_P_DSA):
244                 slave_dev->hard_start_xmit = dsa_xmit;
245                 break;
246 #endif
247 #ifdef CONFIG_NET_DSA_TAG_EDSA
248         case htons(ETH_P_EDSA):
249                 slave_dev->hard_start_xmit = edsa_xmit;
250                 break;
251 #endif
252 #ifdef CONFIG_NET_DSA_TAG_TRAILER
253         case htons(ETH_P_TRAILER):
254                 slave_dev->hard_start_xmit = trailer_xmit;
255                 break;
256 #endif
257         default:
258                 BUG();
259         }
260         slave_dev->open = dsa_slave_open;
261         slave_dev->stop = dsa_slave_close;
262         slave_dev->change_rx_flags = dsa_slave_change_rx_flags;
263         slave_dev->set_rx_mode = dsa_slave_set_rx_mode;
264         slave_dev->set_multicast_list = dsa_slave_set_rx_mode;
265         slave_dev->set_mac_address = dsa_slave_set_mac_address;
266         slave_dev->do_ioctl = dsa_slave_ioctl;
267         SET_NETDEV_DEV(slave_dev, parent);
268         slave_dev->vlan_features = master->vlan_features;
269
270         p = netdev_priv(slave_dev);
271         p->dev = slave_dev;
272         p->parent = ds;
273         p->port = port;
274         p->phy = ds->slave_mii_bus->phy_map[port];
275
276         ret = register_netdev(slave_dev);
277         if (ret) {
278                 printk(KERN_ERR "%s: error %d registering interface %s\n",
279                                 master->name, ret, slave_dev->name);
280                 free_netdev(slave_dev);
281                 return NULL;
282         }
283
284         netif_carrier_off(slave_dev);
285
286         if (p->phy != NULL) {
287                 phy_attach(slave_dev, p->phy->dev.bus_id,
288                            0, PHY_INTERFACE_MODE_GMII);
289
290                 p->phy->autoneg = AUTONEG_ENABLE;
291                 p->phy->speed = 0;
292                 p->phy->duplex = 0;
293                 p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
294                 phy_start_aneg(p->phy);
295         }
296
297         return slave_dev;
298 }