diff options
Diffstat (limited to 'drivers/net/fs_enet/fs_enet-mii.c')
-rw-r--r-- | drivers/net/fs_enet/fs_enet-mii.c | 505 |
1 files changed, 0 insertions, 505 deletions
diff --git a/drivers/net/fs_enet/fs_enet-mii.c b/drivers/net/fs_enet/fs_enet-mii.c deleted file mode 100644 index b7e6e21725cb..000000000000 --- a/drivers/net/fs_enet/fs_enet-mii.c +++ /dev/null | |||
@@ -1,505 +0,0 @@ | |||
1 | /* | ||
2 | * Combined Ethernet driver for Motorola MPC8xx and MPC82xx. | ||
3 | * | ||
4 | * Copyright (c) 2003 Intracom S.A. | ||
5 | * by Pantelis Antoniou <panto@intracom.gr> | ||
6 | * | ||
7 | * 2005 (c) MontaVista Software, Inc. | ||
8 | * Vitaly Bordug <vbordug@ru.mvista.com> | ||
9 | * | ||
10 | * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com> | ||
11 | * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se> | ||
12 | * | ||
13 | * This file is licensed under the terms of the GNU General Public License | ||
14 | * version 2. This program is licensed "as is" without any warranty of any | ||
15 | * kind, whether express or implied. | ||
16 | */ | ||
17 | |||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/string.h> | ||
24 | #include <linux/ptrace.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/netdevice.h> | ||
33 | #include <linux/etherdevice.h> | ||
34 | #include <linux/skbuff.h> | ||
35 | #include <linux/spinlock.h> | ||
36 | #include <linux/mii.h> | ||
37 | #include <linux/ethtool.h> | ||
38 | #include <linux/bitops.h> | ||
39 | |||
40 | #include <asm/pgtable.h> | ||
41 | #include <asm/irq.h> | ||
42 | #include <asm/uaccess.h> | ||
43 | |||
44 | #include "fs_enet.h" | ||
45 | |||
46 | /*************************************************/ | ||
47 | |||
48 | /* | ||
49 | * Generic PHY support. | ||
50 | * Should work for all PHYs, but link change is detected by polling | ||
51 | */ | ||
52 | |||
53 | static void generic_timer_callback(unsigned long data) | ||
54 | { | ||
55 | struct net_device *dev = (struct net_device *)data; | ||
56 | struct fs_enet_private *fep = netdev_priv(dev); | ||
57 | |||
58 | fep->phy_timer_list.expires = jiffies + HZ / 2; | ||
59 | |||
60 | add_timer(&fep->phy_timer_list); | ||
61 | |||
62 | fs_mii_link_status_change_check(dev, 0); | ||
63 | } | ||
64 | |||
65 | static void generic_startup(struct net_device *dev) | ||
66 | { | ||
67 | struct fs_enet_private *fep = netdev_priv(dev); | ||
68 | |||
69 | fep->phy_timer_list.expires = jiffies + HZ / 2; /* every 500ms */ | ||
70 | fep->phy_timer_list.data = (unsigned long)dev; | ||
71 | fep->phy_timer_list.function = generic_timer_callback; | ||
72 | add_timer(&fep->phy_timer_list); | ||
73 | } | ||
74 | |||
75 | static void generic_shutdown(struct net_device *dev) | ||
76 | { | ||
77 | struct fs_enet_private *fep = netdev_priv(dev); | ||
78 | |||
79 | del_timer_sync(&fep->phy_timer_list); | ||
80 | } | ||
81 | |||
82 | /* ------------------------------------------------------------------------- */ | ||
83 | /* The Davicom DM9161 is used on the NETTA board */ | ||
84 | |||
85 | /* register definitions */ | ||
86 | |||
87 | #define MII_DM9161_ANAR 4 /* Aux. Config Register */ | ||
88 | #define MII_DM9161_ACR 16 /* Aux. Config Register */ | ||
89 | #define MII_DM9161_ACSR 17 /* Aux. Config/Status Register */ | ||
90 | #define MII_DM9161_10TCSR 18 /* 10BaseT Config/Status Reg. */ | ||
91 | #define MII_DM9161_INTR 21 /* Interrupt Register */ | ||
92 | #define MII_DM9161_RECR 22 /* Receive Error Counter Reg. */ | ||
93 | #define MII_DM9161_DISCR 23 /* Disconnect Counter Register */ | ||
94 | |||
95 | static void dm9161_startup(struct net_device *dev) | ||
96 | { | ||
97 | struct fs_enet_private *fep = netdev_priv(dev); | ||
98 | |||
99 | fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0000); | ||
100 | /* Start autonegotiation */ | ||
101 | fs_mii_write(dev, fep->mii_if.phy_id, MII_BMCR, 0x1200); | ||
102 | |||
103 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
104 | schedule_timeout(HZ*8); | ||
105 | } | ||
106 | |||
107 | static void dm9161_ack_int(struct net_device *dev) | ||
108 | { | ||
109 | struct fs_enet_private *fep = netdev_priv(dev); | ||
110 | |||
111 | fs_mii_read(dev, fep->mii_if.phy_id, MII_DM9161_INTR); | ||
112 | } | ||
113 | |||
114 | static void dm9161_shutdown(struct net_device *dev) | ||
115 | { | ||
116 | struct fs_enet_private *fep = netdev_priv(dev); | ||
117 | |||
118 | fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0f00); | ||
119 | } | ||
120 | |||
121 | /**********************************************************************************/ | ||
122 | |||
123 | static const struct phy_info phy_info[] = { | ||
124 | { | ||
125 | .id = 0x00181b88, | ||
126 | .name = "DM9161", | ||
127 | .startup = dm9161_startup, | ||
128 | .ack_int = dm9161_ack_int, | ||
129 | .shutdown = dm9161_shutdown, | ||
130 | }, { | ||
131 | .id = 0, | ||
132 | .name = "GENERIC", | ||
133 | .startup = generic_startup, | ||
134 | .shutdown = generic_shutdown, | ||
135 | }, | ||
136 | }; | ||
137 | |||
138 | /**********************************************************************************/ | ||
139 | |||
140 | static int phy_id_detect(struct net_device *dev) | ||
141 | { | ||
142 | struct fs_enet_private *fep = netdev_priv(dev); | ||
143 | const struct fs_platform_info *fpi = fep->fpi; | ||
144 | struct fs_enet_mii_bus *bus = fep->mii_bus; | ||
145 | int i, r, start, end, phytype, physubtype; | ||
146 | const struct phy_info *phy; | ||
147 | int phy_hwid, phy_id; | ||
148 | |||
149 | phy_hwid = -1; | ||
150 | fep->phy = NULL; | ||
151 | |||
152 | /* auto-detect? */ | ||
153 | if (fpi->phy_addr == -1) { | ||
154 | start = 1; | ||
155 | end = 32; | ||
156 | } else { /* direct */ | ||
157 | start = fpi->phy_addr; | ||
158 | end = start + 1; | ||
159 | } | ||
160 | |||
161 | for (phy_id = start; phy_id < end; phy_id++) { | ||
162 | /* skip already used phy addresses on this bus */ | ||
163 | if (bus->usage_map & (1 << phy_id)) | ||
164 | continue; | ||
165 | r = fs_mii_read(dev, phy_id, MII_PHYSID1); | ||
166 | if (r == -1 || (phytype = (r & 0xffff)) == 0xffff) | ||
167 | continue; | ||
168 | r = fs_mii_read(dev, phy_id, MII_PHYSID2); | ||
169 | if (r == -1 || (physubtype = (r & 0xffff)) == 0xffff) | ||
170 | continue; | ||
171 | phy_hwid = (phytype << 16) | physubtype; | ||
172 | if (phy_hwid != -1) | ||
173 | break; | ||
174 | } | ||
175 | |||
176 | if (phy_hwid == -1) { | ||
177 | printk(KERN_ERR DRV_MODULE_NAME | ||
178 | ": %s No PHY detected! range=0x%02x-0x%02x\n", | ||
179 | dev->name, start, end); | ||
180 | return -1; | ||
181 | } | ||
182 | |||
183 | for (i = 0, phy = phy_info; i < ARRAY_SIZE(phy_info); i++, phy++) | ||
184 | if (phy->id == (phy_hwid >> 4) || phy->id == 0) | ||
185 | break; | ||
186 | |||
187 | if (i >= ARRAY_SIZE(phy_info)) { | ||
188 | printk(KERN_ERR DRV_MODULE_NAME | ||
189 | ": %s PHY id 0x%08x is not supported!\n", | ||
190 | dev->name, phy_hwid); | ||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | fep->phy = phy; | ||
195 | |||
196 | /* mark this address as used */ | ||
197 | bus->usage_map |= (1 << phy_id); | ||
198 | |||
199 | printk(KERN_INFO DRV_MODULE_NAME | ||
200 | ": %s Phy @ 0x%x, type %s (0x%08x)%s\n", | ||
201 | dev->name, phy_id, fep->phy->name, phy_hwid, | ||
202 | fpi->phy_addr == -1 ? " (auto-detected)" : ""); | ||
203 | |||
204 | return phy_id; | ||
205 | } | ||
206 | |||
207 | void fs_mii_startup(struct net_device *dev) | ||
208 | { | ||
209 | struct fs_enet_private *fep = netdev_priv(dev); | ||
210 | |||
211 | if (fep->phy->startup) | ||
212 | (*fep->phy->startup) (dev); | ||
213 | } | ||
214 | |||
215 | void fs_mii_shutdown(struct net_device *dev) | ||
216 | { | ||
217 | struct fs_enet_private *fep = netdev_priv(dev); | ||
218 | |||
219 | if (fep->phy->shutdown) | ||
220 | (*fep->phy->shutdown) (dev); | ||
221 | } | ||
222 | |||
223 | void fs_mii_ack_int(struct net_device *dev) | ||
224 | { | ||
225 | struct fs_enet_private *fep = netdev_priv(dev); | ||
226 | |||
227 | if (fep->phy->ack_int) | ||
228 | (*fep->phy->ack_int) (dev); | ||
229 | } | ||
230 | |||
231 | #define MII_LINK 0x0001 | ||
232 | #define MII_HALF 0x0002 | ||
233 | #define MII_FULL 0x0004 | ||
234 | #define MII_BASE4 0x0008 | ||
235 | #define MII_10M 0x0010 | ||
236 | #define MII_100M 0x0020 | ||
237 | #define MII_1G 0x0040 | ||
238 | #define MII_10G 0x0080 | ||
239 | |||
240 | /* return full mii info at one gulp, with a usable form */ | ||
241 | static unsigned int mii_full_status(struct mii_if_info *mii) | ||
242 | { | ||
243 | unsigned int status; | ||
244 | int bmsr, adv, lpa, neg; | ||
245 | struct fs_enet_private* fep = netdev_priv(mii->dev); | ||
246 | |||
247 | /* first, a dummy read, needed to latch some MII phys */ | ||
248 | (void)mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); | ||
249 | bmsr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); | ||
250 | |||
251 | /* no link */ | ||
252 | if ((bmsr & BMSR_LSTATUS) == 0) | ||
253 | return 0; | ||
254 | |||
255 | status = MII_LINK; | ||
256 | |||
257 | /* Lets look what ANEG says if it's supported - otherwize we shall | ||
258 | take the right values from the platform info*/ | ||
259 | if(!mii->force_media) { | ||
260 | /* autoneg not completed; don't bother */ | ||
261 | if ((bmsr & BMSR_ANEGCOMPLETE) == 0) | ||
262 | return 0; | ||
263 | |||
264 | adv = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_ADVERTISE); | ||
265 | lpa = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_LPA); | ||
266 | |||
267 | neg = lpa & adv; | ||
268 | } else { | ||
269 | neg = fep->fpi->bus_info->lpa; | ||
270 | } | ||
271 | |||
272 | if (neg & LPA_100FULL) | ||
273 | status |= MII_FULL | MII_100M; | ||
274 | else if (neg & LPA_100BASE4) | ||
275 | status |= MII_FULL | MII_BASE4 | MII_100M; | ||
276 | else if (neg & LPA_100HALF) | ||
277 | status |= MII_HALF | MII_100M; | ||
278 | else if (neg & LPA_10FULL) | ||
279 | status |= MII_FULL | MII_10M; | ||
280 | else | ||
281 | status |= MII_HALF | MII_10M; | ||
282 | |||
283 | return status; | ||
284 | } | ||
285 | |||
286 | void fs_mii_link_status_change_check(struct net_device *dev, int init_media) | ||
287 | { | ||
288 | struct fs_enet_private *fep = netdev_priv(dev); | ||
289 | struct mii_if_info *mii = &fep->mii_if; | ||
290 | unsigned int mii_status; | ||
291 | int ok_to_print, link, duplex, speed; | ||
292 | unsigned long flags; | ||
293 | |||
294 | ok_to_print = netif_msg_link(fep); | ||
295 | |||
296 | mii_status = mii_full_status(mii); | ||
297 | |||
298 | if (!init_media && mii_status == fep->last_mii_status) | ||
299 | return; | ||
300 | |||
301 | fep->last_mii_status = mii_status; | ||
302 | |||
303 | link = !!(mii_status & MII_LINK); | ||
304 | duplex = !!(mii_status & MII_FULL); | ||
305 | speed = (mii_status & MII_100M) ? 100 : 10; | ||
306 | |||
307 | if (link == 0) { | ||
308 | netif_carrier_off(mii->dev); | ||
309 | netif_stop_queue(dev); | ||
310 | if (!init_media) { | ||
311 | spin_lock_irqsave(&fep->lock, flags); | ||
312 | (*fep->ops->stop)(dev); | ||
313 | spin_unlock_irqrestore(&fep->lock, flags); | ||
314 | } | ||
315 | |||
316 | if (ok_to_print) | ||
317 | printk(KERN_INFO "%s: link down\n", mii->dev->name); | ||
318 | |||
319 | } else { | ||
320 | |||
321 | mii->full_duplex = duplex; | ||
322 | |||
323 | netif_carrier_on(mii->dev); | ||
324 | |||
325 | spin_lock_irqsave(&fep->lock, flags); | ||
326 | fep->duplex = duplex; | ||
327 | fep->speed = speed; | ||
328 | (*fep->ops->restart)(dev); | ||
329 | spin_unlock_irqrestore(&fep->lock, flags); | ||
330 | |||
331 | netif_start_queue(dev); | ||
332 | |||
333 | if (ok_to_print) | ||
334 | printk(KERN_INFO "%s: link up, %dMbps, %s-duplex\n", | ||
335 | dev->name, speed, duplex ? "full" : "half"); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /**********************************************************************************/ | ||
340 | |||
341 | int fs_mii_read(struct net_device *dev, int phy_id, int location) | ||
342 | { | ||
343 | struct fs_enet_private *fep = netdev_priv(dev); | ||
344 | struct fs_enet_mii_bus *bus = fep->mii_bus; | ||
345 | |||
346 | unsigned long flags; | ||
347 | int ret; | ||
348 | |||
349 | spin_lock_irqsave(&bus->mii_lock, flags); | ||
350 | ret = (*bus->mii_read)(bus, phy_id, location); | ||
351 | spin_unlock_irqrestore(&bus->mii_lock, flags); | ||
352 | |||
353 | return ret; | ||
354 | } | ||
355 | |||
356 | void fs_mii_write(struct net_device *dev, int phy_id, int location, int value) | ||
357 | { | ||
358 | struct fs_enet_private *fep = netdev_priv(dev); | ||
359 | struct fs_enet_mii_bus *bus = fep->mii_bus; | ||
360 | unsigned long flags; | ||
361 | |||
362 | spin_lock_irqsave(&bus->mii_lock, flags); | ||
363 | (*bus->mii_write)(bus, phy_id, location, value); | ||
364 | spin_unlock_irqrestore(&bus->mii_lock, flags); | ||
365 | } | ||
366 | |||
367 | /*****************************************************************************/ | ||
368 | |||
369 | /* list of all registered mii buses */ | ||
370 | static LIST_HEAD(fs_mii_bus_list); | ||
371 | |||
372 | static struct fs_enet_mii_bus *lookup_bus(int method, int id) | ||
373 | { | ||
374 | struct list_head *ptr; | ||
375 | struct fs_enet_mii_bus *bus; | ||
376 | |||
377 | list_for_each(ptr, &fs_mii_bus_list) { | ||
378 | bus = list_entry(ptr, struct fs_enet_mii_bus, list); | ||
379 | if (bus->bus_info->method == method && | ||
380 | bus->bus_info->id == id) | ||
381 | return bus; | ||
382 | } | ||
383 | return NULL; | ||
384 | } | ||
385 | |||
386 | static struct fs_enet_mii_bus *create_bus(const struct fs_mii_bus_info *bi) | ||
387 | { | ||
388 | struct fs_enet_mii_bus *bus; | ||
389 | int ret = 0; | ||
390 | |||
391 | bus = kmalloc(sizeof(*bus), GFP_KERNEL); | ||
392 | if (bus == NULL) { | ||
393 | ret = -ENOMEM; | ||
394 | goto err; | ||
395 | } | ||
396 | memset(bus, 0, sizeof(*bus)); | ||
397 | spin_lock_init(&bus->mii_lock); | ||
398 | bus->bus_info = bi; | ||
399 | bus->refs = 0; | ||
400 | bus->usage_map = 0; | ||
401 | |||
402 | /* perform initialization */ | ||
403 | switch (bi->method) { | ||
404 | |||
405 | case fsmii_fixed: | ||
406 | ret = fs_mii_fixed_init(bus); | ||
407 | if (ret != 0) | ||
408 | goto err; | ||
409 | break; | ||
410 | |||
411 | case fsmii_bitbang: | ||
412 | ret = fs_mii_bitbang_init(bus); | ||
413 | if (ret != 0) | ||
414 | goto err; | ||
415 | break; | ||
416 | #ifdef CONFIG_FS_ENET_HAS_FEC | ||
417 | case fsmii_fec: | ||
418 | ret = fs_mii_fec_init(bus); | ||
419 | if (ret != 0) | ||
420 | goto err; | ||
421 | break; | ||
422 | #endif | ||
423 | default: | ||
424 | ret = -EINVAL; | ||
425 | goto err; | ||
426 | } | ||
427 | |||
428 | list_add(&bus->list, &fs_mii_bus_list); | ||
429 | |||
430 | return bus; | ||
431 | |||
432 | err: | ||
433 | kfree(bus); | ||
434 | return ERR_PTR(ret); | ||
435 | } | ||
436 | |||
437 | static void destroy_bus(struct fs_enet_mii_bus *bus) | ||
438 | { | ||
439 | /* remove from bus list */ | ||
440 | list_del(&bus->list); | ||
441 | |||
442 | /* nothing more needed */ | ||
443 | kfree(bus); | ||
444 | } | ||
445 | |||
446 | int fs_mii_connect(struct net_device *dev) | ||
447 | { | ||
448 | struct fs_enet_private *fep = netdev_priv(dev); | ||
449 | const struct fs_platform_info *fpi = fep->fpi; | ||
450 | struct fs_enet_mii_bus *bus = NULL; | ||
451 | |||
452 | /* check method validity */ | ||
453 | switch (fpi->bus_info->method) { | ||
454 | case fsmii_fixed: | ||
455 | case fsmii_bitbang: | ||
456 | break; | ||
457 | #ifdef CONFIG_FS_ENET_HAS_FEC | ||
458 | case fsmii_fec: | ||
459 | break; | ||
460 | #endif | ||
461 | default: | ||
462 | printk(KERN_ERR DRV_MODULE_NAME | ||
463 | ": %s Unknown MII bus method (%d)!\n", | ||
464 | dev->name, fpi->bus_info->method); | ||
465 | return -EINVAL; | ||
466 | } | ||
467 | |||
468 | bus = lookup_bus(fpi->bus_info->method, fpi->bus_info->id); | ||
469 | |||
470 | /* if not found create new bus */ | ||
471 | if (bus == NULL) { | ||
472 | bus = create_bus(fpi->bus_info); | ||
473 | if (IS_ERR(bus)) { | ||
474 | printk(KERN_ERR DRV_MODULE_NAME | ||
475 | ": %s MII bus creation failure!\n", dev->name); | ||
476 | return PTR_ERR(bus); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | bus->refs++; | ||
481 | |||
482 | fep->mii_bus = bus; | ||
483 | |||
484 | fep->mii_if.dev = dev; | ||
485 | fep->mii_if.phy_id_mask = 0x1f; | ||
486 | fep->mii_if.reg_num_mask = 0x1f; | ||
487 | fep->mii_if.mdio_read = fs_mii_read; | ||
488 | fep->mii_if.mdio_write = fs_mii_write; | ||
489 | fep->mii_if.force_media = fpi->bus_info->disable_aneg; | ||
490 | fep->mii_if.phy_id = phy_id_detect(dev); | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | void fs_mii_disconnect(struct net_device *dev) | ||
496 | { | ||
497 | struct fs_enet_private *fep = netdev_priv(dev); | ||
498 | struct fs_enet_mii_bus *bus = NULL; | ||
499 | |||
500 | bus = fep->mii_bus; | ||
501 | fep->mii_bus = NULL; | ||
502 | |||
503 | if (--bus->refs <= 0) | ||
504 | destroy_bus(bus); | ||
505 | } | ||