Merge branch 'merge'
[pandora-kernel.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/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
48 struct fixed_phy_status {
49         u8      link;
50         u16     speed;
51         u8      duplex;
52 };
53
54 /*-----------------------------------------------------------------------------
55  *  Private information hoder for mii_bus
56  *-----------------------------------------------------------------------------*/
57 struct 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  *-----------------------------------------------------------------------------*/
72 int 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 }
89 EXPORT_SYMBOL(fixed_mdio_set_link_update);
90
91 /*-----------------------------------------------------------------------------
92  *  This is used for updating internal mii regs from the status
93  *-----------------------------------------------------------------------------*/
94 static 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
140 static 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
159 static 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
165 static int fixed_mii_reset(struct mii_bus *bus)
166 {
167         /*nothing here - no way/need to reset it*/
168         return 0;
169 }
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 static 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
297 probe_fail:
298         device_unregister(&phydev->dev);
299 bus_register_fail:
300         kfree(phydev);
301 device_create_fail:
302         kfree(dev);
303         kfree(new_bus);
304         kfree(fixed);
305
306         return err;
307 }
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         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
351 static 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
357 module_init(fixed_init);
358 module_exit(fixed_exit);