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                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 = &regs->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