aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/fixed.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/fixed.c')
-rw-r--r--drivers/net/phy/fixed.c445
1 files changed, 168 insertions, 277 deletions
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
index 56191822fa26..73b6d39ef6b0 100644
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -1,362 +1,253 @@
1/* 1/*
2 * drivers/net/phy/fixed.c 2 * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
3 * 3 *
4 * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode. 4 * Author: Vitaly Bordug <vbordug@ru.mvista.com>
5 * Anton Vorontsov <avorontsov@ru.mvista.com>
5 * 6 *
6 * Author: Vitaly Bordug 7 * Copyright (c) 2006-2007 MontaVista Software, Inc.
7 *
8 * Copyright (c) 2006 MontaVista Software, Inc.
9 * 8 *
10 * This program is free software; you can redistribute it and/or modify it 9 * 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 10 * 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 11 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. 12 * option) any later version.
14 *
15 */ 13 */
14
16#include <linux/kernel.h> 15#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> 16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/list.h>
30#include <linux/mii.h> 19#include <linux/mii.h>
31#include <linux/ethtool.h>
32#include <linux/phy.h> 20#include <linux/phy.h>
33#include <linux/phy_fixed.h> 21#include <linux/phy_fixed.h>
34 22
35#include <asm/io.h> 23#define MII_REGS_NUM 29
36#include <asm/irq.h>
37#include <asm/uaccess.h>
38 24
39/* we need to track the allocated pointers in order to free them on exit */ 25struct fixed_mdio_bus {
40static struct fixed_info *fixed_phy_ptrs[CONFIG_FIXED_MII_AMNT*MAX_PHY_AMNT]; 26 int irqs[PHY_MAX_ADDR];
41 27 struct mii_bus mii_bus;
42/*----------------------------------------------------------------------------- 28 struct list_head phys;
43 * If something weird is required to be done with link/speed, 29};
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 *-----------------------------------------------------------------------------*/
47int 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
66EXPORT_SYMBOL(fixed_mdio_set_link_update);
67 30
68struct fixed_info *fixed_mdio_get_phydev (int phydev_ind) 31struct fixed_phy {
69{ 32 int id;
70 if (phydev_ind >= MAX_PHY_AMNT) 33 u16 regs[MII_REGS_NUM];
71 return NULL; 34 struct phy_device *phydev;
72 return fixed_phy_ptrs[phydev_ind]; 35 struct fixed_phy_status status;
73} 36 int (*link_update)(struct net_device *, struct fixed_phy_status *);
37 struct list_head node;
38};
74 39
75EXPORT_SYMBOL(fixed_mdio_get_phydev); 40static struct platform_device *pdev;
41static struct fixed_mdio_bus platform_fmb = {
42 .phys = LIST_HEAD_INIT(platform_fmb.phys),
43};
76 44
77/*----------------------------------------------------------------------------- 45static int fixed_phy_update_regs(struct fixed_phy *fp)
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)
81static int fixed_mdio_update_regs(struct fixed_info *fixed)
82{ 46{
83 u16 *regs = fixed->regs; 47 u16 bmsr = BMSR_ANEGCAPABLE;
84 u16 bmsr = 0;
85 u16 bmcr = 0; 48 u16 bmcr = 0;
49 u16 lpagb = 0;
50 u16 lpa = 0;
86 51
87 if (!regs) { 52 if (fp->status.duplex) {
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; 53 bmcr |= BMCR_FULLDPLX;
97 54
98 switch (fixed->phy_status.speed) { 55 switch (fp->status.speed) {
56 case 1000:
57 bmsr |= BMSR_ESTATEN;
58 bmcr |= BMCR_SPEED1000;
59 lpagb |= LPA_1000FULL;
60 break;
99 case 100: 61 case 100:
100 bmsr |= BMSR_100FULL; 62 bmsr |= BMSR_100FULL;
101 bmcr |= BMCR_SPEED100; 63 bmcr |= BMCR_SPEED100;
64 lpa |= LPA_100FULL;
102 break; 65 break;
103
104 case 10: 66 case 10:
105 bmsr |= BMSR_10FULL; 67 bmsr |= BMSR_10FULL;
68 lpa |= LPA_10FULL;
106 break; 69 break;
70 default:
71 printk(KERN_WARNING "fixed phy: unknown speed\n");
72 return -EINVAL;
107 } 73 }
108 } else { 74 } else {
109 switch (fixed->phy_status.speed) { 75 switch (fp->status.speed) {
76 case 1000:
77 bmsr |= BMSR_ESTATEN;
78 bmcr |= BMCR_SPEED1000;
79 lpagb |= LPA_1000HALF;
80 break;
110 case 100: 81 case 100:
111 bmsr |= BMSR_100HALF; 82 bmsr |= BMSR_100HALF;
112 bmcr |= BMCR_SPEED100; 83 bmcr |= BMCR_SPEED100;
84 lpa |= LPA_100HALF;
113 break; 85 break;
114
115 case 10: 86 case 10:
116 bmsr |= BMSR_100HALF; 87 bmsr |= BMSR_10HALF;
88 lpa |= LPA_10HALF;
117 break; 89 break;
90 default:
91 printk(KERN_WARNING "fixed phy: unknown speed\n");
92 return -EINVAL;
118 } 93 }
119 } 94 }
120 95
121 regs[MII_BMCR] = bmcr; 96 if (fp->status.link)
122 regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx */ 97 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
98
99 if (fp->status.pause)
100 lpa |= LPA_PAUSE_CAP;
101
102 if (fp->status.asym_pause)
103 lpa |= LPA_PAUSE_ASYM;
104
105 fp->regs[MII_PHYSID1] = fp->id >> 16;
106 fp->regs[MII_PHYSID2] = fp->id;
107
108 fp->regs[MII_BMSR] = bmsr;
109 fp->regs[MII_BMCR] = bmcr;
110 fp->regs[MII_LPA] = lpa;
111 fp->regs[MII_STAT1000] = lpagb;
123 112
124 return 0; 113 return 0;
125} 114}
126 115
127static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location) 116static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
128{ 117{
129 struct fixed_info *fixed = bus->priv; 118 struct fixed_mdio_bus *fmb = container_of(bus, struct fixed_mdio_bus,
130 119 mii_bus);
131 /* if user has registered link update callback, use it */ 120 struct fixed_phy *fp;
132 if (fixed->phydev) 121
133 if (fixed->phydev->attached_dev) { 122 if (reg_num >= MII_REGS_NUM)
134 if (fixed->link_update) { 123 return -1;
135 fixed->link_update(fixed->phydev->attached_dev, 124
136 &fixed->phy_status); 125 list_for_each_entry(fp, &fmb->phys, node) {
137 fixed_mdio_update_regs(fixed); 126 if (fp->id == phy_id) {
127 /* Issue callback if user registered it. */
128 if (fp->link_update) {
129 fp->link_update(fp->phydev->attached_dev,
130 &fp->status);
131 fixed_phy_update_regs(fp);
138 } 132 }
133 return fp->regs[reg_num];
139 } 134 }
135 }
140 136
141 if ((unsigned int)location >= fixed->regs_num) 137 return 0xFFFF;
142 return -1;
143 return fixed->regs[location];
144} 138}
145 139
146static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, 140static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
147 u16 val) 141 u16 val)
148{ 142{
149 /* do nothing for now */
150 return 0; 143 return 0;
151} 144}
152 145
153static int fixed_mii_reset(struct mii_bus *bus) 146/*
147 * If something weird is required to be done with link/speed,
148 * network driver is able to assign a function to implement this.
149 * May be useful for PHY's that need to be software-driven.
150 */
151int fixed_phy_set_link_update(struct phy_device *phydev,
152 int (*link_update)(struct net_device *,
153 struct fixed_phy_status *))
154{ 154{
155 /*nothing here - no way/need to reset it */ 155 struct fixed_mdio_bus *fmb = &platform_fmb;
156 return 0; 156 struct fixed_phy *fp;
157}
158#endif
159 157
160static int fixed_config_aneg(struct phy_device *phydev) 158 if (!link_update || !phydev || !phydev->bus)
161{ 159 return -EINVAL;
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 160
169/*----------------------------------------------------------------------------- 161 list_for_each_entry(fp, &fmb->phys, node) {
170 * the manual bind will do the magic - with phy_id_mask == 0 162 if (fp->id == phydev->phy_id) {
171 * match will never return true... 163 fp->link_update = link_update;
172 *-----------------------------------------------------------------------------*/ 164 fp->phydev = phydev;
173static struct phy_driver fixed_mdio_driver = { 165 return 0;
174 .name = "Fixed PHY", 166 }
175#ifdef CONFIG_FIXED_MII_1000_FDX 167 }
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 168
185static void fixed_mdio_release(struct device *dev) 169 return -ENOENT;
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} 170}
171EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
197 172
198/*----------------------------------------------------------------------------- 173int fixed_phy_add(unsigned int irq, int phy_id,
199 * This func is used to create all the necessary stuff, bind 174 struct fixed_phy_status *status)
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)
210struct fixed_info *fixed_mdio_register_device(
211 int bus_id, int speed, int duplex, u8 phy_id)
212{ 175{
213 struct mii_bus *new_bus; 176 int ret;
214 struct fixed_info *fixed; 177 struct fixed_mdio_bus *fmb = &platform_fmb;
215 struct phy_device *phydev; 178 struct fixed_phy *fp;
216 int err;
217 179
218 struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); 180 fp = kzalloc(sizeof(*fp), GFP_KERNEL);
181 if (!fp)
182 return -ENOMEM;
219 183
220 if (dev == NULL) 184 memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
221 goto err_dev_alloc;
222 185
223 new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); 186 fmb->irqs[phy_id] = irq;
224 187
225 if (new_bus == NULL) 188 fp->id = phy_id;
226 goto err_bus_alloc; 189 fp->status = *status;
227 190
228 fixed = kzalloc(sizeof(struct fixed_info), GFP_KERNEL); 191 ret = fixed_phy_update_regs(fp);
192 if (ret)
193 goto err_regs;
229 194
230 if (fixed == NULL) 195 list_add_tail(&fp->node, &fmb->phys);
231 goto err_fixed_alloc;
232 196
233 fixed->regs = kzalloc(MII_REGS_NUM * sizeof(int), GFP_KERNEL); 197 return 0;
234 if (NULL == fixed->regs)
235 goto err_fixed_regs_alloc;
236 198
237 fixed->regs_num = MII_REGS_NUM; 199err_regs:
238 fixed->phy_status.speed = speed; 200 kfree(fp);
239 fixed->phy_status.duplex = duplex; 201 return ret;
240 fixed->phy_status.link = 1; 202}
203EXPORT_SYMBOL_GPL(fixed_phy_add);
241 204
242 new_bus->name = "Fixed MII Bus"; 205static int __init fixed_mdio_bus_init(void)
243 new_bus->read = &fixed_mii_read; 206{
244 new_bus->write = &fixed_mii_write; 207 struct fixed_mdio_bus *fmb = &platform_fmb;
245 new_bus->reset = &fixed_mii_reset; 208 int ret;
246 /*set up workspace */
247 fixed_mdio_update_regs(fixed);
248 new_bus->priv = fixed;
249 209
250 new_bus->dev = dev; 210 pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
251 dev_set_drvdata(dev, new_bus); 211 if (!pdev) {
212 ret = -ENOMEM;
213 goto err_pdev;
214 }
252 215
253 /* create phy_device and register it on the mdio bus */ 216 fmb->mii_bus.id = 0;
254 phydev = phy_device_create(new_bus, 0, 0); 217 fmb->mii_bus.name = "Fixed MDIO Bus";
255 if (phydev == NULL) 218 fmb->mii_bus.dev = &pdev->dev;
256 goto err_phy_dev_create; 219 fmb->mii_bus.read = &fixed_mdio_read;
220 fmb->mii_bus.write = &fixed_mdio_write;
221 fmb->mii_bus.irq = fmb->irqs;
257 222
258 /* 223 ret = mdiobus_register(&fmb->mii_bus);
259 * Put the phydev pointer into the fixed pack so that bus read/write 224 if (ret)
260 * code could be able to access for instance attached netdev. Well it 225 goto err_mdiobus_reg;
261 * doesn't have to do so, only in case of utilizing user-specified
262 * link-update...
263 */
264 226
265 fixed->phydev = phydev; 227 return 0;
266 phydev->speed = speed;
267 phydev->duplex = duplex;
268 228
269 phydev->irq = PHY_IGNORE_INTERRUPT; 229err_mdiobus_reg:
270 phydev->dev.bus = &mdio_bus_type; 230 platform_device_unregister(pdev);
231err_pdev:
232 return ret;
233}
234module_init(fixed_mdio_bus_init);
271 235
272 snprintf(phydev->dev.bus_id, BUS_ID_SIZE, 236static void __exit fixed_mdio_bus_exit(void)
273 PHY_ID_FMT, bus_id, phy_id); 237{
238 struct fixed_mdio_bus *fmb = &platform_fmb;
239 struct fixed_phy *fp;
274 240
275 phydev->bus = new_bus; 241 mdiobus_unregister(&fmb->mii_bus);
242 platform_device_unregister(pdev);
276 243
277 phydev->dev.driver = &fixed_mdio_driver.driver; 244 list_for_each_entry(fp, &fmb->phys, node) {
278 phydev->dev.release = fixed_mdio_release; 245 list_del(&fp->node);
279 err = phydev->dev.driver->probe(&phydev->dev); 246 kfree(fp);
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 } 247 }
291 //phydev->state = PHY_RUNNING; /* make phy go up quick, but in 10Mbit/HDX
292 return fixed;
293
294err_out:
295 kfree(phydev);
296err_phy_dev_create:
297 kfree(fixed->regs);
298err_fixed_regs_alloc:
299 kfree(fixed);
300err_fixed_alloc:
301 kfree(new_bus);
302err_bus_alloc:
303 kfree(dev);
304err_dev_alloc:
305
306 return NULL;
307
308} 248}
309#endif 249module_exit(fixed_mdio_bus_exit);
310 250
311MODULE_DESCRIPTION("Fixed PHY device & driver for PAL"); 251MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
312MODULE_AUTHOR("Vitaly Bordug"); 252MODULE_AUTHOR("Vitaly Bordug");
313MODULE_LICENSE("GPL"); 253MODULE_LICENSE("GPL");
314
315static 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
351static 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
361module_init(fixed_init);
362module_exit(fixed_exit);