Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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