1 | /* |
2 | * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers) |
3 | * |
4 | * Written 1997 by David Woodhouse. |
5 | * Written 1994-1999 by Avery Pennarun. |
6 | * Written 1999-2000 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 | |
29 | #define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt |
30 | |
31 | #include <linux/kernel.h> |
32 | #include <linux/module.h> |
33 | #include <linux/moduleparam.h> |
34 | #include <linux/ioport.h> |
35 | #include <linux/delay.h> |
36 | #include <linux/netdevice.h> |
37 | #include <linux/memblock.h> |
38 | #include <linux/init.h> |
39 | #include <linux/interrupt.h> |
40 | #include <linux/io.h> |
41 | |
42 | #include "arcdevice.h" |
43 | #include "com9026.h" |
44 | |
45 | /* Internal function declarations */ |
46 | |
47 | static int com90io_found(struct net_device *dev); |
48 | static void com90io_command(struct net_device *dev, int command); |
49 | static int com90io_status(struct net_device *dev); |
50 | static void com90io_setmask(struct net_device *dev, int mask); |
51 | static int com90io_reset(struct net_device *dev, int really_reset); |
52 | static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset, |
53 | void *buf, int count); |
54 | static void com90io_copy_from_card(struct net_device *dev, int bufnum, |
55 | int offset, void *buf, int count); |
56 | |
57 | /* Handy defines for ARCnet specific stuff */ |
58 | |
59 | /* The number of low I/O ports used by the card. */ |
60 | #define ARCNET_TOTAL_SIZE 16 |
61 | |
62 | /**************************************************************************** |
63 | * * |
64 | * IO-mapped operation routines * |
65 | * * |
66 | ****************************************************************************/ |
67 | |
68 | #undef ONE_AT_A_TIME_TX |
69 | #undef ONE_AT_A_TIME_RX |
70 | |
71 | static u_char get_buffer_byte(struct net_device *dev, unsigned offset) |
72 | { |
73 | int ioaddr = dev->base_addr; |
74 | |
75 | arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); |
76 | arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); |
77 | |
78 | return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); |
79 | } |
80 | |
81 | #ifdef ONE_AT_A_TIME_TX |
82 | static void put_buffer_byte(struct net_device *dev, unsigned offset, |
83 | u_char datum) |
84 | { |
85 | int ioaddr = dev->base_addr; |
86 | |
87 | arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); |
88 | arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); |
89 | |
90 | arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA); |
91 | } |
92 | |
93 | #endif |
94 | |
95 | static void get_whole_buffer(struct net_device *dev, unsigned offset, |
96 | unsigned length, char *dest) |
97 | { |
98 | int ioaddr = dev->base_addr; |
99 | |
100 | arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); |
101 | arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); |
102 | |
103 | while (length--) |
104 | #ifdef ONE_AT_A_TIME_RX |
105 | *(dest++) = get_buffer_byte(dev, offset++); |
106 | #else |
107 | *(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); |
108 | #endif |
109 | } |
110 | |
111 | static void put_whole_buffer(struct net_device *dev, unsigned offset, |
112 | unsigned length, char *dest) |
113 | { |
114 | int ioaddr = dev->base_addr; |
115 | |
116 | arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); |
117 | arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO); |
118 | |
119 | while (length--) |
120 | #ifdef ONE_AT_A_TIME_TX |
121 | put_buffer_byte(dev, offset++, *(dest++)); |
122 | #else |
123 | arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA); |
124 | #endif |
125 | } |
126 | |
127 | /* We cannot probe for an IO mapped card either, although we can check that |
128 | * it's where we were told it was, and even autoirq |
129 | */ |
130 | static int __init com90io_probe(struct net_device *dev) |
131 | { |
132 | int ioaddr = dev->base_addr, status; |
133 | unsigned long airqmask; |
134 | |
135 | if (BUGLVL(D_NORMAL)) { |
136 | pr_info("%s\n" , "COM90xx IO-mapped mode support (by David Woodhouse et el.)" ); |
137 | pr_info("E-mail me if you actually test this driver, please!\n" ); |
138 | } |
139 | |
140 | if (!ioaddr) { |
141 | arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n" ); |
142 | return -ENODEV; |
143 | } |
144 | if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe" )) { |
145 | arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n" , |
146 | ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); |
147 | return -ENXIO; |
148 | } |
149 | if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) { |
150 | arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n" , |
151 | ioaddr); |
152 | goto err_out; |
153 | } |
154 | arcnet_inb(ioaddr, COM9026_REG_R_RESET); |
155 | mdelay(RESETtime); |
156 | |
157 | status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); |
158 | |
159 | if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) { |
160 | arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n" , |
161 | status); |
162 | goto err_out; |
163 | } |
164 | arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n" , status); |
165 | |
166 | arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, |
167 | ioaddr, COM9026_REG_W_COMMAND); |
168 | |
169 | arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n" , |
170 | status); |
171 | |
172 | status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); |
173 | |
174 | if (status & RESETflag) { |
175 | arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n" , |
176 | status); |
177 | goto err_out; |
178 | } |
179 | arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag, |
180 | ioaddr, COM9026_REG_RW_CONFIG); |
181 | |
182 | /* Read first loc'n of memory */ |
183 | |
184 | arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); |
185 | arcnet_outb(0, ioaddr, COM9026_REG_W_ADDR_LO); |
186 | |
187 | status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); |
188 | if (status != 0xd1) { |
189 | arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n" , |
190 | status); |
191 | goto err_out; |
192 | } |
193 | if (!dev->irq) { |
194 | /* if we do this, we're sure to get an IRQ since the |
195 | * card has just reset and the NORXflag is on until |
196 | * we tell it to start receiving. |
197 | */ |
198 | |
199 | airqmask = probe_irq_on(); |
200 | arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK); |
201 | udelay(1); |
202 | arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK); |
203 | dev->irq = probe_irq_off(airqmask); |
204 | |
205 | if ((int)dev->irq <= 0) { |
206 | arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n" ); |
207 | goto err_out; |
208 | } |
209 | } |
210 | release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */ |
211 | return com90io_found(dev); |
212 | |
213 | err_out: |
214 | release_region(ioaddr, ARCNET_TOTAL_SIZE); |
215 | return -ENODEV; |
216 | } |
217 | |
218 | /* Set up the struct net_device associated with this card. Called after |
219 | * probing succeeds. |
220 | */ |
221 | static int __init com90io_found(struct net_device *dev) |
222 | { |
223 | struct arcnet_local *lp; |
224 | int ioaddr = dev->base_addr; |
225 | int err; |
226 | |
227 | /* Reserve the irq */ |
228 | if (request_irq(irq: dev->irq, handler: arcnet_interrupt, flags: 0, |
229 | name: "arcnet (COM90xx-IO)" , dev)) { |
230 | arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n" , dev->irq); |
231 | return -ENODEV; |
232 | } |
233 | /* Reserve the I/O region */ |
234 | if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, |
235 | "arcnet (COM90xx-IO)" )) { |
236 | free_irq(dev->irq, dev); |
237 | return -EBUSY; |
238 | } |
239 | |
240 | lp = netdev_priv(dev); |
241 | lp->card_name = "COM90xx I/O" ; |
242 | lp->hw.command = com90io_command; |
243 | lp->hw.status = com90io_status; |
244 | lp->hw.intmask = com90io_setmask; |
245 | lp->hw.reset = com90io_reset; |
246 | lp->hw.owner = THIS_MODULE; |
247 | lp->hw.copy_to_card = com90io_copy_to_card; |
248 | lp->hw.copy_from_card = com90io_copy_from_card; |
249 | |
250 | lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag; |
251 | arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); |
252 | |
253 | /* get and check the station ID from offset 1 in shmem */ |
254 | |
255 | arcnet_set_addr(dev, addr: get_buffer_byte(dev, offset: 1)); |
256 | |
257 | err = register_netdev(dev); |
258 | if (err) { |
259 | arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, |
260 | ioaddr, COM9026_REG_RW_CONFIG); |
261 | free_irq(dev->irq, dev); |
262 | release_region(dev->base_addr, ARCNET_TOTAL_SIZE); |
263 | return err; |
264 | } |
265 | |
266 | arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n" , |
267 | dev->dev_addr[0], dev->base_addr, dev->irq); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | /* Do a hardware reset on the card, and set up necessary registers. |
273 | * |
274 | * This should be called as little as possible, because it disrupts the |
275 | * token on the network (causes a RECON) and requires a significant delay. |
276 | * |
277 | * However, it does make sure the card is in a defined state. |
278 | */ |
279 | static int com90io_reset(struct net_device *dev, int really_reset) |
280 | { |
281 | struct arcnet_local *lp = netdev_priv(dev); |
282 | short ioaddr = dev->base_addr; |
283 | |
284 | arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n" , |
285 | dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS)); |
286 | |
287 | if (really_reset) { |
288 | /* reset the card */ |
289 | arcnet_inb(ioaddr, COM9026_REG_R_RESET); |
290 | mdelay(RESETtime); |
291 | } |
292 | /* Set the thing to IO-mapped, 8-bit mode */ |
293 | lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag; |
294 | arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); |
295 | |
296 | arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); |
297 | /* clear flags & end reset */ |
298 | arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); |
299 | |
300 | /* verify that the ARCnet signature byte is present */ |
301 | if (get_buffer_byte(dev, offset: 0) != TESTvalue) { |
302 | arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n" ); |
303 | return 1; |
304 | } |
305 | /* enable extended (512-byte) packets */ |
306 | arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); |
307 | /* done! return success. */ |
308 | return 0; |
309 | } |
310 | |
311 | static void com90io_command(struct net_device *dev, int cmd) |
312 | { |
313 | short ioaddr = dev->base_addr; |
314 | |
315 | arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND); |
316 | } |
317 | |
318 | static int com90io_status(struct net_device *dev) |
319 | { |
320 | short ioaddr = dev->base_addr; |
321 | |
322 | return arcnet_inb(ioaddr, COM9026_REG_R_STATUS); |
323 | } |
324 | |
325 | static void com90io_setmask(struct net_device *dev, int mask) |
326 | { |
327 | short ioaddr = dev->base_addr; |
328 | |
329 | arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK); |
330 | } |
331 | |
332 | static void com90io_copy_to_card(struct net_device *dev, int bufnum, |
333 | int offset, void *buf, int count) |
334 | { |
335 | TIME(dev, "put_whole_buffer" , count, |
336 | put_whole_buffer(dev, bufnum * 512 + offset, count, buf)); |
337 | } |
338 | |
339 | static void com90io_copy_from_card(struct net_device *dev, int bufnum, |
340 | int offset, void *buf, int count) |
341 | { |
342 | TIME(dev, "get_whole_buffer" , count, |
343 | get_whole_buffer(dev, bufnum * 512 + offset, count, buf)); |
344 | } |
345 | |
346 | static int io; /* use the insmod io= irq= shmem= options */ |
347 | static int irq; |
348 | static char device[9]; /* use eg. device=arc1 to change name */ |
349 | |
350 | module_param_hw(io, int, ioport, 0); |
351 | module_param_hw(irq, int, irq, 0); |
352 | module_param_string(device, device, sizeof(device), 0); |
353 | MODULE_LICENSE("GPL" ); |
354 | |
355 | #ifndef MODULE |
356 | static int __init com90io_setup(char *s) |
357 | { |
358 | int ints[4]; |
359 | |
360 | s = get_options(str: s, nints: 4, ints); |
361 | if (!ints[0]) |
362 | return 0; |
363 | switch (ints[0]) { |
364 | default: /* ERROR */ |
365 | pr_err("Too many arguments\n" ); |
366 | fallthrough; |
367 | case 2: /* IRQ */ |
368 | irq = ints[2]; |
369 | fallthrough; |
370 | case 1: /* IO address */ |
371 | io = ints[1]; |
372 | } |
373 | if (*s) |
374 | snprintf(buf: device, size: sizeof(device), fmt: "%s" , s); |
375 | return 1; |
376 | } |
377 | __setup("com90io=" , com90io_setup); |
378 | #endif |
379 | |
380 | static struct net_device *my_dev; |
381 | |
382 | static int __init com90io_init(void) |
383 | { |
384 | struct net_device *dev; |
385 | int err; |
386 | |
387 | dev = alloc_arcdev(name: device); |
388 | if (!dev) |
389 | return -ENOMEM; |
390 | |
391 | dev->base_addr = io; |
392 | dev->irq = irq; |
393 | if (dev->irq == 2) |
394 | dev->irq = 9; |
395 | |
396 | err = com90io_probe(dev); |
397 | |
398 | if (err) { |
399 | free_arcdev(dev); |
400 | return err; |
401 | } |
402 | |
403 | my_dev = dev; |
404 | return 0; |
405 | } |
406 | |
407 | static void __exit com90io_exit(void) |
408 | { |
409 | struct net_device *dev = my_dev; |
410 | int ioaddr = dev->base_addr; |
411 | |
412 | unregister_netdev(dev); |
413 | |
414 | /* In case the old driver is loaded later, |
415 | * set the thing back to MMAP mode |
416 | */ |
417 | arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, |
418 | ioaddr, COM9026_REG_RW_CONFIG); |
419 | |
420 | free_irq(dev->irq, dev); |
421 | release_region(dev->base_addr, ARCNET_TOTAL_SIZE); |
422 | free_arcdev(dev); |
423 | } |
424 | |
425 | module_init(com90io_init) |
426 | module_exit(com90io_exit) |
427 | |