at v2.6.25 5.5 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 = container_of(bus, struct fixed_mdio_bus, 119 mii_bus); 120 struct fixed_phy *fp; 121 122 if (reg_num >= MII_REGS_NUM) 123 return -1; 124 125 list_for_each_entry(fp, &fmb->phys, node) { 126 if (fp->id == phy_id) { 127 /* Issue callback if user registered it. */ 128 if (fp->link_update) { 129 fp->link_update(fp->phydev->attached_dev, 130 &fp->status); 131 fixed_phy_update_regs(fp); 132 } 133 return fp->regs[reg_num]; 134 } 135 } 136 137 return 0xFFFF; 138} 139 140static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num, 141 u16 val) 142{ 143 return 0; 144} 145 146/* 147 * If something weird is required to be done with link/speed, 148 * network driver is able to assign a function to implement this. 149 * May be useful for PHY's that need to be software-driven. 150 */ 151int fixed_phy_set_link_update(struct phy_device *phydev, 152 int (*link_update)(struct net_device *, 153 struct fixed_phy_status *)) 154{ 155 struct fixed_mdio_bus *fmb = &platform_fmb; 156 struct fixed_phy *fp; 157 158 if (!link_update || !phydev || !phydev->bus) 159 return -EINVAL; 160 161 list_for_each_entry(fp, &fmb->phys, node) { 162 if (fp->id == phydev->phy_id) { 163 fp->link_update = link_update; 164 fp->phydev = phydev; 165 return 0; 166 } 167 } 168 169 return -ENOENT; 170} 171EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 172 173int fixed_phy_add(unsigned int irq, int phy_id, 174 struct fixed_phy_status *status) 175{ 176 int ret; 177 struct fixed_mdio_bus *fmb = &platform_fmb; 178 struct fixed_phy *fp; 179 180 fp = kzalloc(sizeof(*fp), GFP_KERNEL); 181 if (!fp) 182 return -ENOMEM; 183 184 memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); 185 186 fmb->irqs[phy_id] = irq; 187 188 fp->id = phy_id; 189 fp->status = *status; 190 191 ret = fixed_phy_update_regs(fp); 192 if (ret) 193 goto err_regs; 194 195 list_add_tail(&fp->node, &fmb->phys); 196 197 return 0; 198 199err_regs: 200 kfree(fp); 201 return ret; 202} 203EXPORT_SYMBOL_GPL(fixed_phy_add); 204 205static int __init fixed_mdio_bus_init(void) 206{ 207 struct fixed_mdio_bus *fmb = &platform_fmb; 208 int ret; 209 210 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); 211 if (!pdev) { 212 ret = -ENOMEM; 213 goto err_pdev; 214 } 215 216 fmb->mii_bus.id = 0; 217 fmb->mii_bus.name = "Fixed MDIO Bus"; 218 fmb->mii_bus.dev = &pdev->dev; 219 fmb->mii_bus.read = &fixed_mdio_read; 220 fmb->mii_bus.write = &fixed_mdio_write; 221 fmb->mii_bus.irq = fmb->irqs; 222 223 ret = mdiobus_register(&fmb->mii_bus); 224 if (ret) 225 goto err_mdiobus_reg; 226 227 return 0; 228 229err_mdiobus_reg: 230 platform_device_unregister(pdev); 231err_pdev: 232 return ret; 233} 234module_init(fixed_mdio_bus_init); 235 236static void __exit fixed_mdio_bus_exit(void) 237{ 238 struct fixed_mdio_bus *fmb = &platform_fmb; 239 struct fixed_phy *fp, *tmp; 240 241 mdiobus_unregister(&fmb->mii_bus); 242 platform_device_unregister(pdev); 243 244 list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 245 list_del(&fp->node); 246 kfree(fp); 247 } 248} 249module_exit(fixed_mdio_bus_exit); 250 251MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 252MODULE_AUTHOR("Vitaly Bordug"); 253MODULE_LICENSE("GPL");