blob: 66da91bb1388945bac1d2c0c259bf3616a6b1e4d [file] [log] [blame]
Vitaly Bordug11b0bac2006-08-14 23:00:29 -07001/*
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 */
Vitaly Bordug11b0bac2006-08-14 23:00:29 -070016#include <linux/kernel.h>
Vitaly Bordug11b0bac2006-08-14 23:00:29 -070017#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
46struct fixed_phy_status {
47 u8 link;
48 u16 speed;
49 u8 duplex;
50};
51
52/*-----------------------------------------------------------------------------
53 * Private information hoder for mii_bus
54 *-----------------------------------------------------------------------------*/
55struct 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 *-----------------------------------------------------------------------------*/
70int 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}
87EXPORT_SYMBOL(fixed_mdio_set_link_update);
88
89/*-----------------------------------------------------------------------------
90 * This is used for updating internal mii regs from the status
91 *-----------------------------------------------------------------------------*/
92static 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
138static 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
157static 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
163static int fixed_mii_reset(struct mii_bus *bus)
164{
165 /*nothing here - no way/need to reset it*/
166 return 0;
167}
168
169static 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 *-----------------------------------------------------------------------------*/
182static 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 *-----------------------------------------------------------------------------*/
197static 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
Andy Fleminga9b14972006-10-19 19:52:26 -0500256 phydev->irq = PHY_IGNORE_INTERRUPT;
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700257 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
Jeff Garzikb7a00ec2006-10-01 07:27:46 -0400290 err = device_bind_driver(&phydev->dev);
291
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700292 up_write(&phydev->dev.bus->subsys.rwsem);
293
Jeff Garzikb7a00ec2006-10-01 07:27:46 -0400294 if (err)
295 goto probe_fail;
296
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700297 return 0;
298
299probe_fail:
300 device_unregister(&phydev->dev);
301bus_register_fail:
302 kfree(phydev);
303device_create_fail:
304 kfree(dev);
305 kfree(new_bus);
306 kfree(fixed);
307
308 return err;
309}
310
311
312MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
313MODULE_AUTHOR("Vitaly Bordug");
314MODULE_LICENSE("GPL");
315
316static int __init fixed_init(void)
317{
Jeff Garzikc2332892006-09-13 14:33:12 -0400318#if 0
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700319 int ret;
320 int duplex = 0;
Jeff Garzikc2332892006-09-13 14:33:12 -0400321#endif
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700322
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
Jeff Garzikc2332892006-09-13 14:33:12 -0400342#if 0
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700343 duplex = 1;
344#endif
Jeff Garzikc2332892006-09-13 14:33:12 -0400345#endif
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700346
347#ifdef CONFIG_FIXED_MII_100_FDX
348 fixed_mdio_register_device(0, 100, 1);
349#endif
350
Vitaly Bordugb1f54ba2007-01-27 00:00:04 -0800351#ifdef CONFIG_FIXED_MII_10_FDX
Vitaly Bordug11b0bac2006-08-14 23:00:29 -0700352 fixed_mdio_register_device(0, 10, 1);
353#endif
354 return 0;
355}
356
357static 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
363module_init(fixed_init);
364module_exit(fixed_exit);