linux/drivers/net/phy/mdio-octeon.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2009-2012 Cavium, Inc.
   7 */
   8
   9#include <linux/platform_device.h>
  10#include <linux/of_mdio.h>
  11#include <linux/delay.h>
  12#include <linux/module.h>
  13#include <linux/gfp.h>
  14#include <linux/phy.h>
  15#include <linux/io.h>
  16
  17#include <asm/octeon/octeon.h>
  18#include <asm/octeon/cvmx-smix-defs.h>
  19
  20#define DRV_VERSION "1.0"
  21#define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
  22
  23#define SMI_CMD         0x0
  24#define SMI_WR_DAT      0x8
  25#define SMI_RD_DAT      0x10
  26#define SMI_CLK         0x18
  27#define SMI_EN          0x20
  28
  29enum octeon_mdiobus_mode {
  30        UNINIT = 0,
  31        C22,
  32        C45
  33};
  34
  35struct octeon_mdiobus {
  36        struct mii_bus *mii_bus;
  37        u64 register_base;
  38        resource_size_t mdio_phys;
  39        resource_size_t regsize;
  40        enum octeon_mdiobus_mode mode;
  41        int phy_irq[PHY_MAX_ADDR];
  42};
  43
  44static void octeon_mdiobus_set_mode(struct octeon_mdiobus *p,
  45                                    enum octeon_mdiobus_mode m)
  46{
  47        union cvmx_smix_clk smi_clk;
  48
  49        if (m == p->mode)
  50                return;
  51
  52        smi_clk.u64 = cvmx_read_csr(p->register_base + SMI_CLK);
  53        smi_clk.s.mode = (m == C45) ? 1 : 0;
  54        smi_clk.s.preamble = 1;
  55        cvmx_write_csr(p->register_base + SMI_CLK, smi_clk.u64);
  56        p->mode = m;
  57}
  58
  59static int octeon_mdiobus_c45_addr(struct octeon_mdiobus *p,
  60                                   int phy_id, int regnum)
  61{
  62        union cvmx_smix_cmd smi_cmd;
  63        union cvmx_smix_wr_dat smi_wr;
  64        int timeout = 1000;
  65
  66        octeon_mdiobus_set_mode(p, C45);
  67
  68        smi_wr.u64 = 0;
  69        smi_wr.s.dat = regnum & 0xffff;
  70        cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
  71
  72        regnum = (regnum >> 16) & 0x1f;
  73
  74        smi_cmd.u64 = 0;
  75        smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
  76        smi_cmd.s.phy_adr = phy_id;
  77        smi_cmd.s.reg_adr = regnum;
  78        cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
  79
  80        do {
  81                /* Wait 1000 clocks so we don't saturate the RSL bus
  82                 * doing reads.
  83                 */
  84                __delay(1000);
  85                smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
  86        } while (smi_wr.s.pending && --timeout);
  87
  88        if (timeout <= 0)
  89                return -EIO;
  90        return 0;
  91}
  92
  93static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
  94{
  95        struct octeon_mdiobus *p = bus->priv;
  96        union cvmx_smix_cmd smi_cmd;
  97        union cvmx_smix_rd_dat smi_rd;
  98        unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
  99        int timeout = 1000;
 100
 101        if (regnum & MII_ADDR_C45) {
 102                int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
 103                if (r < 0)
 104                        return r;
 105
 106                regnum = (regnum >> 16) & 0x1f;
 107                op = 3; /* MDIO_CLAUSE_45_READ */
 108        } else {
 109                octeon_mdiobus_set_mode(p, C22);
 110        }
 111
 112
 113        smi_cmd.u64 = 0;
 114        smi_cmd.s.phy_op = op;
 115        smi_cmd.s.phy_adr = phy_id;
 116        smi_cmd.s.reg_adr = regnum;
 117        cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 118
 119        do {
 120                /* Wait 1000 clocks so we don't saturate the RSL bus
 121                 * doing reads.
 122                 */
 123                __delay(1000);
 124                smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT);
 125        } while (smi_rd.s.pending && --timeout);
 126
 127        if (smi_rd.s.val)
 128                return smi_rd.s.dat;
 129        else
 130                return -EIO;
 131}
 132
 133static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
 134                                int regnum, u16 val)
 135{
 136        struct octeon_mdiobus *p = bus->priv;
 137        union cvmx_smix_cmd smi_cmd;
 138        union cvmx_smix_wr_dat smi_wr;
 139        unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
 140        int timeout = 1000;
 141
 142
 143        if (regnum & MII_ADDR_C45) {
 144                int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
 145                if (r < 0)
 146                        return r;
 147
 148                regnum = (regnum >> 16) & 0x1f;
 149                op = 1; /* MDIO_CLAUSE_45_WRITE */
 150        } else {
 151                octeon_mdiobus_set_mode(p, C22);
 152        }
 153
 154        smi_wr.u64 = 0;
 155        smi_wr.s.dat = val;
 156        cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
 157
 158        smi_cmd.u64 = 0;
 159        smi_cmd.s.phy_op = op;
 160        smi_cmd.s.phy_adr = phy_id;
 161        smi_cmd.s.reg_adr = regnum;
 162        cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
 163
 164        do {
 165                /* Wait 1000 clocks so we don't saturate the RSL bus
 166                 * doing reads.
 167                 */
 168                __delay(1000);
 169                smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
 170        } while (smi_wr.s.pending && --timeout);
 171
 172        if (timeout <= 0)
 173                return -EIO;
 174
 175        return 0;
 176}
 177
 178static int octeon_mdiobus_probe(struct platform_device *pdev)
 179{
 180        struct octeon_mdiobus *bus;
 181        struct resource *res_mem;
 182        union cvmx_smix_en smi_en;
 183        int err = -ENOENT;
 184
 185        bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
 186        if (!bus)
 187                return -ENOMEM;
 188
 189        res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 190
 191        if (res_mem == NULL) {
 192                dev_err(&pdev->dev, "found no memory resource\n");
 193                err = -ENXIO;
 194                goto fail;
 195        }
 196        bus->mdio_phys = res_mem->start;
 197        bus->regsize = resource_size(res_mem);
 198        if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize,
 199                                     res_mem->name)) {
 200                dev_err(&pdev->dev, "request_mem_region failed\n");
 201                goto fail;
 202        }
 203        bus->register_base =
 204                (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize);
 205
 206        bus->mii_bus = mdiobus_alloc();
 207
 208        if (!bus->mii_bus)
 209                goto fail;
 210
 211        smi_en.u64 = 0;
 212        smi_en.s.en = 1;
 213        cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
 214
 215        bus->mii_bus->priv = bus;
 216        bus->mii_bus->irq = bus->phy_irq;
 217        bus->mii_bus->name = "mdio-octeon";
 218        snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
 219        bus->mii_bus->parent = &pdev->dev;
 220
 221        bus->mii_bus->read = octeon_mdiobus_read;
 222        bus->mii_bus->write = octeon_mdiobus_write;
 223
 224        platform_set_drvdata(pdev, bus);
 225
 226        err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
 227        if (err)
 228                goto fail_register;
 229
 230        dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
 231
 232        return 0;
 233fail_register:
 234        mdiobus_free(bus->mii_bus);
 235fail:
 236        smi_en.u64 = 0;
 237        cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
 238        return err;
 239}
 240
 241static int octeon_mdiobus_remove(struct platform_device *pdev)
 242{
 243        struct octeon_mdiobus *bus;
 244        union cvmx_smix_en smi_en;
 245
 246        bus = platform_get_drvdata(pdev);
 247
 248        mdiobus_unregister(bus->mii_bus);
 249        mdiobus_free(bus->mii_bus);
 250        smi_en.u64 = 0;
 251        cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
 252        return 0;
 253}
 254
 255static const struct of_device_id octeon_mdiobus_match[] = {
 256        {
 257                .compatible = "cavium,octeon-3860-mdio",
 258        },
 259        {},
 260};
 261MODULE_DEVICE_TABLE(of, octeon_mdiobus_match);
 262
 263static struct platform_driver octeon_mdiobus_driver = {
 264        .driver = {
 265                .name           = "mdio-octeon",
 266                .of_match_table = octeon_mdiobus_match,
 267        },
 268        .probe          = octeon_mdiobus_probe,
 269        .remove         = octeon_mdiobus_remove,
 270};
 271
 272void octeon_mdiobus_force_mod_depencency(void)
 273{
 274        /* Let ethernet drivers force us to be loaded.  */
 275}
 276EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency);
 277
 278module_platform_driver(octeon_mdiobus_driver);
 279
 280MODULE_DESCRIPTION(DRV_DESCRIPTION);
 281MODULE_VERSION(DRV_VERSION);
 282MODULE_AUTHOR("David Daney");
 283MODULE_LICENSE("GPL");
 284