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 50struct fsl_pq_mdio_priv { 51 void __iomem *map; 52 struct fsl_pq_mdio __iomem *regs; 53}; 54 55/* 56 * Write value to the PHY at mii_id at register regnum, 57 * on the bus attached to the local interface, which may be different from the 58 * generic mdio bus (tied to a single interface), waiting until the write is 59 * done before returning. This is helpful in programming interfaces like 60 * the TBI which control interfaces like onchip SERDES and are always tied to 61 * the local mdio pins, which may not be the same as system mdio bus, used for 62 * controlling the external PHYs, for example. 63 */ 64int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id, 65 int regnum, u16 value) 66{ 67 /* Set the PHY address and the register address we want to write */ 68 out_be32(®s->miimadd, (mii_id << 8) | regnum); 69 70 /* Write out the value we want */ 71 out_be32(®s->miimcon, value); 72 73 /* Wait for the transaction to finish */ 74 while (in_be32(®s->miimind) & MIIMIND_BUSY) 75 cpu_relax(); 76 77 return 0; 78} 79 80/* 81 * Read the bus for PHY at addr mii_id, register regnum, and 82 * return the value. Clears miimcom first. All PHY operation 83 * done on the bus attached to the local interface, 84 * which may be different from the generic mdio bus 85 * This is helpful in programming interfaces like 86 * the TBI which, in turn, control interfaces like onchip SERDES 87 * and are always tied to the local mdio pins, which may not be the 88 * same as system mdio bus, used for controlling the external PHYs, for eg. 89 */ 90int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs, 91 int mii_id, int regnum) 92{ 93 u16 value; 94 95 /* Set the PHY address and the register address we want to read */ 96 out_be32(®s->miimadd, (mii_id << 8) | regnum); 97 98 /* Clear miimcom, and then initiate a read */ 99 out_be32(®s->miimcom, 0); 100 out_be32(®s->miimcom, MII_READ_COMMAND); 101 102 /* Wait for the transaction to finish */ 103 while (in_be32(®s->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY)) 104 cpu_relax(); 105 106 /* Grab the value of the register from miimstat */ 107 value = in_be32(®s->miimstat); 108 109 return value; 110} 111 112static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus) 113{ 114 struct fsl_pq_mdio_priv *priv = bus->priv; 115 116 return priv->regs; 117} 118 119/* 120 * Write value to the PHY at mii_id at register regnum, 121 * on the bus, waiting until the write is done before returning. 122 */ 123int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) 124{ 125 struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); 126 127 /* Write to the local MII regs */ 128 return(fsl_pq_local_mdio_write(regs, mii_id, regnum, value)); 129} 130 131/* 132 * Read the bus for PHY at addr mii_id, register regnum, and 133 * return the value. Clears miimcom first. 134 */ 135int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 136{ 137 struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); 138 139 /* Read the local MII regs */ 140 return(fsl_pq_local_mdio_read(regs, mii_id, regnum)); 141} 142 143/* Reset the MIIM registers, and wait for the bus to free */ 144static int fsl_pq_mdio_reset(struct mii_bus *bus) 145{ 146 struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus); 147 int timeout = PHY_INIT_TIMEOUT; 148 149 mutex_lock(&bus->mdio_lock); 150 151 /* Reset the management interface */ 152 out_be32(®s->miimcfg, MIIMCFG_RESET); 153 154 /* Setup the MII Mgmt clock speed */ 155 out_be32(®s->miimcfg, MIIMCFG_INIT_VALUE); 156 157 /* Wait until the bus is free */ 158 while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--) 159 cpu_relax(); 160 161 mutex_unlock(&bus->mdio_lock); 162 163 if (timeout < 0) { 164 printk(KERN_ERR "%s: The MII Bus is stuck!\n", 165 bus->name); 166 return -EBUSY; 167 } 168 169 return 0; 170} 171 172void fsl_pq_mdio_bus_name(char *name, struct device_node *np) 173{ 174 const u32 *addr; 175 u64 taddr = OF_BAD_ADDR; 176 177 addr = of_get_address(np, 0, NULL, NULL); 178 if (addr) 179 taddr = of_translate_address(np, addr); 180 181 snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name, 182 (unsigned long long)taddr); 183} 184EXPORT_SYMBOL_GPL(fsl_pq_mdio_bus_name); 185 186/* Scan the bus in reverse, looking for an empty spot */ 187static int fsl_pq_mdio_find_free(struct mii_bus *new_bus) 188{ 189 int i; 190 191 for (i = PHY_MAX_ADDR; i > 0; i--) { 192 u32 phy_id; 193 194 if (get_phy_id(new_bus, i, &phy_id)) 195 return -1; 196 197 if (phy_id == 0xffffffff) 198 break; 199 } 200 201 return i; 202} 203 204 205#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) 206static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np) 207{ 208 struct gfar __iomem *enet_regs; 209 210 /* 211 * This is mildly evil, but so is our hardware for doing this. 212 * Also, we have to cast back to struct gfar because of 213 * definition weirdness done in gianfar.h. 214 */ 215 if(of_device_is_compatible(np, "fsl,gianfar-mdio") || 216 of_device_is_compatible(np, "fsl,gianfar-tbi") || 217 of_device_is_compatible(np, "gianfar")) { 218 enet_regs = (struct gfar __iomem *)regs; 219 return &enet_regs->tbipa; 220 } else if (of_device_is_compatible(np, "fsl,etsec2-mdio") || 221 of_device_is_compatible(np, "fsl,etsec2-tbi")) { 222 return of_iomap(np, 1); 223 } else 224 return NULL; 225} 226#endif 227 228 229#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) 230static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id) 231{ 232 struct device_node *np = NULL; 233 int err = 0; 234 235 for_each_compatible_node(np, NULL, "ucc_geth") { 236 struct resource tempres; 237 238 err = of_address_to_resource(np, 0, &tempres); 239 if (err) 240 continue; 241 242 /* if our mdio regs fall within this UCC regs range */ 243 if ((start >= tempres.start) && (end <= tempres.end)) { 244 /* Find the id of the UCC */ 245 const u32 *id; 246 247 id = of_get_property(np, "cell-index", NULL); 248 if (!id) { 249 id = of_get_property(np, "device-id", NULL); 250 if (!id) 251 continue; 252 } 253 254 *ucc_id = *id; 255 256 return 0; 257 } 258 } 259 260 if (err) 261 return err; 262 else 263 return -EINVAL; 264} 265#endif 266 267 268static int fsl_pq_mdio_probe(struct platform_device *ofdev, 269 const struct of_device_id *match) 270{ 271 struct device_node *np = ofdev->dev.of_node; 272 struct device_node *tbi; 273 struct fsl_pq_mdio_priv *priv; 274 struct fsl_pq_mdio __iomem *regs = NULL; 275 void __iomem *map; 276 u32 __iomem *tbipa; 277 struct mii_bus *new_bus; 278 int tbiaddr = -1; 279 const u32 *addrp; 280 u64 addr = 0, size = 0; 281 int err; 282 283 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 284 if (!priv) 285 return -ENOMEM; 286 287 new_bus = mdiobus_alloc(); 288 if (!new_bus) { 289 err = -ENOMEM; 290 goto err_free_priv; 291 } 292 293 new_bus->name = "Freescale PowerQUICC MII Bus", 294 new_bus->read = &fsl_pq_mdio_read, 295 new_bus->write = &fsl_pq_mdio_write, 296 new_bus->reset = &fsl_pq_mdio_reset, 297 new_bus->priv = priv; 298 fsl_pq_mdio_bus_name(new_bus->id, np); 299 300 addrp = of_get_address(np, 0, &size, NULL); 301 if (!addrp) { 302 err = -EINVAL; 303 goto err_free_bus; 304 } 305 306 /* Set the PHY base address */ 307 addr = of_translate_address(np, addrp); 308 if (addr == OF_BAD_ADDR) { 309 err = -EINVAL; 310 goto err_free_bus; 311 } 312 313 map = ioremap(addr, size); 314 if (!map) { 315 err = -ENOMEM; 316 goto err_free_bus; 317 } 318 priv->map = map; 319 320 if (of_device_is_compatible(np, "fsl,gianfar-mdio") || 321 of_device_is_compatible(np, "fsl,gianfar-tbi") || 322 of_device_is_compatible(np, "fsl,ucc-mdio") || 323 of_device_is_compatible(np, "ucc_geth_phy")) 324 map -= offsetof(struct fsl_pq_mdio, miimcfg); 325 regs = map; 326 priv->regs = regs; 327 328 new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); 329 330 if (NULL == new_bus->irq) { 331 err = -ENOMEM; 332 goto err_unmap_regs; 333 } 334 335 new_bus->parent = &ofdev->dev; 336 dev_set_drvdata(&ofdev->dev, new_bus); 337 338 if (of_device_is_compatible(np, "fsl,gianfar-mdio") || 339 of_device_is_compatible(np, "fsl,gianfar-tbi") || 340 of_device_is_compatible(np, "fsl,etsec2-mdio") || 341 of_device_is_compatible(np, "fsl,etsec2-tbi") || 342 of_device_is_compatible(np, "gianfar")) { 343#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) 344 tbipa = get_gfar_tbipa(regs, np); 345 if (!tbipa) { 346 err = -EINVAL; 347 goto err_free_irqs; 348 } 349#else 350 err = -ENODEV; 351 goto err_free_irqs; 352#endif 353 } else if (of_device_is_compatible(np, "fsl,ucc-mdio") || 354 of_device_is_compatible(np, "ucc_geth_phy")) { 355#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) 356 u32 id; 357 static u32 mii_mng_master; 358 359 tbipa = ®s->utbipar; 360 361 if ((err = get_ucc_id_for_range(addr, addr + size, &id))) 362 goto err_free_irqs; 363 364 if (!mii_mng_master) { 365 mii_mng_master = id; 366 ucc_set_qe_mux_mii_mng(id - 1); 367 } 368#else 369 err = -ENODEV; 370 goto err_free_irqs; 371#endif 372 } else { 373 err = -ENODEV; 374 goto err_free_irqs; 375 } 376 377 for_each_child_of_node(np, tbi) { 378 if (!strncmp(tbi->type, "tbi-phy", 8)) 379 break; 380 } 381 382 if (tbi) { 383 const u32 *prop = of_get_property(tbi, "reg", NULL); 384 385 if (prop) 386 tbiaddr = *prop; 387 } 388 389 if (tbiaddr == -1) { 390 out_be32(tbipa, 0); 391 392 tbiaddr = fsl_pq_mdio_find_free(new_bus); 393 } 394 395 /* 396 * We define TBIPA at 0 to be illegal, opting to fail for boards that 397 * have PHYs at 1-31, rather than change tbipa and rescan. 398 */ 399 if (tbiaddr == 0) { 400 err = -EBUSY; 401 402 goto err_free_irqs; 403 } 404 405 out_be32(tbipa, tbiaddr); 406 407 err = of_mdiobus_register(new_bus, np); 408 if (err) { 409 printk (KERN_ERR "%s: Cannot register as MDIO bus\n", 410 new_bus->name); 411 goto err_free_irqs; 412 } 413 414 return 0; 415 416err_free_irqs: 417 kfree(new_bus->irq); 418err_unmap_regs: 419 iounmap(priv->map); 420err_free_bus: 421 kfree(new_bus); 422err_free_priv: 423 kfree(priv); 424 return err; 425} 426 427 428static int fsl_pq_mdio_remove(struct platform_device *ofdev) 429{ 430 struct device *device = &ofdev->dev; 431 struct mii_bus *bus = dev_get_drvdata(device); 432 struct fsl_pq_mdio_priv *priv = bus->priv; 433 434 mdiobus_unregister(bus); 435 436 dev_set_drvdata(device, NULL); 437 438 iounmap(priv->map); 439 bus->priv = NULL; 440 mdiobus_free(bus); 441 kfree(priv); 442 443 return 0; 444} 445 446static struct of_device_id fsl_pq_mdio_match[] = { 447 { 448 .type = "mdio", 449 .compatible = "ucc_geth_phy", 450 }, 451 { 452 .type = "mdio", 453 .compatible = "gianfar", 454 }, 455 { 456 .compatible = "fsl,ucc-mdio", 457 }, 458 { 459 .compatible = "fsl,gianfar-tbi", 460 }, 461 { 462 .compatible = "fsl,gianfar-mdio", 463 }, 464 { 465 .compatible = "fsl,etsec2-tbi", 466 }, 467 { 468 .compatible = "fsl,etsec2-mdio", 469 }, 470 {}, 471}; 472MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); 473 474static struct of_platform_driver fsl_pq_mdio_driver = { 475 .driver = { 476 .name = "fsl-pq_mdio", 477 .owner = THIS_MODULE, 478 .of_match_table = fsl_pq_mdio_match, 479 }, 480 .probe = fsl_pq_mdio_probe, 481 .remove = fsl_pq_mdio_remove, 482}; 483 484int __init fsl_pq_mdio_init(void) 485{ 486 return of_register_platform_driver(&fsl_pq_mdio_driver); 487} 488module_init(fsl_pq_mdio_init); 489 490void fsl_pq_mdio_exit(void) 491{ 492 of_unregister_platform_driver(&fsl_pq_mdio_driver); 493} 494module_exit(fsl_pq_mdio_exit); 495MODULE_LICENSE("GPL"); 496