Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[deliverable/linux.git] / drivers / net / phy / fixed.c
CommitLineData
11b0bacd
VB
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 */
11b0bacd
VB
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/unistd.h>
21#include <linux/slab.h>
22#include <linux/interrupt.h>
23#include <linux/init.h>
24#include <linux/delay.h>
25#include <linux/netdevice.h>
26#include <linux/etherdevice.h>
27#include <linux/skbuff.h>
28#include <linux/spinlock.h>
29#include <linux/mm.h>
30#include <linux/module.h>
31#include <linux/mii.h>
32#include <linux/ethtool.h>
33#include <linux/phy.h>
34
35#include <asm/io.h>
36#include <asm/irq.h>
37#include <asm/uaccess.h>
38
39#define MII_REGS_NUM 7
40
41/*
42 The idea is to emulate normal phy behavior by responding with
43 pre-defined values to mii BMCR read, so that read_status hook could
44 take all the needed info.
45*/
46
47struct fixed_phy_status {
48 u8 link;
49 u16 speed;
50 u8 duplex;
51};
52
53/*-----------------------------------------------------------------------------
54 * Private information hoder for mii_bus
55 *-----------------------------------------------------------------------------*/
56struct fixed_info {
57 u16 *regs;
58 u8 regs_num;
59 struct fixed_phy_status phy_status;
60 struct phy_device *phydev; /* pointer to the container */
61 /* link & speed cb */
62 int(*link_update)(struct net_device*, struct fixed_phy_status*);
63
64};
65
66/*-----------------------------------------------------------------------------
67 * If something weird is required to be done with link/speed,
68 * network driver is able to assign a function to implement this.
69 * May be useful for PHY's that need to be software-driven.
70 *-----------------------------------------------------------------------------*/
71int fixed_mdio_set_link_update(struct phy_device* phydev,
72 int(*link_update)(struct net_device*, struct fixed_phy_status*))
73{
74 struct fixed_info *fixed;
75
76 if(link_update == NULL)
77 return -EINVAL;
78
79 if(phydev) {
80 if(phydev->bus) {
81 fixed = phydev->bus->priv;
82 fixed->link_update = link_update;
83 return 0;
84 }
85 }
86 return -EINVAL;
87}
88EXPORT_SYMBOL(fixed_mdio_set_link_update);
89
90/*-----------------------------------------------------------------------------
91 * This is used for updating internal mii regs from the status
92 *-----------------------------------------------------------------------------*/
93static 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
139static 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
158static 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
164static int fixed_mii_reset(struct mii_bus *bus)
165{
166 /*nothing here - no way/need to reset it*/
167 return 0;
168}
169
170static int fixed_config_aneg(struct phy_device *phydev)
171{
172 /* :TODO:03/13/2006 09:45:37 PM::
173 The full autoneg funcionality can be emulated,
174 but no need to have anything here for now
175 */
176 return 0;
177}
178
179/*-----------------------------------------------------------------------------
180 * the manual bind will do the magic - with phy_id_mask == 0
181 * match will never return true...
182 *-----------------------------------------------------------------------------*/
183static struct phy_driver fixed_mdio_driver = {
184 .name = "Fixed PHY",
185 .features = PHY_BASIC_FEATURES,
186 .config_aneg = fixed_config_aneg,
187 .read_status = genphy_read_status,
188 .driver = { .owner = THIS_MODULE,},
189};
190
191/*-----------------------------------------------------------------------------
192 * This func is used to create all the necessary stuff, bind
193 * the fixed phy driver and register all it on the mdio_bus_type.
194 * speed is either 10 or 100, duplex is boolean.
195 * number is used to create multiple fixed PHYs, so that several devices can
196 * utilize them simultaneously.
197 *-----------------------------------------------------------------------------*/
198static int fixed_mdio_register_device(int number, int speed, int duplex)
199{
200 struct mii_bus *new_bus;
201 struct fixed_info *fixed;
202 struct phy_device *phydev;
203 int err = 0;
204
205 struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
206
207 if (NULL == dev)
208 return -ENOMEM;
209
210 new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
211
212 if (NULL == new_bus) {
213 kfree(dev);
214 return -ENOMEM;
215 }
216 fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
217
218 if (NULL == fixed) {
219 kfree(dev);
220 kfree(new_bus);
221 return -ENOMEM;
222 }
223
224 fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
225 fixed->regs_num = MII_REGS_NUM;
226 fixed->phy_status.speed = speed;
227 fixed->phy_status.duplex = duplex;
228 fixed->phy_status.link = 1;
229
230 new_bus->name = "Fixed MII Bus",
231 new_bus->read = &fixed_mii_read,
232 new_bus->write = &fixed_mii_write,
233 new_bus->reset = &fixed_mii_reset,
234
235 /*set up workspace*/
236 fixed_mdio_update_regs(fixed);
237 new_bus->priv = fixed;
238
239 new_bus->dev = dev;
240 dev_set_drvdata(dev, new_bus);
241
242 /* create phy_device and register it on the mdio bus */
243 phydev = phy_device_create(new_bus, 0, 0);
244
245 /*
246 Put the phydev pointer into the fixed pack so that bus read/write code could
247 be able to access for instance attached netdev. Well it doesn't have to do
248 so, only in case of utilizing user-specified link-update...
249 */
250 fixed->phydev = phydev;
251
252 if(NULL == phydev) {
253 err = -ENOMEM;
254 goto device_create_fail;
255 }
256
a9b14973 257 phydev->irq = PHY_IGNORE_INTERRUPT;
11b0bacd
VB
258 phydev->dev.bus = &mdio_bus_type;
259
260 if(number)
261 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
262 "fixed_%d@%d:%d", number, speed, duplex);
263 else
264 snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
265 "fixed@%d:%d", speed, duplex);
266 phydev->bus = new_bus;
267
268 err = device_register(&phydev->dev);
269 if(err) {
270 printk(KERN_ERR "Phy %s failed to register\n",
271 phydev->dev.bus_id);
272 goto bus_register_fail;
273 }
274
275 /*
276 the mdio bus has phy_id match... In order not to do it
277 artificially, we are binding the driver here by hand;
278 it will be the same for all the fixed phys anyway.
279 */
280 down_write(&phydev->dev.bus->subsys.rwsem);
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 up_write(&phydev->dev.bus->subsys.rwsem);
288 goto probe_fail;
289 }
290
b7a00ecd
JG
291 err = device_bind_driver(&phydev->dev);
292
11b0bacd
VB
293 up_write(&phydev->dev.bus->subsys.rwsem);
294
b7a00ecd
JG
295 if (err)
296 goto probe_fail;
297
11b0bacd
VB
298 return 0;
299
300probe_fail:
301 device_unregister(&phydev->dev);
302bus_register_fail:
303 kfree(phydev);
304device_create_fail:
305 kfree(dev);
306 kfree(new_bus);
307 kfree(fixed);
308
309 return err;
310}
311
312
313MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
314MODULE_AUTHOR("Vitaly Bordug");
315MODULE_LICENSE("GPL");
316
317static int __init fixed_init(void)
318{
c233289c 319#if 0
11b0bacd
VB
320 int ret;
321 int duplex = 0;
c233289c 322#endif
11b0bacd
VB
323
324 /* register on the bus... Not expected to be matched with anything there... */
325 phy_driver_register(&fixed_mdio_driver);
326
327 /* So let the fun begin...
328 We will create several mdio devices here, and will bound the upper
329 driver to them.
330
331 Then the external software can lookup the phy bus by searching
332 fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
333 virtual 100M Fdx phy.
334
335 In case several virtual PHYs required, the bus_id will be in form
336 fixed_<num>@<speed>:<duplex>, which make it able even to define
337 driver-specific link control callback, if for instance PHY is completely
338 SW-driven.
339
340 */
341
342#ifdef CONFIG_FIXED_MII_DUPLEX
c233289c 343#if 0
11b0bacd
VB
344 duplex = 1;
345#endif
c233289c 346#endif
11b0bacd
VB
347
348#ifdef CONFIG_FIXED_MII_100_FDX
349 fixed_mdio_register_device(0, 100, 1);
350#endif
351
352#ifdef CONFIX_FIXED_MII_10_FDX
353 fixed_mdio_register_device(0, 10, 1);
354#endif
355 return 0;
356}
357
358static void __exit fixed_exit(void)
359{
360 phy_driver_unregister(&fixed_mdio_driver);
361 /* :WARNING:02/18/2006 04:32:40 AM:: Cleanup all the created stuff */
362}
363
364module_init(fixed_init);
365module_exit(fixed_exit);
This page took 0.067884 seconds and 5 git commands to generate.