aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/fixed.c
diff options
context:
space:
mode:
authorVitaly Bordug <vitb@kernel.crashing.org>2007-12-06 17:51:22 -0500
committerKumar Gala <galak@kernel.crashing.org>2008-01-23 20:33:58 -0500
commita79d8e93d300adb84cccc38ac396cfb118c238ad (patch)
treed3c6e163a697029b0c7a14c745318f4f58a69445 /drivers/net/phy/fixed.c
parent9b6d19dd1d87fcca43ebadfad2f50cee07fbef5e (diff)
phy/fixed.c: rework to not duplicate PHY layer functionality
With that patch fixed.c now fully emulates MDIO bus, thus no need to duplicate PHY layer functionality. That, in turn, drastically simplifies the code, and drops down line count. As an additional bonus, now there is no need to register MDIO bus for each PHY, all emulated PHYs placed on the platform fixed MDIO bus. There is also no more need to pre-allocate PHYs via .config option, this is all now handled dynamically. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org> Acked-by: Jeff Garzik <jeff@garzik.org> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
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);