xref: /linux/drivers/net/dsa/b53/b53_mdio.c (revision 2f804aca48322f02a8f44cca540663845ee80fb1)
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 const 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 #define B53_BRCM_OUI_4	0x01410c00
289 #define B53_BRCM_OUI_5	0xae025000
290 
291 static int b53_mdio_probe(struct mdio_device *mdiodev)
292 {
293 	struct b53_device *dev;
294 	u32 phy_id;
295 	int ret;
296 
297 	/* allow the generic PHY driver to take over the non-management MDIO
298 	 * addresses
299 	 */
300 	if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) {
301 		dev_err(&mdiodev->dev, "leaving address %d to PHY\n",
302 			mdiodev->addr);
303 		return -ENODEV;
304 	}
305 
306 	/* read the first port's id */
307 	phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16;
308 	phy_id |= mdiobus_read(mdiodev->bus, 0, 3);
309 
310 	/* BCM5325, BCM539x (OUI_1)
311 	 * BCM53125, BCM53128 (OUI_2)
312 	 * BCM5365 (OUI_3)
313 	 */
314 	if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&
315 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&
316 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 &&
317 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 &&
318 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) {
319 		dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);
320 		return -ENODEV;
321 	}
322 
323 	/* First probe will come from SWITCH_MDIO controller on the 7445D0
324 	 * switch, which will conflict with the 7445 integrated switch
325 	 * pseudo-phy (we end-up programming both). In that case, we return
326 	 * -EPROBE_DEFER for the first time we get here, and wait until we come
327 	 * back with the slave MDIO bus which has the correct indirection
328 	 * layer setup
329 	 */
330 	if (of_machine_is_compatible("brcm,bcm7445d0") &&
331 	    strcmp(mdiodev->bus->name, "sf2 slave mii"))
332 		return -EPROBE_DEFER;
333 
334 	dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus);
335 	if (!dev)
336 		return -ENOMEM;
337 
338 	/* we don't use page 0xff, so force a page set */
339 	dev->current_page = 0xff;
340 	dev->bus = mdiodev->bus;
341 
342 	dev_set_drvdata(&mdiodev->dev, dev);
343 
344 	ret = b53_switch_register(dev);
345 	if (ret) {
346 		dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret);
347 		return ret;
348 	}
349 
350 	return ret;
351 }
352 
353 static void b53_mdio_remove(struct mdio_device *mdiodev)
354 {
355 	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
356 
357 	if (!dev)
358 		return;
359 
360 	b53_switch_remove(dev);
361 }
362 
363 static void b53_mdio_shutdown(struct mdio_device *mdiodev)
364 {
365 	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
366 
367 	if (!dev)
368 		return;
369 
370 	b53_switch_shutdown(dev);
371 
372 	dev_set_drvdata(&mdiodev->dev, NULL);
373 }
374 
375 static const struct of_device_id b53_of_match[] = {
376 	{ .compatible = "brcm,bcm5325" },
377 	{ .compatible = "brcm,bcm53115" },
378 	{ .compatible = "brcm,bcm53125" },
379 	{ .compatible = "brcm,bcm53128" },
380 	{ .compatible = "brcm,bcm53134" },
381 	{ .compatible = "brcm,bcm5365" },
382 	{ .compatible = "brcm,bcm5389" },
383 	{ .compatible = "brcm,bcm5395" },
384 	{ .compatible = "brcm,bcm5397" },
385 	{ .compatible = "brcm,bcm5398" },
386 	{ /* sentinel */ },
387 };
388 MODULE_DEVICE_TABLE(of, b53_of_match);
389 
390 static struct mdio_driver b53_mdio_driver = {
391 	.probe	= b53_mdio_probe,
392 	.remove	= b53_mdio_remove,
393 	.shutdown = b53_mdio_shutdown,
394 	.mdiodrv.driver = {
395 		.name = "bcm53xx",
396 		.of_match_table = b53_of_match,
397 	},
398 };
399 mdio_module_driver(b53_mdio_driver);
400 
401 MODULE_DESCRIPTION("B53 MDIO access driver");
402 MODULE_LICENSE("Dual BSD/GPL");
403