surprise :p
[openwrt.git] / target / linux / ar71xx / files / drivers / net / ag71xx / ag71xx_mii.c
1 /*
2  *  Atheros AR71xx built-in ethernet mac driver
3  *
4  *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
5  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
6  *
7  *  Based on Atheros' AG7100 driver
8  *
9  *  This program is free software; you can redistribute it and/or modify it
10  *  under the terms of the GNU General Public License version 2 as published
11  *  by the Free Software Foundation.
12  */
13
14 #include "ag71xx.h"
15
16 #define AG71XX_MII_RETRY        1000
17 #define AG71XX_MII_DELAY        5
18
19 static inline void ag71xx_mii_ctrl_wr(struct ag71xx *ag, u32 value)
20 {
21         __raw_writel(value, ag->mii_ctrl);
22 }
23
24 static inline u32 ag71xx_mii_ctrl_rr(struct ag71xx *ag)
25 {
26         return __raw_readl(ag->mii_ctrl);
27 }
28
29 void ag71xx_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if)
30 {
31         u32 t;
32
33         t = ag71xx_mii_ctrl_rr(ag);
34         t &= ~(0x3);
35         t |= (mii_if & 0x3);
36         ag71xx_mii_ctrl_wr(ag, t);
37 }
38
39 void ag71xx_mii_ctrl_set_speed(struct ag71xx *ag, unsigned int speed)
40 {
41         u32 t;
42
43         t = ag71xx_mii_ctrl_rr(ag);
44         t &= ~(0x3 << 4);
45         t |= (speed & 0x3) << 4;
46         ag71xx_mii_ctrl_wr(ag, t);
47 }
48
49 static int ag71xx_mii_read(struct ag71xx *ag, int addr, int reg)
50 {
51         int ret;
52         int i;
53
54         ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
55         ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
56                         ((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
57         ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_READ);
58
59         i = AG71XX_MII_RETRY;
60         while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
61                 if (i-- == 0) {
62                         printk(KERN_ERR "%s: mii_read timed out\n",
63                                 ag->dev->name);
64                         ret = 0xffff;
65                         goto out;
66                 }
67                 udelay(AG71XX_MII_DELAY);
68         }
69
70         ret = ag71xx_rr(ag, AG71XX_REG_MII_STATUS) & 0xffff;
71         ag71xx_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
72
73         DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
74
75 out:
76         return ret;
77 }
78
79 static void ag71xx_mii_write(struct ag71xx *ag, int addr, int reg, u16 val)
80 {
81         int i;
82
83         DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
84
85         ag71xx_wr(ag, AG71XX_REG_MII_ADDR,
86                         ((addr & 0xff) << MII_ADDR_S) | (reg & 0xff));
87         ag71xx_wr(ag, AG71XX_REG_MII_CTRL, val);
88
89         i = AG71XX_MII_RETRY;
90         while (ag71xx_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
91                 if (i-- == 0) {
92                         printk(KERN_ERR "%s: mii_write timed out\n",
93                                 ag->dev->name);
94                         break;
95                 }
96                 udelay(AG71XX_MII_DELAY);
97         }
98 }
99
100 int ag71xx_mii_peek(struct ag71xx *ag)
101 {
102         int cnt;
103         int i;
104
105         cnt = 0;
106         for (i = 0; i < PHY_MAX_ADDR; i++) {
107                 u16 bmsr, id1, id2, bmcr, advert, lpa;
108
109                 bmsr = ag71xx_mii_read(ag, i, MII_BMSR);
110                 bmcr = ag71xx_mii_read(ag, i, MII_BMCR);
111                 id1 = ag71xx_mii_read(ag, i, MII_PHYSID1);
112                 id2 = ag71xx_mii_read(ag, i, MII_PHYSID2);
113                 advert = ag71xx_mii_read(ag, i, MII_ADVERTISE);
114                 lpa = ag71xx_mii_read(ag, i, MII_LPA);
115                 DBG("%s: phy%02d bmsr=%04x, bmcr=%04x, "
116                         "id=%04x.%04x, advertise=%04x, lpa=%04x\n",
117                         ag->dev->name, i,
118                         bmsr, bmcr, id1, id2, advert, lpa);
119
120                 if ((bmsr | bmcr | id1 | id2 | advert | lpa) != 0)
121                         cnt++;
122         }
123
124         return cnt;
125 }
126
127 #define PLL_SEC_CONFIG          0x18050004
128 #define PLL_ETH0_INT_CLOCK      0x18050010
129 #define PLL_ETH1_INT_CLOCK      0x18050014
130 #define PLL_ETH_EXT_CLOCK       0x18050018
131
132 #define ag7100_pll_shift(_ag)   (((_ag)->pdev->id) ? 19 : 17)
133 #define ag7100_pll_offset(_ag)  (((_ag)->pdev->id) ? PLL_ETH1_INT_CLOCK \
134                                                    : PLL_ETH0_INT_CLOCK)
135
136 static void ag71xx_set_pll(struct ag71xx *ag, u32 pll_val)
137 {
138         void __iomem *pll_reg = ioremap_nocache(ag7100_pll_offset(ag), 4);
139         void __iomem *pll_cfg = ioremap_nocache(PLL_SEC_CONFIG, 4);
140         u32 s;
141         u32 t;
142
143         s = ag7100_pll_shift(ag);
144
145         t = __raw_readl(pll_cfg);
146         t &= ~(3 << s);
147         t |=  (2 << s);
148         __raw_writel(t, pll_cfg);
149         udelay(100);
150
151         __raw_writel(pll_val, pll_reg);
152
153         t |= (3 << s);
154         __raw_writel(t, pll_cfg);
155         udelay(100);
156
157         t &= ~(3 << s);
158         __raw_writel(t, pll_cfg);
159         udelay(100);
160         DBG("%s: pll_reg %#x: %#x\n", ag->dev->name,
161                         (unsigned int)pll_reg, __raw_readl(pll_reg));
162
163         iounmap(pll_cfg);
164         iounmap(pll_reg);
165 }
166
167 static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
168 {
169         switch (ag->speed) {
170         case SPEED_1000:
171                 return "1000";
172         case SPEED_100:
173                 return "100";
174         case SPEED_10:
175                 return "10";
176         }
177
178         return "?";
179 }
180
181 #if 1
182 #define PLL_VAL_1000    0x00110000
183 #define PLL_VAL_100     0x00001099
184 #define PLL_VAL_10      0x00991099
185 #else
186 #define PLL_VAL_1000    0x01111000
187 #define PLL_VAL_100     0x09991000
188 #define PLL_VAL_10      0x09991999
189 #endif
190
191 void ag71xx_link_update(struct ag71xx *ag)
192 {
193         u32 cfg2;
194         u32 ifctl;
195         u32 pll;
196         u32 fifo5;
197         u32 mii_speed;
198
199         if (!ag->link) {
200                 netif_carrier_off(ag->dev);
201                 printk(KERN_INFO "%s: link down\n", ag->dev->name);
202                 return;
203         }
204
205         cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
206         cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
207         cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
208
209         ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
210         ifctl &= ~(MAC_IFCTL_SPEED);
211
212         fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
213         fifo5 &= ~FIFO_CFG5_BYTE_PER_CLK;
214
215         switch (ag->speed) {
216         case SPEED_1000:
217                 mii_speed =  MII_CTRL_SPEED_1000;
218                 cfg2 |= MAC_CFG2_IF_1000;
219                 pll = PLL_VAL_1000;
220                 fifo5 |= FIFO_CFG5_BYTE_PER_CLK;
221                 break;
222         case SPEED_100:
223                 mii_speed = MII_CTRL_SPEED_100;
224                 cfg2 |= MAC_CFG2_IF_10_100;
225                 ifctl |= MAC_IFCTL_SPEED;
226                 pll = PLL_VAL_100;
227                 break;
228         case SPEED_10:
229                 mii_speed = MII_CTRL_SPEED_10;
230                 cfg2 |= MAC_CFG2_IF_10_100;
231                 pll = PLL_VAL_10;
232                 break;
233         default:
234                 BUG();
235                 return;
236         }
237
238         ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);
239         ag71xx_set_pll(ag, pll);
240         ag71xx_mii_ctrl_set_speed(ag, mii_speed);
241
242         ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
243         ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
244         ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
245
246         netif_carrier_on(ag->dev);
247         printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
248                 ag->dev->name,
249                 ag71xx_speed_str(ag),
250                 (DUPLEX_FULL == ag->duplex) ? "Full" : "Half");
251
252         DBG("%s: fifo1=%#x, fifo2=%#x, fifo3=%#x, fifo4=%#x, fifo5=%#x\n",
253                 ag->dev->name,
254                 ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
255                 ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2),
256                 ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
257                 ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
258                 ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
259
260         DBG("%s: mac_cfg2=%#x, ifctl=%#x, mii_ctrl=%#x\n",
261                 ag->dev->name,
262                 ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
263                 ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
264                 ag71xx_mii_ctrl_rr(ag));
265 }
266
267 static void ag71xx_link_adjust(struct net_device *dev)
268 {
269         struct ag71xx *ag = netdev_priv(dev);
270         struct phy_device *phydev = ag->phy_dev;
271         unsigned long flags;
272         int status_change = 0;
273
274         spin_lock_irqsave(&ag->lock, flags);
275
276         if (phydev->link) {
277                 if (ag->duplex != phydev->duplex
278                     || ag->speed != phydev->speed) {
279                         status_change = 1;
280                 }
281         }
282
283         if (phydev->link != ag->link) {
284                 if (phydev->link)
285                         netif_schedule(dev);
286
287                 status_change = 1;
288         }
289
290         ag->link = phydev->link;
291         ag->duplex = phydev->duplex;
292         ag->speed = phydev->speed;
293
294         if (status_change)
295                 ag71xx_link_update(ag);
296
297         spin_unlock_irqrestore(&ag->lock, flags);
298 }
299
300 static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
301 {
302         struct ag71xx *ag = bus->priv;
303
304         return ag71xx_mii_read(ag, addr, reg);
305 }
306
307 static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
308 {
309         struct ag71xx *ag = bus->priv;
310
311         ag71xx_mii_write(ag, addr, reg, val);
312         return 0;
313 }
314
315 static int ag71xx_mdio_reset(struct mii_bus *bus)
316 {
317         /* TODO */
318         return 0;
319 }
320
321 static int ag71xx_mdio_probe(struct ag71xx *ag)
322 {
323         struct net_device *dev = ag->dev;
324         struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
325         struct phy_device *phydev = NULL;
326         int phy_count = 0;
327         int phy_addr;
328
329         for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
330                 if (!(pdata->phy_mask & (1 << phy_addr)))
331                         continue;
332
333                 if (ag->mii_bus.phy_map[phy_addr] == NULL)
334                         continue;
335
336                 DBG("%s: PHY found at %s, uid=%08x\n",
337                         dev->name,
338                         ag->mii_bus.phy_map[phy_addr]->dev.bus_id,
339                         ag->mii_bus.phy_map[phy_addr]->phy_id);
340
341                 if (phydev == NULL)
342                         phydev = ag->mii_bus.phy_map[phy_addr];
343
344                 phy_count++;
345         }
346
347         switch (phy_count) {
348         case 0:
349                 printk(KERN_ERR "%s: no PHY found\n", dev->name);
350                 return -ENODEV;
351         case 1:
352                 ag->phy_dev = phy_connect(dev, phydev->dev.bus_id,
353                         &ag71xx_link_adjust, 0, pdata->phy_if_mode);
354
355                 if (IS_ERR(ag->phy_dev)) {
356                         printk(KERN_ERR "%s: could not connect to PHY at %s\n",
357                                 dev->name, phydev->dev.bus_id);
358                         return PTR_ERR(ag->phy_dev);
359                 }
360
361                 /* mask with MAC supported features */
362                 phydev->supported &= (SUPPORTED_10baseT_Half
363                         | SUPPORTED_10baseT_Full
364                         | SUPPORTED_100baseT_Half
365                         | SUPPORTED_100baseT_Full
366                         | SUPPORTED_Autoneg
367                         | SUPPORTED_MII
368                         | SUPPORTED_TP);
369
370                 phydev->advertising = phydev->supported;
371
372                 printk(KERN_DEBUG "%s: connected to PHY at %s "
373                         "[uid=%08x, driver=%s]\n",
374                         dev->name, phydev->dev.bus_id,
375                         phydev->phy_id, phydev->drv->name);
376
377                 ag->link = 0;
378                 ag->speed = 0;
379                 ag->duplex = -1;
380                 break;
381         default:
382                 ag->phy_dev = NULL;
383                 printk(KERN_DEBUG "%s: connected to multiple PHYs (%d)\n",
384                         dev->name, phy_count);
385                 break;
386         }
387
388         return 0;
389 }
390
391 int ag71xx_mdio_init(struct ag71xx *ag, int id)
392 {
393         int err;
394         int i;
395
396         ag->mii_bus.name = "ag71xx_mii";
397         ag->mii_bus.read = ag71xx_mdio_read;
398         ag->mii_bus.write = ag71xx_mdio_write;
399         ag->mii_bus.reset = ag71xx_mdio_reset;
400         ag->mii_bus.id = id;
401         ag->mii_bus.priv = ag;
402         ag->mii_bus.dev = &ag->dev->dev;
403
404         ag->mii_bus.irq = kmalloc(sizeof(*ag->mii_bus.irq) * PHY_MAX_ADDR,
405                                   GFP_KERNEL);
406         if (!ag->mii_bus.irq) {
407                 err = -ENOMEM;
408                 goto err_out;
409         }
410
411         for (i = 0; i < PHY_MAX_ADDR; i++)
412                 ag->mii_bus.irq[i] = PHY_POLL;
413
414         err = mdiobus_register(&ag->mii_bus);
415         if (err)
416                 goto err_free_irqs;
417
418         err = ag71xx_mdio_probe(ag);
419         if (err)
420                 goto err_unregister_bus;
421
422         return 0;
423
424 err_unregister_bus:
425         mdiobus_unregister(&ag->mii_bus);
426 err_free_irqs:
427         kfree(ag->mii_bus.irq);
428 err_out:
429         return err;
430 }
431
432 void ag71xx_mdio_cleanup(struct ag71xx *ag)
433 {
434         mdiobus_unregister(&ag->mii_bus);
435         kfree(ag->mii_bus.irq);
436 }