aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/fixed.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/fixed.c')
-rw-r--r--drivers/net/phy/fixed.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
new file mode 100644
index 000000000000..341036df4710
--- /dev/null
+++ b/drivers/net/phy/fixed.c
@@ -0,0 +1,358 @@
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/config.h>
17#include <linux/kernel.h>
18#include <linux/sched.h>
19#include <linux/string.h>
20#include <linux/errno.h>
21#include <linux/unistd.h>
22#include <linux/slab.h>
23#include <linux/interrupt.h>
24#include <linux/init.h>
25#include <linux/delay.h>
26#include <linux/netdevice.h>
27#include <linux/etherdevice.h>
28#include <linux/skbuff.h>
29#include <linux/spinlock.h>
30#include <linux/mm.h>
31#include <linux/module.h>
32#include <linux/mii.h>
33#include <linux/ethtool.h>
34#include <linux/phy.h>
35
36#include <asm/io.h>
37#include <asm/irq.h>
38#include <asm/uaccess.h>
39
40#define MII_REGS_NUM 7
41
42/*
43 The idea is to emulate normal phy behavior by responding with
44 pre-defined values to mii BMCR read, so that read_status hook could
45 take all the needed info.
46*/
47
48struct fixed_phy_status {
49 u8 link;
50 u16 speed;
51 u8 duplex;
52};
53
54/*-----------------------------------------------------------------------------
55 * Private information hoder for mii_bus
56 *-----------------------------------------------------------------------------*/
57struct fixed_info {
58 u16 *regs;
59 u8 regs_num;
60 struct fixed_phy_status phy_status;
61 struct phy_device *phydev; /* pointer to the container */
62 /* link & speed cb */
63 int(*link_update)(struct net_device*, struct fixed_phy_status*);
64
65};
66
67/*-----------------------------------------------------------------------------
68 * If something weird is required to be done with link/speed,
69 * network driver is able to assign a function to implement this.
70 * May be useful for PHY's that need to be software-driven.
71 *-----------------------------------------------------------------------------*/
72int fixed_mdio_set_link_update(struct phy_device* phydev,
73 int(*link_update)(struct net_device*, struct fixed_phy_status*))
74{
75 struct fixed_info *fixed;
76
77 if(link_update == NULL)
78 return -EINVAL;
79
80 if(phydev) {
81 if(phydev->bus) {
82 fixed = phydev->bus->priv;
83 fixed->link_update = link_update;
84 return 0;
85 }
86 }
87 return -EINVAL;
88}
89EXPORT_SYMBOL(fixed_mdio_set_link_update);
90
91/*-----------------------------------------------------------------------------
92 * This is used for updating internal mii regs from the status
93 *-----------------------------------------------------------------------------*/
94static int fixed_mdio_update_regs(struct fixed_info *fixed)
95{
96 u16 *regs = fixed->regs;
97 u16 bmsr = 0;
98 u16 bmcr = 0;
99
100 if(!regs) {
101 printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
102 return -EINVAL;
103 }
104
105 if(fixed->phy_status.link)
106 bmsr |= BMSR_LSTATUS;
107
108 if(fixed->phy_status.duplex) {
109 bmcr |= BMCR_FULLDPLX;
110
111 switch ( fixed->phy_status.speed ) {
112 case 100:
113 bmsr |= BMSR_100FULL;
114 bmcr |= BMCR_SPEED100;
115 break;
116
117 case 10:
118 bmsr |= BMSR_10FULL;
119 break;
120 }
121 } else {
122 switch ( fixed->phy_status.speed ) {
123 case 100:
124 bmsr |= BMSR_100HALF;
125 bmcr |= BMCR_SPEED100;
126 break;
127
128 case 10:
129 bmsr |= BMSR_100HALF;
130 break;
131 }
132 }
133
134 regs[MII_BMCR] = bmcr;
135 regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx*/
136
137 return 0;
138}
139
140static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
141{
142 struct fixed_info *fixed = bus->priv;
143
144 /* if user has registered link update callback, use it */
145 if(fixed->phydev)
146 if(fixed->phydev->attached_dev) {
147 if(fixed->link_update) {
148 fixed->link_update(fixed->phydev->attached_dev,
149 &fixed->phy_status);
150 fixed_mdio_update_regs(fixed);
151 }
152 }
153
154 if ((unsigned int)location >= fixed->regs_num)
155 return -1;
156 return fixed->regs[location];
157}
158
159static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
160{
161 /* do nothing for now*/
162 return 0;
163}
164
165static int fixed_mii_reset(struct mii_bus *bus)
166{
167 /*nothing here - no way/need to reset it*/
168 return 0;
169}
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 *-----------------------------------------------------------------------------*/
199static int fixed_mdio_register_device(int number, int speed, int duplex)
200{
201 struct mii_bus *new_bus;
202 struct fixed_info *fixed;
203 struct phy_device *phydev;
204 int err = 0;
205
206 struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
207
208 if (NULL == dev)
209 return -ENOMEM;
210
211 new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
212
213 if (NULL == new_bus) {
214 kfree(dev);
215 return -ENOMEM;
216 }
217 fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
218
219 if (NULL == fixed) {
220 kfree(dev);
221 kfree(new_bus);
222 return -ENOMEM;
223 }
224
225 fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
226 fixed->regs_num = MII_REGS_NUM;
227 fixed->phy_status.speed = speed;
228 fixed->phy_status.duplex = duplex;
229 fixed->phy_status.link = 1;
230
231 new_bus->name = "Fixed MII Bus",
232 new_bus->read = &fixed_mii_read,
233 new_bus->write = &fixed_mii_write,
234 new_bus->reset = &fixed_mii_reset,
235
236 /*set up workspace*/
237 fixed_mdio_update_regs(fixed);
238 new_bus->priv = fixed;
239
240 new_bus->dev = dev;
241 dev_set_drvdata(dev, new_bus);
242
243 /* create phy_device and register it on the mdio bus */
244 phydev = phy_device_create(new_bus, 0, 0);
245
246 /*
247 Put the phydev pointer into the fixed pack so that bus read/write code could
248 be able to access for instance attached netdev. Well it doesn't have to do
249 so, only in case of utilizing user-specified link-update...
250 */
251 fixed->phydev = phydev;
252
253 if(NULL == phydev) {
254 err = -ENOMEM;
255 goto device_create_fail;
256 }
257
258 phydev->irq = -1;
259 phydev->dev.bus = &mdio_bus_type;
260
261 if(number)
262 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
263 "fixed_%d@%d:%d", number, speed, duplex);
264 else
265 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
266 "fixed@%d:%d", speed, duplex);
267 phydev->bus = new_bus;
268
269 err = device_register(&phydev->dev);
270 if(err) {
271 printk(KERN_ERR "Phy %s failed to register\n",
272 phydev->dev.bus_id);
273 goto bus_register_fail;
274 }
275
276 /*
277 the mdio bus has phy_id match... In order not to do it
278 artificially, we are binding the driver here by hand;
279 it will be the same for all the fixed phys anyway.
280 */
281 down_write(&phydev->dev.bus->subsys.rwsem);
282
283 phydev->dev.driver = &fixed_mdio_driver.driver;
284
285 err = phydev->dev.driver->probe(&phydev->dev);
286 if(err < 0) {
287 printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
288 up_write(&phydev->dev.bus->subsys.rwsem);
289 goto probe_fail;
290 }
291
292 device_bind_driver(&phydev->dev);
293 up_write(&phydev->dev.bus->subsys.rwsem);
294
295 return 0;
296
297probe_fail:
298 device_unregister(&phydev->dev);
299bus_register_fail:
300 kfree(phydev);
301device_create_fail:
302 kfree(dev);
303 kfree(new_bus);
304 kfree(fixed);
305
306 return err;
307}
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 int ret;
317 int duplex = 0;
318
319 /* register on the bus... Not expected to be matched with anything there... */
320 phy_driver_register(&fixed_mdio_driver);
321
322 /* So let the fun begin...
323 We will create several mdio devices here, and will bound the upper
324 driver to them.
325
326 Then the external software can lookup the phy bus by searching
327 fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
328 virtual 100M Fdx phy.
329
330 In case several virtual PHYs required, the bus_id will be in form
331 fixed_<num>@<speed>:<duplex>, which make it able even to define
332 driver-specific link control callback, if for instance PHY is completely
333 SW-driven.
334
335 */
336
337#ifdef CONFIG_FIXED_MII_DUPLEX
338 duplex = 1;
339#endif
340
341#ifdef CONFIG_FIXED_MII_100_FDX
342 fixed_mdio_register_device(0, 100, 1);
343#endif
344
345#ifdef CONFIX_FIXED_MII_10_FDX
346 fixed_mdio_register_device(0, 10, 1);
347#endif
348 return 0;
349}
350
351static void __exit fixed_exit(void)
352{
353 phy_driver_unregister(&fixed_mdio_driver);
354 /* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
355}
356
357module_init(fixed_init);
358module_exit(fixed_exit);