Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.8 216 lines 5.1 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,2011 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 30struct octeon_mdiobus { 31 struct mii_bus *mii_bus; 32 u64 register_base; 33 resource_size_t mdio_phys; 34 resource_size_t regsize; 35 int phy_irq[PHY_MAX_ADDR]; 36}; 37 38static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum) 39{ 40 struct octeon_mdiobus *p = bus->priv; 41 union cvmx_smix_cmd smi_cmd; 42 union cvmx_smix_rd_dat smi_rd; 43 int timeout = 1000; 44 45 smi_cmd.u64 = 0; 46 smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */ 47 smi_cmd.s.phy_adr = phy_id; 48 smi_cmd.s.reg_adr = regnum; 49 cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64); 50 51 do { 52 /* 53 * Wait 1000 clocks so we don't saturate the RSL bus 54 * doing reads. 55 */ 56 __delay(1000); 57 smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT); 58 } while (smi_rd.s.pending && --timeout); 59 60 if (smi_rd.s.val) 61 return smi_rd.s.dat; 62 else 63 return -EIO; 64} 65 66static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, 67 int regnum, u16 val) 68{ 69 struct octeon_mdiobus *p = bus->priv; 70 union cvmx_smix_cmd smi_cmd; 71 union cvmx_smix_wr_dat smi_wr; 72 int timeout = 1000; 73 74 smi_wr.u64 = 0; 75 smi_wr.s.dat = val; 76 cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64); 77 78 smi_cmd.u64 = 0; 79 smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */ 80 smi_cmd.s.phy_adr = phy_id; 81 smi_cmd.s.reg_adr = regnum; 82 cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64); 83 84 do { 85 /* 86 * Wait 1000 clocks so we don't saturate the RSL bus 87 * doing reads. 88 */ 89 __delay(1000); 90 smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT); 91 } while (smi_wr.s.pending && --timeout); 92 93 if (timeout <= 0) 94 return -EIO; 95 96 return 0; 97} 98 99static int octeon_mdiobus_probe(struct platform_device *pdev) 100{ 101 struct octeon_mdiobus *bus; 102 struct resource *res_mem; 103 union cvmx_smix_en smi_en; 104 int err = -ENOENT; 105 106 bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); 107 if (!bus) 108 return -ENOMEM; 109 110 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 111 112 if (res_mem == NULL) { 113 dev_err(&pdev->dev, "found no memory resource\n"); 114 err = -ENXIO; 115 goto fail; 116 } 117 bus->mdio_phys = res_mem->start; 118 bus->regsize = resource_size(res_mem); 119 if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize, 120 res_mem->name)) { 121 dev_err(&pdev->dev, "request_mem_region failed\n"); 122 goto fail; 123 } 124 bus->register_base = 125 (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize); 126 127 bus->mii_bus = mdiobus_alloc(); 128 129 if (!bus->mii_bus) 130 goto fail; 131 132 smi_en.u64 = 0; 133 smi_en.s.en = 1; 134 cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); 135 136 bus->mii_bus->priv = bus; 137 bus->mii_bus->irq = bus->phy_irq; 138 bus->mii_bus->name = "mdio-octeon"; 139 snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base); 140 bus->mii_bus->parent = &pdev->dev; 141 142 bus->mii_bus->read = octeon_mdiobus_read; 143 bus->mii_bus->write = octeon_mdiobus_write; 144 145 dev_set_drvdata(&pdev->dev, bus); 146 147 err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node); 148 if (err) 149 goto fail_register; 150 151 dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); 152 153 return 0; 154fail_register: 155 mdiobus_free(bus->mii_bus); 156fail: 157 smi_en.u64 = 0; 158 cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); 159 return err; 160} 161 162static int octeon_mdiobus_remove(struct platform_device *pdev) 163{ 164 struct octeon_mdiobus *bus; 165 union cvmx_smix_en smi_en; 166 167 bus = dev_get_drvdata(&pdev->dev); 168 169 mdiobus_unregister(bus->mii_bus); 170 mdiobus_free(bus->mii_bus); 171 smi_en.u64 = 0; 172 cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); 173 return 0; 174} 175 176static struct of_device_id octeon_mdiobus_match[] = { 177 { 178 .compatible = "cavium,octeon-3860-mdio", 179 }, 180 {}, 181}; 182MODULE_DEVICE_TABLE(of, octeon_mdiobus_match); 183 184static struct platform_driver octeon_mdiobus_driver = { 185 .driver = { 186 .name = "mdio-octeon", 187 .owner = THIS_MODULE, 188 .of_match_table = octeon_mdiobus_match, 189 }, 190 .probe = octeon_mdiobus_probe, 191 .remove = octeon_mdiobus_remove, 192}; 193 194void octeon_mdiobus_force_mod_depencency(void) 195{ 196 /* Let ethernet drivers force us to be loaded. */ 197} 198EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency); 199 200static int __init octeon_mdiobus_mod_init(void) 201{ 202 return platform_driver_register(&octeon_mdiobus_driver); 203} 204 205static void __exit octeon_mdiobus_mod_exit(void) 206{ 207 platform_driver_unregister(&octeon_mdiobus_driver); 208} 209 210module_init(octeon_mdiobus_mod_init); 211module_exit(octeon_mdiobus_mod_exit); 212 213MODULE_DESCRIPTION(DRV_DESCRIPTION); 214MODULE_VERSION(DRV_VERSION); 215MODULE_AUTHOR("David Daney"); 216MODULE_LICENSE("GPL");