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#include <linux/of.h>
25
26#define MII_REGS_NUM 29
27
28struct fixed_mdio_bus {
29	int irqs[PHY_MAX_ADDR];
30	struct mii_bus *mii_bus;
31	struct list_head phys;
32};
33
34struct fixed_phy {
35	int addr;
36	u16 regs[MII_REGS_NUM];
37	struct phy_device *phydev;
38	struct fixed_phy_status status;
39	int (*link_update)(struct net_device *, struct fixed_phy_status *);
40	struct list_head node;
41};
42
43static struct platform_device *pdev;
44static struct fixed_mdio_bus platform_fmb = {
45	.phys = LIST_HEAD_INIT(platform_fmb.phys),
46};
47
48static int fixed_phy_update_regs(struct fixed_phy *fp)
49{
50	u16 bmsr = BMSR_ANEGCAPABLE;
51	u16 bmcr = 0;
52	u16 lpagb = 0;
53	u16 lpa = 0;
54
55	if (!fp->status.link)
56		goto done;
57	bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
58
59	if (fp->status.duplex) {
60		bmcr |= BMCR_FULLDPLX;
61
62		switch (fp->status.speed) {
63		case 1000:
64			bmsr |= BMSR_ESTATEN;
65			bmcr |= BMCR_SPEED1000;
66			lpagb |= LPA_1000FULL;
67			break;
68		case 100:
69			bmsr |= BMSR_100FULL;
70			bmcr |= BMCR_SPEED100;
71			lpa |= LPA_100FULL;
72			break;
73		case 10:
74			bmsr |= BMSR_10FULL;
75			lpa |= LPA_10FULL;
76			break;
77		default:
78			pr_warn("fixed phy: unknown speed\n");
79			return -EINVAL;
80		}
81	} else {
82		switch (fp->status.speed) {
83		case 1000:
84			bmsr |= BMSR_ESTATEN;
85			bmcr |= BMCR_SPEED1000;
86			lpagb |= LPA_1000HALF;
87			break;
88		case 100:
89			bmsr |= BMSR_100HALF;
90			bmcr |= BMCR_SPEED100;
91			lpa |= LPA_100HALF;
92			break;
93		case 10:
94			bmsr |= BMSR_10HALF;
95			lpa |= LPA_10HALF;
96			break;
97		default:
98			pr_warn("fixed phy: unknown speed\n");
99			return -EINVAL;
100		}
101	}
102
103	if (fp->status.pause)
104		lpa |= LPA_PAUSE_CAP;
105
106	if (fp->status.asym_pause)
107		lpa |= LPA_PAUSE_ASYM;
108
109done:
110	fp->regs[MII_PHYSID1] = 0;
111	fp->regs[MII_PHYSID2] = 0;
112
113	fp->regs[MII_BMSR] = bmsr;
114	fp->regs[MII_BMCR] = bmcr;
115	fp->regs[MII_LPA] = lpa;
116	fp->regs[MII_STAT1000] = lpagb;
117
118	return 0;
119}
120
121static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
122{
123	struct fixed_mdio_bus *fmb = bus->priv;
124	struct fixed_phy *fp;
125
126	if (reg_num >= MII_REGS_NUM)
127		return -1;
128
129	/* We do not support emulating Clause 45 over Clause 22 register reads
130	 * return an error instead of bogus data.
131	 */
132	switch (reg_num) {
133	case MII_MMD_CTRL:
134	case MII_MMD_DATA:
135		return -1;
136	default:
137		break;
138	}
139
140	list_for_each_entry(fp, &fmb->phys, node) {
141		if (fp->addr == phy_addr) {
142			/* Issue callback if user registered it. */
143			if (fp->link_update) {
144				fp->link_update(fp->phydev->attached_dev,
145						&fp->status);
146				fixed_phy_update_regs(fp);
147			}
148			return fp->regs[reg_num];
149		}
150	}
151
152	return 0xFFFF;
153}
154
155static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
156			    u16 val)
157{
158	return 0;
159}
160
161/*
162 * If something weird is required to be done with link/speed,
163 * network driver is able to assign a function to implement this.
164 * May be useful for PHY's that need to be software-driven.
165 */
166int fixed_phy_set_link_update(struct phy_device *phydev,
167			      int (*link_update)(struct net_device *,
168						 struct fixed_phy_status *))
169{
170	struct fixed_mdio_bus *fmb = &platform_fmb;
171	struct fixed_phy *fp;
172
173	if (!phydev || !phydev->bus)
174		return -EINVAL;
175
176	list_for_each_entry(fp, &fmb->phys, node) {
177		if (fp->addr == phydev->addr) {
178			fp->link_update = link_update;
179			fp->phydev = phydev;
180			return 0;
181		}
182	}
183
184	return -ENOENT;
185}
186EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
187
188int fixed_phy_update_state(struct phy_device *phydev,
189			   const struct fixed_phy_status *status,
190			   const struct fixed_phy_status *changed)
191{
192	struct fixed_mdio_bus *fmb = &platform_fmb;
193	struct fixed_phy *fp;
194
195	if (!phydev || !phydev->bus)
196		return -EINVAL;
197
198	list_for_each_entry(fp, &fmb->phys, node) {
199		if (fp->addr == phydev->addr) {
200#define _UPD(x) if (changed->x) \
201	fp->status.x = status->x
202			_UPD(link);
203			_UPD(speed);
204			_UPD(duplex);
205			_UPD(pause);
206			_UPD(asym_pause);
207#undef _UPD
208			fixed_phy_update_regs(fp);
209			return 0;
210		}
211	}
212
213	return -ENOENT;
214}
215EXPORT_SYMBOL(fixed_phy_update_state);
216
217int fixed_phy_add(unsigned int irq, int phy_addr,
218		  struct fixed_phy_status *status)
219{
220	int ret;
221	struct fixed_mdio_bus *fmb = &platform_fmb;
222	struct fixed_phy *fp;
223
224	fp = kzalloc(sizeof(*fp), GFP_KERNEL);
225	if (!fp)
226		return -ENOMEM;
227
228	memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
229
230	fmb->irqs[phy_addr] = irq;
231
232	fp->addr = phy_addr;
233	fp->status = *status;
234
235	ret = fixed_phy_update_regs(fp);
236	if (ret)
237		goto err_regs;
238
239	list_add_tail(&fp->node, &fmb->phys);
240
241	return 0;
242
243err_regs:
244	kfree(fp);
245	return ret;
246}
247EXPORT_SYMBOL_GPL(fixed_phy_add);
248
249void fixed_phy_del(int phy_addr)
250{
251	struct fixed_mdio_bus *fmb = &platform_fmb;
252	struct fixed_phy *fp, *tmp;
253
254	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
255		if (fp->addr == phy_addr) {
256			list_del(&fp->node);
257			kfree(fp);
258			return;
259		}
260	}
261}
262EXPORT_SYMBOL_GPL(fixed_phy_del);
263
264static int phy_fixed_addr;
265static DEFINE_SPINLOCK(phy_fixed_addr_lock);
266
267struct phy_device *fixed_phy_register(unsigned int irq,
268				      struct fixed_phy_status *status,
269				      struct device_node *np)
270{
271	struct fixed_mdio_bus *fmb = &platform_fmb;
272	struct phy_device *phy;
273	int phy_addr;
274	int ret;
275
276	/* Get the next available PHY address, up to PHY_MAX_ADDR */
277	spin_lock(&phy_fixed_addr_lock);
278	if (phy_fixed_addr == PHY_MAX_ADDR) {
279		spin_unlock(&phy_fixed_addr_lock);
280		return ERR_PTR(-ENOSPC);
281	}
282	phy_addr = phy_fixed_addr++;
283	spin_unlock(&phy_fixed_addr_lock);
284
285	ret = fixed_phy_add(PHY_POLL, phy_addr, status);
286	if (ret < 0)
287		return ERR_PTR(ret);
288
289	phy = get_phy_device(fmb->mii_bus, phy_addr, false);
290	if (!phy || IS_ERR(phy)) {
291		fixed_phy_del(phy_addr);
292		return ERR_PTR(-EINVAL);
293	}
294
295	of_node_get(np);
296	phy->dev.of_node = np;
297
298	ret = phy_device_register(phy);
299	if (ret) {
300		phy_device_free(phy);
301		of_node_put(np);
302		fixed_phy_del(phy_addr);
303		return ERR_PTR(ret);
304	}
305
306	return phy;
307}
308EXPORT_SYMBOL_GPL(fixed_phy_register);
309
310static int __init fixed_mdio_bus_init(void)
311{
312	struct fixed_mdio_bus *fmb = &platform_fmb;
313	int ret;
314
315	pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
316	if (IS_ERR(pdev)) {
317		ret = PTR_ERR(pdev);
318		goto err_pdev;
319	}
320
321	fmb->mii_bus = mdiobus_alloc();
322	if (fmb->mii_bus == NULL) {
323		ret = -ENOMEM;
324		goto err_mdiobus_reg;
325	}
326
327	snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
328	fmb->mii_bus->name = "Fixed MDIO Bus";
329	fmb->mii_bus->priv = fmb;
330	fmb->mii_bus->parent = &pdev->dev;
331	fmb->mii_bus->read = &fixed_mdio_read;
332	fmb->mii_bus->write = &fixed_mdio_write;
333	fmb->mii_bus->irq = fmb->irqs;
334
335	ret = mdiobus_register(fmb->mii_bus);
336	if (ret)
337		goto err_mdiobus_alloc;
338
339	return 0;
340
341err_mdiobus_alloc:
342	mdiobus_free(fmb->mii_bus);
343err_mdiobus_reg:
344	platform_device_unregister(pdev);
345err_pdev:
346	return ret;
347}
348module_init(fixed_mdio_bus_init);
349
350static void __exit fixed_mdio_bus_exit(void)
351{
352	struct fixed_mdio_bus *fmb = &platform_fmb;
353	struct fixed_phy *fp, *tmp;
354
355	mdiobus_unregister(fmb->mii_bus);
356	mdiobus_free(fmb->mii_bus);
357	platform_device_unregister(pdev);
358
359	list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
360		list_del(&fp->node);
361		kfree(fp);
362	}
363}
364module_exit(fixed_mdio_bus_exit);
365
366MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
367MODULE_AUTHOR("Vitaly Bordug");
368MODULE_LICENSE("GPL");
369