2386fbe9a34757d0de9e843f4aa34a9a4730546b
[openwrt/svn-archive/archive.git] / target / linux / brcm47xx / patches-3.10 / 205-b44-add-phylib-support.patch
1 From 31963d998d2984079dc4f4b36b7df170d85f6d66 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 6/9] 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 | 181 ++++++++++++++++++++++++++++++++-
25 drivers/net/ethernet/broadcom/b44.h | 4 +
26 3 files changed, 183 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->flags & B44_FLAG_EXTERNAL_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->flags & B44_FLAG_EXTERNAL_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->flags & B44_FLAG_EXTERNAL_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,20 @@ 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 + if (bp->flags & B44_FLAG_EXTERNAL_PHY) {
269 + err = b44_register_phy_one(bp);
270 + if (err) {
271 + dev_err(sdev->dev, "Cannot register PHY, aborting\n");
272 + goto err_out_unregister_netdev;
273 + }
274 + }
275 +
276 netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
277
278 return 0;
279
280 +err_out_unregister_netdev:
281 + unregister_netdev(dev);
282 err_out_powerdown:
283 ssb_bus_may_powerdown(sdev->bus);
284
285 @@ -2263,8 +2435,11 @@ out:
286 static void b44_remove_one(struct ssb_device *sdev)
287 {
288 struct net_device *dev = ssb_get_drvdata(sdev);
289 + struct b44 *bp = netdev_priv(dev);
290
291 unregister_netdev(dev);
292 + if (bp->flags & B44_FLAG_EXTERNAL_PHY)
293 + b44_unregister_phy_one(bp);
294 ssb_device_disable(sdev, 0);
295 ssb_bus_may_powerdown(sdev->bus);
296 free_netdev(dev);
297 --- a/drivers/net/ethernet/broadcom/b44.h
298 +++ b/drivers/net/ethernet/broadcom/b44.h
299 @@ -397,6 +397,10 @@ struct b44 {
300 u32 tx_pending;
301 u8 phy_addr;
302 u8 force_copybreak;
303 + struct phy_device *phydev;
304 + struct mii_bus *mii_bus;
305 + int old_link;
306 + int old_duplex;
307 struct mii_if_info mii_if;
308 };
309