aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/fec_8xx/fec_mii.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/fec_8xx/fec_mii.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/net/fec_8xx/fec_mii.c')
-rw-r--r--drivers/net/fec_8xx/fec_mii.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/drivers/net/fec_8xx/fec_mii.c b/drivers/net/fec_8xx/fec_mii.c
new file mode 100644
index 000000000000..803eb095cf8e
--- /dev/null
+++ b/drivers/net/fec_8xx/fec_mii.c
@@ -0,0 +1,380 @@
1/*
2 * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
3 *
4 * Copyright (c) 2003 Intracom S.A.
5 * by Pantelis Antoniou <panto@intracom.gr>
6 *
7 * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com>
8 * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
9 *
10 * Released under the GPL
11 */
12
13#include <linux/config.h>
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/string.h>
19#include <linux/ptrace.h>
20#include <linux/errno.h>
21#include <linux/ioport.h>
22#include <linux/slab.h>
23#include <linux/interrupt.h>
24#include <linux/pci.h>
25#include <linux/init.h>
26#include <linux/delay.h>
27#include <linux/netdevice.h>
28#include <linux/etherdevice.h>
29#include <linux/skbuff.h>
30#include <linux/spinlock.h>
31#include <linux/mii.h>
32#include <linux/ethtool.h>
33#include <linux/bitops.h>
34
35#include <asm/8xx_immap.h>
36#include <asm/pgtable.h>
37#include <asm/mpc8xx.h>
38#include <asm/irq.h>
39#include <asm/uaccess.h>
40#include <asm/commproc.h>
41
42/*************************************************/
43
44#include "fec_8xx.h"
45
46/*************************************************/
47
48/* Make MII read/write commands for the FEC.
49*/
50#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
51#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
52#define mk_mii_end 0
53
54/*************************************************/
55
56/* XXX both FECs use the MII interface of FEC1 */
57static DEFINE_SPINLOCK(fec_mii_lock);
58
59#define FEC_MII_LOOPS 10000
60
61int fec_mii_read(struct net_device *dev, int phy_id, int location)
62{
63 struct fec_enet_private *fep = netdev_priv(dev);
64 fec_t *fecp;
65 int i, ret = -1;
66 unsigned long flags;
67
68 /* XXX MII interface is only connected to FEC1 */
69 fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec;
70
71 spin_lock_irqsave(&fec_mii_lock, flags);
72
73 if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0) {
74 FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
75 FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
76 FW(fecp, ievent, FEC_ENET_MII);
77 }
78
79 /* Add PHY address to register command. */
80 FW(fecp, mii_speed, fep->fec_phy_speed);
81 FW(fecp, mii_data, (phy_id << 23) | mk_mii_read(location));
82
83 for (i = 0; i < FEC_MII_LOOPS; i++)
84 if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
85 break;
86
87 if (i < FEC_MII_LOOPS) {
88 FW(fecp, ievent, FEC_ENET_MII);
89 ret = FR(fecp, mii_data) & 0xffff;
90 }
91
92 spin_unlock_irqrestore(&fec_mii_lock, flags);
93
94 return ret;
95}
96
97void fec_mii_write(struct net_device *dev, int phy_id, int location, int value)
98{
99 struct fec_enet_private *fep = netdev_priv(dev);
100 fec_t *fecp;
101 unsigned long flags;
102 int i;
103
104 /* XXX MII interface is only connected to FEC1 */
105 fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec;
106
107 spin_lock_irqsave(&fec_mii_lock, flags);
108
109 if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0) {
110 FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
111 FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
112 FW(fecp, ievent, FEC_ENET_MII);
113 }
114
115 /* Add PHY address to register command. */
116 FW(fecp, mii_speed, fep->fec_phy_speed); /* always adapt mii speed */
117 FW(fecp, mii_data, (phy_id << 23) | mk_mii_write(location, value));
118
119 for (i = 0; i < FEC_MII_LOOPS; i++)
120 if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
121 break;
122
123 if (i < FEC_MII_LOOPS)
124 FW(fecp, ievent, FEC_ENET_MII);
125
126 spin_unlock_irqrestore(&fec_mii_lock, flags);
127}
128
129/*************************************************/
130
131#ifdef CONFIG_FEC_8XX_GENERIC_PHY
132
133/*
134 * Generic PHY support.
135 * Should work for all PHYs, but link change is detected by polling
136 */
137
138static void generic_timer_callback(unsigned long data)
139{
140 struct net_device *dev = (struct net_device *)data;
141 struct fec_enet_private *fep = netdev_priv(dev);
142
143 fep->phy_timer_list.expires = jiffies + HZ / 2;
144
145 add_timer(&fep->phy_timer_list);
146
147 fec_mii_link_status_change_check(dev, 0);
148}
149
150static void generic_startup(struct net_device *dev)
151{
152 struct fec_enet_private *fep = netdev_priv(dev);
153
154 fep->phy_timer_list.expires = jiffies + HZ / 2; /* every 500ms */
155 fep->phy_timer_list.data = (unsigned long)dev;
156 fep->phy_timer_list.function = generic_timer_callback;
157 add_timer(&fep->phy_timer_list);
158}
159
160static void generic_shutdown(struct net_device *dev)
161{
162 struct fec_enet_private *fep = netdev_priv(dev);
163
164 del_timer_sync(&fep->phy_timer_list);
165}
166
167#endif
168
169#ifdef CONFIG_FEC_8XX_DM9161_PHY
170
171/* ------------------------------------------------------------------------- */
172/* The Davicom DM9161 is used on the NETTA board */
173
174/* register definitions */
175
176#define MII_DM9161_ACR 16 /* Aux. Config Register */
177#define MII_DM9161_ACSR 17 /* Aux. Config/Status Register */
178#define MII_DM9161_10TCSR 18 /* 10BaseT Config/Status Reg. */
179#define MII_DM9161_INTR 21 /* Interrupt Register */
180#define MII_DM9161_RECR 22 /* Receive Error Counter Reg. */
181#define MII_DM9161_DISCR 23 /* Disconnect Counter Register */
182
183static void dm9161_startup(struct net_device *dev)
184{
185 struct fec_enet_private *fep = netdev_priv(dev);
186
187 fec_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0000);
188}
189
190static void dm9161_ack_int(struct net_device *dev)
191{
192 struct fec_enet_private *fep = netdev_priv(dev);
193
194 fec_mii_read(dev, fep->mii_if.phy_id, MII_DM9161_INTR);
195}
196
197static void dm9161_shutdown(struct net_device *dev)
198{
199 struct fec_enet_private *fep = netdev_priv(dev);
200
201 fec_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0f00);
202}
203
204#endif
205
206/**********************************************************************************/
207
208static const struct phy_info phy_info[] = {
209#ifdef CONFIG_FEC_8XX_DM9161_PHY
210 {
211 .id = 0x00181b88,
212 .name = "DM9161",
213 .startup = dm9161_startup,
214 .ack_int = dm9161_ack_int,
215 .shutdown = dm9161_shutdown,
216 },
217#endif
218#ifdef CONFIG_FEC_8XX_GENERIC_PHY
219 {
220 .id = 0,
221 .name = "GENERIC",
222 .startup = generic_startup,
223 .shutdown = generic_shutdown,
224 },
225#endif
226};
227
228/**********************************************************************************/
229
230int fec_mii_phy_id_detect(struct net_device *dev)
231{
232 struct fec_enet_private *fep = netdev_priv(dev);
233 const struct fec_platform_info *fpi = fep->fpi;
234 int i, r, start, end, phytype, physubtype;
235 const struct phy_info *phy;
236 int phy_hwid, phy_id;
237
238 /* if no MDIO */
239 if (fpi->use_mdio == 0)
240 return -1;
241
242 phy_hwid = -1;
243 fep->phy = NULL;
244
245 /* auto-detect? */
246 if (fpi->phy_addr == -1) {
247 start = 0;
248 end = 32;
249 } else { /* direct */
250 start = fpi->phy_addr;
251 end = start + 1;
252 }
253
254 for (phy_id = start; phy_id < end; phy_id++) {
255 r = fec_mii_read(dev, phy_id, MII_PHYSID1);
256 if (r == -1 || (phytype = (r & 0xffff)) == 0xffff)
257 continue;
258 r = fec_mii_read(dev, phy_id, MII_PHYSID2);
259 if (r == -1 || (physubtype = (r & 0xffff)) == 0xffff)
260 continue;
261 phy_hwid = (phytype << 16) | physubtype;
262 if (phy_hwid != -1)
263 break;
264 }
265
266 if (phy_hwid == -1) {
267 printk(KERN_ERR DRV_MODULE_NAME
268 ": %s No PHY detected!\n", dev->name);
269 return -1;
270 }
271
272 for (i = 0, phy = phy_info; i < sizeof(phy_info) / sizeof(phy_info[0]);
273 i++, phy++)
274 if (phy->id == (phy_hwid >> 4) || phy->id == 0)
275 break;
276
277 if (i >= sizeof(phy_info) / sizeof(phy_info[0])) {
278 printk(KERN_ERR DRV_MODULE_NAME
279 ": %s PHY id 0x%08x is not supported!\n",
280 dev->name, phy_hwid);
281 return -1;
282 }
283
284 fep->phy = phy;
285
286 printk(KERN_INFO DRV_MODULE_NAME
287 ": %s Phy @ 0x%x, type %s (0x%08x)\n",
288 dev->name, phy_id, fep->phy->name, phy_hwid);
289
290 return phy_id;
291}
292
293void fec_mii_startup(struct net_device *dev)
294{
295 struct fec_enet_private *fep = netdev_priv(dev);
296 const struct fec_platform_info *fpi = fep->fpi;
297
298 if (!fpi->use_mdio || fep->phy == NULL)
299 return;
300
301 if (fep->phy->startup == NULL)
302 return;
303
304 (*fep->phy->startup) (dev);
305}
306
307void fec_mii_shutdown(struct net_device *dev)
308{
309 struct fec_enet_private *fep = netdev_priv(dev);
310 const struct fec_platform_info *fpi = fep->fpi;
311
312 if (!fpi->use_mdio || fep->phy == NULL)
313 return;
314
315 if (fep->phy->shutdown == NULL)
316 return;
317
318 (*fep->phy->shutdown) (dev);
319}
320
321void fec_mii_ack_int(struct net_device *dev)
322{
323 struct fec_enet_private *fep = netdev_priv(dev);
324 const struct fec_platform_info *fpi = fep->fpi;
325
326 if (!fpi->use_mdio || fep->phy == NULL)
327 return;
328
329 if (fep->phy->ack_int == NULL)
330 return;
331
332 (*fep->phy->ack_int) (dev);
333}
334
335/* helper function */
336static int mii_negotiated(struct mii_if_info *mii)
337{
338 int advert, lpa, val;
339
340 if (!mii_link_ok(mii))
341 return 0;
342
343 val = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_BMSR);
344 if ((val & BMSR_ANEGCOMPLETE) == 0)
345 return 0;
346
347 advert = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_ADVERTISE);
348 lpa = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_LPA);
349
350 return mii_nway_result(advert & lpa);
351}
352
353void fec_mii_link_status_change_check(struct net_device *dev, int init_media)
354{
355 struct fec_enet_private *fep = netdev_priv(dev);
356 unsigned int media;
357 unsigned long flags;
358
359 if (mii_check_media(&fep->mii_if, netif_msg_link(fep), init_media) == 0)
360 return;
361
362 media = mii_negotiated(&fep->mii_if);
363
364 if (netif_carrier_ok(dev)) {
365 spin_lock_irqsave(&fep->lock, flags);
366 fec_restart(dev, !!(media & ADVERTISE_FULL),
367 (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) ?
368 100 : 10);
369 spin_unlock_irqrestore(&fep->lock, flags);
370
371 netif_start_queue(dev);
372 } else {
373 netif_stop_queue(dev);
374
375 spin_lock_irqsave(&fep->lock, flags);
376 fec_stop(dev);
377 spin_unlock_irqrestore(&fep->lock, flags);
378
379 }
380}