1 /*
2 * Linux ARCnet driver - COM20020 PCI support
3 * Contemporary Controls PCI20 and SOHARD SH-ARC PCI
4 *
5 * Written 1994-1999 by Avery Pennarun,
6 * based on an ISA version by David Woodhouse.
7 * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
8 * Derived from skeleton.c by Donald Becker.
9 *
10 * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
11 * for sponsoring the further development of this driver.
12 *
13 * **********************
14 *
15 * The original copyright of skeleton.c was as follows:
16 *
17 * skeleton.c Written 1993 by Donald Becker.
18 * Copyright 1993 United States Government as represented by the
19 * Director, National Security Agency. This software may only be used
20 * and distributed according to the terms of the GNU General Public License as
21 * modified by SRC, incorporated herein by reference.
22 *
23 * **********************
24 *
25 * For more details, see drivers/net/arcnet.c
26 *
27 * **********************
28 */
29
30 #define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
31
32 #include <linux/module.h>
33 #include <linux/moduleparam.h>
34 #include <linux/kernel.h>
35 #include <linux/types.h>
36 #include <linux/ioport.h>
37 #include <linux/errno.h>
38 #include <linux/netdevice.h>
39 #include <linux/init.h>
40 #include <linux/interrupt.h>
41 #include <linux/pci.h>
42 #include <linux/list.h>
43 #include <linux/io.h>
44 #include <linux/leds.h>
45
46 #include "arcdevice.h"
47 #include "com20020.h"
48
49 /* Module parameters */
50
51 static int node;
52 static char device[9]; /* use eg. device="arc1" to change name */
53 static int timeout = 3;
54 static int backplane;
55 static int clockp;
56 static int clockm;
57
58 module_param(node, int, 0);
59 module_param_string(device, device, sizeof(device), 0);
60 module_param(timeout, int, 0);
61 module_param(backplane, int, 0);
62 module_param(clockp, int, 0);
63 module_param(clockm, int, 0);
64 MODULE_DESCRIPTION("ARCnet COM20020 chipset PCI driver");
65 MODULE_LICENSE("GPL");
66
led_tx_set(struct led_classdev * led_cdev,enum led_brightness value)67 static void led_tx_set(struct led_classdev *led_cdev,
68 enum led_brightness value)
69 {
70 struct com20020_dev *card;
71 struct com20020_priv *priv;
72 struct com20020_pci_card_info *ci;
73
74 card = container_of(led_cdev, struct com20020_dev, tx_led);
75
76 priv = card->pci_priv;
77 ci = priv->ci;
78
79 outb(!!value, priv->misc + ci->leds[card->index].green);
80 }
81
led_recon_set(struct led_classdev * led_cdev,enum led_brightness value)82 static void led_recon_set(struct led_classdev *led_cdev,
83 enum led_brightness value)
84 {
85 struct com20020_dev *card;
86 struct com20020_priv *priv;
87 struct com20020_pci_card_info *ci;
88
89 card = container_of(led_cdev, struct com20020_dev, recon_led);
90
91 priv = card->pci_priv;
92 ci = priv->ci;
93
94 outb(!!value, priv->misc + ci->leds[card->index].red);
95 }
96
backplane_mode_show(struct device * dev,struct device_attribute * attr,char * buf)97 static ssize_t backplane_mode_show(struct device *dev,
98 struct device_attribute *attr,
99 char *buf)
100 {
101 struct net_device *net_dev = to_net_dev(dev);
102 struct arcnet_local *lp = netdev_priv(net_dev);
103
104 return sprintf(buf, "%s\n", lp->backplane ? "true" : "false");
105 }
106 static DEVICE_ATTR_RO(backplane_mode);
107
108 static struct attribute *com20020_state_attrs[] = {
109 &dev_attr_backplane_mode.attr,
110 NULL,
111 };
112
113 static const struct attribute_group com20020_state_group = {
114 .name = NULL,
115 .attrs = com20020_state_attrs,
116 };
117
118 static struct com20020_pci_card_info card_info_2p5mbit;
119
120 static void com20020pci_remove(struct pci_dev *pdev);
121
com20020pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)122 static int com20020pci_probe(struct pci_dev *pdev,
123 const struct pci_device_id *id)
124 {
125 struct com20020_pci_card_info *ci;
126 struct com20020_pci_channel_map *mm;
127 struct net_device *dev;
128 struct arcnet_local *lp;
129 struct com20020_priv *priv;
130 int i, ioaddr, ret;
131 struct resource *r;
132
133 ret = 0;
134
135 if (pci_enable_device(pdev))
136 return -EIO;
137
138 priv = devm_kzalloc(&pdev->dev, sizeof(struct com20020_priv),
139 GFP_KERNEL);
140 if (!priv)
141 return -ENOMEM;
142
143 ci = (struct com20020_pci_card_info *)id->driver_data;
144 if (!ci)
145 ci = &card_info_2p5mbit;
146
147 priv->ci = ci;
148 mm = &ci->misc_map;
149
150 pci_set_drvdata(pdev, priv);
151
152 INIT_LIST_HEAD(&priv->list_dev);
153
154 if (mm->size) {
155 ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset;
156 r = devm_request_region(&pdev->dev, ioaddr, mm->size,
157 "com20020-pci");
158 if (!r) {
159 pr_err("IO region %xh-%xh already allocated.\n",
160 ioaddr, ioaddr + mm->size - 1);
161 return -EBUSY;
162 }
163 priv->misc = ioaddr;
164 }
165
166 for (i = 0; i < ci->devcount; i++) {
167 struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
168 struct com20020_dev *card;
169 int dev_id_mask = 0xf;
170
171 dev = alloc_arcdev(device);
172 if (!dev) {
173 ret = -ENOMEM;
174 break;
175 }
176 dev->dev_port = i;
177
178 dev->netdev_ops = &com20020_netdev_ops;
179
180 lp = netdev_priv(dev);
181
182 arc_printk(D_NORMAL, dev, "%s Controls\n", ci->name);
183 ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;
184
185 r = devm_request_region(&pdev->dev, ioaddr, cm->size,
186 "com20020-pci");
187 if (!r) {
188 pr_err("IO region %xh-%xh already allocated\n",
189 ioaddr, ioaddr + cm->size - 1);
190 ret = -EBUSY;
191 goto err_free_arcdev;
192 }
193
194 /* Dummy access after Reset
195 * ARCNET controller needs
196 * this access to detect bustype
197 */
198 arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND);
199 arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT);
200
201 SET_NETDEV_DEV(dev, &pdev->dev);
202 dev->base_addr = ioaddr;
203 arcnet_set_addr(dev, node);
204 dev->sysfs_groups[0] = &com20020_state_group;
205 dev->irq = pdev->irq;
206 lp->card_name = "PCI COM20020";
207 lp->card_flags = ci->flags;
208 lp->backplane = backplane;
209 lp->clockp = clockp & 7;
210 lp->clockm = clockm & 3;
211 lp->timeout = timeout;
212 lp->hw.owner = THIS_MODULE;
213
214 lp->backplane = (inb(priv->misc) >> (2 + i)) & 0x1;
215
216 if (!strncmp(ci->name, "EAE PLX-PCI FB2", 15))
217 lp->backplane = 1;
218
219 if (ci->flags & ARC_HAS_ROTARY) {
220 /* Get the dev_id from the PLX rotary coder */
221 if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15))
222 dev_id_mask = 0x3;
223 dev->dev_id = (inb(priv->misc + ci->rotary) >> 4) & dev_id_mask;
224 snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i);
225 }
226
227 if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
228 pr_err("IO address %Xh is empty!\n", ioaddr);
229 ret = -EIO;
230 goto err_free_arcdev;
231 }
232 if (com20020_check(dev)) {
233 ret = -EIO;
234 goto err_free_arcdev;
235 }
236
237 ret = com20020_found(dev, IRQF_SHARED);
238 if (ret)
239 goto err_free_arcdev;
240
241 card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
242 GFP_KERNEL);
243 if (!card) {
244 ret = -ENOMEM;
245 goto err_free_arcdev;
246 }
247
248 card->index = i;
249 card->pci_priv = priv;
250
251 if (ci->flags & ARC_HAS_LED) {
252 card->tx_led.brightness_set = led_tx_set;
253 card->tx_led.default_trigger = devm_kasprintf(&pdev->dev,
254 GFP_KERNEL, "arc%d-%d-tx",
255 dev->dev_id, i);
256 if (!card->tx_led.default_trigger) {
257 ret = -ENOMEM;
258 goto err_free_arcdev;
259 }
260 card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
261 "pci:green:tx:%d-%d",
262 dev->dev_id, i);
263 if (!card->tx_led.name) {
264 ret = -ENOMEM;
265 goto err_free_arcdev;
266 }
267 card->tx_led.dev = &dev->dev;
268 card->recon_led.brightness_set = led_recon_set;
269 card->recon_led.default_trigger = devm_kasprintf(&pdev->dev,
270 GFP_KERNEL, "arc%d-%d-recon",
271 dev->dev_id, i);
272 if (!card->recon_led.default_trigger) {
273 ret = -ENOMEM;
274 goto err_free_arcdev;
275 }
276 card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
277 "pci:red:recon:%d-%d",
278 dev->dev_id, i);
279 if (!card->recon_led.name) {
280 ret = -ENOMEM;
281 goto err_free_arcdev;
282 }
283 card->recon_led.dev = &dev->dev;
284
285 ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
286 if (ret)
287 goto err_free_arcdev;
288
289 ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
290 if (ret)
291 goto err_free_arcdev;
292
293 dev_set_drvdata(&dev->dev, card);
294 devm_arcnet_led_init(dev, dev->dev_id, i);
295 }
296
297 card->dev = dev;
298 list_add(&card->list, &priv->list_dev);
299 continue;
300
301 err_free_arcdev:
302 free_arcdev(dev);
303 break;
304 }
305 if (ret)
306 com20020pci_remove(pdev);
307 return ret;
308 }
309
com20020pci_remove(struct pci_dev * pdev)310 static void com20020pci_remove(struct pci_dev *pdev)
311 {
312 struct com20020_dev *card, *tmpcard;
313 struct com20020_priv *priv;
314
315 priv = pci_get_drvdata(pdev);
316
317 list_for_each_entry_safe(card, tmpcard, &priv->list_dev, list) {
318 struct net_device *dev = card->dev;
319
320 unregister_netdev(dev);
321 free_irq(dev->irq, dev);
322 free_arcdev(dev);
323 }
324 }
325
326 static struct com20020_pci_card_info card_info_10mbit = {
327 .name = "ARC-PCI",
328 .devcount = 1,
329 .chan_map_tbl = {
330 {
331 .bar = 2,
332 .offset = 0x00,
333 .size = 0x08,
334 },
335 },
336 .flags = ARC_CAN_10MBIT,
337 };
338
339 static struct com20020_pci_card_info card_info_5mbit = {
340 .name = "ARC-PCI",
341 .devcount = 1,
342 .chan_map_tbl = {
343 {
344 .bar = 2,
345 .offset = 0x00,
346 .size = 0x08,
347 },
348 },
349 .flags = ARC_IS_5MBIT,
350 };
351
352 static struct com20020_pci_card_info card_info_2p5mbit = {
353 .name = "ARC-PCI",
354 .devcount = 1,
355 .chan_map_tbl = {
356 {
357 .bar = 2,
358 .offset = 0x00,
359 .size = 0x08,
360 },
361 },
362 };
363
364 static struct com20020_pci_card_info card_info_sohard = {
365 .name = "SOHARD SH ARC-PCI",
366 .devcount = 1,
367 /* SOHARD needs PCI base addr 4 */
368 .chan_map_tbl = {
369 {
370 .bar = 4,
371 .offset = 0x00,
372 .size = 0x08
373 },
374 },
375 .flags = ARC_CAN_10MBIT,
376 };
377
378 static struct com20020_pci_card_info card_info_eae_arc1 = {
379 .name = "EAE PLX-PCI ARC1",
380 .devcount = 1,
381 .chan_map_tbl = {
382 {
383 .bar = 2,
384 .offset = 0x00,
385 .size = 0x08,
386 },
387 },
388 .misc_map = {
389 .bar = 2,
390 .offset = 0x10,
391 .size = 0x04,
392 },
393 .leds = {
394 {
395 .green = 0x0,
396 .red = 0x1,
397 },
398 },
399 .rotary = 0x0,
400 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,
401 };
402
403 static struct com20020_pci_card_info card_info_eae_ma1 = {
404 .name = "EAE PLX-PCI MA1",
405 .devcount = 2,
406 .chan_map_tbl = {
407 {
408 .bar = 2,
409 .offset = 0x00,
410 .size = 0x08,
411 }, {
412 .bar = 2,
413 .offset = 0x08,
414 .size = 0x08,
415 }
416 },
417 .misc_map = {
418 .bar = 2,
419 .offset = 0x10,
420 .size = 0x04,
421 },
422 .leds = {
423 {
424 .green = 0x0,
425 .red = 0x1,
426 }, {
427 .green = 0x2,
428 .red = 0x3,
429 },
430 },
431 .rotary = 0x0,
432 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,
433 };
434
435 static struct com20020_pci_card_info card_info_eae_fb2 = {
436 .name = "EAE PLX-PCI FB2",
437 .devcount = 1,
438 .chan_map_tbl = {
439 {
440 .bar = 2,
441 .offset = 0x00,
442 .size = 0x08,
443 },
444 },
445 .misc_map = {
446 .bar = 2,
447 .offset = 0x10,
448 .size = 0x04,
449 },
450 .leds = {
451 {
452 .green = 0x0,
453 .red = 0x1,
454 },
455 },
456 .rotary = 0x0,
457 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,
458 };
459
460 static const struct pci_device_id com20020pci_id_table[] = {
461 {
462 0x1571, 0xa001,
463 PCI_ANY_ID, PCI_ANY_ID,
464 0, 0,
465 0,
466 },
467 {
468 0x1571, 0xa002,
469 PCI_ANY_ID, PCI_ANY_ID,
470 0, 0,
471 0,
472 },
473 {
474 0x1571, 0xa003,
475 PCI_ANY_ID, PCI_ANY_ID,
476 0, 0,
477 0
478 },
479 {
480 0x1571, 0xa004,
481 PCI_ANY_ID, PCI_ANY_ID,
482 0, 0,
483 0,
484 },
485 {
486 0x1571, 0xa005,
487 PCI_ANY_ID, PCI_ANY_ID,
488 0, 0,
489 0
490 },
491 {
492 0x1571, 0xa006,
493 PCI_ANY_ID, PCI_ANY_ID,
494 0, 0,
495 0
496 },
497 {
498 0x1571, 0xa007,
499 PCI_ANY_ID, PCI_ANY_ID,
500 0, 0,
501 0
502 },
503 {
504 0x1571, 0xa008,
505 PCI_ANY_ID, PCI_ANY_ID,
506 0, 0,
507 0
508 },
509 {
510 0x1571, 0xa009,
511 PCI_ANY_ID, PCI_ANY_ID,
512 0, 0,
513 (kernel_ulong_t)&card_info_5mbit
514 },
515 {
516 0x1571, 0xa00a,
517 PCI_ANY_ID, PCI_ANY_ID,
518 0, 0,
519 (kernel_ulong_t)&card_info_5mbit
520 },
521 {
522 0x1571, 0xa00b,
523 PCI_ANY_ID, PCI_ANY_ID,
524 0, 0,
525 (kernel_ulong_t)&card_info_5mbit
526 },
527 {
528 0x1571, 0xa00c,
529 PCI_ANY_ID, PCI_ANY_ID,
530 0, 0,
531 (kernel_ulong_t)&card_info_5mbit
532 },
533 {
534 0x1571, 0xa00d,
535 PCI_ANY_ID, PCI_ANY_ID,
536 0, 0,
537 (kernel_ulong_t)&card_info_5mbit
538 },
539 {
540 0x1571, 0xa00e,
541 PCI_ANY_ID, PCI_ANY_ID,
542 0, 0,
543 (kernel_ulong_t)&card_info_5mbit
544 },
545 {
546 0x1571, 0xa201,
547 PCI_ANY_ID, PCI_ANY_ID,
548 0, 0,
549 (kernel_ulong_t)&card_info_10mbit
550 },
551 {
552 0x1571, 0xa202,
553 PCI_ANY_ID, PCI_ANY_ID,
554 0, 0,
555 (kernel_ulong_t)&card_info_10mbit
556 },
557 {
558 0x1571, 0xa203,
559 PCI_ANY_ID, PCI_ANY_ID,
560 0, 0,
561 (kernel_ulong_t)&card_info_10mbit
562 },
563 {
564 0x1571, 0xa204,
565 PCI_ANY_ID, PCI_ANY_ID,
566 0, 0,
567 (kernel_ulong_t)&card_info_10mbit
568 },
569 {
570 0x1571, 0xa205,
571 PCI_ANY_ID, PCI_ANY_ID,
572 0, 0,
573 (kernel_ulong_t)&card_info_10mbit
574 },
575 {
576 0x1571, 0xa206,
577 PCI_ANY_ID, PCI_ANY_ID,
578 0, 0,
579 (kernel_ulong_t)&card_info_10mbit
580 },
581 {
582 0x10B5, 0x9030,
583 0x10B5, 0x2978,
584 0, 0,
585 (kernel_ulong_t)&card_info_sohard
586 },
587 {
588 0x10B5, 0x9050,
589 0x10B5, 0x2273,
590 0, 0,
591 (kernel_ulong_t)&card_info_sohard
592 },
593 {
594 0x10B5, 0x9050,
595 0x10B5, 0x3263,
596 0, 0,
597 (kernel_ulong_t)&card_info_eae_arc1
598 },
599 {
600 0x10B5, 0x9050,
601 0x10B5, 0x3292,
602 0, 0,
603 (kernel_ulong_t)&card_info_eae_ma1
604 },
605 {
606 0x10B5, 0x9050,
607 0x10B5, 0x3294,
608 0, 0,
609 (kernel_ulong_t)&card_info_eae_fb2
610 },
611 {
612 0x14BA, 0x6000,
613 PCI_ANY_ID, PCI_ANY_ID,
614 0, 0,
615 (kernel_ulong_t)&card_info_10mbit
616 },
617 {
618 0x10B5, 0x2200,
619 PCI_ANY_ID, PCI_ANY_ID,
620 0, 0,
621 (kernel_ulong_t)&card_info_10mbit
622 },
623 { 0, }
624 };
625
626 MODULE_DEVICE_TABLE(pci, com20020pci_id_table);
627
628 static struct pci_driver com20020pci_driver = {
629 .name = "com20020",
630 .id_table = com20020pci_id_table,
631 .probe = com20020pci_probe,
632 .remove = com20020pci_remove,
633 };
634
635 module_pci_driver(com20020pci_driver);
636