1 /*
2  * Freescale PowerQUICC Ethernet Driver -- MIIM bus implementation
3  * Provides Bus interface for MIIM regs
4  *
5  * Author: Andy Fleming <afleming@freescale.com>
6  * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
7  *
8  * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
9  *
10  * Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips)
11  *
12  * This program is free software; you can redistribute  it and/or modify it
13  * under  the terms of  the GNU General  Public License as published by the
14  * Free Software Foundation;  either version 2 of the  License, or (at your
15  * option) any later version.
16  *
17  */
18 
19 #include <linux/kernel.h>
20 #include <linux/string.h>
21 #include <linux/errno.h>
22 #include <linux/unistd.h>
23 #include <linux/slab.h>
24 #include <linux/interrupt.h>
25 #include <linux/init.h>
26 #include <linux/delay.h>
27 #include <linux/netdevice.h>
28 #include <linux/etherdevice.h>
29 #include <linux/skbuff.h>
30 #include <linux/spinlock.h>
31 #include <linux/mm.h>
32 #include <linux/module.h>
33 #include <linux/platform_device.h>
34 #include <linux/crc32.h>
35 #include <linux/mii.h>
36 #include <linux/phy.h>
37 #include <linux/of.h>
38 #include <linux/of_address.h>
39 #include <linux/of_mdio.h>
40 #include <linux/of_platform.h>
41 
42 #include <asm/io.h>
43 #include <asm/irq.h>
44 #include <asm/uaccess.h>
45 #include <asm/ucc.h>
46 
47 #include "gianfar.h"
48 #include "fsl_pq_mdio.h"
49 
50 /* Number of microseconds to wait for an MII register to respond */
51 #define MII_TIMEOUT	1000
52 
53 struct fsl_pq_mdio_priv {
54 	void __iomem *map;
55 	struct fsl_pq_mdio __iomem *regs;
56 };
57 
58 /*
59  * Write value to the PHY at mii_id at register regnum,
60  * on the bus attached to the local interface, which may be different from the
61  * generic mdio bus (tied to a single interface), waiting until the write is
62  * done before returning. This is helpful in programming interfaces like
63  * the TBI which control interfaces like onchip SERDES and are always tied to
64  * the local mdio pins, which may not be the same as system mdio bus, used for
65  * controlling the external PHYs, for example.
66  */
67 int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
68 		int regnum, u16 value)
69 {
70 	u32 status;
71 
72 	/* Set the PHY address and the register address we want to write */
73 	out_be32(&regs->miimadd, (mii_id << 8) | regnum);
74 
75 	/* Write out the value we want */
76 	out_be32(&regs->miimcon, value);
77 
78 	/* Wait for the transaction to finish */
79 	status = spin_event_timeout(!(in_be32(&regs->miimind) &	MIIMIND_BUSY),
80 				    MII_TIMEOUT, 0);
81 
82 	return status ? 0 : -ETIMEDOUT;
83 }
84 
85 /*
86  * Read the bus for PHY at addr mii_id, register regnum, and
87  * return the value.  Clears miimcom first.  All PHY operation
88  * done on the bus attached to the local interface,
89  * which may be different from the generic mdio bus
90  * This is helpful in programming interfaces like
91  * the TBI which, in turn, control interfaces like onchip SERDES
92  * and are always tied to the local mdio pins, which may not be the
93  * same as system mdio bus, used for controlling the external PHYs, for eg.
94  */
95 int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs,
96 		int mii_id, int regnum)
97 {
98 	u16 value;
99 	u32 status;
100 
101 	/* Set the PHY address and the register address we want to read */
102 	out_be32(&regs->miimadd, (mii_id << 8) | regnum);
103 
104 	/* Clear miimcom, and then initiate a read */
105 	out_be32(&regs->miimcom, 0);
106 	out_be32(&regs->miimcom, MII_READ_COMMAND);
107 
108 	/* Wait for the transaction to finish, normally less than 100us */
109 	status = spin_event_timeout(!(in_be32(&regs->miimind) &
110 				    (MIIMIND_NOTVALID | MIIMIND_BUSY)),
111 				    MII_TIMEOUT, 0);
112 	if (!status)
113 		return -ETIMEDOUT;
114 
115 	/* Grab the value of the register from miimstat */
116 	value = in_be32(&regs->miimstat);
117 
118 	return value;
119 }
120 
121 static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
122 {
123 	struct fsl_pq_mdio_priv *priv = bus->priv;
124 
125 	return priv->regs;
126 }
127 
128 /*
129  * Write value to the PHY at mii_id at register regnum,
130  * on the bus, waiting until the write is done before returning.
131  */
132 int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
133 {
134 	struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
135 
136 	/* Write to the local MII regs */
137 	return fsl_pq_local_mdio_write(regs, mii_id, regnum, value);
138 }
139 
140 /*
141  * Read the bus for PHY at addr mii_id, register regnum, and
142  * return the value.  Clears miimcom first.
143  */
144 int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
145 {
146 	struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
147 
148 	/* Read the local MII regs */
149 	return fsl_pq_local_mdio_read(regs, mii_id, regnum);
150 }
151 
152 /* Reset the MIIM registers, and wait for the bus to free */
153 static int fsl_pq_mdio_reset(struct mii_bus *bus)
154 {
155 	struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
156 	u32 status;
157 
158 	mutex_lock(&bus->mdio_lock);
159 
160 	/* Reset the management interface */
161 	out_be32(&regs->miimcfg, MIIMCFG_RESET);
162 
163 	/* Setup the MII Mgmt clock speed */
164 	out_be32(&regs->miimcfg, MIIMCFG_INIT_VALUE);
165 
166 	/* Wait until the bus is free */
167 	status = spin_event_timeout(!(in_be32(&regs->miimind) &	MIIMIND_BUSY),
168 				    MII_TIMEOUT, 0);
169 
170 	mutex_unlock(&bus->mdio_lock);
171 
172 	if (!status) {
173 		printk(KERN_ERR "%s: The MII Bus is stuck!\n",
174 				bus->name);
175 		return -EBUSY;
176 	}
177 
178 	return 0;
179 }
180 
181 void fsl_pq_mdio_bus_name(char *name, struct device_node *np)
182 {
183 	const u32 *addr;
184 	u64 taddr = OF_BAD_ADDR;
185 
186 	addr = of_get_address(np, 0, NULL, NULL);
187 	if (addr)
188 		taddr = of_translate_address(np, addr);
189 
190 	snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name,
191 		(unsigned long long)taddr);
192 }
193 EXPORT_SYMBOL_GPL(fsl_pq_mdio_bus_name);
194 
195 
196 static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np)
197 {
198 #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
199 	struct gfar __iomem *enet_regs;
200 
201 	/*
202 	 * This is mildly evil, but so is our hardware for doing this.
203 	 * Also, we have to cast back to struct gfar because of
204 	 * definition weirdness done in gianfar.h.
205 	 */
206 	if(of_device_is_compatible(np, "fsl,gianfar-mdio") ||
207 		of_device_is_compatible(np, "fsl,gianfar-tbi") ||
208 		of_device_is_compatible(np, "gianfar")) {
209 		enet_regs = (struct gfar __iomem *)regs;
210 		return &enet_regs->tbipa;
211 	} else if (of_device_is_compatible(np, "fsl,etsec2-mdio") ||
212 			of_device_is_compatible(np, "fsl,etsec2-tbi")) {
213 		return of_iomap(np, 1);
214 	}
215 #endif
216 	return NULL;
217 }
218 
219 
220 static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id)
221 {
222 #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE)
223 	struct device_node *np = NULL;
224 	int err = 0;
225 
226 	for_each_compatible_node(np, NULL, "ucc_geth") {
227 		struct resource tempres;
228 
229 		err = of_address_to_resource(np, 0, &tempres);
230 		if (err)
231 			continue;
232 
233 		/* if our mdio regs fall within this UCC regs range */
234 		if ((start >= tempres.start) && (end <= tempres.end)) {
235 			/* Find the id of the UCC */
236 			const u32 *id;
237 
238 			id = of_get_property(np, "cell-index", NULL);
239 			if (!id) {
240 				id = of_get_property(np, "device-id", NULL);
241 				if (!id)
242 					continue;
243 			}
244 
245 			*ucc_id = *id;
246 
247 			return 0;
248 		}
249 	}
250 
251 	if (err)
252 		return err;
253 	else
254 		return -EINVAL;
255 #else
256 	return -ENODEV;
257 #endif
258 }
259 
260 static int fsl_pq_mdio_probe(struct platform_device *ofdev)
261 {
262 	struct device_node *np = ofdev->dev.of_node;
263 	struct device_node *tbi;
264 	struct fsl_pq_mdio_priv *priv;
265 	struct fsl_pq_mdio __iomem *regs = NULL;
266 	void __iomem *map;
267 	u32 __iomem *tbipa;
268 	struct mii_bus *new_bus;
269 	int tbiaddr = -1;
270 	const u32 *addrp;
271 	u64 addr = 0, size = 0;
272 	int err;
273 
274 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
275 	if (!priv)
276 		return -ENOMEM;
277 
278 	new_bus = mdiobus_alloc();
279 	if (!new_bus) {
280 		err = -ENOMEM;
281 		goto err_free_priv;
282 	}
283 
284 	new_bus->name = "Freescale PowerQUICC MII Bus",
285 	new_bus->read = &fsl_pq_mdio_read,
286 	new_bus->write = &fsl_pq_mdio_write,
287 	new_bus->reset = &fsl_pq_mdio_reset,
288 	new_bus->priv = priv;
289 	fsl_pq_mdio_bus_name(new_bus->id, np);
290 
291 	addrp = of_get_address(np, 0, &size, NULL);
292 	if (!addrp) {
293 		err = -EINVAL;
294 		goto err_free_bus;
295 	}
296 
297 	/* Set the PHY base address */
298 	addr = of_translate_address(np, addrp);
299 	if (addr == OF_BAD_ADDR) {
300 		err = -EINVAL;
301 		goto err_free_bus;
302 	}
303 
304 	map = ioremap(addr, size);
305 	if (!map) {
306 		err = -ENOMEM;
307 		goto err_free_bus;
308 	}
309 	priv->map = map;
310 
311 	if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
312 			of_device_is_compatible(np, "fsl,gianfar-tbi") ||
313 			of_device_is_compatible(np, "fsl,ucc-mdio") ||
314 			of_device_is_compatible(np, "ucc_geth_phy"))
315 		map -= offsetof(struct fsl_pq_mdio, miimcfg);
316 	regs = map;
317 	priv->regs = regs;
318 
319 	new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
320 
321 	if (NULL == new_bus->irq) {
322 		err = -ENOMEM;
323 		goto err_unmap_regs;
324 	}
325 
326 	new_bus->parent = &ofdev->dev;
327 	dev_set_drvdata(&ofdev->dev, new_bus);
328 
329 	if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
330 			of_device_is_compatible(np, "fsl,gianfar-tbi") ||
331 			of_device_is_compatible(np, "fsl,etsec2-mdio") ||
332 			of_device_is_compatible(np, "fsl,etsec2-tbi") ||
333 			of_device_is_compatible(np, "gianfar")) {
334 		tbipa = get_gfar_tbipa(regs, np);
335 		if (!tbipa) {
336 			err = -EINVAL;
337 			goto err_free_irqs;
338 		}
339 	} else if (of_device_is_compatible(np, "fsl,ucc-mdio") ||
340 			of_device_is_compatible(np, "ucc_geth_phy")) {
341 		u32 id;
342 		static u32 mii_mng_master;
343 
344 		tbipa = &regs->utbipar;
345 
346 		if ((err = get_ucc_id_for_range(addr, addr + size, &id)))
347 			goto err_free_irqs;
348 
349 		if (!mii_mng_master) {
350 			mii_mng_master = id;
351 			ucc_set_qe_mux_mii_mng(id - 1);
352 		}
353 	} else {
354 		err = -ENODEV;
355 		goto err_free_irqs;
356 	}
357 
358 	for_each_child_of_node(np, tbi) {
359 		if (!strncmp(tbi->type, "tbi-phy", 8))
360 			break;
361 	}
362 
363 	if (tbi) {
364 		const u32 *prop = of_get_property(tbi, "reg", NULL);
365 
366 		if (prop)
367 			tbiaddr = *prop;
368 
369 		if (tbiaddr == -1) {
370 			err = -EBUSY;
371 			goto err_free_irqs;
372 		} else {
373 			out_be32(tbipa, tbiaddr);
374 		}
375 	}
376 
377 	err = of_mdiobus_register(new_bus, np);
378 	if (err) {
379 		printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
380 				new_bus->name);
381 		goto err_free_irqs;
382 	}
383 
384 	return 0;
385 
386 err_free_irqs:
387 	kfree(new_bus->irq);
388 err_unmap_regs:
389 	iounmap(priv->map);
390 err_free_bus:
391 	kfree(new_bus);
392 err_free_priv:
393 	kfree(priv);
394 	return err;
395 }
396 
397 
398 static int fsl_pq_mdio_remove(struct platform_device *ofdev)
399 {
400 	struct device *device = &ofdev->dev;
401 	struct mii_bus *bus = dev_get_drvdata(device);
402 	struct fsl_pq_mdio_priv *priv = bus->priv;
403 
404 	mdiobus_unregister(bus);
405 
406 	dev_set_drvdata(device, NULL);
407 
408 	iounmap(priv->map);
409 	bus->priv = NULL;
410 	mdiobus_free(bus);
411 	kfree(priv);
412 
413 	return 0;
414 }
415 
416 static struct of_device_id fsl_pq_mdio_match[] = {
417 	{
418 		.type = "mdio",
419 		.compatible = "ucc_geth_phy",
420 	},
421 	{
422 		.type = "mdio",
423 		.compatible = "gianfar",
424 	},
425 	{
426 		.compatible = "fsl,ucc-mdio",
427 	},
428 	{
429 		.compatible = "fsl,gianfar-tbi",
430 	},
431 	{
432 		.compatible = "fsl,gianfar-mdio",
433 	},
434 	{
435 		.compatible = "fsl,etsec2-tbi",
436 	},
437 	{
438 		.compatible = "fsl,etsec2-mdio",
439 	},
440 	{},
441 };
442 MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
443 
444 static struct platform_driver fsl_pq_mdio_driver = {
445 	.driver = {
446 		.name = "fsl-pq_mdio",
447 		.owner = THIS_MODULE,
448 		.of_match_table = fsl_pq_mdio_match,
449 	},
450 	.probe = fsl_pq_mdio_probe,
451 	.remove = fsl_pq_mdio_remove,
452 };
453 
454 module_platform_driver(fsl_pq_mdio_driver);
455 
456 MODULE_LICENSE("GPL");
457