ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / net / hplance.c
1 /* hplance.c  : the  Linux/hp300/lance ethernet driver
2  *
3  * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
4  * Based on the Sun Lance driver and the NetBSD HP Lance driver
5  * Uses the generic 7990.c LANCE code.
6  */
7
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/types.h>
11 #include <linux/interrupt.h>
12 #include <linux/ioport.h>
13 #include <linux/slab.h>
14 #include <linux/string.h>
15 #include <linux/delay.h>
16 #include <linux/init.h>
17 #include <linux/errno.h>
18 /* Used for the temporal inet entries and routing */
19 #include <linux/socket.h>
20 #include <linux/route.h>
21 #include <linux/dio.h>
22 #include <linux/netdevice.h>
23 #include <linux/etherdevice.h>
24 #include <linux/skbuff.h>
25
26 #include <asm/system.h>
27 #include <asm/io.h>
28 #include <asm/pgtable.h>
29
30 #include "hplance.h"
31
32 /* We have 16834 bytes of RAM for the init block and buffers. This places
33  * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
34  * buffers and 2 Tx buffers.
35  */
36 #define LANCE_LOG_TX_BUFFERS 1
37 #define LANCE_LOG_RX_BUFFERS 3
38
39 #include "7990.h"                                 /* use generic LANCE code */
40
41 /* Our private data structure */
42 struct hplance_private {
43   struct lance_private lance;
44   unsigned int scode;
45   void *base;
46 };
47
48 /* function prototypes... This is easy because all the grot is in the
49  * generic LANCE support. All we have to support is probing for boards,
50  * plus board-specific init, open and close actions. 
51  * Oh, and we need to tell the generic code how to read and write LANCE registers...
52  */
53 static void hplance_init(struct net_device *dev, int scode);
54 static int hplance_open(struct net_device *dev);
55 static int hplance_close(struct net_device *dev);
56 static void hplance_writerap(void *priv, unsigned short value);
57 static void hplance_writerdp(void *priv, unsigned short value);
58 static unsigned short hplance_readrdp(void *priv);
59
60 #ifdef MODULE
61 static struct hplance_private *root_hplance_dev;
62 #endif
63
64 static void cleanup_card(struct net_device *dev)
65 {
66         struct hplance_private *lp = netdev_priv(dev);
67         dio_unconfig_board(lp->scode);
68 }
69
70 /* Find all the HP Lance boards and initialise them... */
71 struct net_device * __init hplance_probe(int unit)
72 {
73         struct net_device *dev;
74
75         if (!MACH_IS_HP300)
76                 return ERR_PTR(-ENODEV);
77
78         dev = alloc_etherdev(sizeof(struct hplance_private));
79         if (!dev)
80                 return ERR_PTR(-ENOMEM);
81
82         if (unit >= 0) {
83                 sprintf(dev->name, "eth%d", unit);
84                 netdev_boot_setup_check(dev);
85         }
86
87         SET_MODULE_OWNER(dev);
88         
89         /* Isn't DIO nice? */
90         for(;;)
91         {
92                 int scode = dio_find(DIO_ID_LAN);
93                                 
94                 if (!scode)
95                         break;
96                 
97                 dio_config_board(scode);
98                 hplance_init(dev, scode);
99                 if (!register_netdev(dev)) {
100                         struct hplance_private *lp = netdev_priv(dev);
101                         lp->next_module = root_hplance_dev;
102                         root_hplance_dev = lp;
103                         return dev;
104                 }
105                 cleanup_card(dev);
106         }
107         free_netdev(dev);
108         return ERR_PTR(-ENODEV);
109 }
110
111 /* Initialise a single lance board at the given select code */
112 static void __init hplance_init(struct net_device *dev, int scode)
113 {
114         const char *name = dio_scodetoname(scode);
115         void *va = dio_scodetoviraddr(scode);
116         struct hplance_private *lp;
117         int i;
118         
119         printk("%s: %s; select code %d, addr", dev->name, name, scode);
120
121         /* reset the board */
122         out_8(va+DIO_IDOFF, 0xff);
123         udelay(100);                              /* ariba! ariba! udelay! udelay! */
124
125         /* Fill the dev fields */
126         dev->base_addr = (unsigned long)va;
127         dev->open = &hplance_open;
128         dev->stop = &hplance_close;
129         dev->hard_start_xmit = &lance_start_xmit;
130         dev->get_stats = &lance_get_stats;
131         dev->set_multicast_list = &lance_set_multicast;
132         dev->dma = 0;
133         
134         for (i=0; i<6; i++)
135         {
136                 /* The NVRAM holds our ethernet address, one nibble per byte,
137                  * at bytes NVRAMOFF+1,3,5,7,9...
138                  */
139                 dev->dev_addr[i] = ((in_8(va + HPLANCE_NVRAMOFF + i*4 + 1) & 0xF) << 4)
140                         | (in_8(va + HPLANCE_NVRAMOFF + i*4 + 3) & 0xF);
141                 printk("%c%2.2x", i == 0 ? ' ' : ':', dev->dev_addr[i]);
142         }
143         
144         lp = netdev_priv(dev);
145         lp->lance.name = (char*)name;                   /* discards const, shut up gcc */
146         lp->lance.ll = (struct lance_regs *)(va + HPLANCE_REGOFF);
147         lp->lance.init_block = (struct lance_init_block *)(va + HPLANCE_MEMOFF); /* CPU addr */
148         lp->lance.lance_init_block = 0;                 /* LANCE addr of same RAM */
149         lp->lance.busmaster_regval = LE_C3_BSWP;        /* we're bigendian */
150         lp->lance.irq = dio_scodetoipl(scode);
151         lp->lance.writerap = hplance_writerap;
152         lp->lance.writerdp = hplance_writerdp;
153         lp->lance.readrdp = hplance_readrdp;
154         lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
155         lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS;
156         lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK;
157         lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK;
158         lp->scode = scode;
159         lp->base = va;
160         printk(", irq %d\n", lp->lance.irq);
161 }
162
163 /* This is disgusting. We have to check the DIO status register for ack every
164  * time we read or write the LANCE registers.
165  */
166 static void hplance_writerap(void *priv, unsigned short value)
167 {
168         struct hplance_private *lp = (struct hplance_private *)priv;
169         struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
170         do {
171                 lp->lance.ll->rap = value;
172         } while ((hpregs->status & LE_ACK) == 0);
173 }
174
175 static void hplance_writerdp(void *priv, unsigned short value)
176 {
177         struct hplance_private *lp = (struct hplance_private *)priv;
178         struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
179         do {
180                 lp->lance.ll->rdp = value;
181         } while ((hpregs->status & LE_ACK) == 0);
182 }
183
184 static unsigned short hplance_readrdp(void *priv)
185 {
186         unsigned short val;
187         struct hplance_private *lp = (struct hplance_private *)priv;
188         struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
189         do {
190                 val = lp->lance.ll->rdp;
191         } while ((hpregs->status & LE_ACK) == 0);
192         return val;
193 }
194
195 static int hplance_open(struct net_device *dev)
196 {
197         int status;
198         struct hplance_private *lp = netdev_priv(dev);
199         struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
200         
201         status = lance_open(dev);                 /* call generic lance open code */
202         if (status)
203                 return status;
204         /* enable interrupts at board level. */
205         out_8(&(hpregs->status), LE_IE);
206
207         return 0;
208 }
209
210 static int hplance_close(struct net_device *dev)
211 {
212         struct hplance_private *lp = netdev_priv(dev);
213         struct hplance_reg *hpregs = (struct hplance_reg *)lp->base;
214         out_8(&(hpregs->status), 8);              /* disable interrupts at boardlevel */
215         lance_close(dev);
216         return 0;
217 }
218
219 #ifdef MODULE
220 MODULE_LICENSE("GPL");
221 int init_module(void)
222 {
223         int found = 0;
224         while (!IS_ERR(hplance_probe(-1)))
225                 found++;
226         return found ? 0 : -ENODEV;
227 }
228
229 void cleanup_module(void)
230 {
231         /* Walk the chain of devices, unregistering them */
232         struct hplance_private *lp;
233         while (root_hplance_dev) {
234                 lp = root_hplance_dev->next_module;
235                 unregister_netdev(root_lance_dev->dev);
236                 cleanup_card(root_lance_dev->dev);
237                 free_netdev(root_lance_dev->dev);
238                 root_lance_dev = lp;
239         }
240 }
241
242 #endif /* MODULE */