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