[ar71xx] ag71xx driver: minor cleanup
[openwrt.git] / target / linux / ar71xx / files / drivers / net / ag71xx / ag71xx_mdio.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_MDIO_RETRY       1000
17 #define AG71XX_MDIO_DELAY       5
18
19 struct ag71xx_mdio *ag71xx_mdio_bus;
20
21 static inline void ag71xx_mdio_wr(struct ag71xx_mdio *am, unsigned reg,
22                                   u32 value)
23 {
24         __raw_writel(value, am->mdio_base + reg - AG71XX_REG_MII_CFG);
25 }
26
27 static inline u32 ag71xx_mdio_rr(struct ag71xx_mdio *am, unsigned reg)
28 {
29         return __raw_readl(am->mdio_base + reg - AG71XX_REG_MII_CFG);
30 }
31
32 static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am)
33 {
34         DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n",
35                 am->mii_bus.name,
36                 ag71xx_mdio_rr(am, AG71XX_REG_MII_CFG),
37                 ag71xx_mdio_rr(am, AG71XX_REG_MII_CMD),
38                 ag71xx_mdio_rr(am, AG71XX_REG_MII_ADDR));
39         DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n",
40                 am->mii_bus.name,
41                 ag71xx_mdio_rr(am, AG71XX_REG_MII_CTRL),
42                 ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS),
43                 ag71xx_mdio_rr(am, AG71XX_REG_MII_IND));
44 }
45
46 static int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg)
47 {
48         int ret;
49         int i;
50
51         ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
52         ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
53                         ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
54         ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ);
55
56         i = AG71XX_MDIO_RETRY;
57         while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
58                 if (i-- == 0) {
59                         printk(KERN_ERR "%s: mii_read timed out\n",
60                                 am->mii_bus.name);
61                         ret = 0xffff;
62                         goto out;
63                 }
64                 udelay(AG71XX_MDIO_DELAY);
65         }
66
67         ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff;
68         ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
69
70         DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
71
72  out:
73         return ret;
74 }
75
76 static void ag71xx_mdio_mii_write(struct ag71xx_mdio *am,
77                                   int addr, int reg, u16 val)
78 {
79         int i;
80
81         DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
82
83         ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
84                         ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
85         ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val);
86
87         i = AG71XX_MDIO_RETRY;
88         while (ag71xx_mdio_rr(am, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
89                 if (i-- == 0) {
90                         printk(KERN_ERR "%s: mii_write timed out\n",
91                                 am->mii_bus.name);
92                         break;
93                 }
94                 udelay(AG71XX_MDIO_DELAY);
95         }
96 }
97
98 static int ag71xx_mdio_reset(struct mii_bus *bus)
99 {
100         struct ag71xx_mdio *am = bus->priv;
101
102         ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_RESET);
103         udelay(100);
104
105         ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_28);
106         udelay(100);
107
108         return 0;
109 }
110
111 static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
112 {
113         struct ag71xx_mdio *am = bus->priv;
114
115         return ag71xx_mdio_mii_read(am, addr, reg);
116 }
117
118 static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
119 {
120         struct ag71xx_mdio *am = bus->priv;
121
122         ag71xx_mdio_mii_write(am, addr, reg, val);
123         return 0;
124 }
125
126 static int __init ag71xx_mdio_probe(struct platform_device *pdev)
127 {
128         struct ag71xx_mdio_platform_data *pdata;
129         struct ag71xx_mdio *am;
130         struct resource *res;
131         int i;
132         int err;
133
134         if (ag71xx_mdio_bus)
135                 return -EBUSY;
136
137         am = kzalloc(sizeof(*am), GFP_KERNEL);
138         if (!am) {
139                 err = -ENOMEM;
140                 goto err_out;
141         }
142
143         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
144         if (!res) {
145                 dev_err(&pdev->dev, "no iomem resource found\n");
146                 err = -ENXIO;
147                 goto err_out;
148         }
149
150         am->mdio_base = ioremap_nocache(res->start, res->end - res->start + 1);
151         if (!am->mdio_base) {
152                 dev_err(&pdev->dev, "unable to ioremap registers\n");
153                 err = -ENOMEM;
154                 goto err_free_mdio;
155         }
156
157         am->mii_bus.name = "ag71xx_mdio";
158         am->mii_bus.read = ag71xx_mdio_read;
159         am->mii_bus.write = ag71xx_mdio_write;
160         am->mii_bus.reset = ag71xx_mdio_reset;
161         am->mii_bus.irq = am->mii_irq;
162         am->mii_bus.priv = am;
163         am->mii_bus.dev = &pdev->dev;
164         snprintf(am->mii_bus.id, MII_BUS_ID_SIZE, "%x", 0);
165
166         pdata = pdev->dev.platform_data;
167         if (pdata)
168                 am->mii_bus.phy_mask = pdata->phy_mask;
169
170         for (i = 0; i < PHY_MAX_ADDR; i++)
171                 am->mii_irq[i] = PHY_POLL;
172
173         err = mdiobus_register(&am->mii_bus);
174         if (err)
175                 goto err_iounmap;
176
177         ag71xx_mdio_dump_regs(am);
178
179         platform_set_drvdata(pdev, am);
180         ag71xx_mdio_bus = am;
181         return 0;
182
183  err_iounmap:
184         iounmap(am->mdio_base);
185  err_free_mdio:
186         kfree(am);
187  err_out:
188         return err;
189 }
190
191 static int __exit ag71xx_mdio_remove(struct platform_device *pdev)
192 {
193         struct ag71xx_mdio *am = platform_get_drvdata(pdev);
194
195         if (am) {
196                 ag71xx_mdio_bus = NULL;
197                 mdiobus_unregister(&am->mii_bus);
198                 iounmap(am->mdio_base);
199                 kfree(am);
200                 platform_set_drvdata(pdev, NULL);
201         }
202
203         return 0;
204 }
205
206 static struct platform_driver ag71xx_mdio_driver = {
207         .probe          = ag71xx_mdio_probe,
208         .remove         = __exit_p(ag71xx_mdio_remove),
209         .driver = {
210                 .name   = "ag71xx-mdio",
211         }
212 };
213
214 int ag71xx_mdio_driver_init(void)
215 {
216         return platform_driver_register(&ag71xx_mdio_driver);
217 }
218
219 void ag71xx_mdio_driver_exit(void)
220 {
221         platform_driver_unregister(&ag71xx_mdio_driver);
222 }