]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/net/phy/fixed.c
Pull battery into release branch
[linux-2.6-omap-h63xx.git] / drivers / net / phy / fixed.c
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
46 struct fixed_phy_status {
47         u8      link;
48         u16     speed;
49         u8      duplex;
50 };
51
52 /*-----------------------------------------------------------------------------
53  *  Private information hoder for mii_bus
54  *-----------------------------------------------------------------------------*/
55 struct 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  *-----------------------------------------------------------------------------*/
70 int 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 }
87 EXPORT_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)
93 static 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
139 static 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
158 static 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
164 static 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
171 static 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  *-----------------------------------------------------------------------------*/
184 static 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)
200 static 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
296 probe_fail:
297         device_unregister(&phydev->dev);
298 bus_register_fail:
299         kfree(phydev);
300 device_create_fail:
301         kfree(dev);
302         kfree(new_bus);
303         kfree(fixed);
304
305         return err;
306 }
307 #endif
308
309
310 MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
311 MODULE_AUTHOR("Vitaly Bordug");
312 MODULE_LICENSE("GPL");
313
314 static int __init fixed_init(void)
315 {
316 #if 0
317         int ret;
318         int duplex = 0;
319 #endif
320
321         /* register on the bus... Not expected to be matched with anything there... */
322         phy_driver_register(&fixed_mdio_driver);
323
324         /* So let the fun begin...
325            We will create several mdio devices here, and will bound the upper
326            driver to them.
327
328            Then the external software can lookup the phy bus by searching
329            fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
330            virtual 100M Fdx phy.
331
332            In case several virtual PHYs required, the bus_id will be in form
333            fixed_<num>@<speed>:<duplex>, which make it able even to define
334            driver-specific link control callback, if for instance PHY is completely
335            SW-driven.
336
337         */
338
339 #ifdef CONFIG_FIXED_MII_DUPLEX
340 #if 0
341         duplex = 1;
342 #endif
343 #endif
344
345 #ifdef CONFIG_FIXED_MII_100_FDX
346         fixed_mdio_register_device(0, 100, 1);
347 #endif
348
349 #ifdef CONFIG_FIXED_MII_10_FDX
350         fixed_mdio_register_device(0, 10, 1);
351 #endif
352         return 0;
353 }
354
355 static void __exit fixed_exit(void)
356 {
357         phy_driver_unregister(&fixed_mdio_driver);
358         /* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
359 }
360
361 module_init(fixed_init);
362 module_exit(fixed_exit);