linux/drivers/net/fsl_pq_mdio.c
<<
>>
Prefs
   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(&regs->miimadd, (mii_id << 8) | regnum);
  69
  70        /* Write out the value we want */
  71        out_be32(&regs->miimcon, value);
  72
  73        /* Wait for the transaction to finish */
  74        while (in_be32(&regs->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(&regs->miimadd, (mii_id << 8) | regnum);
  97
  98        /* Clear miimcom, and then initiate a read */
  99        out_be32(&regs->miimcom, 0);
 100        out_be32(&regs->miimcom, MII_READ_COMMAND);
 101
 102        /* Wait for the transaction to finish */
 103        while (in_be32(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
 104                cpu_relax();
 105
 106        /* Grab the value of the register from miimstat */
 107        value = in_be32(&regs->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(&regs->miimcfg, MIIMCFG_RESET);
 153
 154        /* Setup the MII Mgmt clock speed */
 155        out_be32(&regs->miimcfg, MIIMCFG_INIT_VALUE);
 156
 157        /* Wait until the bus is free */
 158        while ((in_be32(&regs->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{
 270        struct device_node *np = ofdev->dev.of_node;
 271        struct device_node *tbi;
 272        struct fsl_pq_mdio_priv *priv;
 273        struct fsl_pq_mdio __iomem *regs = NULL;
 274        void __iomem *map;
 275        u32 __iomem *tbipa;
 276        struct mii_bus *new_bus;
 277        int tbiaddr = -1;
 278        const u32 *addrp;
 279        u64 addr = 0, size = 0;
 280        int err;
 281
 282        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 283        if (!priv)
 284                return -ENOMEM;
 285
 286        new_bus = mdiobus_alloc();
 287        if (!new_bus) {
 288                err = -ENOMEM;
 289                goto err_free_priv;
 290        }
 291
 292        new_bus->name = "Freescale PowerQUICC MII Bus",
 293        new_bus->read = &fsl_pq_mdio_read,
 294        new_bus->write = &fsl_pq_mdio_write,
 295        new_bus->reset = &fsl_pq_mdio_reset,
 296        new_bus->priv = priv;
 297        fsl_pq_mdio_bus_name(new_bus->id, np);
 298
 299        addrp = of_get_address(np, 0, &size, NULL);
 300        if (!addrp) {
 301                err = -EINVAL;
 302                goto err_free_bus;
 303        }
 304
 305        /* Set the PHY base address */
 306        addr = of_translate_address(np, addrp);
 307        if (addr == OF_BAD_ADDR) {
 308                err = -EINVAL;
 309                goto err_free_bus;
 310        }
 311
 312        map = ioremap(addr, size);
 313        if (!map) {
 314                err = -ENOMEM;
 315                goto err_free_bus;
 316        }
 317        priv->map = map;
 318
 319        if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
 320                        of_device_is_compatible(np, "fsl,gianfar-tbi") ||
 321                        of_device_is_compatible(np, "fsl,ucc-mdio") ||
 322                        of_device_is_compatible(np, "ucc_geth_phy"))
 323                map -= offsetof(struct fsl_pq_mdio, miimcfg);
 324        regs = map;
 325        priv->regs = regs;
 326
 327        new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
 328
 329        if (NULL == new_bus->irq) {
 330                err = -ENOMEM;
 331                goto err_unmap_regs;
 332        }
 333
 334        new_bus->parent = &ofdev->dev;
 335        dev_set_drvdata(&ofdev->dev, new_bus);
 336
 337        if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
 338                        of_device_is_compatible(np, "fsl,gianfar-tbi") ||
 339                        of_device_is_compatible(np, "fsl,etsec2-mdio") ||
 340                        of_device_is_compatible(np, "fsl,etsec2-tbi") ||
 341                        of_device_is_compatible(np, "gianfar")) {
 342#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
 343                tbipa = get_gfar_tbipa(regs, np);
 344                if (!tbipa) {
 345                        err = -EINVAL;
 346                        goto err_free_irqs;
 347                }
 348#else
 349                err = -ENODEV;
 350                goto err_free_irqs;
 351#endif
 352        } else if (of_device_is_compatible(np, "fsl,ucc-mdio") ||
 353                        of_device_is_compatible(np, "ucc_geth_phy")) {
 354#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE)
 355                u32 id;
 356                static u32 mii_mng_master;
 357
 358                tbipa = &regs->utbipar;
 359
 360                if ((err = get_ucc_id_for_range(addr, addr + size, &id)))
 361                        goto err_free_irqs;
 362
 363                if (!mii_mng_master) {
 364                        mii_mng_master = id;
 365                        ucc_set_qe_mux_mii_mng(id - 1);
 366                }
 367#else
 368                err = -ENODEV;
 369                goto err_free_irqs;
 370#endif
 371        } else {
 372                err = -ENODEV;
 373                goto err_free_irqs;
 374        }
 375
 376        for_each_child_of_node(np, tbi) {
 377                if (!strncmp(tbi->type, "tbi-phy", 8))
 378                        break;
 379        }
 380
 381        if (tbi) {
 382                const u32 *prop = of_get_property(tbi, "reg", NULL);
 383
 384                if (prop)
 385                        tbiaddr = *prop;
 386        }
 387
 388        if (tbiaddr == -1) {
 389                out_be32(tbipa, 0);
 390
 391                tbiaddr = fsl_pq_mdio_find_free(new_bus);
 392        }
 393
 394        /*
 395         * We define TBIPA at 0 to be illegal, opting to fail for boards that
 396         * have PHYs at 1-31, rather than change tbipa and rescan.
 397         */
 398        if (tbiaddr == 0) {
 399                err = -EBUSY;
 400
 401                goto err_free_irqs;
 402        }
 403
 404        out_be32(tbipa, tbiaddr);
 405
 406        err = of_mdiobus_register(new_bus, np);
 407        if (err) {
 408                printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
 409                                new_bus->name);
 410                goto err_free_irqs;
 411        }
 412
 413        return 0;
 414
 415err_free_irqs:
 416        kfree(new_bus->irq);
 417err_unmap_regs:
 418        iounmap(priv->map);
 419err_free_bus:
 420        kfree(new_bus);
 421err_free_priv:
 422        kfree(priv);
 423        return err;
 424}
 425
 426
 427static int fsl_pq_mdio_remove(struct platform_device *ofdev)
 428{
 429        struct device *device = &ofdev->dev;
 430        struct mii_bus *bus = dev_get_drvdata(device);
 431        struct fsl_pq_mdio_priv *priv = bus->priv;
 432
 433        mdiobus_unregister(bus);
 434
 435        dev_set_drvdata(device, NULL);
 436
 437        iounmap(priv->map);
 438        bus->priv = NULL;
 439        mdiobus_free(bus);
 440        kfree(priv);
 441
 442        return 0;
 443}
 444
 445static struct of_device_id fsl_pq_mdio_match[] = {
 446        {
 447                .type = "mdio",
 448                .compatible = "ucc_geth_phy",
 449        },
 450        {
 451                .type = "mdio",
 452                .compatible = "gianfar",
 453        },
 454        {
 455                .compatible = "fsl,ucc-mdio",
 456        },
 457        {
 458                .compatible = "fsl,gianfar-tbi",
 459        },
 460        {
 461                .compatible = "fsl,gianfar-mdio",
 462        },
 463        {
 464                .compatible = "fsl,etsec2-tbi",
 465        },
 466        {
 467                .compatible = "fsl,etsec2-mdio",
 468        },
 469        {},
 470};
 471MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
 472
 473static struct platform_driver fsl_pq_mdio_driver = {
 474        .driver = {
 475                .name = "fsl-pq_mdio",
 476                .owner = THIS_MODULE,
 477                .of_match_table = fsl_pq_mdio_match,
 478        },
 479        .probe = fsl_pq_mdio_probe,
 480        .remove = fsl_pq_mdio_remove,
 481};
 482
 483int __init fsl_pq_mdio_init(void)
 484{
 485        return platform_driver_register(&fsl_pq_mdio_driver);
 486}
 487module_init(fsl_pq_mdio_init);
 488
 489void fsl_pq_mdio_exit(void)
 490{
 491        platform_driver_unregister(&fsl_pq_mdio_driver);
 492}
 493module_exit(fsl_pq_mdio_exit);
 494MODULE_LICENSE("GPL");
 495