brcm47xx: fix detection of some boards
[openwrt.git] / target / linux / brcm47xx / patches-3.10 / 205-b44-add-phylib-support.patch
1 From 46e5460f446109565b3f4a0cb728171d74bce33b Mon Sep 17 00:00:00 2001
2 From: Hauke Mehrtens <hauke@hauke-m.de>
3 Date: Thu, 3 Oct 2013 22:07:11 +0200
4 Subject: [PATCH 5/6] b44: add phylib support
5
6 Most of the older home routers based on the Broadcom BCM47XX SoC series
7 are using a MAC that is supported by b44. On most of these routers not
8 the internal PHY of this MAC core is used, but a switch sometimes on an
9 external chip or integrated into the same SoC as the Ethernet core.
10 For this switch a special PHY driver is needed which should not be
11 integrated into b44 as the same switches are also used by other
12 Broadcom home networking SoCs which are using different Ethernet MAC
13 drivers. This was tested with the b53 switch driver which is currently
14 on its way to mainline.
15
16 With this patch we scan the mdio bus when the sprom or nvram says that
17 the PHY address is 30, if a PHY was found at this address b44 uses it.
18
19 This was tested with a BCM4704, BCM4712 and BCM5354.
20
21 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
22 ---
23  drivers/net/ethernet/broadcom/Kconfig |    1 +
24  drivers/net/ethernet/broadcom/b44.c   |  183 ++++++++++++++++++++++++++++++++-
25  drivers/net/ethernet/broadcom/b44.h   |    5 +
26  3 files changed, 186 insertions(+), 3 deletions(-)
27
28 --- a/drivers/net/ethernet/broadcom/Kconfig
29 +++ b/drivers/net/ethernet/broadcom/Kconfig
30 @@ -24,6 +24,7 @@ config B44
31         select SSB
32         select NET_CORE
33         select MII
34 +       select PHYLIB
35         ---help---
36           If you have a network (Ethernet) controller of this type, say Y
37           or M and read the Ethernet-HOWTO, available from
38 --- a/drivers/net/ethernet/broadcom/b44.c
39 +++ b/drivers/net/ethernet/broadcom/b44.c
40 @@ -6,6 +6,7 @@
41   * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org)
42   * Copyright (C) 2006 Broadcom Corporation.
43   * Copyright (C) 2007 Michael Buesch <m@bues.ch>
44 + * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
45   *
46   * Distribute under GPL.
47   */
48 @@ -29,6 +30,7 @@
49  #include <linux/dma-mapping.h>
50  #include <linux/ssb/ssb.h>
51  #include <linux/slab.h>
52 +#include <linux/phy.h>
53  
54  #include <asm/uaccess.h>
55  #include <asm/io.h>
56 @@ -316,6 +318,23 @@ static void b44_mdio_write_mii(struct ne
57         __b44_writephy(bp, phy_id, location, val);
58  }
59  
60 +static int b44_mdio_read_phylib(struct mii_bus *bus, int phy_id, int location)
61 +{
62 +       u32 val;
63 +       struct b44 *bp = bus->priv;
64 +       int rc = __b44_readphy(bp, phy_id, location, &val);
65 +       if (rc)
66 +               return 0xffffffff;
67 +       return val;
68 +}
69 +
70 +static int b44_mdio_write_phylib(struct mii_bus *bus, int phy_id, int location,
71 +                                u16 val)
72 +{
73 +       struct b44 *bp = bus->priv;
74 +       return __b44_writephy(bp, phy_id, location, val);
75 +}
76 +
77  static int b44_phy_reset(struct b44 *bp)
78  {
79         u32 val;
80 @@ -1805,6 +1824,11 @@ static int b44_get_settings(struct net_d
81  {
82         struct b44 *bp = netdev_priv(dev);
83  
84 +       if (bp->has_phy) {
85 +               BUG_ON(!bp->phydev);
86 +               return phy_ethtool_gset(bp->phydev, cmd);
87 +       }
88 +
89         cmd->supported = (SUPPORTED_Autoneg);
90         cmd->supported |= (SUPPORTED_100baseT_Half |
91                           SUPPORTED_100baseT_Full |
92 @@ -1846,7 +1870,23 @@ static int b44_get_settings(struct net_d
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 +       u32 speed;
98 +       int ret;
99 +
100 +       if (bp->has_phy) {
101 +               BUG_ON(!bp->phydev);
102 +               spin_lock_irq(&bp->lock);
103 +               if (netif_running(dev))
104 +                       b44_setup_phy(bp);
105 +
106 +               ret = phy_ethtool_sset(bp->phydev, cmd);
107 +
108 +               spin_unlock_irq(&bp->lock);
109 +
110 +               return ret;
111 +       }
112 +
113 +       speed = ethtool_cmd_speed(cmd);
114  
115         /* We do not support gigabit. */
116         if (cmd->autoneg == AUTONEG_ENABLE) {
117 @@ -2076,7 +2116,6 @@ static const struct ethtool_ops b44_etht
118  
119  static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
120  {
121 -       struct mii_ioctl_data *data = if_mii(ifr);
122         struct b44 *bp = netdev_priv(dev);
123         int err = -EINVAL;
124  
125 @@ -2084,7 +2123,12 @@ static int b44_ioctl(struct net_device *
126                 goto out;
127  
128         spin_lock_irq(&bp->lock);
129 -       err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL);
130 +       if (bp->has_phy) {
131 +               BUG_ON(bp->phydev);
132 +               err = phy_mii_ioctl(bp->phydev, ifr, cmd);
133 +       } else {
134 +               err = generic_mii_ioctl(&bp->mii_if, if_mii(ifr), cmd, NULL);
135 +       }
136         spin_unlock_irq(&bp->lock);
137  out:
138         return err;
139 @@ -2146,6 +2190,124 @@ static const struct net_device_ops b44_n
140  #endif
141  };
142  
143 +static void b44_adjust_link(struct net_device *dev)
144 +{
145 +       struct b44 *bp = netdev_priv(dev);
146 +       struct phy_device *phydev = bp->phydev;
147 +       bool status_changed = 0;
148 +
149 +       BUG_ON(!phydev);
150 +
151 +       if (bp->old_link != phydev->link) {
152 +               status_changed = 1;
153 +               bp->old_link = phydev->link;
154 +       }
155 +
156 +       /* reflect duplex change */
157 +       if (phydev->link && (bp->old_duplex != phydev->duplex)) {
158 +               status_changed = 1;
159 +               bp->old_duplex = phydev->duplex;
160 +       }
161 +
162 +       if (status_changed)
163 +               phy_print_status(phydev);
164 +}
165 +
166 +static int b44_register_phy_one(struct b44 *bp)
167 +{
168 +       struct mii_bus *mii_bus;
169 +       struct ssb_device *sdev = bp->sdev;
170 +       struct phy_device *phydev;
171 +       int err;
172 +
173 +       mii_bus = mdiobus_alloc();
174 +       if (!mii_bus) {
175 +               dev_err(sdev->dev, "mdiobus_alloc() failed\n");
176 +               err = -ENOMEM;
177 +               goto err_out;
178 +       }
179 +
180 +       mii_bus->priv = bp;
181 +       mii_bus->read = b44_mdio_read_phylib;
182 +       mii_bus->write = b44_mdio_write_phylib;
183 +       mii_bus->name = "b44_eth_mii";
184 +       mii_bus->parent = sdev->dev;
185 +       mii_bus->phy_mask = ~(1 << bp->phy_addr);
186 +       snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
187 +       mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
188 +       if (!mii_bus->irq) {
189 +               dev_err(sdev->dev, "mii_bus irq allocation failed\n");
190 +               err = -ENOMEM;
191 +               goto err_out_mdiobus;
192 +       }
193 +
194 +       memset(mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
195 +
196 +       bp->mii_bus = mii_bus;
197 +
198 +       err = mdiobus_register(mii_bus);
199 +       if (err) {
200 +               dev_err(sdev->dev, "failed to register MII bus\n");
201 +               goto err_out_mdiobus_irq;
202 +       }
203 +
204 +       phydev = bp->mii_bus->phy_map[bp->phy_addr];
205 +       if (!phydev) {
206 +               dev_err(sdev->dev, "could not find PHY at %i\n", bp->phy_addr);
207 +               err = -ENODEV;
208 +               goto err_out_mdiobus_unregister;
209 +       }
210 +
211 +       err = phy_connect_direct(bp->dev, phydev, &b44_adjust_link,
212 +                                PHY_INTERFACE_MODE_MII);
213 +       if (err < 0) {
214 +               dev_err(sdev->dev, "could not attach PHY at %i\n",
215 +                       bp->phy_addr);
216 +               goto err_out_mdiobus_unregister;
217 +       }
218 +
219 +       /* mask with MAC supported features */
220 +       phydev->supported &= (SUPPORTED_10baseT_Half |
221 +                             SUPPORTED_10baseT_Full |
222 +                             SUPPORTED_100baseT_Half |
223 +                             SUPPORTED_100baseT_Full |
224 +                             SUPPORTED_Autoneg |
225 +                             SUPPORTED_MII);
226 +       phydev->advertising = phydev->supported;
227 +
228 +       bp->phydev = phydev;
229 +       bp->old_link = 0;
230 +       bp->old_duplex = -1;
231 +       bp->phy_addr = phydev->addr;
232 +
233 +       dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
234 +                phydev->drv->name, dev_name(&phydev->dev));
235 +
236 +       return 0;
237 +
238 +err_out_mdiobus_unregister:
239 +       mdiobus_unregister(mii_bus);
240 +
241 +err_out_mdiobus_irq:
242 +       kfree(mii_bus->irq);
243 +
244 +err_out_mdiobus:
245 +       mdiobus_free(mii_bus);
246 +
247 +err_out:
248 +       return err;
249 +}
250 +
251 +static void b44_unregister_phy_one(struct b44 *bp)
252 +{
253 +       struct mii_bus *mii_bus = bp->mii_bus;
254 +
255 +       phy_disconnect(bp->phydev);
256 +       mdiobus_unregister(mii_bus);
257 +       kfree(mii_bus->irq);
258 +       mdiobus_free(mii_bus);
259 +}
260 +
261  static int b44_init_one(struct ssb_device *sdev,
262                         const struct ssb_device_id *ent)
263  {
264 @@ -2246,10 +2408,22 @@ static int b44_init_one(struct ssb_devic
265         if (b44_phy_reset(bp) < 0)
266                 bp->phy_addr = B44_PHY_ADDR_NO_LOACL_PHY;
267  
268 +       bp->has_phy = bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY;
269 +
270 +       if (bp->has_phy) {
271 +               err = b44_register_phy_one(bp);
272 +               if (err) {
273 +                       dev_err(sdev->dev, "Cannot register PHY, aborting\n");
274 +                       goto err_out_unregister_netdev;
275 +               }
276 +       }
277 +
278         netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
279  
280         return 0;
281  
282 +err_out_unregister_netdev:
283 +       unregister_netdev(dev);
284  err_out_powerdown:
285         ssb_bus_may_powerdown(sdev->bus);
286  
287 @@ -2263,8 +2437,11 @@ out:
288  static void b44_remove_one(struct ssb_device *sdev)
289  {
290         struct net_device *dev = ssb_get_drvdata(sdev);
291 +       struct b44 *bp = netdev_priv(dev);
292  
293         unregister_netdev(dev);
294 +       if (bp->has_phy)
295 +               b44_unregister_phy_one(bp);
296         ssb_device_disable(sdev, 0);
297         ssb_bus_may_powerdown(sdev->bus);
298         free_netdev(dev);
299 --- a/drivers/net/ethernet/broadcom/b44.h
300 +++ b/drivers/net/ethernet/broadcom/b44.h
301 @@ -397,6 +397,11 @@ struct b44 {
302         u32                     tx_pending;
303         u8                      phy_addr;
304         u8                      force_copybreak;
305 +       bool                    has_phy;
306 +       struct phy_device       *phydev;
307 +       struct mii_bus          *mii_bus;
308 +       int                     old_link;
309 +       int                     old_duplex;
310         struct mii_if_info      mii_if;
311  };
312