1/*
2 * drivers/net/phy/fixed.c
3 *
4 * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode.
5 *
6 * Author: Vitaly Bordug
7 *
8 * Copyright (c) 2006 MontaVista Software, Inc.
9 *
10 * This program is free software; you can redistribute  it and/or modify it
11 * under  the terms of  the GNU General  Public License as published by the
12 * Free Software Foundation;  either version 2 of the  License, or (at your
13 * option) any later version.
14 *
15 */
16#include <linux/kernel.h>
17#include <linux/string.h>
18#include <linux/errno.h>
19#include <linux/unistd.h>
20#include <linux/slab.h>
21#include <linux/interrupt.h>
22#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/netdevice.h>
25#include <linux/etherdevice.h>
26#include <linux/skbuff.h>
27#include <linux/spinlock.h>
28#include <linux/mm.h>
29#include <linux/module.h>
30#include <linux/mii.h>
31#include <linux/ethtool.h>
32#include <linux/phy.h>
33
34#include <asm/io.h>
35#include <asm/irq.h>
36#include <asm/uaccess.h>
37
38#define MII_REGS_NUM	7
39
40/*
41    The idea is to emulate normal phy behavior by responding with
42    pre-defined values to mii BMCR read, so that read_status hook could
43    take all the needed info.
44*/
45
46struct fixed_phy_status {
47	u8 	link;
48	u16	speed;
49	u8 	duplex;
50};
51
52/*-----------------------------------------------------------------------------
53 *  Private information hoder for mii_bus
54 *-----------------------------------------------------------------------------*/
55struct fixed_info {
56	u16 *regs;
57	u8 regs_num;
58	struct fixed_phy_status phy_status;
59	struct phy_device *phydev; /* pointer to the container */
60	/* link & speed cb */
61	int(*link_update)(struct net_device*, struct fixed_phy_status*);
62
63};
64
65/*-----------------------------------------------------------------------------
66 *  If something weird is required to be done with link/speed,
67 * network driver is able to assign a function to implement this.
68 * May be useful for PHY's that need to be software-driven.
69 *-----------------------------------------------------------------------------*/
70int fixed_mdio_set_link_update(struct phy_device* phydev,
71		int(*link_update)(struct net_device*, struct fixed_phy_status*))
72{
73	struct fixed_info *fixed;
74
75	if(link_update == NULL)
76		return -EINVAL;
77
78	if(phydev) {
79		if(phydev->bus)	{
80			fixed = phydev->bus->priv;
81			fixed->link_update = link_update;
82			return 0;
83		}
84	}
85	return -EINVAL;
86}
87EXPORT_SYMBOL(fixed_mdio_set_link_update);
88
89/*-----------------------------------------------------------------------------
90 *  This is used for updating internal mii regs from the status
91 *-----------------------------------------------------------------------------*/
92#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX)
93static int fixed_mdio_update_regs(struct fixed_info *fixed)
94{
95	u16 *regs = fixed->regs;
96	u16 bmsr = 0;
97	u16 bmcr = 0;
98
99	if(!regs) {
100		printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
101		return -EINVAL;
102	}
103
104	if(fixed->phy_status.link)
105		bmsr |= BMSR_LSTATUS;
106
107	if(fixed->phy_status.duplex) {
108		bmcr |= BMCR_FULLDPLX;
109
110		switch ( fixed->phy_status.speed ) {
111		case 100:
112			bmsr |= BMSR_100FULL;
113			bmcr |= BMCR_SPEED100;
114		break;
115
116		case 10:
117			bmsr |= BMSR_10FULL;
118		break;
119		}
120	} else {
121		switch ( fixed->phy_status.speed ) {
122		case 100:
123			bmsr |= BMSR_100HALF;
124			bmcr |= BMCR_SPEED100;
125		break;
126
127		case 10:
128			bmsr |= BMSR_100HALF;
129		break;
130		}
131	}
132
133	regs[MII_BMCR] =  bmcr;
134	regs[MII_BMSR] =  bmsr | 0x800; /*we are always capable of 10 hdx*/
135
136	return 0;
137}
138
139static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
140{
141	struct fixed_info *fixed = bus->priv;
142
143	/* if user has registered link update callback, use it */
144	if(fixed->phydev)
145		if(fixed->phydev->attached_dev) {
146			if(fixed->link_update) {
147				fixed->link_update(fixed->phydev->attached_dev,
148						&fixed->phy_status);
149				fixed_mdio_update_regs(fixed);
150			}
151	}
152
153	if ((unsigned int)location >= fixed->regs_num)
154		return -1;
155	return fixed->regs[location];
156}
157
158static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
159{
160	/* do nothing for now*/
161	return 0;
162}
163
164static int fixed_mii_reset(struct mii_bus *bus)
165{
166	/*nothing here - no way/need to reset it*/
167	return 0;
168}
169#endif
170
171static int fixed_config_aneg(struct phy_device *phydev)
172{
173	/* :TODO:03/13/2006 09:45:37 PM::
174	 The full autoneg funcionality can be emulated,
175	 but no need to have anything here for now
176	 */
177	return 0;
178}
179
180/*-----------------------------------------------------------------------------
181 * the manual bind will do the magic - with phy_id_mask == 0
182 * match will never return true...
183 *-----------------------------------------------------------------------------*/
184static struct phy_driver fixed_mdio_driver = {
185	.name		= "Fixed PHY",
186	.features	= PHY_BASIC_FEATURES,
187	.config_aneg	= fixed_config_aneg,
188	.read_status	= genphy_read_status,
189	.driver 	= { .owner = THIS_MODULE,},
190};
191
192/*-----------------------------------------------------------------------------
193 *  This func is used to create all the necessary stuff, bind
194 * the fixed phy driver and register all it on the mdio_bus_type.
195 * speed is either 10 or 100, duplex is boolean.
196 * number is used to create multiple fixed PHYs, so that several devices can
197 * utilize them simultaneously.
198 *-----------------------------------------------------------------------------*/
199#if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX)
200static int fixed_mdio_register_device(int number, int speed, int duplex)
201{
202	struct mii_bus *new_bus;
203	struct fixed_info *fixed;
204	struct phy_device *phydev;
205	int err = 0;
206
207	struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
208
209	if (NULL == dev)
210		return -ENOMEM;
211
212	new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
213
214	if (NULL == new_bus) {
215		kfree(dev);
216		return -ENOMEM;
217	}
218	fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
219
220	if (NULL == fixed) {
221		kfree(dev);
222		kfree(new_bus);
223		return -ENOMEM;
224	}
225
226	fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
227	fixed->regs_num = MII_REGS_NUM;
228	fixed->phy_status.speed = speed;
229	fixed->phy_status.duplex = duplex;
230	fixed->phy_status.link = 1;
231
232	new_bus->name = "Fixed MII Bus",
233	new_bus->read = &fixed_mii_read,
234	new_bus->write = &fixed_mii_write,
235	new_bus->reset = &fixed_mii_reset,
236
237	/*set up workspace*/
238	fixed_mdio_update_regs(fixed);
239	new_bus->priv = fixed;
240
241	new_bus->dev = dev;
242	dev_set_drvdata(dev, new_bus);
243
244	/* create phy_device and register it on the mdio bus */
245	phydev = phy_device_create(new_bus, 0, 0);
246
247	/*
248	 Put the phydev pointer into the fixed pack so that bus read/write code could
249	 be able to access for instance attached netdev. Well it doesn't have to do
250	 so, only in case of utilizing user-specified link-update...
251	 */
252	fixed->phydev = phydev;
253
254	if(NULL == phydev) {
255		err = -ENOMEM;
256		goto device_create_fail;
257	}
258
259	phydev->irq = PHY_IGNORE_INTERRUPT;
260	phydev->dev.bus = &mdio_bus_type;
261
262	if(number)
263		snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
264				"fixed_%d@%d:%d", number, speed, duplex);
265	else
266		snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
267				"fixed@%d:%d", speed, duplex);
268	phydev->bus = new_bus;
269
270	err = device_register(&phydev->dev);
271	if(err) {
272		printk(KERN_ERR "Phy %s failed to register\n",
273				phydev->dev.bus_id);
274		goto bus_register_fail;
275	}
276
277	/*
278	   the mdio bus has phy_id match... In order not to do it
279	   artificially, we are binding the driver here by hand;
280	   it will be the same for all the fixed phys anyway.
281	 */
282	phydev->dev.driver = &fixed_mdio_driver.driver;
283
284	err = phydev->dev.driver->probe(&phydev->dev);
285	if(err < 0) {
286		printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
287		goto probe_fail;
288	}
289
290	err = device_bind_driver(&phydev->dev);
291	if (err)
292		goto probe_fail;
293
294	return 0;
295
296probe_fail:
297	device_unregister(&phydev->dev);
298bus_register_fail:
299	kfree(phydev);
300device_create_fail:
301	kfree(dev);
302	kfree(new_bus);
303	kfree(fixed);
304
305	return err;
306}
307#endif
308
309
310MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
311MODULE_AUTHOR("Vitaly Bordug");
312MODULE_LICENSE("GPL");
313
314static int __init fixed_init(void)
315{
316
317	/* register on the bus... Not expected to be matched with anything there... */
318	phy_driver_register(&fixed_mdio_driver);
319
320	/* So let the fun begin...
321	   We will create several mdio devices here, and will bound the upper
322	   driver to them.
323
324	   Then the external software can lookup the phy bus by searching
325	   fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
326	   virtual 100M Fdx phy.
327
328	   In case several virtual PHYs required, the bus_id will be in form
329	   fixed_<num>@<speed>:<duplex>, which make it able even to define
330	   driver-specific link control callback, if for instance PHY is completely
331	   SW-driven.
332
333	*/
334
335#ifdef CONFIG_FIXED_MII_DUPLEX
336#endif
337
338#ifdef CONFIG_FIXED_MII_100_FDX
339	fixed_mdio_register_device(0, 100, 1);
340#endif
341
342#ifdef CONFIG_FIXED_MII_10_FDX
343	fixed_mdio_register_device(0, 10, 1);
344#endif
345	return 0;
346}
347
348static void __exit fixed_exit(void)
349{
350	phy_driver_unregister(&fixed_mdio_driver);
351	/* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
352}
353
354module_init(fixed_init);
355module_exit(fixed_exit);
356