]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/net/phy/fixed.c
Merge branch 'master' of git://git.infradead.org/~dedekind/ubi-2.6
[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 #include <linux/phy_fixed.h>
34
35 #include <asm/io.h>
36 #include <asm/irq.h>
37 #include <asm/uaccess.h>
38
39 /* we need to track the allocated pointers in order to free them on exit */
40 static struct fixed_info *fixed_phy_ptrs[CONFIG_FIXED_MII_AMNT*MAX_PHY_AMNT];
41
42 /*-----------------------------------------------------------------------------
43  *  If something weird is required to be done with link/speed,
44  * network driver is able to assign a function to implement this.
45  * May be useful for PHY's that need to be software-driven.
46  *-----------------------------------------------------------------------------*/
47 int fixed_mdio_set_link_update(struct phy_device *phydev,
48                                int (*link_update) (struct net_device *,
49                                                    struct fixed_phy_status *))
50 {
51         struct fixed_info *fixed;
52
53         if (link_update == NULL)
54                 return -EINVAL;
55
56         if (phydev) {
57                 if (phydev->bus) {
58                         fixed = phydev->bus->priv;
59                         fixed->link_update = link_update;
60                         return 0;
61                 }
62         }
63         return -EINVAL;
64 }
65
66 EXPORT_SYMBOL(fixed_mdio_set_link_update);
67
68 struct fixed_info *fixed_mdio_get_phydev (int phydev_ind)
69 {
70         if (phydev_ind >= MAX_PHY_AMNT)
71                 return NULL;
72         return fixed_phy_ptrs[phydev_ind];
73 }
74
75 EXPORT_SYMBOL(fixed_mdio_get_phydev);
76
77 /*-----------------------------------------------------------------------------
78  *  This is used for updating internal mii regs from the status
79  *-----------------------------------------------------------------------------*/
80 #if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX)
81 static int fixed_mdio_update_regs(struct fixed_info *fixed)
82 {
83         u16 *regs = fixed->regs;
84         u16 bmsr = 0;
85         u16 bmcr = 0;
86
87         if (!regs) {
88                 printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
89                 return -EINVAL;
90         }
91
92         if (fixed->phy_status.link)
93                 bmsr |= BMSR_LSTATUS;
94
95         if (fixed->phy_status.duplex) {
96                 bmcr |= BMCR_FULLDPLX;
97
98                 switch (fixed->phy_status.speed) {
99                 case 100:
100                         bmsr |= BMSR_100FULL;
101                         bmcr |= BMCR_SPEED100;
102                         break;
103
104                 case 10:
105                         bmsr |= BMSR_10FULL;
106                         break;
107                 }
108         } else {
109                 switch (fixed->phy_status.speed) {
110                 case 100:
111                         bmsr |= BMSR_100HALF;
112                         bmcr |= BMCR_SPEED100;
113                         break;
114
115                 case 10:
116                         bmsr |= BMSR_100HALF;
117                         break;
118                 }
119         }
120
121         regs[MII_BMCR] = bmcr;
122         regs[MII_BMSR] = bmsr | 0x800;  /*we are always capable of 10 hdx */
123
124         return 0;
125 }
126
127 static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
128 {
129         struct fixed_info *fixed = bus->priv;
130
131         /* if user has registered link update callback, use it */
132         if (fixed->phydev)
133                 if (fixed->phydev->attached_dev) {
134                         if (fixed->link_update) {
135                                 fixed->link_update(fixed->phydev->attached_dev,
136                                                    &fixed->phy_status);
137                                 fixed_mdio_update_regs(fixed);
138                         }
139                 }
140
141         if ((unsigned int)location >= fixed->regs_num)
142                 return -1;
143         return fixed->regs[location];
144 }
145
146 static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location,
147                            u16 val)
148 {
149         /* do nothing for now */
150         return 0;
151 }
152
153 static int fixed_mii_reset(struct mii_bus *bus)
154 {
155         /*nothing here - no way/need to reset it */
156         return 0;
157 }
158 #endif
159
160 static int fixed_config_aneg(struct phy_device *phydev)
161 {
162         /* :TODO:03/13/2006 09:45:37 PM::
163            The full autoneg funcionality can be emulated,
164            but no need to have anything here for now
165          */
166         return 0;
167 }
168
169 /*-----------------------------------------------------------------------------
170  * the manual bind will do the magic - with phy_id_mask == 0
171  * match will never return true...
172  *-----------------------------------------------------------------------------*/
173 static struct phy_driver fixed_mdio_driver = {
174         .name = "Fixed PHY",
175 #ifdef CONFIG_FIXED_MII_1000_FDX
176         .features = PHY_GBIT_FEATURES,
177 #else
178         .features = PHY_BASIC_FEATURES,
179 #endif
180         .config_aneg = fixed_config_aneg,
181         .read_status = genphy_read_status,
182         .driver = { .owner = THIS_MODULE, },
183 };
184
185 static void fixed_mdio_release(struct device *dev)
186 {
187         struct phy_device *phydev = container_of(dev, struct phy_device, dev);
188         struct mii_bus *bus = phydev->bus;
189         struct fixed_info *fixed = bus->priv;
190
191         kfree(phydev);
192         kfree(bus->dev);
193         kfree(bus);
194         kfree(fixed->regs);
195         kfree(fixed);
196 }
197
198 /*-----------------------------------------------------------------------------
199  *  This func is used to create all the necessary stuff, bind
200  * the fixed phy driver and register all it on the mdio_bus_type.
201  * speed is either 10 or 100 or 1000, duplex is boolean.
202  * number is used to create multiple fixed PHYs, so that several devices can
203  * utilize them simultaneously.
204  *
205  * The device on mdio bus will look like [bus_id]:[phy_id],
206  * bus_id = number
207  * phy_id = speed+duplex.
208  *-----------------------------------------------------------------------------*/
209 #if defined(CONFIG_FIXED_MII_100_FDX) || defined(CONFIG_FIXED_MII_10_FDX) || defined(CONFIG_FIXED_MII_1000_FDX)
210 struct fixed_info *fixed_mdio_register_device(
211         int bus_id, int speed, int duplex, u8 phy_id)
212 {
213         struct mii_bus *new_bus;
214         struct fixed_info *fixed;
215         struct phy_device *phydev;
216         int err;
217
218         struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
219
220         if (dev == NULL)
221                 goto err_dev_alloc;
222
223         new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
224
225         if (new_bus == NULL)
226                 goto err_bus_alloc;
227
228         fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
229
230         if (fixed == NULL)
231                 goto err_fixed_alloc;
232
233         fixed->regs = kzalloc(MII_REGS_NUM * sizeof(int), GFP_KERNEL);
234         if (NULL == fixed->regs)
235                 goto err_fixed_regs_alloc;
236
237         fixed->regs_num = MII_REGS_NUM;
238         fixed->phy_status.speed = speed;
239         fixed->phy_status.duplex = duplex;
240         fixed->phy_status.link = 1;
241
242         new_bus->name = "Fixed MII Bus";
243         new_bus->read = &fixed_mii_read;
244         new_bus->write = &fixed_mii_write;
245         new_bus->reset = &fixed_mii_reset;
246         /*set up workspace */
247         fixed_mdio_update_regs(fixed);
248         new_bus->priv = fixed;
249
250         new_bus->dev = dev;
251         dev_set_drvdata(dev, new_bus);
252
253         /* create phy_device and register it on the mdio bus */
254         phydev = phy_device_create(new_bus, 0, 0);
255         if (phydev == NULL)
256                 goto err_phy_dev_create;
257
258         /*
259          * Put the phydev pointer into the fixed pack so that bus read/write
260          * code could be able to access for instance attached netdev. Well it
261          * doesn't have to do so, only in case of utilizing user-specified
262          * link-update...
263          */
264
265         fixed->phydev = phydev;
266         phydev->speed = speed;
267         phydev->duplex = duplex;
268
269         phydev->irq = PHY_IGNORE_INTERRUPT;
270         phydev->dev.bus = &mdio_bus_type;
271
272         snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
273                  PHY_ID_FMT, bus_id, phy_id);
274
275         phydev->bus = new_bus;
276
277         phydev->dev.driver = &fixed_mdio_driver.driver;
278         phydev->dev.release = fixed_mdio_release;
279         err = phydev->dev.driver->probe(&phydev->dev);
280         if (err < 0) {
281                 printk(KERN_ERR "Phy %s: problems with fixed driver\n",
282                        phydev->dev.bus_id);
283                 goto err_out;
284         }
285         err = device_register(&phydev->dev);
286         if (err) {
287                 printk(KERN_ERR "Phy %s failed to register\n",
288                        phydev->dev.bus_id);
289                 goto err_out;
290         }
291         //phydev->state = PHY_RUNNING; /* make phy go up quick, but in 10Mbit/HDX
292         return fixed;
293
294 err_out:
295         kfree(phydev);
296 err_phy_dev_create:
297         kfree(fixed->regs);
298 err_fixed_regs_alloc:
299         kfree(fixed);
300 err_fixed_alloc:
301         kfree(new_bus);
302 err_bus_alloc:
303         kfree(dev);
304 err_dev_alloc:
305
306         return NULL;
307
308 }
309 #endif
310
311 MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
312 MODULE_AUTHOR("Vitaly Bordug");
313 MODULE_LICENSE("GPL");
314
315 static int __init fixed_init(void)
316 {
317         int cnt = 0;
318         int i;
319 /* register on the bus... Not expected to be matched
320  * with anything there...
321  *
322  */
323         phy_driver_register(&fixed_mdio_driver);
324
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  * for 0:101, to be connected to the virtual 100M Fdx phy.
330  *
331  * In case several virtual PHYs required, the bus_id will be in form
332  * [num]:[duplex]+[speed], which make it able even to define
333  * driver-specific link control callback, if for instance PHY is
334  * completely SW-driven.
335  */
336         for (i=1; i <= CONFIG_FIXED_MII_AMNT; i++) {
337 #ifdef CONFIG_FIXED_MII_1000_FDX
338                 fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(0, 1000, 1, i);
339 #endif
340 #ifdef CONFIG_FIXED_MII_100_FDX
341                 fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(1, 100, 1, i);
342 #endif
343 #ifdef CONFIG_FIXED_MII_10_FDX
344                 fixed_phy_ptrs[cnt++] = fixed_mdio_register_device(2, 10, 1, i);
345 #endif
346         }
347
348         return 0;
349 }
350
351 static void __exit fixed_exit(void)
352 {
353         int i;
354
355         phy_driver_unregister(&fixed_mdio_driver);
356         for (i=0; i < MAX_PHY_AMNT; i++)
357                 if ( fixed_phy_ptrs[i] )
358                         device_unregister(&fixed_phy_ptrs[i]->phydev->dev);
359 }
360
361 module_init(fixed_init);
362 module_exit(fixed_exit);