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