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 Cavium Networks 7 */ 8 9#include <linux/gfp.h> 10#include <linux/init.h> 11#include <linux/module.h> 12#include <linux/platform_device.h> 13#include <linux/phy.h> 14 15#include <asm/octeon/octeon.h> 16#include <asm/octeon/cvmx-smix-defs.h> 17 18#define DRV_VERSION "1.0" 19#define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver" 20 21struct octeon_mdiobus { 22 struct mii_bus *mii_bus; 23 int unit; 24 int phy_irq[PHY_MAX_ADDR]; 25}; 26 27static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum) 28{ 29 struct octeon_mdiobus *p = bus->priv; 30 union cvmx_smix_cmd smi_cmd; 31 union cvmx_smix_rd_dat smi_rd; 32 int timeout = 1000; 33 34 smi_cmd.u64 = 0; 35 smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */ 36 smi_cmd.s.phy_adr = phy_id; 37 smi_cmd.s.reg_adr = regnum; 38 cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64); 39 40 do { 41 /* 42 * Wait 1000 clocks so we don't saturate the RSL bus 43 * doing reads. 44 */ 45 cvmx_wait(1000); 46 smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(p->unit)); 47 } while (smi_rd.s.pending && --timeout); 48 49 if (smi_rd.s.val) 50 return smi_rd.s.dat; 51 else 52 return -EIO; 53} 54 55static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, 56 int regnum, u16 val) 57{ 58 struct octeon_mdiobus *p = bus->priv; 59 union cvmx_smix_cmd smi_cmd; 60 union cvmx_smix_wr_dat smi_wr; 61 int timeout = 1000; 62 63 smi_wr.u64 = 0; 64 smi_wr.s.dat = val; 65 cvmx_write_csr(CVMX_SMIX_WR_DAT(p->unit), smi_wr.u64); 66 67 smi_cmd.u64 = 0; 68 smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */ 69 smi_cmd.s.phy_adr = phy_id; 70 smi_cmd.s.reg_adr = regnum; 71 cvmx_write_csr(CVMX_SMIX_CMD(p->unit), smi_cmd.u64); 72 73 do { 74 /* 75 * Wait 1000 clocks so we don't saturate the RSL bus 76 * doing reads. 77 */ 78 cvmx_wait(1000); 79 smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(p->unit)); 80 } while (smi_wr.s.pending && --timeout); 81 82 if (timeout <= 0) 83 return -EIO; 84 85 return 0; 86} 87 88static int __devinit octeon_mdiobus_probe(struct platform_device *pdev) 89{ 90 struct octeon_mdiobus *bus; 91 union cvmx_smix_en smi_en; 92 int i; 93 int err = -ENOENT; 94 95 bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); 96 if (!bus) 97 return -ENOMEM; 98 99 /* The platform_device id is our unit number. */ 100 bus->unit = pdev->id; 101 102 bus->mii_bus = mdiobus_alloc(); 103 104 if (!bus->mii_bus) 105 goto err; 106 107 smi_en.u64 = 0; 108 smi_en.s.en = 1; 109 cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); 110 111 /* 112 * Standard Octeon evaluation boards don't support phy 113 * interrupts, we need to poll. 114 */ 115 for (i = 0; i < PHY_MAX_ADDR; i++) 116 bus->phy_irq[i] = PHY_POLL; 117 118 bus->mii_bus->priv = bus; 119 bus->mii_bus->irq = bus->phy_irq; 120 bus->mii_bus->name = "mdio-octeon"; 121 snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 122 bus->mii_bus->name, bus->unit); 123 bus->mii_bus->parent = &pdev->dev; 124 125 bus->mii_bus->read = octeon_mdiobus_read; 126 bus->mii_bus->write = octeon_mdiobus_write; 127 128 dev_set_drvdata(&pdev->dev, bus); 129 130 err = mdiobus_register(bus->mii_bus); 131 if (err) 132 goto err_register; 133 134 dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); 135 136 return 0; 137err_register: 138 mdiobus_free(bus->mii_bus); 139 140err: 141 devm_kfree(&pdev->dev, bus); 142 smi_en.u64 = 0; 143 cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); 144 return err; 145} 146 147static int __devexit octeon_mdiobus_remove(struct platform_device *pdev) 148{ 149 struct octeon_mdiobus *bus; 150 union cvmx_smix_en smi_en; 151 152 bus = dev_get_drvdata(&pdev->dev); 153 154 mdiobus_unregister(bus->mii_bus); 155 mdiobus_free(bus->mii_bus); 156 smi_en.u64 = 0; 157 cvmx_write_csr(CVMX_SMIX_EN(bus->unit), smi_en.u64); 158 return 0; 159} 160 161static struct platform_driver octeon_mdiobus_driver = { 162 .driver = { 163 .name = "mdio-octeon", 164 .owner = THIS_MODULE, 165 }, 166 .probe = octeon_mdiobus_probe, 167 .remove = __devexit_p(octeon_mdiobus_remove), 168}; 169 170void octeon_mdiobus_force_mod_depencency(void) 171{ 172 /* Let ethernet drivers force us to be loaded. */ 173} 174EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency); 175 176static int __init octeon_mdiobus_mod_init(void) 177{ 178 return platform_driver_register(&octeon_mdiobus_driver); 179} 180 181static void __exit octeon_mdiobus_mod_exit(void) 182{ 183 platform_driver_unregister(&octeon_mdiobus_driver); 184} 185 186module_init(octeon_mdiobus_mod_init); 187module_exit(octeon_mdiobus_mod_exit); 188 189MODULE_DESCRIPTION(DRV_DESCRIPTION); 190MODULE_VERSION(DRV_VERSION); 191MODULE_AUTHOR("David Daney"); 192MODULE_LICENSE("GPL"); 193