diff options
Diffstat (limited to 'drivers/net/arcnet/com20020.c')
-rw-r--r-- | drivers/net/arcnet/com20020.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c new file mode 100644 index 000000000000..0dc70c7b7940 --- /dev/null +++ b/drivers/net/arcnet/com20020.c | |||
@@ -0,0 +1,357 @@ | |||
1 | /* | ||
2 | * Linux ARCnet driver - COM20020 chipset support | ||
3 | * | ||
4 | * Written 1997 by David Woodhouse. | ||
5 | * Written 1994-1999 by Avery Pennarun. | ||
6 | * Written 1999 by Martin Mares <mj@ucw.cz>. | ||
7 | * Derived from skeleton.c by Donald Becker. | ||
8 | * | ||
9 | * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) | ||
10 | * for sponsoring the further development of this driver. | ||
11 | * | ||
12 | * ********************** | ||
13 | * | ||
14 | * The original copyright of skeleton.c was as follows: | ||
15 | * | ||
16 | * skeleton.c Written 1993 by Donald Becker. | ||
17 | * Copyright 1993 United States Government as represented by the | ||
18 | * Director, National Security Agency. This software may only be used | ||
19 | * and distributed according to the terms of the GNU General Public License as | ||
20 | * modified by SRC, incorporated herein by reference. | ||
21 | * | ||
22 | * ********************** | ||
23 | * | ||
24 | * For more details, see drivers/net/arcnet.c | ||
25 | * | ||
26 | * ********************** | ||
27 | */ | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <linux/netdevice.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/arcdevice.h> | ||
38 | #include <linux/com20020.h> | ||
39 | |||
40 | #include <asm/io.h> | ||
41 | |||
42 | #define VERSION "arcnet: COM20020 chipset support (by David Woodhouse et al.)\n" | ||
43 | |||
44 | static char *clockrates[] = | ||
45 | {"10 Mb/s", "Reserved", "5 Mb/s", | ||
46 | "2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", | ||
47 | "156.25 Kb/s", "Reserved", "Reserved", "Reserved"}; | ||
48 | |||
49 | static void com20020_command(struct net_device *dev, int command); | ||
50 | static int com20020_status(struct net_device *dev); | ||
51 | static void com20020_setmask(struct net_device *dev, int mask); | ||
52 | static int com20020_reset(struct net_device *dev, int really_reset); | ||
53 | static void com20020_copy_to_card(struct net_device *dev, int bufnum, | ||
54 | int offset, void *buf, int count); | ||
55 | static void com20020_copy_from_card(struct net_device *dev, int bufnum, | ||
56 | int offset, void *buf, int count); | ||
57 | static void com20020_set_mc_list(struct net_device *dev); | ||
58 | static void com20020_close(struct net_device *); | ||
59 | |||
60 | static void com20020_copy_from_card(struct net_device *dev, int bufnum, | ||
61 | int offset, void *buf, int count) | ||
62 | { | ||
63 | int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; | ||
64 | |||
65 | /* set up the address register */ | ||
66 | outb((ofs >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI); | ||
67 | outb(ofs & 0xff, _ADDR_LO); | ||
68 | |||
69 | /* copy the data */ | ||
70 | TIME("insb", count, insb(_MEMDATA, buf, count)); | ||
71 | } | ||
72 | |||
73 | |||
74 | static void com20020_copy_to_card(struct net_device *dev, int bufnum, | ||
75 | int offset, void *buf, int count) | ||
76 | { | ||
77 | int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; | ||
78 | |||
79 | /* set up the address register */ | ||
80 | outb((ofs >> 8) | AUTOINCflag, _ADDR_HI); | ||
81 | outb(ofs & 0xff, _ADDR_LO); | ||
82 | |||
83 | /* copy the data */ | ||
84 | TIME("outsb", count, outsb(_MEMDATA, buf, count)); | ||
85 | } | ||
86 | |||
87 | |||
88 | /* Reset the card and check some basic stuff during the detection stage. */ | ||
89 | int com20020_check(struct net_device *dev) | ||
90 | { | ||
91 | int ioaddr = dev->base_addr, status; | ||
92 | struct arcnet_local *lp = dev->priv; | ||
93 | |||
94 | ARCRESET0; | ||
95 | mdelay(RESETtime); | ||
96 | |||
97 | lp->setup = lp->clockm ? 0 : (lp->clockp << 1); | ||
98 | lp->setup2 = (lp->clockm << 4) | 8; | ||
99 | |||
100 | /* CHECK: should we do this for SOHARD cards ? */ | ||
101 | /* Enable P1Mode for backplane mode */ | ||
102 | lp->setup = lp->setup | P1MODE; | ||
103 | |||
104 | SET_SUBADR(SUB_SETUP1); | ||
105 | outb(lp->setup, _XREG); | ||
106 | |||
107 | if (lp->card_flags & ARC_CAN_10MBIT) | ||
108 | { | ||
109 | SET_SUBADR(SUB_SETUP2); | ||
110 | outb(lp->setup2, _XREG); | ||
111 | |||
112 | /* must now write the magic "restart operation" command */ | ||
113 | mdelay(1); | ||
114 | outb(0x18, _COMMAND); | ||
115 | } | ||
116 | |||
117 | lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2); | ||
118 | /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ | ||
119 | SETCONF; | ||
120 | outb(0x42, ioaddr + BUS_ALIGN*7); | ||
121 | |||
122 | status = ASTATUS(); | ||
123 | |||
124 | if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) { | ||
125 | BUGMSG(D_NORMAL, "status invalid (%Xh).\n", status); | ||
126 | return -ENODEV; | ||
127 | } | ||
128 | BUGMSG(D_INIT_REASONS, "status after reset: %X\n", status); | ||
129 | |||
130 | /* Enable TX */ | ||
131 | outb(0x39, _CONFIG); | ||
132 | outb(inb(ioaddr + BUS_ALIGN*8), ioaddr + BUS_ALIGN*7); | ||
133 | |||
134 | ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); | ||
135 | |||
136 | status = ASTATUS(); | ||
137 | BUGMSG(D_INIT_REASONS, "status after reset acknowledged: %X\n", | ||
138 | status); | ||
139 | |||
140 | /* Read first location of memory */ | ||
141 | outb(0 | RDDATAflag | AUTOINCflag, _ADDR_HI); | ||
142 | outb(0, _ADDR_LO); | ||
143 | |||
144 | if ((status = inb(_MEMDATA)) != TESTvalue) { | ||
145 | BUGMSG(D_NORMAL, "Signature byte not found (%02Xh != D1h).\n", | ||
146 | status); | ||
147 | return -ENODEV; | ||
148 | } | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | /* Set up the struct net_device associated with this card. Called after | ||
153 | * probing succeeds. | ||
154 | */ | ||
155 | int com20020_found(struct net_device *dev, int shared) | ||
156 | { | ||
157 | struct arcnet_local *lp; | ||
158 | int ioaddr = dev->base_addr; | ||
159 | |||
160 | /* Initialize the rest of the device structure. */ | ||
161 | |||
162 | lp = dev->priv; | ||
163 | |||
164 | lp->hw.owner = THIS_MODULE; | ||
165 | lp->hw.command = com20020_command; | ||
166 | lp->hw.status = com20020_status; | ||
167 | lp->hw.intmask = com20020_setmask; | ||
168 | lp->hw.reset = com20020_reset; | ||
169 | lp->hw.copy_to_card = com20020_copy_to_card; | ||
170 | lp->hw.copy_from_card = com20020_copy_from_card; | ||
171 | lp->hw.close = com20020_close; | ||
172 | |||
173 | dev->set_multicast_list = com20020_set_mc_list; | ||
174 | |||
175 | if (!dev->dev_addr[0]) | ||
176 | dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this some other way! */ | ||
177 | |||
178 | SET_SUBADR(SUB_SETUP1); | ||
179 | outb(lp->setup, _XREG); | ||
180 | |||
181 | if (lp->card_flags & ARC_CAN_10MBIT) | ||
182 | { | ||
183 | SET_SUBADR(SUB_SETUP2); | ||
184 | outb(lp->setup2, _XREG); | ||
185 | |||
186 | /* must now write the magic "restart operation" command */ | ||
187 | mdelay(1); | ||
188 | outb(0x18, _COMMAND); | ||
189 | } | ||
190 | |||
191 | lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) | 1; | ||
192 | /* Default 0x38 + register: Node ID */ | ||
193 | SETCONF; | ||
194 | outb(dev->dev_addr[0], _XREG); | ||
195 | |||
196 | /* reserve the irq */ | ||
197 | if (request_irq(dev->irq, &arcnet_interrupt, shared, | ||
198 | "arcnet (COM20020)", dev)) { | ||
199 | BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); | ||
200 | return -ENODEV; | ||
201 | } | ||
202 | |||
203 | dev->base_addr = ioaddr; | ||
204 | |||
205 | BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n", | ||
206 | lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq); | ||
207 | |||
208 | if (lp->backplane) | ||
209 | BUGMSG(D_NORMAL, "Using backplane mode.\n"); | ||
210 | |||
211 | if (lp->timeout != 3) | ||
212 | BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout); | ||
213 | |||
214 | BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n", | ||
215 | lp->setup >> 1, | ||
216 | clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]); | ||
217 | |||
218 | if (register_netdev(dev)) { | ||
219 | free_irq(dev->irq, dev); | ||
220 | return -EIO; | ||
221 | } | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | |||
226 | /* | ||
227 | * Do a hardware reset on the card, and set up necessary registers. | ||
228 | * | ||
229 | * This should be called as little as possible, because it disrupts the | ||
230 | * token on the network (causes a RECON) and requires a significant delay. | ||
231 | * | ||
232 | * However, it does make sure the card is in a defined state. | ||
233 | */ | ||
234 | static int com20020_reset(struct net_device *dev, int really_reset) | ||
235 | { | ||
236 | struct arcnet_local *lp = dev->priv; | ||
237 | u_int ioaddr = dev->base_addr; | ||
238 | u_char inbyte; | ||
239 | |||
240 | BUGMSG(D_DEBUG, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n", | ||
241 | __FILE__,__LINE__,__FUNCTION__,dev,lp,dev->name); | ||
242 | BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", | ||
243 | dev->name, ASTATUS()); | ||
244 | |||
245 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
246 | lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2); | ||
247 | /* power-up defaults */ | ||
248 | SETCONF; | ||
249 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
250 | |||
251 | if (really_reset) { | ||
252 | /* reset the card */ | ||
253 | ARCRESET; | ||
254 | mdelay(RESETtime * 2); /* COM20020 seems to be slower sometimes */ | ||
255 | } | ||
256 | /* clear flags & end reset */ | ||
257 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
258 | ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); | ||
259 | |||
260 | /* verify that the ARCnet signature byte is present */ | ||
261 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
262 | |||
263 | com20020_copy_from_card(dev, 0, 0, &inbyte, 1); | ||
264 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
265 | if (inbyte != TESTvalue) { | ||
266 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
267 | BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); | ||
268 | return 1; | ||
269 | } | ||
270 | /* enable extended (512-byte) packets */ | ||
271 | ACOMMAND(CONFIGcmd | EXTconf); | ||
272 | BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__); | ||
273 | |||
274 | /* done! return success. */ | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | |||
279 | static void com20020_setmask(struct net_device *dev, int mask) | ||
280 | { | ||
281 | u_int ioaddr = dev->base_addr; | ||
282 | BUGMSG(D_DURING, "Setting mask to %x at %x\n",mask,ioaddr); | ||
283 | AINTMASK(mask); | ||
284 | } | ||
285 | |||
286 | |||
287 | static void com20020_command(struct net_device *dev, int cmd) | ||
288 | { | ||
289 | u_int ioaddr = dev->base_addr; | ||
290 | ACOMMAND(cmd); | ||
291 | } | ||
292 | |||
293 | |||
294 | static int com20020_status(struct net_device *dev) | ||
295 | { | ||
296 | u_int ioaddr = dev->base_addr; | ||
297 | |||
298 | return ASTATUS() + (ADIAGSTATUS()<<8); | ||
299 | } | ||
300 | |||
301 | static void com20020_close(struct net_device *dev) | ||
302 | { | ||
303 | struct arcnet_local *lp = dev->priv; | ||
304 | int ioaddr = dev->base_addr; | ||
305 | |||
306 | /* disable transmitter */ | ||
307 | lp->config &= ~TXENcfg; | ||
308 | SETCONF; | ||
309 | } | ||
310 | |||
311 | /* Set or clear the multicast filter for this adaptor. | ||
312 | * num_addrs == -1 Promiscuous mode, receive all packets | ||
313 | * num_addrs == 0 Normal mode, clear multicast list | ||
314 | * num_addrs > 0 Multicast mode, receive normal and MC packets, and do | ||
315 | * best-effort filtering. | ||
316 | * FIXME - do multicast stuff, not just promiscuous. | ||
317 | */ | ||
318 | static void com20020_set_mc_list(struct net_device *dev) | ||
319 | { | ||
320 | struct arcnet_local *lp = dev->priv; | ||
321 | int ioaddr = dev->base_addr; | ||
322 | |||
323 | if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { /* Enable promiscuous mode */ | ||
324 | if (!(lp->setup & PROMISCset)) | ||
325 | BUGMSG(D_NORMAL, "Setting promiscuous flag...\n"); | ||
326 | SET_SUBADR(SUB_SETUP1); | ||
327 | lp->setup |= PROMISCset; | ||
328 | outb(lp->setup, _XREG); | ||
329 | } else | ||
330 | /* Disable promiscuous mode, use normal mode */ | ||
331 | { | ||
332 | if ((lp->setup & PROMISCset)) | ||
333 | BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n"); | ||
334 | SET_SUBADR(SUB_SETUP1); | ||
335 | lp->setup &= ~PROMISCset; | ||
336 | outb(lp->setup, _XREG); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | #ifdef MODULE | ||
341 | |||
342 | EXPORT_SYMBOL(com20020_check); | ||
343 | EXPORT_SYMBOL(com20020_found); | ||
344 | |||
345 | MODULE_LICENSE("GPL"); | ||
346 | |||
347 | int init_module(void) | ||
348 | { | ||
349 | BUGLVL(D_NORMAL) printk(VERSION); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | void cleanup_module(void) | ||
354 | { | ||
355 | } | ||
356 | |||
357 | #endif /* MODULE */ | ||