Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa
[sfrench/cifs-2.6.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 static int fixed_mdio_update_regs(struct fixed_info *fixed)
93 {
94         u16 *regs = fixed->regs;
95         u16 bmsr = 0;
96         u16 bmcr = 0;
97
98         if(!regs) {
99                 printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
100                 return -EINVAL;
101         }
102
103         if(fixed->phy_status.link)
104                 bmsr |= BMSR_LSTATUS;
105
106         if(fixed->phy_status.duplex) {
107                 bmcr |= BMCR_FULLDPLX;
108
109                 switch ( fixed->phy_status.speed ) {
110                 case 100:
111                         bmsr |= BMSR_100FULL;
112                         bmcr |= BMCR_SPEED100;
113                 break;
114
115                 case 10:
116                         bmsr |= BMSR_10FULL;
117                 break;
118                 }
119         } else {
120                 switch ( fixed->phy_status.speed ) {
121                 case 100:
122                         bmsr |= BMSR_100HALF;
123                         bmcr |= BMCR_SPEED100;
124                 break;
125
126                 case 10:
127                         bmsr |= BMSR_100HALF;
128                 break;
129                 }
130         }
131
132         regs[MII_BMCR] =  bmcr;
133         regs[MII_BMSR] =  bmsr | 0x800; /*we are always capable of 10 hdx*/
134
135         return 0;
136 }
137
138 static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
139 {
140         struct fixed_info *fixed = bus->priv;
141
142         /* if user has registered link update callback, use it */
143         if(fixed->phydev)
144                 if(fixed->phydev->attached_dev) {
145                         if(fixed->link_update) {
146                                 fixed->link_update(fixed->phydev->attached_dev,
147                                                 &fixed->phy_status);
148                                 fixed_mdio_update_regs(fixed);
149                         }
150         }
151
152         if ((unsigned int)location >= fixed->regs_num)
153                 return -1;
154         return fixed->regs[location];
155 }
156
157 static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
158 {
159         /* do nothing for now*/
160         return 0;
161 }
162
163 static int fixed_mii_reset(struct mii_bus *bus)
164 {
165         /*nothing here - no way/need to reset it*/
166         return 0;
167 }
168
169 static int fixed_config_aneg(struct phy_device *phydev)
170 {
171         /* :TODO:03/13/2006 09:45:37 PM::
172          The full autoneg funcionality can be emulated,
173          but no need to have anything here for now
174          */
175         return 0;
176 }
177
178 /*-----------------------------------------------------------------------------
179  * the manual bind will do the magic - with phy_id_mask == 0
180  * match will never return true...
181  *-----------------------------------------------------------------------------*/
182 static struct phy_driver fixed_mdio_driver = {
183         .name           = "Fixed PHY",
184         .features       = PHY_BASIC_FEATURES,
185         .config_aneg    = fixed_config_aneg,
186         .read_status    = genphy_read_status,
187         .driver         = { .owner = THIS_MODULE,},
188 };
189
190 /*-----------------------------------------------------------------------------
191  *  This func is used to create all the necessary stuff, bind
192  * the fixed phy driver and register all it on the mdio_bus_type.
193  * speed is either 10 or 100, duplex is boolean.
194  * number is used to create multiple fixed PHYs, so that several devices can
195  * utilize them simultaneously.
196  *-----------------------------------------------------------------------------*/
197 static int fixed_mdio_register_device(int number, int speed, int duplex)
198 {
199         struct mii_bus *new_bus;
200         struct fixed_info *fixed;
201         struct phy_device *phydev;
202         int err = 0;
203
204         struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
205
206         if (NULL == dev)
207                 return -ENOMEM;
208
209         new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
210
211         if (NULL == new_bus) {
212                 kfree(dev);
213                 return -ENOMEM;
214         }
215         fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
216
217         if (NULL == fixed) {
218                 kfree(dev);
219                 kfree(new_bus);
220                 return -ENOMEM;
221         }
222
223         fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
224         fixed->regs_num = MII_REGS_NUM;
225         fixed->phy_status.speed = speed;
226         fixed->phy_status.duplex = duplex;
227         fixed->phy_status.link = 1;
228
229         new_bus->name = "Fixed MII Bus",
230         new_bus->read = &fixed_mii_read,
231         new_bus->write = &fixed_mii_write,
232         new_bus->reset = &fixed_mii_reset,
233
234         /*set up workspace*/
235         fixed_mdio_update_regs(fixed);
236         new_bus->priv = fixed;
237
238         new_bus->dev = dev;
239         dev_set_drvdata(dev, new_bus);
240
241         /* create phy_device and register it on the mdio bus */
242         phydev = phy_device_create(new_bus, 0, 0);
243
244         /*
245          Put the phydev pointer into the fixed pack so that bus read/write code could
246          be able to access for instance attached netdev. Well it doesn't have to do
247          so, only in case of utilizing user-specified link-update...
248          */
249         fixed->phydev = phydev;
250
251         if(NULL == phydev) {
252                 err = -ENOMEM;
253                 goto device_create_fail;
254         }
255
256         phydev->irq = PHY_IGNORE_INTERRUPT;
257         phydev->dev.bus = &mdio_bus_type;
258
259         if(number)
260                 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
261                                 "fixed_%d@%d:%d", number, speed, duplex);
262         else
263                 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
264                                 "fixed@%d:%d", speed, duplex);
265         phydev->bus = new_bus;
266
267         err = device_register(&phydev->dev);
268         if(err) {
269                 printk(KERN_ERR "Phy %s failed to register\n",
270                                 phydev->dev.bus_id);
271                 goto bus_register_fail;
272         }
273
274         /*
275            the mdio bus has phy_id match... In order not to do it
276            artificially, we are binding the driver here by hand;
277            it will be the same for all the fixed phys anyway.
278          */
279         down_write(&phydev->dev.bus->subsys.rwsem);
280
281         phydev->dev.driver = &fixed_mdio_driver.driver;
282
283         err = phydev->dev.driver->probe(&phydev->dev);
284         if(err < 0) {
285                 printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id);
286                 up_write(&phydev->dev.bus->subsys.rwsem);
287                 goto probe_fail;
288         }
289
290         err = device_bind_driver(&phydev->dev);
291
292         up_write(&phydev->dev.bus->subsys.rwsem);
293
294         if (err)
295                 goto probe_fail;
296
297         return 0;
298
299 probe_fail:
300         device_unregister(&phydev->dev);
301 bus_register_fail:
302         kfree(phydev);
303 device_create_fail:
304         kfree(dev);
305         kfree(new_bus);
306         kfree(fixed);
307
308         return err;
309 }
310
311
312 MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
313 MODULE_AUTHOR("Vitaly Bordug");
314 MODULE_LICENSE("GPL");
315
316 static int __init fixed_init(void)
317 {
318 #if 0
319         int ret;
320         int duplex = 0;
321 #endif
322
323         /* register on the bus... Not expected to be matched with anything there... */
324         phy_driver_register(&fixed_mdio_driver);
325
326         /* So let the fun begin...
327            We will create several mdio devices here, and will bound the upper
328            driver to them.
329
330            Then the external software can lookup the phy bus by searching
331            fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
332            virtual 100M Fdx phy.
333
334            In case several virtual PHYs required, the bus_id will be in form
335            fixed_<num>@<speed>:<duplex>, which make it able even to define
336            driver-specific link control callback, if for instance PHY is completely
337            SW-driven.
338
339         */
340
341 #ifdef CONFIG_FIXED_MII_DUPLEX
342 #if 0
343         duplex = 1;
344 #endif
345 #endif
346
347 #ifdef CONFIG_FIXED_MII_100_FDX
348         fixed_mdio_register_device(0, 100, 1);
349 #endif
350
351 #ifdef CONFIG_FIXED_MII_10_FDX
352         fixed_mdio_register_device(0, 10, 1);
353 #endif
354         return 0;
355 }
356
357 static void __exit fixed_exit(void)
358 {
359         phy_driver_unregister(&fixed_mdio_driver);
360         /* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
361 }
362
363 module_init(fixed_init);
364 module_exit(fixed_exit);