xref: /openbmc/linux/drivers/net/dsa/b53/b53_mdio.c (revision f44a9010)
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/of.h>
23 #include <linux/delay.h>
24 #include <linux/brcmphy.h>
25 #include <linux/rtnetlink.h>
26 #include <net/dsa.h>
27 
28 #include "b53_priv.h"
29 
30 /* MII registers */
31 #define REG_MII_PAGE    0x10    /* MII Page register */
32 #define REG_MII_ADDR    0x11    /* MII Address register */
33 #define REG_MII_DATA0   0x18    /* MII Data register 0 */
34 #define REG_MII_DATA1   0x19    /* MII Data register 1 */
35 #define REG_MII_DATA2   0x1a    /* MII Data register 2 */
36 #define REG_MII_DATA3   0x1b    /* MII Data register 3 */
37 
38 #define REG_MII_PAGE_ENABLE     BIT(0)
39 #define REG_MII_ADDR_WRITE      BIT(0)
40 #define REG_MII_ADDR_READ       BIT(1)
41 
b53_mdio_op(struct b53_device * dev,u8 page,u8 reg,u16 op)42 static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
43 {
44 	int i;
45 	u16 v;
46 	int ret;
47 	struct mii_bus *bus = dev->priv;
48 
49 	if (dev->current_page != page) {
50 		/* set page number */
51 		v = (page << 8) | REG_MII_PAGE_ENABLE;
52 		ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
53 					   REG_MII_PAGE, v);
54 		if (ret)
55 			return ret;
56 		dev->current_page = page;
57 	}
58 
59 	/* set register address */
60 	v = (reg << 8) | op;
61 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_ADDR, v);
62 	if (ret)
63 		return ret;
64 
65 	/* check if operation completed */
66 	for (i = 0; i < 5; ++i) {
67 		v = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
68 					REG_MII_ADDR);
69 		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
70 			break;
71 		usleep_range(10, 100);
72 	}
73 
74 	if (WARN_ON(i == 5))
75 		return -EIO;
76 
77 	return 0;
78 }
79 
b53_mdio_read8(struct b53_device * dev,u8 page,u8 reg,u8 * val)80 static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
81 {
82 	struct mii_bus *bus = dev->priv;
83 	int ret;
84 
85 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
86 	if (ret)
87 		return ret;
88 
89 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
90 				   REG_MII_DATA0) & 0xff;
91 
92 	return 0;
93 }
94 
b53_mdio_read16(struct b53_device * dev,u8 page,u8 reg,u16 * val)95 static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
96 {
97 	struct mii_bus *bus = dev->priv;
98 	int ret;
99 
100 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
101 	if (ret)
102 		return ret;
103 
104 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
105 
106 	return 0;
107 }
108 
b53_mdio_read32(struct b53_device * dev,u8 page,u8 reg,u32 * val)109 static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
110 {
111 	struct mii_bus *bus = dev->priv;
112 	int ret;
113 
114 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
115 	if (ret)
116 		return ret;
117 
118 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
119 	*val |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
120 				    REG_MII_DATA1) << 16;
121 
122 	return 0;
123 }
124 
b53_mdio_read48(struct b53_device * dev,u8 page,u8 reg,u64 * val)125 static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
126 {
127 	struct mii_bus *bus = dev->priv;
128 	u64 temp = 0;
129 	int i;
130 	int ret;
131 
132 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
133 	if (ret)
134 		return ret;
135 
136 	for (i = 2; i >= 0; i--) {
137 		temp <<= 16;
138 		temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
139 				     REG_MII_DATA0 + i);
140 	}
141 
142 	*val = temp;
143 
144 	return 0;
145 }
146 
b53_mdio_read64(struct b53_device * dev,u8 page,u8 reg,u64 * val)147 static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
148 {
149 	struct mii_bus *bus = dev->priv;
150 	u64 temp = 0;
151 	int i;
152 	int ret;
153 
154 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
155 	if (ret)
156 		return ret;
157 
158 	for (i = 3; i >= 0; i--) {
159 		temp <<= 16;
160 		temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
161 					    REG_MII_DATA0 + i);
162 	}
163 
164 	*val = temp;
165 
166 	return 0;
167 }
168 
b53_mdio_write8(struct b53_device * dev,u8 page,u8 reg,u8 value)169 static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
170 {
171 	struct mii_bus *bus = dev->priv;
172 	int ret;
173 
174 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
175 				   REG_MII_DATA0, value);
176 	if (ret)
177 		return ret;
178 
179 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
180 }
181 
b53_mdio_write16(struct b53_device * dev,u8 page,u8 reg,u16 value)182 static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
183 			    u16 value)
184 {
185 	struct mii_bus *bus = dev->priv;
186 	int ret;
187 
188 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
189 				   REG_MII_DATA0, value);
190 	if (ret)
191 		return ret;
192 
193 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
194 }
195 
b53_mdio_write32(struct b53_device * dev,u8 page,u8 reg,u32 value)196 static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
197 			    u32 value)
198 {
199 	struct mii_bus *bus = dev->priv;
200 	unsigned int i;
201 	u32 temp = value;
202 
203 	for (i = 0; i < 2; i++) {
204 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
205 					       REG_MII_DATA0 + i,
206 					       temp & 0xffff);
207 		if (ret)
208 			return ret;
209 		temp >>= 16;
210 	}
211 
212 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
213 }
214 
b53_mdio_write48(struct b53_device * dev,u8 page,u8 reg,u64 value)215 static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
216 			    u64 value)
217 {
218 	struct mii_bus *bus = dev->priv;
219 	unsigned int i;
220 	u64 temp = value;
221 
222 	for (i = 0; i < 3; i++) {
223 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
224 					       REG_MII_DATA0 + i,
225 					       temp & 0xffff);
226 		if (ret)
227 			return ret;
228 		temp >>= 16;
229 	}
230 
231 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
232 }
233 
b53_mdio_write64(struct b53_device * dev,u8 page,u8 reg,u64 value)234 static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
235 			    u64 value)
236 {
237 	struct mii_bus *bus = dev->priv;
238 	unsigned int i;
239 	u64 temp = value;
240 
241 	for (i = 0; i < 4; i++) {
242 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
243 					       REG_MII_DATA0 + i,
244 					       temp & 0xffff);
245 		if (ret)
246 			return ret;
247 		temp >>= 16;
248 	}
249 
250 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
251 }
252 
b53_mdio_phy_read16(struct b53_device * dev,int addr,int reg,u16 * value)253 static int b53_mdio_phy_read16(struct b53_device *dev, int addr, int reg,
254 			       u16 *value)
255 {
256 	struct mii_bus *bus = dev->priv;
257 
258 	*value = mdiobus_read_nested(bus, addr, reg);
259 
260 	return 0;
261 }
262 
b53_mdio_phy_write16(struct b53_device * dev,int addr,int reg,u16 value)263 static int b53_mdio_phy_write16(struct b53_device *dev, int addr, int reg,
264 				u16 value)
265 {
266 	struct mii_bus *bus = dev->bus;
267 
268 	return mdiobus_write_nested(bus, addr, reg, value);
269 }
270 
271 static const struct b53_io_ops b53_mdio_ops = {
272 	.read8 = b53_mdio_read8,
273 	.read16 = b53_mdio_read16,
274 	.read32 = b53_mdio_read32,
275 	.read48 = b53_mdio_read48,
276 	.read64 = b53_mdio_read64,
277 	.write8 = b53_mdio_write8,
278 	.write16 = b53_mdio_write16,
279 	.write32 = b53_mdio_write32,
280 	.write48 = b53_mdio_write48,
281 	.write64 = b53_mdio_write64,
282 	.phy_read16 = b53_mdio_phy_read16,
283 	.phy_write16 = b53_mdio_phy_write16,
284 };
285 
286 #define B53_BRCM_OUI_1	0x0143bc00
287 #define B53_BRCM_OUI_2	0x03625c00
288 #define B53_BRCM_OUI_3	0x00406000
289 #define B53_BRCM_OUI_4	0x01410c00
290 #define B53_BRCM_OUI_5	0xae025000
291 
b53_mdio_probe(struct mdio_device * mdiodev)292 static int b53_mdio_probe(struct mdio_device *mdiodev)
293 {
294 	struct b53_device *dev;
295 	u32 phy_id;
296 	int ret;
297 
298 	/* allow the generic PHY driver to take over the non-management MDIO
299 	 * addresses
300 	 */
301 	if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) {
302 		dev_err(&mdiodev->dev, "leaving address %d to PHY\n",
303 			mdiodev->addr);
304 		return -ENODEV;
305 	}
306 
307 	/* read the first port's id */
308 	phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16;
309 	phy_id |= mdiobus_read(mdiodev->bus, 0, 3);
310 
311 	/* BCM5325, BCM539x (OUI_1)
312 	 * BCM53125, BCM53128 (OUI_2)
313 	 * BCM5365 (OUI_3)
314 	 */
315 	if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&
316 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&
317 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 &&
318 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 &&
319 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) {
320 		dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);
321 		return -ENODEV;
322 	}
323 
324 	/* First probe will come from SWITCH_MDIO controller on the 7445D0
325 	 * switch, which will conflict with the 7445 integrated switch
326 	 * pseudo-phy (we end-up programming both). In that case, we return
327 	 * -EPROBE_DEFER for the first time we get here, and wait until we come
328 	 * back with the slave MDIO bus which has the correct indirection
329 	 * layer setup
330 	 */
331 	if (of_machine_is_compatible("brcm,bcm7445d0") &&
332 	    strcmp(mdiodev->bus->name, "sf2 slave mii"))
333 		return -EPROBE_DEFER;
334 
335 	dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus);
336 	if (!dev)
337 		return -ENOMEM;
338 
339 	/* we don't use page 0xff, so force a page set */
340 	dev->current_page = 0xff;
341 	dev->bus = mdiodev->bus;
342 
343 	dev_set_drvdata(&mdiodev->dev, dev);
344 
345 	ret = b53_switch_register(dev);
346 	if (ret) {
347 		dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret);
348 		return ret;
349 	}
350 
351 	return ret;
352 }
353 
b53_mdio_remove(struct mdio_device * mdiodev)354 static void b53_mdio_remove(struct mdio_device *mdiodev)
355 {
356 	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
357 
358 	if (!dev)
359 		return;
360 
361 	b53_switch_remove(dev);
362 }
363 
b53_mdio_shutdown(struct mdio_device * mdiodev)364 static void b53_mdio_shutdown(struct mdio_device *mdiodev)
365 {
366 	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
367 
368 	if (!dev)
369 		return;
370 
371 	b53_switch_shutdown(dev);
372 
373 	dev_set_drvdata(&mdiodev->dev, NULL);
374 }
375 
376 static const struct of_device_id b53_of_match[] = {
377 	{ .compatible = "brcm,bcm5325" },
378 	{ .compatible = "brcm,bcm53115" },
379 	{ .compatible = "brcm,bcm53125" },
380 	{ .compatible = "brcm,bcm53128" },
381 	{ .compatible = "brcm,bcm53134" },
382 	{ .compatible = "brcm,bcm5365" },
383 	{ .compatible = "brcm,bcm5389" },
384 	{ .compatible = "brcm,bcm5395" },
385 	{ .compatible = "brcm,bcm5397" },
386 	{ .compatible = "brcm,bcm5398" },
387 	{ /* sentinel */ },
388 };
389 MODULE_DEVICE_TABLE(of, b53_of_match);
390 
391 static struct mdio_driver b53_mdio_driver = {
392 	.probe	= b53_mdio_probe,
393 	.remove	= b53_mdio_remove,
394 	.shutdown = b53_mdio_shutdown,
395 	.mdiodrv.driver = {
396 		.name = "bcm53xx",
397 		.of_match_table = b53_of_match,
398 	},
399 };
400 mdio_module_driver(b53_mdio_driver);
401 
402 MODULE_DESCRIPTION("B53 MDIO access driver");
403 MODULE_LICENSE("Dual BSD/GPL");
404