brcm47xx: bgmac: b44: add support for Byte Queue Limits
[openwrt.git] / target / linux / brcm47xx / patches-3.10 / 780-b44-phylib.patch
1 --- a/drivers/net/ethernet/broadcom/Kconfig
2 +++ b/drivers/net/ethernet/broadcom/Kconfig
3 @@ -24,6 +24,7 @@ config B44
4         select SSB
5         select NET_CORE
6         select MII
7 +       select PHYLIB
8         ---help---
9           If you have a network (Ethernet) controller of this type, say Y
10           or M and read the Ethernet-HOWTO, available from
11 --- a/drivers/net/ethernet/broadcom/b44.c
12 +++ b/drivers/net/ethernet/broadcom/b44.c
13 @@ -29,6 +29,7 @@
14  #include <linux/dma-mapping.h>
15  #include <linux/ssb/ssb.h>
16  #include <linux/slab.h>
17 +#include <linux/phy.h>
18  
19  #include <asm/uaccess.h>
20  #include <asm/io.h>
21 @@ -300,21 +301,23 @@ static inline int b44_writephy(struct b4
22  }
23  
24  /* miilib interface */
25 -static int b44_mii_read(struct net_device *dev, int phy_id, int location)
26 +static int b44_mii_read(struct mii_bus *bus, int phy_id, int location)
27  {
28         u32 val;
29 -       struct b44 *bp = netdev_priv(dev);
30 +       struct b44 *bp = bus->priv;
31         int rc = __b44_readphy(bp, phy_id, location, &val);
32         if (rc)
33                 return 0xffffffff;
34         return val;
35  }
36  
37 -static void b44_mii_write(struct net_device *dev, int phy_id, int location,
38 -                        int val)
39 +static int b44_mii_write(struct mii_bus *bus, int phy_id, int location,
40 +                        u16 val)
41  {
42 -       struct b44 *bp = netdev_priv(dev);
43 +       struct b44 *bp = bus->priv;
44         __b44_writephy(bp, phy_id, location, val);
45 +
46 +       return 0;
47  }
48  
49  static int b44_phy_reset(struct b44 *bp)
50 @@ -1831,102 +1834,24 @@ static int b44_get_settings(struct net_d
51  {
52         struct b44 *bp = netdev_priv(dev);
53  
54 -       cmd->supported = (SUPPORTED_Autoneg);
55 -       cmd->supported |= (SUPPORTED_100baseT_Half |
56 -                         SUPPORTED_100baseT_Full |
57 -                         SUPPORTED_10baseT_Half |
58 -                         SUPPORTED_10baseT_Full |
59 -                         SUPPORTED_MII);
60 -
61 -       cmd->advertising = 0;
62 -       if (bp->flags & B44_FLAG_ADV_10HALF)
63 -               cmd->advertising |= ADVERTISED_10baseT_Half;
64 -       if (bp->flags & B44_FLAG_ADV_10FULL)
65 -               cmd->advertising |= ADVERTISED_10baseT_Full;
66 -       if (bp->flags & B44_FLAG_ADV_100HALF)
67 -               cmd->advertising |= ADVERTISED_100baseT_Half;
68 -       if (bp->flags & B44_FLAG_ADV_100FULL)
69 -               cmd->advertising |= ADVERTISED_100baseT_Full;
70 -       cmd->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
71 -       ethtool_cmd_speed_set(cmd, ((bp->flags & B44_FLAG_100_BASE_T) ?
72 -                                   SPEED_100 : SPEED_10));
73 -       cmd->duplex = (bp->flags & B44_FLAG_FULL_DUPLEX) ?
74 -               DUPLEX_FULL : DUPLEX_HALF;
75 -       cmd->port = 0;
76 -       cmd->phy_address = bp->phy_addr;
77 -       cmd->transceiver = (bp->flags & B44_FLAG_INTERNAL_PHY) ?
78 -               XCVR_INTERNAL : XCVR_EXTERNAL;
79 -       cmd->autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ?
80 -               AUTONEG_DISABLE : AUTONEG_ENABLE;
81 -       if (cmd->autoneg == AUTONEG_ENABLE)
82 -               cmd->advertising |= ADVERTISED_Autoneg;
83 -       if (!netif_running(dev)){
84 -               ethtool_cmd_speed_set(cmd, 0);
85 -               cmd->duplex = 0xff;
86 -       }
87 -       cmd->maxtxpkt = 0;
88 -       cmd->maxrxpkt = 0;
89 -       return 0;
90 +       return phy_ethtool_gset(bp->phydev, cmd);
91  }
92  
93  static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
94  {
95         struct b44 *bp = netdev_priv(dev);
96 -       u32 speed = ethtool_cmd_speed(cmd);
97 -
98 -       /* We do not support gigabit. */
99 -       if (cmd->autoneg == AUTONEG_ENABLE) {
100 -               if (cmd->advertising &
101 -                   (ADVERTISED_1000baseT_Half |
102 -                    ADVERTISED_1000baseT_Full))
103 -                       return -EINVAL;
104 -       } else if ((speed != SPEED_100 &&
105 -                   speed != SPEED_10) ||
106 -                  (cmd->duplex != DUPLEX_HALF &&
107 -                   cmd->duplex != DUPLEX_FULL)) {
108 -                       return -EINVAL;
109 -       }
110 +       int ret;
111  
112         spin_lock_irq(&bp->lock);
113  
114 -       if (cmd->autoneg == AUTONEG_ENABLE) {
115 -               bp->flags &= ~(B44_FLAG_FORCE_LINK |
116 -                              B44_FLAG_100_BASE_T |
117 -                              B44_FLAG_FULL_DUPLEX |
118 -                              B44_FLAG_ADV_10HALF |
119 -                              B44_FLAG_ADV_10FULL |
120 -                              B44_FLAG_ADV_100HALF |
121 -                              B44_FLAG_ADV_100FULL);
122 -               if (cmd->advertising == 0) {
123 -                       bp->flags |= (B44_FLAG_ADV_10HALF |
124 -                                     B44_FLAG_ADV_10FULL |
125 -                                     B44_FLAG_ADV_100HALF |
126 -                                     B44_FLAG_ADV_100FULL);
127 -               } else {
128 -                       if (cmd->advertising & ADVERTISED_10baseT_Half)
129 -                               bp->flags |= B44_FLAG_ADV_10HALF;
130 -                       if (cmd->advertising & ADVERTISED_10baseT_Full)
131 -                               bp->flags |= B44_FLAG_ADV_10FULL;
132 -                       if (cmd->advertising & ADVERTISED_100baseT_Half)
133 -                               bp->flags |= B44_FLAG_ADV_100HALF;
134 -                       if (cmd->advertising & ADVERTISED_100baseT_Full)
135 -                               bp->flags |= B44_FLAG_ADV_100FULL;
136 -               }
137 -       } else {
138 -               bp->flags |= B44_FLAG_FORCE_LINK;
139 -               bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX);
140 -               if (speed == SPEED_100)
141 -                       bp->flags |= B44_FLAG_100_BASE_T;
142 -               if (cmd->duplex == DUPLEX_FULL)
143 -                       bp->flags |= B44_FLAG_FULL_DUPLEX;
144 -       }
145 -
146         if (netif_running(dev))
147                 b44_setup_phy(bp);
148  
149 +       ret = phy_ethtool_sset(bp->phydev, cmd);
150 +
151         spin_unlock_irq(&bp->lock);
152  
153 -       return 0;
154 +       return ret;
155  }
156  
157  static void b44_get_ringparam(struct net_device *dev,
158 @@ -2102,20 +2027,81 @@ static const struct ethtool_ops b44_etht
159  
160  static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
161  {
162 -       struct mii_ioctl_data *data = if_mii(ifr);
163         struct b44 *bp = netdev_priv(dev);
164         int err = -EINVAL;
165  
166         if (!netif_running(dev))
167                 goto out;
168  
169 +       if (!bp->phydev)
170 +               return -EINVAL;
171 +
172         spin_lock_irq(&bp->lock);
173 -       err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL);
174 +       err = phy_mii_ioctl(bp->phydev, ifr, cmd);
175         spin_unlock_irq(&bp->lock);
176  out:
177         return err;
178  }
179  
180 +static void b44_adjust_link(struct net_device *dev)
181 +{
182 +       struct b44 *bp = netdev_priv(dev);
183 +       struct phy_device *phydev = bp->phydev;
184 +       int status_changed = 0;
185 +
186 +       BUG_ON(!phydev);
187 +
188 +       if (bp->old_link != phydev->link) {
189 +               status_changed = 1;
190 +               bp->old_link = phydev->link;
191 +       }
192 +
193 +       /* reflect duplex change */
194 +       if (phydev->link && (bp->old_duplex != phydev->duplex)) {
195 +               status_changed = 1;
196 +               bp->old_duplex = phydev->duplex;
197 +       }
198 +
199 +       if (status_changed) {
200 +               pr_info("%s: link %s", dev->name, phydev->link ?
201 +                       "UP" : "DOWN");
202 +               if (phydev->link)
203 +                       pr_cont(" - %d/%s", phydev->speed,
204 +                       phydev->duplex == DUPLEX_FULL ? "full" : "half");
205 +               pr_cont("\n");
206 +       }
207 +}
208 +
209 +static int b44_mii_probe(struct net_device *dev)
210 +{
211 +       struct b44 *bp = netdev_priv(dev);
212 +       struct phy_device *phydev = NULL;
213 +       char phy_id[MII_BUS_ID_SIZE + 3];
214 +
215 +       /* connect to PHY */
216 +       snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
217 +                bp->mii_bus->id, bp->phy_addr);
218 +
219 +       phydev = phy_connect(dev, phy_id, &b44_adjust_link,
220 +                            PHY_INTERFACE_MODE_MII);
221 +       if (IS_ERR(phydev)) {
222 +               netdev_err(dev, "could not attach PHY: %s", phy_id);
223 +               bp->phy_addr = B44_PHY_ADDR_NO_PHY;
224 +               return PTR_ERR(phydev);
225 +       }
226 +
227 +       bp->phydev = phydev;
228 +       bp->old_link = 0;
229 +       bp->old_duplex = -1;
230 +       bp->phy_addr = phydev->addr;
231 +
232 +       netdev_info(dev, "attached PHY driver [%s] "
233 +               "(mii_bus:phy_addr=%s)\n",
234 +               phydev->drv->name, dev_name(&phydev->dev));
235 +
236 +       return 0;
237 +}
238 +
239  static int b44_get_invariants(struct b44 *bp)
240  {
241         struct ssb_device *sdev = bp->sdev;
242 @@ -2235,12 +2221,38 @@ static int b44_init_one(struct ssb_devic
243                 goto err_out_powerdown;
244         }
245  
246 -       bp->mii_if.dev = dev;
247 -       bp->mii_if.mdio_read = b44_mii_read;
248 -       bp->mii_if.mdio_write = b44_mii_write;
249 -       bp->mii_if.phy_id = bp->phy_addr;
250 -       bp->mii_if.phy_id_mask = 0x1f;
251 -       bp->mii_if.reg_num_mask = 0x1f;
252 +       bp->mii_bus = mdiobus_alloc();
253 +       if (!bp->mii_bus) {
254 +               dev_err(sdev->dev, "mdiobus_alloc() failed\n");
255 +               err = -ENOMEM;
256 +               goto err_out_powerdown;
257 +       }
258 +
259 +       bp->mii_bus->priv = bp;
260 +       bp->mii_bus->read = b44_mii_read;
261 +       bp->mii_bus->write = b44_mii_write;
262 +       bp->mii_bus->name = "b44_eth_mii";
263 +       snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
264 +       bp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
265 +       if (!bp->mii_bus->irq) {
266 +               dev_err(sdev->dev, "mii_bus irq allocation failed\n");
267 +               err = -ENOMEM;
268 +               goto err_out_mdiobus;
269 +       }
270 +
271 +       memset(bp->mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
272 +
273 +       err = mdiobus_register(bp->mii_bus);
274 +       if (err) {
275 +               dev_err(sdev->dev, "failed to register MII bus\n");
276 +               goto err_out_mdiobus_irq;
277 +       }
278 +
279 +       err = b44_mii_probe(dev);
280 +       if (err) {
281 +               dev_err(sdev->dev, "failed to probe MII bus\n");
282 +               goto err_out_mdiobus_unregister;
283 +       }
284  
285         /* By default, advertise all speed/duplex settings. */
286         bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL |
287 @@ -2272,6 +2284,16 @@ static int b44_init_one(struct ssb_devic
288  
289         return 0;
290  
291 +
292 +err_out_mdiobus_unregister:
293 +       mdiobus_unregister(bp->mii_bus);
294 +
295 +err_out_mdiobus_irq:
296 +       kfree(bp->mii_bus->irq);
297 +
298 +err_out_mdiobus:
299 +       mdiobus_free(bp->mii_bus);
300 +
301  err_out_powerdown:
302         ssb_bus_may_powerdown(sdev->bus);
303  
304 @@ -2285,8 +2307,12 @@ out:
305  static void b44_remove_one(struct ssb_device *sdev)
306  {
307         struct net_device *dev = ssb_get_drvdata(sdev);
308 +       struct b44 *bp = netdev_priv(dev);
309  
310         unregister_netdev(dev);
311 +       mdiobus_unregister(bp->mii_bus);
312 +       kfree(bp->mii_bus->irq);
313 +       mdiobus_free(bp->mii_bus);
314         ssb_device_disable(sdev, 0);
315         ssb_bus_may_powerdown(sdev->bus);
316         free_netdev(dev);
317 --- a/drivers/net/ethernet/broadcom/b44.h
318 +++ b/drivers/net/ethernet/broadcom/b44.h
319 @@ -396,7 +396,10 @@ struct b44 {
320         u32                     tx_pending;
321         u8                      phy_addr;
322         u8                      force_copybreak;
323 -       struct mii_if_info      mii_if;
324 +       struct phy_device       *phydev;
325 +       struct mii_bus          *mii_bus;
326 +       int                     old_link;
327 +       int                     old_duplex;
328  };
329  
330  #endif /* _B44_H */