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