16539c44dSDavid S. Miller /* 26539c44dSDavid S. Miller * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) 36539c44dSDavid S. Miller * 46539c44dSDavid S. Miller * Author: Vitaly Bordug <vbordug@ru.mvista.com> 56539c44dSDavid S. Miller * Anton Vorontsov <avorontsov@ru.mvista.com> 66539c44dSDavid S. Miller * 76539c44dSDavid S. Miller * Copyright (c) 2006-2007 MontaVista Software, Inc. 86539c44dSDavid S. Miller * 96539c44dSDavid S. Miller * This program is free software; you can redistribute it and/or modify it 106539c44dSDavid S. Miller * under the terms of the GNU General Public License as published by the 116539c44dSDavid S. Miller * Free Software Foundation; either version 2 of the License, or (at your 126539c44dSDavid S. Miller * option) any later version. 136539c44dSDavid S. Miller */ 146539c44dSDavid S. Miller 156539c44dSDavid S. Miller #include <linux/kernel.h> 166539c44dSDavid S. Miller #include <linux/module.h> 176539c44dSDavid S. Miller #include <linux/platform_device.h> 186539c44dSDavid S. Miller #include <linux/list.h> 196539c44dSDavid S. Miller #include <linux/mii.h> 206539c44dSDavid S. Miller #include <linux/phy.h> 216539c44dSDavid S. Miller #include <linux/phy_fixed.h> 226539c44dSDavid S. Miller #include <linux/err.h> 236539c44dSDavid S. Miller #include <linux/slab.h> 246539c44dSDavid S. Miller #include <linux/of.h> 25a5597008SAndrew Lunn #include <linux/gpio.h> 266539c44dSDavid S. Miller 276539c44dSDavid S. Miller #define MII_REGS_NUM 29 286539c44dSDavid S. Miller 296539c44dSDavid S. Miller struct fixed_mdio_bus { 306539c44dSDavid S. Miller struct mii_bus *mii_bus; 316539c44dSDavid S. Miller struct list_head phys; 326539c44dSDavid S. Miller }; 336539c44dSDavid S. Miller 346539c44dSDavid S. Miller struct fixed_phy { 356539c44dSDavid S. Miller int addr; 366539c44dSDavid S. Miller u16 regs[MII_REGS_NUM]; 376539c44dSDavid S. Miller struct phy_device *phydev; 386539c44dSDavid S. Miller struct fixed_phy_status status; 396539c44dSDavid S. Miller int (*link_update)(struct net_device *, struct fixed_phy_status *); 406539c44dSDavid S. Miller struct list_head node; 41a5597008SAndrew Lunn int link_gpio; 426539c44dSDavid S. Miller }; 436539c44dSDavid S. Miller 446539c44dSDavid S. Miller static struct platform_device *pdev; 456539c44dSDavid S. Miller static struct fixed_mdio_bus platform_fmb = { 466539c44dSDavid S. Miller .phys = LIST_HEAD_INIT(platform_fmb.phys), 476539c44dSDavid S. Miller }; 486539c44dSDavid S. Miller 496539c44dSDavid S. Miller static int fixed_phy_update_regs(struct fixed_phy *fp) 506539c44dSDavid S. Miller { 516539c44dSDavid S. Miller u16 bmsr = BMSR_ANEGCAPABLE; 526539c44dSDavid S. Miller u16 bmcr = 0; 536539c44dSDavid S. Miller u16 lpagb = 0; 546539c44dSDavid S. Miller u16 lpa = 0; 556539c44dSDavid S. Miller 56a5597008SAndrew Lunn if (gpio_is_valid(fp->link_gpio)) 57a5597008SAndrew Lunn fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio); 58a5597008SAndrew Lunn 59bc0f4a87SAndrew Lunn if (fp->status.duplex) { 60bc0f4a87SAndrew Lunn switch (fp->status.speed) { 61bc0f4a87SAndrew Lunn case 1000: 62bc0f4a87SAndrew Lunn bmsr |= BMSR_ESTATEN; 63bc0f4a87SAndrew Lunn break; 64bc0f4a87SAndrew Lunn case 100: 65bc0f4a87SAndrew Lunn bmsr |= BMSR_100FULL; 66bc0f4a87SAndrew Lunn break; 67bc0f4a87SAndrew Lunn case 10: 68bc0f4a87SAndrew Lunn bmsr |= BMSR_10FULL; 69bc0f4a87SAndrew Lunn break; 70bc0f4a87SAndrew Lunn default: 71bc0f4a87SAndrew Lunn break; 72bc0f4a87SAndrew Lunn } 73bc0f4a87SAndrew Lunn } else { 74bc0f4a87SAndrew Lunn switch (fp->status.speed) { 75bc0f4a87SAndrew Lunn case 1000: 76bc0f4a87SAndrew Lunn bmsr |= BMSR_ESTATEN; 77bc0f4a87SAndrew Lunn break; 78bc0f4a87SAndrew Lunn case 100: 79bc0f4a87SAndrew Lunn bmsr |= BMSR_100HALF; 80bc0f4a87SAndrew Lunn break; 81bc0f4a87SAndrew Lunn case 10: 82bc0f4a87SAndrew Lunn bmsr |= BMSR_10HALF; 83bc0f4a87SAndrew Lunn break; 84bc0f4a87SAndrew Lunn default: 85bc0f4a87SAndrew Lunn break; 86bc0f4a87SAndrew Lunn } 87bc0f4a87SAndrew Lunn } 88bc0f4a87SAndrew Lunn 89bc0f4a87SAndrew Lunn if (fp->status.link) { 90868a4215SStas Sergeev bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; 91868a4215SStas Sergeev 926539c44dSDavid S. Miller if (fp->status.duplex) { 936539c44dSDavid S. Miller bmcr |= BMCR_FULLDPLX; 946539c44dSDavid S. Miller 956539c44dSDavid S. Miller switch (fp->status.speed) { 966539c44dSDavid S. Miller case 1000: 976539c44dSDavid S. Miller bmcr |= BMCR_SPEED1000; 986539c44dSDavid S. Miller lpagb |= LPA_1000FULL; 996539c44dSDavid S. Miller break; 1006539c44dSDavid S. Miller case 100: 1016539c44dSDavid S. Miller bmcr |= BMCR_SPEED100; 1026539c44dSDavid S. Miller lpa |= LPA_100FULL; 1036539c44dSDavid S. Miller break; 1046539c44dSDavid S. Miller case 10: 1056539c44dSDavid S. Miller lpa |= LPA_10FULL; 1066539c44dSDavid S. Miller break; 1076539c44dSDavid S. Miller default: 1086539c44dSDavid S. Miller pr_warn("fixed phy: unknown speed\n"); 1096539c44dSDavid S. Miller return -EINVAL; 1106539c44dSDavid S. Miller } 1116539c44dSDavid S. Miller } else { 1126539c44dSDavid S. Miller switch (fp->status.speed) { 1136539c44dSDavid S. Miller case 1000: 1146539c44dSDavid S. Miller bmcr |= BMCR_SPEED1000; 1156539c44dSDavid S. Miller lpagb |= LPA_1000HALF; 1166539c44dSDavid S. Miller break; 1176539c44dSDavid S. Miller case 100: 1186539c44dSDavid S. Miller bmcr |= BMCR_SPEED100; 1196539c44dSDavid S. Miller lpa |= LPA_100HALF; 1206539c44dSDavid S. Miller break; 1216539c44dSDavid S. Miller case 10: 1226539c44dSDavid S. Miller lpa |= LPA_10HALF; 1236539c44dSDavid S. Miller break; 1246539c44dSDavid S. Miller default: 1256539c44dSDavid S. Miller pr_warn("fixed phy: unknown speed\n"); 1266539c44dSDavid S. Miller return -EINVAL; 1276539c44dSDavid S. Miller } 1286539c44dSDavid S. Miller } 1296539c44dSDavid S. Miller 1306539c44dSDavid S. Miller if (fp->status.pause) 1316539c44dSDavid S. Miller lpa |= LPA_PAUSE_CAP; 1326539c44dSDavid S. Miller 1336539c44dSDavid S. Miller if (fp->status.asym_pause) 1346539c44dSDavid S. Miller lpa |= LPA_PAUSE_ASYM; 135bc0f4a87SAndrew Lunn } 1366539c44dSDavid S. Miller 1376539c44dSDavid S. Miller fp->regs[MII_PHYSID1] = 0; 1386539c44dSDavid S. Miller fp->regs[MII_PHYSID2] = 0; 1396539c44dSDavid S. Miller 1406539c44dSDavid S. Miller fp->regs[MII_BMSR] = bmsr; 1416539c44dSDavid S. Miller fp->regs[MII_BMCR] = bmcr; 1426539c44dSDavid S. Miller fp->regs[MII_LPA] = lpa; 1436539c44dSDavid S. Miller fp->regs[MII_STAT1000] = lpagb; 1446539c44dSDavid S. Miller 1456539c44dSDavid S. Miller return 0; 1466539c44dSDavid S. Miller } 1476539c44dSDavid S. Miller 1486539c44dSDavid S. Miller static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) 1496539c44dSDavid S. Miller { 1506539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = bus->priv; 1516539c44dSDavid S. Miller struct fixed_phy *fp; 1526539c44dSDavid S. Miller 1536539c44dSDavid S. Miller if (reg_num >= MII_REGS_NUM) 1546539c44dSDavid S. Miller return -1; 1556539c44dSDavid S. Miller 1566539c44dSDavid S. Miller /* We do not support emulating Clause 45 over Clause 22 register reads 1576539c44dSDavid S. Miller * return an error instead of bogus data. 1586539c44dSDavid S. Miller */ 1596539c44dSDavid S. Miller switch (reg_num) { 1606539c44dSDavid S. Miller case MII_MMD_CTRL: 1616539c44dSDavid S. Miller case MII_MMD_DATA: 1626539c44dSDavid S. Miller return -1; 1636539c44dSDavid S. Miller default: 1646539c44dSDavid S. Miller break; 1656539c44dSDavid S. Miller } 1666539c44dSDavid S. Miller 1676539c44dSDavid S. Miller list_for_each_entry(fp, &fmb->phys, node) { 1686539c44dSDavid S. Miller if (fp->addr == phy_addr) { 1696539c44dSDavid S. Miller /* Issue callback if user registered it. */ 1706539c44dSDavid S. Miller if (fp->link_update) { 1716539c44dSDavid S. Miller fp->link_update(fp->phydev->attached_dev, 1726539c44dSDavid S. Miller &fp->status); 1736539c44dSDavid S. Miller fixed_phy_update_regs(fp); 1746539c44dSDavid S. Miller } 1756539c44dSDavid S. Miller return fp->regs[reg_num]; 1766539c44dSDavid S. Miller } 1776539c44dSDavid S. Miller } 1786539c44dSDavid S. Miller 1796539c44dSDavid S. Miller return 0xFFFF; 1806539c44dSDavid S. Miller } 1816539c44dSDavid S. Miller 1826539c44dSDavid S. Miller static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, 1836539c44dSDavid S. Miller u16 val) 1846539c44dSDavid S. Miller { 1856539c44dSDavid S. Miller return 0; 1866539c44dSDavid S. Miller } 1876539c44dSDavid S. Miller 1886539c44dSDavid S. Miller /* 1896539c44dSDavid S. Miller * If something weird is required to be done with link/speed, 1906539c44dSDavid S. Miller * network driver is able to assign a function to implement this. 1916539c44dSDavid S. Miller * May be useful for PHY's that need to be software-driven. 1926539c44dSDavid S. Miller */ 1936539c44dSDavid S. Miller int fixed_phy_set_link_update(struct phy_device *phydev, 1946539c44dSDavid S. Miller int (*link_update)(struct net_device *, 1956539c44dSDavid S. Miller struct fixed_phy_status *)) 1966539c44dSDavid S. Miller { 1976539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 1986539c44dSDavid S. Miller struct fixed_phy *fp; 1996539c44dSDavid S. Miller 200e5a03bfdSAndrew Lunn if (!phydev || !phydev->mdio.bus) 2016539c44dSDavid S. Miller return -EINVAL; 2026539c44dSDavid S. Miller 2036539c44dSDavid S. Miller list_for_each_entry(fp, &fmb->phys, node) { 204e5a03bfdSAndrew Lunn if (fp->addr == phydev->mdio.addr) { 2056539c44dSDavid S. Miller fp->link_update = link_update; 2066539c44dSDavid S. Miller fp->phydev = phydev; 2076539c44dSDavid S. Miller return 0; 2086539c44dSDavid S. Miller } 2096539c44dSDavid S. Miller } 2106539c44dSDavid S. Miller 2116539c44dSDavid S. Miller return -ENOENT; 2126539c44dSDavid S. Miller } 2136539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); 2146539c44dSDavid S. Miller 215a3bebdceSStas Sergeev int fixed_phy_update_state(struct phy_device *phydev, 216a3bebdceSStas Sergeev const struct fixed_phy_status *status, 217a3bebdceSStas Sergeev const struct fixed_phy_status *changed) 218a3bebdceSStas Sergeev { 219a3bebdceSStas Sergeev struct fixed_mdio_bus *fmb = &platform_fmb; 220a3bebdceSStas Sergeev struct fixed_phy *fp; 221a3bebdceSStas Sergeev 222e5a03bfdSAndrew Lunn if (!phydev || phydev->mdio.bus != fmb->mii_bus) 223a3bebdceSStas Sergeev return -EINVAL; 224a3bebdceSStas Sergeev 225a3bebdceSStas Sergeev list_for_each_entry(fp, &fmb->phys, node) { 226e5a03bfdSAndrew Lunn if (fp->addr == phydev->mdio.addr) { 227a3bebdceSStas Sergeev #define _UPD(x) if (changed->x) \ 228a3bebdceSStas Sergeev fp->status.x = status->x 229a3bebdceSStas Sergeev _UPD(link); 230a3bebdceSStas Sergeev _UPD(speed); 231a3bebdceSStas Sergeev _UPD(duplex); 232a3bebdceSStas Sergeev _UPD(pause); 233a3bebdceSStas Sergeev _UPD(asym_pause); 234a3bebdceSStas Sergeev #undef _UPD 235a3bebdceSStas Sergeev fixed_phy_update_regs(fp); 236a3bebdceSStas Sergeev return 0; 237a3bebdceSStas Sergeev } 238a3bebdceSStas Sergeev } 239a3bebdceSStas Sergeev 240a3bebdceSStas Sergeev return -ENOENT; 241a3bebdceSStas Sergeev } 242a3bebdceSStas Sergeev EXPORT_SYMBOL(fixed_phy_update_state); 243a3bebdceSStas Sergeev 2446539c44dSDavid S. Miller int fixed_phy_add(unsigned int irq, int phy_addr, 245a5597008SAndrew Lunn struct fixed_phy_status *status, 246a5597008SAndrew Lunn int link_gpio) 2476539c44dSDavid S. Miller { 2486539c44dSDavid S. Miller int ret; 2496539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 2506539c44dSDavid S. Miller struct fixed_phy *fp; 2516539c44dSDavid S. Miller 2526539c44dSDavid S. Miller fp = kzalloc(sizeof(*fp), GFP_KERNEL); 2536539c44dSDavid S. Miller if (!fp) 2546539c44dSDavid S. Miller return -ENOMEM; 2556539c44dSDavid S. Miller 2566539c44dSDavid S. Miller memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); 2576539c44dSDavid S. Miller 258e7f4dc35SAndrew Lunn fmb->mii_bus->irq[phy_addr] = irq; 2596539c44dSDavid S. Miller 2606539c44dSDavid S. Miller fp->addr = phy_addr; 2616539c44dSDavid S. Miller fp->status = *status; 262a5597008SAndrew Lunn fp->link_gpio = link_gpio; 263a5597008SAndrew Lunn 264a5597008SAndrew Lunn if (gpio_is_valid(fp->link_gpio)) { 265a5597008SAndrew Lunn ret = gpio_request_one(fp->link_gpio, GPIOF_DIR_IN, 266a5597008SAndrew Lunn "fixed-link-gpio-link"); 267a5597008SAndrew Lunn if (ret) 268a5597008SAndrew Lunn goto err_regs; 269a5597008SAndrew Lunn } 2706539c44dSDavid S. Miller 2716539c44dSDavid S. Miller ret = fixed_phy_update_regs(fp); 2726539c44dSDavid S. Miller if (ret) 273a5597008SAndrew Lunn goto err_gpio; 2746539c44dSDavid S. Miller 2756539c44dSDavid S. Miller list_add_tail(&fp->node, &fmb->phys); 2766539c44dSDavid S. Miller 2776539c44dSDavid S. Miller return 0; 2786539c44dSDavid S. Miller 279a5597008SAndrew Lunn err_gpio: 280a5597008SAndrew Lunn if (gpio_is_valid(fp->link_gpio)) 281a5597008SAndrew Lunn gpio_free(fp->link_gpio); 2826539c44dSDavid S. Miller err_regs: 2836539c44dSDavid S. Miller kfree(fp); 2846539c44dSDavid S. Miller return ret; 2856539c44dSDavid S. Miller } 2866539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_add); 2876539c44dSDavid S. Miller 2885bcbe0f3SAndrew Lunn static void fixed_phy_del(int phy_addr) 2896539c44dSDavid S. Miller { 2906539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 2916539c44dSDavid S. Miller struct fixed_phy *fp, *tmp; 2926539c44dSDavid S. Miller 2936539c44dSDavid S. Miller list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 2946539c44dSDavid S. Miller if (fp->addr == phy_addr) { 2956539c44dSDavid S. Miller list_del(&fp->node); 296a5597008SAndrew Lunn if (gpio_is_valid(fp->link_gpio)) 297a5597008SAndrew Lunn gpio_free(fp->link_gpio); 2986539c44dSDavid S. Miller kfree(fp); 2996539c44dSDavid S. Miller return; 3006539c44dSDavid S. Miller } 3016539c44dSDavid S. Miller } 3026539c44dSDavid S. Miller } 3036539c44dSDavid S. Miller 3046539c44dSDavid S. Miller static int phy_fixed_addr; 3056539c44dSDavid S. Miller static DEFINE_SPINLOCK(phy_fixed_addr_lock); 3066539c44dSDavid S. Miller 3076539c44dSDavid S. Miller struct phy_device *fixed_phy_register(unsigned int irq, 3086539c44dSDavid S. Miller struct fixed_phy_status *status, 309a5597008SAndrew Lunn int link_gpio, 3106539c44dSDavid S. Miller struct device_node *np) 3116539c44dSDavid S. Miller { 3126539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 3136539c44dSDavid S. Miller struct phy_device *phy; 3146539c44dSDavid S. Miller int phy_addr; 3156539c44dSDavid S. Miller int ret; 3166539c44dSDavid S. Miller 3176539c44dSDavid S. Miller /* Get the next available PHY address, up to PHY_MAX_ADDR */ 3186539c44dSDavid S. Miller spin_lock(&phy_fixed_addr_lock); 3196539c44dSDavid S. Miller if (phy_fixed_addr == PHY_MAX_ADDR) { 3206539c44dSDavid S. Miller spin_unlock(&phy_fixed_addr_lock); 3216539c44dSDavid S. Miller return ERR_PTR(-ENOSPC); 3226539c44dSDavid S. Miller } 3236539c44dSDavid S. Miller phy_addr = phy_fixed_addr++; 3246539c44dSDavid S. Miller spin_unlock(&phy_fixed_addr_lock); 3256539c44dSDavid S. Miller 326bd1a05eeSSergei Shtylyov ret = fixed_phy_add(irq, phy_addr, status, link_gpio); 3276539c44dSDavid S. Miller if (ret < 0) 3286539c44dSDavid S. Miller return ERR_PTR(ret); 3296539c44dSDavid S. Miller 3306539c44dSDavid S. Miller phy = get_phy_device(fmb->mii_bus, phy_addr, false); 3314914a584SSergei Shtylyov if (IS_ERR(phy)) { 3326539c44dSDavid S. Miller fixed_phy_del(phy_addr); 3336539c44dSDavid S. Miller return ERR_PTR(-EINVAL); 3346539c44dSDavid S. Miller } 3356539c44dSDavid S. Miller 3364b195360SMadalin Bucur /* propagate the fixed link values to struct phy_device */ 3374b195360SMadalin Bucur phy->link = status->link; 3384b195360SMadalin Bucur if (status->link) { 3394b195360SMadalin Bucur phy->speed = status->speed; 3404b195360SMadalin Bucur phy->duplex = status->duplex; 3414b195360SMadalin Bucur phy->pause = status->pause; 3424b195360SMadalin Bucur phy->asym_pause = status->asym_pause; 3434b195360SMadalin Bucur } 3444b195360SMadalin Bucur 3456539c44dSDavid S. Miller of_node_get(np); 346e5a03bfdSAndrew Lunn phy->mdio.dev.of_node = np; 3475a11dd7dSFlorian Fainelli phy->is_pseudo_fixed_link = true; 3486539c44dSDavid S. Miller 34934b31da4SAndrew Lunn switch (status->speed) { 35034b31da4SAndrew Lunn case SPEED_1000: 35134b31da4SAndrew Lunn phy->supported = PHY_1000BT_FEATURES; 35234b31da4SAndrew Lunn break; 35334b31da4SAndrew Lunn case SPEED_100: 35434b31da4SAndrew Lunn phy->supported = PHY_100BT_FEATURES; 35534b31da4SAndrew Lunn break; 35634b31da4SAndrew Lunn case SPEED_10: 35734b31da4SAndrew Lunn default: 35834b31da4SAndrew Lunn phy->supported = PHY_10BT_FEATURES; 35934b31da4SAndrew Lunn } 36034b31da4SAndrew Lunn 3616539c44dSDavid S. Miller ret = phy_device_register(phy); 3626539c44dSDavid S. Miller if (ret) { 3636539c44dSDavid S. Miller phy_device_free(phy); 3646539c44dSDavid S. Miller of_node_put(np); 3656539c44dSDavid S. Miller fixed_phy_del(phy_addr); 3666539c44dSDavid S. Miller return ERR_PTR(ret); 3676539c44dSDavid S. Miller } 3686539c44dSDavid S. Miller 3696539c44dSDavid S. Miller return phy; 3706539c44dSDavid S. Miller } 3716539c44dSDavid S. Miller EXPORT_SYMBOL_GPL(fixed_phy_register); 3726539c44dSDavid S. Miller 3735bcbe0f3SAndrew Lunn void fixed_phy_unregister(struct phy_device *phy) 3745bcbe0f3SAndrew Lunn { 3755bcbe0f3SAndrew Lunn phy_device_remove(phy); 3765bcbe0f3SAndrew Lunn 3775bcbe0f3SAndrew Lunn fixed_phy_del(phy->mdio.addr); 3785bcbe0f3SAndrew Lunn } 3795bcbe0f3SAndrew Lunn EXPORT_SYMBOL_GPL(fixed_phy_unregister); 3805bcbe0f3SAndrew Lunn 3816539c44dSDavid S. Miller static int __init fixed_mdio_bus_init(void) 3826539c44dSDavid S. Miller { 3836539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 3846539c44dSDavid S. Miller int ret; 3856539c44dSDavid S. Miller 3866539c44dSDavid S. Miller pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); 3876539c44dSDavid S. Miller if (IS_ERR(pdev)) { 3886539c44dSDavid S. Miller ret = PTR_ERR(pdev); 3896539c44dSDavid S. Miller goto err_pdev; 3906539c44dSDavid S. Miller } 3916539c44dSDavid S. Miller 3926539c44dSDavid S. Miller fmb->mii_bus = mdiobus_alloc(); 3936539c44dSDavid S. Miller if (fmb->mii_bus == NULL) { 3946539c44dSDavid S. Miller ret = -ENOMEM; 3956539c44dSDavid S. Miller goto err_mdiobus_reg; 3966539c44dSDavid S. Miller } 3976539c44dSDavid S. Miller 3986539c44dSDavid S. Miller snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); 3996539c44dSDavid S. Miller fmb->mii_bus->name = "Fixed MDIO Bus"; 4006539c44dSDavid S. Miller fmb->mii_bus->priv = fmb; 4016539c44dSDavid S. Miller fmb->mii_bus->parent = &pdev->dev; 4026539c44dSDavid S. Miller fmb->mii_bus->read = &fixed_mdio_read; 4036539c44dSDavid S. Miller fmb->mii_bus->write = &fixed_mdio_write; 4046539c44dSDavid S. Miller 4056539c44dSDavid S. Miller ret = mdiobus_register(fmb->mii_bus); 4066539c44dSDavid S. Miller if (ret) 4076539c44dSDavid S. Miller goto err_mdiobus_alloc; 4086539c44dSDavid S. Miller 4096539c44dSDavid S. Miller return 0; 4106539c44dSDavid S. Miller 4116539c44dSDavid S. Miller err_mdiobus_alloc: 4126539c44dSDavid S. Miller mdiobus_free(fmb->mii_bus); 4136539c44dSDavid S. Miller err_mdiobus_reg: 4146539c44dSDavid S. Miller platform_device_unregister(pdev); 4156539c44dSDavid S. Miller err_pdev: 4166539c44dSDavid S. Miller return ret; 4176539c44dSDavid S. Miller } 4186539c44dSDavid S. Miller module_init(fixed_mdio_bus_init); 4196539c44dSDavid S. Miller 4206539c44dSDavid S. Miller static void __exit fixed_mdio_bus_exit(void) 4216539c44dSDavid S. Miller { 4226539c44dSDavid S. Miller struct fixed_mdio_bus *fmb = &platform_fmb; 4236539c44dSDavid S. Miller struct fixed_phy *fp, *tmp; 4246539c44dSDavid S. Miller 4256539c44dSDavid S. Miller mdiobus_unregister(fmb->mii_bus); 4266539c44dSDavid S. Miller mdiobus_free(fmb->mii_bus); 4276539c44dSDavid S. Miller platform_device_unregister(pdev); 4286539c44dSDavid S. Miller 4296539c44dSDavid S. Miller list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { 4306539c44dSDavid S. Miller list_del(&fp->node); 4316539c44dSDavid S. Miller kfree(fp); 4326539c44dSDavid S. Miller } 4336539c44dSDavid S. Miller } 4346539c44dSDavid S. Miller module_exit(fixed_mdio_bus_exit); 4356539c44dSDavid S. Miller 4366539c44dSDavid S. Miller MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); 4376539c44dSDavid S. Miller MODULE_AUTHOR("Vitaly Bordug"); 4386539c44dSDavid S. Miller MODULE_LICENSE("GPL"); 439