net: dsa: b53: Add support for Broadcom RoboSwitch
[linux.git] / drivers / net / dsa / b53 / b53_mdio.c
1 /*
2  * B53 register access through MII registers
3  *
4  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/phy.h>
21 #include <linux/module.h>
22 #include <linux/delay.h>
23 #include <linux/brcmphy.h>
24 #include <linux/rtnetlink.h>
25 #include <net/dsa.h>
26
27 #include "b53_priv.h"
28
29 /* MII registers */
30 #define REG_MII_PAGE    0x10    /* MII Page register */
31 #define REG_MII_ADDR    0x11    /* MII Address register */
32 #define REG_MII_DATA0   0x18    /* MII Data register 0 */
33 #define REG_MII_DATA1   0x19    /* MII Data register 1 */
34 #define REG_MII_DATA2   0x1a    /* MII Data register 2 */
35 #define REG_MII_DATA3   0x1b    /* MII Data register 3 */
36
37 #define REG_MII_PAGE_ENABLE     BIT(0)
38 #define REG_MII_ADDR_WRITE      BIT(0)
39 #define REG_MII_ADDR_READ       BIT(1)
40
41 static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
42 {
43         int i;
44         u16 v;
45         int ret;
46         struct mii_bus *bus = dev->priv;
47
48         if (dev->current_page != page) {
49                 /* set page number */
50                 v = (page << 8) | REG_MII_PAGE_ENABLE;
51                 ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
52                                            REG_MII_PAGE, v);
53                 if (ret)
54                         return ret;
55                 dev->current_page = page;
56         }
57
58         /* set register address */
59         v = (reg << 8) | op;
60         ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_ADDR, v);
61         if (ret)
62                 return ret;
63
64         /* check if operation completed */
65         for (i = 0; i < 5; ++i) {
66                 v = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
67                                         REG_MII_ADDR);
68                 if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
69                         break;
70                 usleep_range(10, 100);
71         }
72
73         if (WARN_ON(i == 5))
74                 return -EIO;
75
76         return 0;
77 }
78
79 static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
80 {
81         struct mii_bus *bus = dev->priv;
82         int ret;
83
84         ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
85         if (ret)
86                 return ret;
87
88         *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
89                                    REG_MII_DATA0) & 0xff;
90
91         return 0;
92 }
93
94 static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
95 {
96         struct mii_bus *bus = dev->priv;
97         int ret;
98
99         ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
100         if (ret)
101                 return ret;
102
103         *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
104
105         return 0;
106 }
107
108 static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
109 {
110         struct mii_bus *bus = dev->priv;
111         int ret;
112
113         ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
114         if (ret)
115                 return ret;
116
117         *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
118         *val |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
119                                     REG_MII_DATA1) << 16;
120
121         return 0;
122 }
123
124 static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
125 {
126         struct mii_bus *bus = dev->priv;
127         u64 temp = 0;
128         int i;
129         int ret;
130
131         ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
132         if (ret)
133                 return ret;
134
135         for (i = 2; i >= 0; i--) {
136                 temp <<= 16;
137                 temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
138                                      REG_MII_DATA0 + i);
139         }
140
141         *val = temp;
142
143         return 0;
144 }
145
146 static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
147 {
148         struct mii_bus *bus = dev->priv;
149         u64 temp = 0;
150         int i;
151         int ret;
152
153         ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
154         if (ret)
155                 return ret;
156
157         for (i = 3; i >= 0; i--) {
158                 temp <<= 16;
159                 temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
160                                             REG_MII_DATA0 + i);
161         }
162
163         *val = temp;
164
165         return 0;
166 }
167
168 static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
169 {
170         struct mii_bus *bus = dev->priv;
171         int ret;
172
173         ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
174                                    REG_MII_DATA0, value);
175         if (ret)
176                 return ret;
177
178         return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
179 }
180
181 static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
182                             u16 value)
183 {
184         struct mii_bus *bus = dev->priv;
185         int ret;
186
187         ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
188                                    REG_MII_DATA0, value);
189         if (ret)
190                 return ret;
191
192         return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
193 }
194
195 static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
196                             u32 value)
197 {
198         struct mii_bus *bus = dev->priv;
199         unsigned int i;
200         u32 temp = value;
201
202         for (i = 0; i < 2; i++) {
203                 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
204                                                REG_MII_DATA0 + i,
205                                                temp & 0xffff);
206                 if (ret)
207                         return ret;
208                 temp >>= 16;
209         }
210
211         return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
212 }
213
214 static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
215                             u64 value)
216 {
217         struct mii_bus *bus = dev->priv;
218         unsigned int i;
219         u64 temp = value;
220
221         for (i = 0; i < 3; i++) {
222                 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
223                                                REG_MII_DATA0 + i,
224                                                temp & 0xffff);
225                 if (ret)
226                         return ret;
227                 temp >>= 16;
228         }
229
230         return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
231 }
232
233 static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
234                             u64 value)
235 {
236         struct mii_bus *bus = dev->priv;
237         unsigned int i;
238         u64 temp = value;
239
240         for (i = 0; i < 4; i++) {
241                 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
242                                                REG_MII_DATA0 + i,
243                                                temp & 0xffff);
244                 if (ret)
245                         return ret;
246                 temp >>= 16;
247         }
248
249         return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
250 }
251
252 static int b53_mdio_phy_read16(struct b53_device *dev, int addr, int reg,
253                                u16 *value)
254 {
255         struct mii_bus *bus = dev->priv;
256
257         *value = mdiobus_read_nested(bus, addr, reg);
258
259         return 0;
260 }
261
262 static int b53_mdio_phy_write16(struct b53_device *dev, int addr, int reg,
263                                 u16 value)
264 {
265         struct mii_bus *bus = dev->bus;
266
267         return mdiobus_write_nested(bus, addr, reg, value);
268 }
269
270 static struct b53_io_ops b53_mdio_ops = {
271         .read8 = b53_mdio_read8,
272         .read16 = b53_mdio_read16,
273         .read32 = b53_mdio_read32,
274         .read48 = b53_mdio_read48,
275         .read64 = b53_mdio_read64,
276         .write8 = b53_mdio_write8,
277         .write16 = b53_mdio_write16,
278         .write32 = b53_mdio_write32,
279         .write48 = b53_mdio_write48,
280         .write64 = b53_mdio_write64,
281         .phy_read16 = b53_mdio_phy_read16,
282         .phy_write16 = b53_mdio_phy_write16,
283 };
284
285 #define B53_BRCM_OUI_1  0x0143bc00
286 #define B53_BRCM_OUI_2  0x03625c00
287 #define B53_BRCM_OUI_3  0x00406000
288
289 static int b53_mdio_probe(struct mdio_device *mdiodev)
290 {
291         struct b53_device *dev;
292         u32 phy_id;
293         int ret;
294
295         /* allow the generic PHY driver to take over the non-management MDIO
296          * addresses
297          */
298         if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) {
299                 dev_err(&mdiodev->dev, "leaving address %d to PHY\n",
300                         mdiodev->addr);
301                 return -ENODEV;
302         }
303
304         /* read the first port's id */
305         phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16;
306         phy_id |= mdiobus_read(mdiodev->bus, 0, 3);
307
308         /* BCM5325, BCM539x (OUI_1)
309          * BCM53125, BCM53128 (OUI_2)
310          * BCM5365 (OUI_3)
311          */
312         if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&
313             (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&
314             (phy_id & 0xfffffc00) != B53_BRCM_OUI_3) {
315                 dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);
316                 return -ENODEV;
317         }
318
319         dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus);
320         if (!dev)
321                 return -ENOMEM;
322
323         /* we don't use page 0xff, so force a page set */
324         dev->current_page = 0xff;
325         dev->bus = mdiodev->bus;
326
327         dev_set_drvdata(&mdiodev->dev, dev);
328
329         ret = b53_switch_register(dev);
330         if (ret) {
331                 dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret);
332                 return ret;
333         }
334
335         return ret;
336 }
337
338 static void b53_mdio_remove(struct mdio_device *mdiodev)
339 {
340         struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
341         struct dsa_switch *ds = dev->ds;
342
343         dsa_unregister_switch(ds);
344 }
345
346 static const struct of_device_id b53_of_match[] = {
347         { .compatible = "brcm,bcm5325" },
348         { .compatible = "brcm,bcm53115" },
349         { .compatible = "brcm,bcm53125" },
350         { .compatible = "brcm,bcm53128" },
351         { .compatible = "brcm,bcm5365" },
352         { .compatible = "brcm,bcm5395" },
353         { .compatible = "brcm,bcm5397" },
354         { .compatible = "brcm,bcm5398" },
355         { /* sentinel */ },
356 };
357 MODULE_DEVICE_TABLE(of, b53_of_match);
358
359 static struct mdio_driver b53_mdio_driver = {
360         .probe  = b53_mdio_probe,
361         .remove = b53_mdio_remove,
362         .mdiodrv.driver = {
363                 .name = "bcm53xx",
364                 .of_match_table = b53_of_match,
365         },
366 };
367
368 static int __init b53_mdio_driver_register(void)
369 {
370         return mdio_driver_register(&b53_mdio_driver);
371 }
372 module_init(b53_mdio_driver_register);
373
374 static void __exit b53_mdio_driver_unregister(void)
375 {
376         mdio_driver_unregister(&b53_mdio_driver);
377 }
378 module_exit(b53_mdio_driver_unregister);
379
380 MODULE_DESCRIPTION("B53 MDIO access driver");
381 MODULE_LICENSE("Dual BSD/GPL");