at v3.2-rc2 5.7 kB view raw
1/* 2 * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) 3 * 4 * Author: Vitaly Bordug <vbordug@ru.mvista.com> 5 * Anton Vorontsov <avorontsov@ru.mvista.com> 6 * 7 * Copyright (c) 2006-2007 MontaVista Software, Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15#include <linux/kernel.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/list.h> 19#include <linux/mii.h> 20#include <linux/phy.h> 21#include <linux/phy_fixed.h> 22#include <linux/err.h> 23#include <linux/slab.h> 24 25#define MII_REGS_NUM 29 26 27struct fixed_mdio_bus { 28 int irqs[PHY_MAX_ADDR]; 29 struct mii_bus *mii_bus; 30 struct list_head phys; 31}; 32 33struct fixed_phy { 34 int id; 35 u16 regs[MII_REGS_NUM]; 36 struct phy_device *phydev; 37 struct fixed_phy_status status; 38 int (*link_update)(struct net_device *, struct fixed_phy_status *); 39 struct list_head node; 40}; 41 42static struct platform_device *pdev; 43static struct fixed_mdio_bus platform_fmb = { 44 .phys = LIST_HEAD_INIT(platform_fmb.phys), 45}; 46 47static int fixed_phy_update_regs(struct fixed_phy *fp) 48{ 49 u16 bmsr = BMSR_ANEGCAPABLE; 50 u16 bmcr = 0; 51 u16 lpagb = 0; 52 u16 lpa = 0; 53 54 if (fp->status.duplex) { 55 bmcr |= BMCR_FULLDPLX; 56 57 switch (fp->status.speed) { 58 case 1000: 59 bmsr |= BMSR_ESTATEN; 60 bmcr |= BMCR_SPEED1000; 61 lpagb |= LPA_1000FULL; 62 break; 63 case 100: 64 bmsr |= BMSR_100FULL; 65 bmcr |= BMCR_SPEED100; 66 lpa |= LPA_100FULL; 67 break; 68 case 10: 69 bmsr |= BMSR_10FULL; 70 lpa |= LPA_10FULL; 71 break; 72 default: 73 printk(KERN_WARNING "fixed phy: unknown speed\n"); 74 return -EINVAL; 75 } 76 } else { 77 switch (fp->status.speed) { 78 case 1000: 79 bmsr |= BMSR_ESTATEN; 80 bmcr |= BMCR_SPEED1000; 81 lpagb |= LPA_1000HALF; 82 break; 83 case 100: 84 bmsr |= BMSR_100HALF; 85 bmcr |= BMCR_SPEED100; 86 lpa |= LPA_100HALF; 87 break; 88 case 10: 89 bmsr |= BMSR_10HALF; 90 lpa |= LPA_10HALF; 91 break; 92 default: 93 printk(KERN_WARNING "fixed phy: unknown speed\n"); 94 return -EINVAL; 95 } 96 } 97 98 if (fp->status.link) 99 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; 100 101 if (fp->status.pause) 102 lpa |= LPA_PAUSE_CAP; 103 104 if (fp->status.asym_pause) 105 lpa |= LPA_PAUSE_ASYM; 106 107 fp->regs[MII_PHYSID1] = fp->id >> 16; 108 fp->regs[MII_PHYSID2] = fp->id; 109 110 fp->regs[MII_BMSR] = bmsr; 111 fp->regs[MII_BMCR] = bmcr; 112 fp->regs[MII_LPA] = lpa; 113 fp->regs[MII_STAT1000] = lpagb; 114 115 return 0; 116} 117 118static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) 119{ 120 struct fixed_mdio_bus *fmb = bus->priv; 121 struct fixed_phy *fp; 122 123 if (reg_num >= MII_REGS_NUM) 124 return -1; 125 126 list_for_each_entry(fp, &fmb->phys, node) { 127 if (fp->id == phy_id) { 128 /* Issue callback if user registered it. */ 129 if (fp->link_update) { 130 fp->link_update(fp->phydev->attached_dev, 131 &fp->status); 132 fixed_phy_update_regs(fp); 133 } 134 return fp->regs[reg_num]; 135 } 136 } 137 138 return 0xFFFF; 139} 140 141static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num, 142 u16 val) 143{ 144 return 0; 145} 146 147/* 148 * If something weird is required to be done with link/speed, 149 * network driver is able to assign a function to implement this. 150 * May be useful for PHY's that need to be software-driven. 151 */ 152int fixed_phy_set_link_update(struct phy_device *phydev, 153 int (*link_update)(struct net_device *, 154 struct fixed_phy_status *)) 155{ 156 struct fixed_mdio_bus *fmb = &platform_fmb; 157 struct fixed_phy *fp; 158 159 if (!link_update || !phydev || !phydev->bus) 160 return -EINVAL; 161 162 list_for_each_entry(fp, &fmb->phys, node) { 163 if (fp->id == phydev->phy_id) { 164 fp->link_update = link_update; 165 fp->phydev = phydev; 166 return 0; 167 } 168 } 169 170 return -ENOENT; 171} 172EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 173 174int fixed_phy_add(unsigned int irq, int phy_id, 175 struct fixed_phy_status *status) 176{ 177 int ret; 178 struct fixed_mdio_bus *fmb = &platform_fmb; 179 struct fixed_phy *fp; 180 181 fp = kzalloc(sizeof(*fp), GFP_KERNEL); 182 if (!fp) 183 return -ENOMEM; 184 185 memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); 186 187 fmb->irqs[phy_id] = irq; 188 189 fp->id = phy_id; 190 fp->status = *status; 191 192 ret = fixed_phy_update_regs(fp); 193 if (ret) 194 goto err_regs; 195 196 list_add_tail(&fp->node, &fmb->phys); 197 198 return 0; 199 200err_regs: 201 kfree(fp); 202 return ret; 203} 204EXPORT_SYMBOL_GPL(fixed_phy_add); 205 206static int __init fixed_mdio_bus_init(void) 207{ 208 struct fixed_mdio_bus *fmb = &platform_fmb; 209 int ret; 210 211 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); 212 if (IS_ERR(pdev)) { 213 ret = PTR_ERR(pdev); 214 goto err_pdev; 215 } 216 217 fmb->mii_bus = mdiobus_alloc(); 218 if (fmb->mii_bus == NULL) { 219 ret = -ENOMEM; 220 goto err_mdiobus_reg; 221 } 222 223 snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "0"); 224 fmb->mii_bus->name = "Fixed MDIO Bus"; 225 fmb->mii_bus->priv = fmb; 226 fmb->mii_bus->parent = &pdev->dev; 227 fmb->mii_bus->read = &fixed_mdio_read; 228 fmb->mii_bus->write = &fixed_mdio_write; 229 fmb->mii_bus->irq = fmb->irqs; 230 231 ret = mdiobus_register(fmb->mii_bus); 232 if (ret) 233 goto err_mdiobus_alloc; 234 235 return 0; 236 237err_mdiobus_alloc: 238 mdiobus_free(fmb->mii_bus); 239err_mdiobus_reg: 240 platform_device_unregister(pdev); 241err_pdev: 242 return ret; 243} 244module_init(fixed_mdio_bus_init); 245 246static void __exit fixed_mdio_bus_exit(void) 247{ 248 struct fixed_mdio_bus *fmb = &platform_fmb; 249 struct fixed_phy *fp, *tmp; 250 251 mdiobus_unregister(fmb->mii_bus); 252 mdiobus_free(fmb->mii_bus); 253 platform_device_unregister(pdev); 254 255 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 256 list_del(&fp->node); 257 kfree(fp); 258 } 259} 260module_exit(fixed_mdio_bus_exit); 261 262MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 263MODULE_AUTHOR("Vitaly Bordug"); 264MODULE_LICENSE("GPL");