Linux Audio

Check our new training course

Loading...
v3.1
  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");
v3.15
  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			pr_warn("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			pr_warn("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, "fixed-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");