620 lines · c
1/*2 * Linux ARCnet driver - COM20020 PCI support3 * Contemporary Controls PCI20 and SOHARD SH-ARC PCI4 *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 the19 * Director, National Security Agency. This software may only be used20 * and distributed according to the terms of the GNU General Public License as21 * modified by SRC, incorporated herein by reference.22 *23 * **********************24 *25 * For more details, see drivers/net/arcnet.c26 *27 * **********************28 */29 30#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt31 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 51static int node;52static char device[9]; /* use eg. device="arc1" to change name */53static int timeout = 3;54static int backplane;55static int clockp;56static int clockm;57 58module_param(node, int, 0);59module_param_string(device, device, sizeof(device), 0);60module_param(timeout, int, 0);61module_param(backplane, int, 0);62module_param(clockp, int, 0);63module_param(clockm, int, 0);64MODULE_DESCRIPTION("ARCnet COM20020 chipset PCI driver");65MODULE_LICENSE("GPL");66 67static 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 82static 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 97static 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}106static DEVICE_ATTR_RO(backplane_mode);107 108static struct attribute *com20020_state_attrs[] = {109 &dev_attr_backplane_mode.attr,110 NULL,111};112 113static const struct attribute_group com20020_state_group = {114 .name = NULL,115 .attrs = com20020_state_attrs,116};117 118static void com20020pci_remove(struct pci_dev *pdev);119 120static int com20020pci_probe(struct pci_dev *pdev,121 const struct pci_device_id *id)122{123 struct com20020_pci_card_info *ci;124 struct com20020_pci_channel_map *mm;125 struct net_device *dev;126 struct arcnet_local *lp;127 struct com20020_priv *priv;128 int i, ioaddr, ret;129 struct resource *r;130 131 ret = 0;132 133 if (pci_enable_device(pdev))134 return -EIO;135 136 priv = devm_kzalloc(&pdev->dev, sizeof(struct com20020_priv),137 GFP_KERNEL);138 if (!priv)139 return -ENOMEM;140 141 ci = (struct com20020_pci_card_info *)id->driver_data;142 if (!ci)143 return -EINVAL;144 145 priv->ci = ci;146 mm = &ci->misc_map;147 148 pci_set_drvdata(pdev, priv);149 150 INIT_LIST_HEAD(&priv->list_dev);151 152 if (mm->size) {153 ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset;154 r = devm_request_region(&pdev->dev, ioaddr, mm->size,155 "com20020-pci");156 if (!r) {157 pr_err("IO region %xh-%xh already allocated.\n",158 ioaddr, ioaddr + mm->size - 1);159 return -EBUSY;160 }161 priv->misc = ioaddr;162 }163 164 for (i = 0; i < ci->devcount; i++) {165 struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];166 struct com20020_dev *card;167 int dev_id_mask = 0xf;168 169 dev = alloc_arcdev(device);170 if (!dev) {171 ret = -ENOMEM;172 break;173 }174 dev->dev_port = i;175 176 dev->netdev_ops = &com20020_netdev_ops;177 178 lp = netdev_priv(dev);179 180 arc_printk(D_NORMAL, dev, "%s Controls\n", ci->name);181 ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;182 183 r = devm_request_region(&pdev->dev, ioaddr, cm->size,184 "com20020-pci");185 if (!r) {186 pr_err("IO region %xh-%xh already allocated\n",187 ioaddr, ioaddr + cm->size - 1);188 ret = -EBUSY;189 goto err_free_arcdev;190 }191 192 /* Dummy access after Reset193 * ARCNET controller needs194 * this access to detect bustype195 */196 arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND);197 arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT);198 199 SET_NETDEV_DEV(dev, &pdev->dev);200 dev->base_addr = ioaddr;201 arcnet_set_addr(dev, node);202 dev->sysfs_groups[0] = &com20020_state_group;203 dev->irq = pdev->irq;204 lp->card_name = "PCI COM20020";205 lp->card_flags = ci->flags;206 lp->backplane = backplane;207 lp->clockp = clockp & 7;208 lp->clockm = clockm & 3;209 lp->timeout = timeout;210 lp->hw.owner = THIS_MODULE;211 212 lp->backplane = (inb(priv->misc) >> (2 + i)) & 0x1;213 214 if (!strncmp(ci->name, "EAE PLX-PCI FB2", 15))215 lp->backplane = 1;216 217 if (ci->flags & ARC_HAS_ROTARY) {218 /* Get the dev_id from the PLX rotary coder */219 if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15))220 dev_id_mask = 0x3;221 dev->dev_id = (inb(priv->misc + ci->rotary) >> 4) & dev_id_mask;222 snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i);223 }224 225 if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {226 pr_err("IO address %Xh is empty!\n", ioaddr);227 ret = -EIO;228 goto err_free_arcdev;229 }230 if (com20020_check(dev)) {231 ret = -EIO;232 goto err_free_arcdev;233 }234 235 ret = com20020_found(dev, IRQF_SHARED);236 if (ret)237 goto err_free_arcdev;238 239 card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),240 GFP_KERNEL);241 if (!card) {242 ret = -ENOMEM;243 goto err_free_arcdev;244 }245 246 card->index = i;247 card->pci_priv = priv;248 249 if (ci->flags & ARC_HAS_LED) {250 card->tx_led.brightness_set = led_tx_set;251 card->tx_led.default_trigger = devm_kasprintf(&pdev->dev,252 GFP_KERNEL, "arc%d-%d-tx",253 dev->dev_id, i);254 card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,255 "pci:green:tx:%d-%d",256 dev->dev_id, i);257 258 card->tx_led.dev = &dev->dev;259 card->recon_led.brightness_set = led_recon_set;260 card->recon_led.default_trigger = devm_kasprintf(&pdev->dev,261 GFP_KERNEL, "arc%d-%d-recon",262 dev->dev_id, i);263 card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,264 "pci:red:recon:%d-%d",265 dev->dev_id, i);266 card->recon_led.dev = &dev->dev;267 268 ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);269 if (ret)270 goto err_free_arcdev;271 272 ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);273 if (ret)274 goto err_free_arcdev;275 276 dev_set_drvdata(&dev->dev, card);277 devm_arcnet_led_init(dev, dev->dev_id, i);278 }279 280 card->dev = dev;281 list_add(&card->list, &priv->list_dev);282 continue;283 284err_free_arcdev:285 free_arcdev(dev);286 break;287 }288 if (ret)289 com20020pci_remove(pdev);290 return ret;291}292 293static void com20020pci_remove(struct pci_dev *pdev)294{295 struct com20020_dev *card, *tmpcard;296 struct com20020_priv *priv;297 298 priv = pci_get_drvdata(pdev);299 300 list_for_each_entry_safe(card, tmpcard, &priv->list_dev, list) {301 struct net_device *dev = card->dev;302 303 unregister_netdev(dev);304 free_irq(dev->irq, dev);305 free_arcdev(dev);306 }307}308 309static struct com20020_pci_card_info card_info_10mbit = {310 .name = "ARC-PCI",311 .devcount = 1,312 .chan_map_tbl = {313 {314 .bar = 2,315 .offset = 0x00,316 .size = 0x08,317 },318 },319 .flags = ARC_CAN_10MBIT,320};321 322static struct com20020_pci_card_info card_info_5mbit = {323 .name = "ARC-PCI",324 .devcount = 1,325 .chan_map_tbl = {326 {327 .bar = 2,328 .offset = 0x00,329 .size = 0x08,330 },331 },332 .flags = ARC_IS_5MBIT,333};334 335static struct com20020_pci_card_info card_info_sohard = {336 .name = "SOHARD SH ARC-PCI",337 .devcount = 1,338 /* SOHARD needs PCI base addr 4 */339 .chan_map_tbl = {340 {341 .bar = 4,342 .offset = 0x00,343 .size = 0x08344 },345 },346 .flags = ARC_CAN_10MBIT,347};348 349static struct com20020_pci_card_info card_info_eae_arc1 = {350 .name = "EAE PLX-PCI ARC1",351 .devcount = 1,352 .chan_map_tbl = {353 {354 .bar = 2,355 .offset = 0x00,356 .size = 0x08,357 },358 },359 .misc_map = {360 .bar = 2,361 .offset = 0x10,362 .size = 0x04,363 },364 .leds = {365 {366 .green = 0x0,367 .red = 0x1,368 },369 },370 .rotary = 0x0,371 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,372};373 374static struct com20020_pci_card_info card_info_eae_ma1 = {375 .name = "EAE PLX-PCI MA1",376 .devcount = 2,377 .chan_map_tbl = {378 {379 .bar = 2,380 .offset = 0x00,381 .size = 0x08,382 }, {383 .bar = 2,384 .offset = 0x08,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 .green = 0x2,399 .red = 0x3,400 },401 },402 .rotary = 0x0,403 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,404};405 406static struct com20020_pci_card_info card_info_eae_fb2 = {407 .name = "EAE PLX-PCI FB2",408 .devcount = 1,409 .chan_map_tbl = {410 {411 .bar = 2,412 .offset = 0x00,413 .size = 0x08,414 },415 },416 .misc_map = {417 .bar = 2,418 .offset = 0x10,419 .size = 0x04,420 },421 .leds = {422 {423 .green = 0x0,424 .red = 0x1,425 },426 },427 .rotary = 0x0,428 .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT,429};430 431static const struct pci_device_id com20020pci_id_table[] = {432 {433 0x1571, 0xa001,434 PCI_ANY_ID, PCI_ANY_ID,435 0, 0,436 0,437 },438 {439 0x1571, 0xa002,440 PCI_ANY_ID, PCI_ANY_ID,441 0, 0,442 0,443 },444 {445 0x1571, 0xa003,446 PCI_ANY_ID, PCI_ANY_ID,447 0, 0,448 0449 },450 {451 0x1571, 0xa004,452 PCI_ANY_ID, PCI_ANY_ID,453 0, 0,454 0,455 },456 {457 0x1571, 0xa005,458 PCI_ANY_ID, PCI_ANY_ID,459 0, 0,460 0461 },462 {463 0x1571, 0xa006,464 PCI_ANY_ID, PCI_ANY_ID,465 0, 0,466 0467 },468 {469 0x1571, 0xa007,470 PCI_ANY_ID, PCI_ANY_ID,471 0, 0,472 0473 },474 {475 0x1571, 0xa008,476 PCI_ANY_ID, PCI_ANY_ID,477 0, 0,478 0479 },480 {481 0x1571, 0xa009,482 PCI_ANY_ID, PCI_ANY_ID,483 0, 0,484 (kernel_ulong_t)&card_info_5mbit485 },486 {487 0x1571, 0xa00a,488 PCI_ANY_ID, PCI_ANY_ID,489 0, 0,490 (kernel_ulong_t)&card_info_5mbit491 },492 {493 0x1571, 0xa00b,494 PCI_ANY_ID, PCI_ANY_ID,495 0, 0,496 (kernel_ulong_t)&card_info_5mbit497 },498 {499 0x1571, 0xa00c,500 PCI_ANY_ID, PCI_ANY_ID,501 0, 0,502 (kernel_ulong_t)&card_info_5mbit503 },504 {505 0x1571, 0xa00d,506 PCI_ANY_ID, PCI_ANY_ID,507 0, 0,508 (kernel_ulong_t)&card_info_5mbit509 },510 {511 0x1571, 0xa00e,512 PCI_ANY_ID, PCI_ANY_ID,513 0, 0,514 (kernel_ulong_t)&card_info_5mbit515 },516 {517 0x1571, 0xa201,518 PCI_ANY_ID, PCI_ANY_ID,519 0, 0,520 (kernel_ulong_t)&card_info_10mbit521 },522 {523 0x1571, 0xa202,524 PCI_ANY_ID, PCI_ANY_ID,525 0, 0,526 (kernel_ulong_t)&card_info_10mbit527 },528 {529 0x1571, 0xa203,530 PCI_ANY_ID, PCI_ANY_ID,531 0, 0,532 (kernel_ulong_t)&card_info_10mbit533 },534 {535 0x1571, 0xa204,536 PCI_ANY_ID, PCI_ANY_ID,537 0, 0,538 (kernel_ulong_t)&card_info_10mbit539 },540 {541 0x1571, 0xa205,542 PCI_ANY_ID, PCI_ANY_ID,543 0, 0,544 (kernel_ulong_t)&card_info_10mbit545 },546 {547 0x1571, 0xa206,548 PCI_ANY_ID, PCI_ANY_ID,549 0, 0,550 (kernel_ulong_t)&card_info_10mbit551 },552 {553 0x10B5, 0x9030,554 0x10B5, 0x2978,555 0, 0,556 (kernel_ulong_t)&card_info_sohard557 },558 {559 0x10B5, 0x9050,560 0x10B5, 0x2273,561 0, 0,562 (kernel_ulong_t)&card_info_sohard563 },564 {565 0x10B5, 0x9050,566 0x10B5, 0x3263,567 0, 0,568 (kernel_ulong_t)&card_info_eae_arc1569 },570 {571 0x10B5, 0x9050,572 0x10B5, 0x3292,573 0, 0,574 (kernel_ulong_t)&card_info_eae_ma1575 },576 {577 0x10B5, 0x9050,578 0x10B5, 0x3294,579 0, 0,580 (kernel_ulong_t)&card_info_eae_fb2581 },582 {583 0x14BA, 0x6000,584 PCI_ANY_ID, PCI_ANY_ID,585 0, 0,586 (kernel_ulong_t)&card_info_10mbit587 },588 {589 0x10B5, 0x2200,590 PCI_ANY_ID, PCI_ANY_ID,591 0, 0,592 (kernel_ulong_t)&card_info_10mbit593 },594 { 0, }595};596 597MODULE_DEVICE_TABLE(pci, com20020pci_id_table);598 599static struct pci_driver com20020pci_driver = {600 .name = "com20020",601 .id_table = com20020pci_id_table,602 .probe = com20020pci_probe,603 .remove = com20020pci_remove,604};605 606static int __init com20020pci_init(void)607{608 if (BUGLVL(D_NORMAL))609 pr_info("%s\n", "COM20020 PCI support");610 return pci_register_driver(&com20020pci_driver);611}612 613static void __exit com20020pci_cleanup(void)614{615 pci_unregister_driver(&com20020pci_driver);616}617 618module_init(com20020pci_init)619module_exit(com20020pci_cleanup)620