xref: /openbmc/linux/drivers/net/phy/fixed_phy.c (revision 4914a584)
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