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 | |
29 | #define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt |
30 | |
31 | #include <linux/module.h> |
32 | #include <linux/kernel.h> |
33 | #include <linux/types.h> |
34 | #include <linux/ioport.h> |
35 | #include <linux/errno.h> |
36 | #include <linux/delay.h> |
37 | #include <linux/netdevice.h> |
38 | #include <linux/init.h> |
39 | #include <linux/interrupt.h> |
40 | #include <linux/io.h> |
41 | |
42 | #include "arcdevice.h" |
43 | #include "com20020.h" |
44 | |
45 | static const char * const clockrates[] = { |
46 | "XXXXXXX" , "XXXXXXXX" , "XXXXXX" , "2.5 Mb/s" , |
47 | "1.25Mb/s" , "625 Kb/s" , "312.5 Kb/s" , "156.25 Kb/s" , |
48 | "Reserved" , "Reserved" , "Reserved" |
49 | }; |
50 | |
51 | static void com20020_command(struct net_device *dev, int command); |
52 | static int com20020_status(struct net_device *dev); |
53 | static void com20020_setmask(struct net_device *dev, int mask); |
54 | static int com20020_reset(struct net_device *dev, int really_reset); |
55 | static void com20020_copy_to_card(struct net_device *dev, int bufnum, |
56 | int offset, void *buf, int count); |
57 | static void com20020_copy_from_card(struct net_device *dev, int bufnum, |
58 | int offset, void *buf, int count); |
59 | static void com20020_set_mc_list(struct net_device *dev); |
60 | static void com20020_close(struct net_device *); |
61 | |
62 | static void com20020_copy_from_card(struct net_device *dev, int bufnum, |
63 | int offset, void *buf, int count) |
64 | { |
65 | int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; |
66 | |
67 | /* set up the address register */ |
68 | arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag, |
69 | ioaddr, COM20020_REG_W_ADDR_HI); |
70 | arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); |
71 | |
72 | /* copy the data */ |
73 | TIME(dev, "insb" , count, |
74 | arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); |
75 | } |
76 | |
77 | static void com20020_copy_to_card(struct net_device *dev, int bufnum, |
78 | int offset, void *buf, int count) |
79 | { |
80 | int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; |
81 | |
82 | /* set up the address register */ |
83 | arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI); |
84 | arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); |
85 | |
86 | /* copy the data */ |
87 | TIME(dev, "outsb" , count, |
88 | arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); |
89 | } |
90 | |
91 | /* Reset the card and check some basic stuff during the detection stage. */ |
92 | int com20020_check(struct net_device *dev) |
93 | { |
94 | int ioaddr = dev->base_addr, status; |
95 | struct arcnet_local *lp = netdev_priv(dev); |
96 | |
97 | arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); |
98 | udelay(5); |
99 | arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG); |
100 | mdelay(RESETtime); |
101 | |
102 | lp->setup = lp->clockm ? 0 : (lp->clockp << 1); |
103 | lp->setup2 = (lp->clockm << 4) | 8; |
104 | |
105 | /* CHECK: should we do this for SOHARD cards ? */ |
106 | /* Enable P1Mode for backplane mode */ |
107 | lp->setup = lp->setup | P1MODE; |
108 | |
109 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); |
110 | arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); |
111 | |
112 | if (lp->clockm != 0) { |
113 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); |
114 | arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); |
115 | |
116 | /* must now write the magic "restart operation" command */ |
117 | mdelay(1); |
118 | arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); |
119 | } |
120 | |
121 | lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; |
122 | /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ |
123 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
124 | arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG); |
125 | |
126 | status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); |
127 | |
128 | if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) { |
129 | arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n" , status); |
130 | return -ENODEV; |
131 | } |
132 | arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n" , status); |
133 | |
134 | arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, |
135 | ioaddr, COM20020_REG_W_COMMAND); |
136 | status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); |
137 | arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n" , |
138 | status); |
139 | |
140 | /* Read first location of memory */ |
141 | arcnet_outb(0 | RDDATAflag | AUTOINCflag, |
142 | ioaddr, COM20020_REG_W_ADDR_HI); |
143 | arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO); |
144 | |
145 | status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA); |
146 | if (status != TESTvalue) { |
147 | arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n" , |
148 | status); |
149 | return -ENODEV; |
150 | } |
151 | return 0; |
152 | } |
153 | |
154 | static int com20020_set_hwaddr(struct net_device *dev, void *addr) |
155 | { |
156 | int ioaddr = dev->base_addr; |
157 | struct arcnet_local *lp = netdev_priv(dev); |
158 | struct sockaddr *hwaddr = addr; |
159 | |
160 | dev_addr_set(dev, addr: hwaddr->sa_data); |
161 | com20020_set_subaddress(lp, ioaddr, SUB_NODE); |
162 | arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | static int com20020_netdev_open(struct net_device *dev) |
168 | { |
169 | int ioaddr = dev->base_addr; |
170 | struct arcnet_local *lp = netdev_priv(dev); |
171 | |
172 | lp->config |= TXENcfg; |
173 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
174 | |
175 | return arcnet_open(dev); |
176 | } |
177 | |
178 | static int com20020_netdev_close(struct net_device *dev) |
179 | { |
180 | int ioaddr = dev->base_addr; |
181 | struct arcnet_local *lp = netdev_priv(dev); |
182 | |
183 | arcnet_close(dev); |
184 | |
185 | /* disable transmitter */ |
186 | lp->config &= ~TXENcfg; |
187 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
188 | return 0; |
189 | } |
190 | |
191 | const struct net_device_ops com20020_netdev_ops = { |
192 | .ndo_open = com20020_netdev_open, |
193 | .ndo_stop = com20020_netdev_close, |
194 | .ndo_start_xmit = arcnet_send_packet, |
195 | .ndo_tx_timeout = arcnet_timeout, |
196 | .ndo_set_mac_address = com20020_set_hwaddr, |
197 | .ndo_set_rx_mode = com20020_set_mc_list, |
198 | }; |
199 | |
200 | /* Set up the struct net_device associated with this card. Called after |
201 | * probing succeeds. |
202 | */ |
203 | int com20020_found(struct net_device *dev, int shared) |
204 | { |
205 | struct arcnet_local *lp; |
206 | int ioaddr = dev->base_addr; |
207 | |
208 | /* Initialize the rest of the device structure. */ |
209 | |
210 | lp = netdev_priv(dev); |
211 | |
212 | lp->hw.owner = THIS_MODULE; |
213 | lp->hw.command = com20020_command; |
214 | lp->hw.status = com20020_status; |
215 | lp->hw.intmask = com20020_setmask; |
216 | lp->hw.reset = com20020_reset; |
217 | lp->hw.copy_to_card = com20020_copy_to_card; |
218 | lp->hw.copy_from_card = com20020_copy_from_card; |
219 | lp->hw.close = com20020_close; |
220 | |
221 | /* FIXME: do this some other way! */ |
222 | if (!dev->dev_addr[0]) |
223 | arcnet_set_addr(dev, arcnet_inb(ioaddr, 8)); |
224 | |
225 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); |
226 | arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); |
227 | |
228 | if (lp->card_flags & ARC_CAN_10MBIT) { |
229 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); |
230 | arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); |
231 | |
232 | /* must now write the magic "restart operation" command */ |
233 | mdelay(1); |
234 | arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); |
235 | } |
236 | |
237 | lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; |
238 | /* Default 0x38 + register: Node ID */ |
239 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
240 | arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); |
241 | |
242 | /* reserve the irq */ |
243 | if (request_irq(irq: dev->irq, handler: arcnet_interrupt, flags: shared, |
244 | name: "arcnet (COM20020)" , dev)) { |
245 | arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n" , dev->irq); |
246 | return -ENODEV; |
247 | } |
248 | |
249 | arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n" , |
250 | lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq); |
251 | |
252 | if (lp->backplane) |
253 | arc_printk(D_NORMAL, dev, "Using backplane mode.\n" ); |
254 | |
255 | if (lp->timeout != 3) |
256 | arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n" , |
257 | lp->timeout); |
258 | |
259 | arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n" , |
260 | lp->setup >> 1, |
261 | clockrates[3 - |
262 | ((lp->setup2 & 0xF0) >> 4) + |
263 | ((lp->setup & 0x0F) >> 1)]); |
264 | /* The clockrates array index looks very fragile. |
265 | * It seems like it could have negative indexing. |
266 | */ |
267 | |
268 | if (register_netdev(dev)) { |
269 | free_irq(dev->irq, dev); |
270 | return -EIO; |
271 | } |
272 | return 0; |
273 | } |
274 | |
275 | /* Do a hardware reset on the card, and set up necessary registers. |
276 | * |
277 | * This should be called as little as possible, because it disrupts the |
278 | * token on the network (causes a RECON) and requires a significant delay. |
279 | * |
280 | * However, it does make sure the card is in a defined state. |
281 | */ |
282 | static int com20020_reset(struct net_device *dev, int really_reset) |
283 | { |
284 | struct arcnet_local *lp = netdev_priv(dev); |
285 | u_int ioaddr = dev->base_addr; |
286 | u_char inbyte; |
287 | |
288 | arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n" , |
289 | __FILE__, __LINE__, __func__, dev, lp, dev->name); |
290 | arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n" , |
291 | dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); |
292 | |
293 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
294 | lp->config |= (lp->timeout << 3) | (lp->backplane << 2); |
295 | /* power-up defaults */ |
296 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
297 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
298 | |
299 | if (really_reset) { |
300 | /* reset the card */ |
301 | arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); |
302 | udelay(5); |
303 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
304 | mdelay(RESETtime * 2); |
305 | /* COM20020 seems to be slower sometimes */ |
306 | } |
307 | /* clear flags & end reset */ |
308 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
309 | arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, |
310 | ioaddr, COM20020_REG_W_COMMAND); |
311 | |
312 | /* verify that the ARCnet signature byte is present */ |
313 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
314 | |
315 | com20020_copy_from_card(dev, bufnum: 0, offset: 0, buf: &inbyte, count: 1); |
316 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
317 | if (inbyte != TESTvalue) { |
318 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , |
319 | __FILE__, __LINE__, __func__); |
320 | arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n" ); |
321 | return 1; |
322 | } |
323 | /* enable extended (512-byte) packets */ |
324 | arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND); |
325 | |
326 | arc_printk(D_DEBUG, dev, "%s: %d: %s\n" , __FILE__, __LINE__, __func__); |
327 | |
328 | /* done! return success. */ |
329 | return 0; |
330 | } |
331 | |
332 | static void com20020_setmask(struct net_device *dev, int mask) |
333 | { |
334 | u_int ioaddr = dev->base_addr; |
335 | |
336 | arc_printk(D_DURING, dev, "Setting mask to %x at %x\n" , mask, ioaddr); |
337 | arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK); |
338 | } |
339 | |
340 | static void com20020_command(struct net_device *dev, int cmd) |
341 | { |
342 | u_int ioaddr = dev->base_addr; |
343 | |
344 | arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND); |
345 | } |
346 | |
347 | static int com20020_status(struct net_device *dev) |
348 | { |
349 | u_int ioaddr = dev->base_addr; |
350 | |
351 | return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) + |
352 | (arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8); |
353 | } |
354 | |
355 | static void com20020_close(struct net_device *dev) |
356 | { |
357 | struct arcnet_local *lp = netdev_priv(dev); |
358 | int ioaddr = dev->base_addr; |
359 | |
360 | /* disable transmitter */ |
361 | lp->config &= ~TXENcfg; |
362 | arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); |
363 | } |
364 | |
365 | /* Set or clear the multicast filter for this adaptor. |
366 | * num_addrs == -1 Promiscuous mode, receive all packets |
367 | * num_addrs == 0 Normal mode, clear multicast list |
368 | * num_addrs > 0 Multicast mode, receive normal and MC packets, and do |
369 | * best-effort filtering. |
370 | * FIXME - do multicast stuff, not just promiscuous. |
371 | */ |
372 | static void com20020_set_mc_list(struct net_device *dev) |
373 | { |
374 | struct arcnet_local *lp = netdev_priv(dev); |
375 | int ioaddr = dev->base_addr; |
376 | |
377 | if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { |
378 | /* Enable promiscuous mode */ |
379 | if (!(lp->setup & PROMISCset)) |
380 | arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n" ); |
381 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); |
382 | lp->setup |= PROMISCset; |
383 | arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); |
384 | } else { |
385 | /* Disable promiscuous mode, use normal mode */ |
386 | if ((lp->setup & PROMISCset)) |
387 | arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n" ); |
388 | com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); |
389 | lp->setup &= ~PROMISCset; |
390 | arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); |
391 | } |
392 | } |
393 | |
394 | #if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \ |
395 | defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \ |
396 | defined(CONFIG_ARCNET_COM20020_CS_MODULE) |
397 | EXPORT_SYMBOL(com20020_check); |
398 | EXPORT_SYMBOL(com20020_found); |
399 | EXPORT_SYMBOL(com20020_netdev_ops); |
400 | #endif |
401 | |
402 | MODULE_LICENSE("GPL" ); |
403 | |
404 | #ifdef MODULE |
405 | |
406 | static int __init com20020_module_init(void) |
407 | { |
408 | if (BUGLVL(D_NORMAL)) |
409 | pr_info("%s\n" , "COM20020 chipset support (by David Woodhouse et al.)" ); |
410 | return 0; |
411 | } |
412 | |
413 | static void __exit com20020_module_exit(void) |
414 | { |
415 | } |
416 | module_init(com20020_module_init); |
417 | module_exit(com20020_module_exit); |
418 | #endif /* MODULE */ |
419 | |