/* * SBE 2T3E3 synchronous serial card driver for Linux * * Copyright (C) 2009-2010 Krzysztof Halasa * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * This code is based on a driver written by SBE Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "2t3e3.h" static void check_leds(unsigned long arg) { struct card *card = (struct card *)arg; struct channel *channel0 = &card->channels[0]; static int blinker; update_led(channel0, ++blinker); if (has_two_ports(channel0->pdev)) update_led(&card->channels[1], blinker); card->timer.expires = jiffies + HZ / 10; add_timer(&card->timer); } static void t3e3_remove_channel(struct channel *channel) { struct pci_dev *pdev = channel->pdev; struct net_device *dev = channel->dev; /* system hangs if board asserts irq while module is unloaded */ cpld_stop_intr(channel); free_irq(dev->irq, dev); dc_drop_descriptor_list(channel); unregister_hdlc_device(dev); free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } static int t3e3_init_channel(struct channel *channel, struct pci_dev *pdev, struct card *card) { struct net_device *dev; unsigned int val; int err; err = pci_enable_device(pdev); if (err) return err; err = pci_request_regions(pdev, "SBE 2T3E3"); if (err) goto disable; dev = alloc_hdlcdev(channel); if (!dev) { pr_err("Out of memory\n"); err = -ENOMEM; goto free_regions; } t3e3_sc_init(channel); dev_to_priv(dev) = channel; channel->pdev = pdev; channel->dev = dev; channel->card = card; channel->addr = pci_resource_start(pdev, 0); if (pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P1) channel->h.slot = 1; else channel->h.slot = 0; err = setup_device(dev, channel); if (err) goto free_dev; pci_read_config_dword(channel->pdev, 0x40, &val); /* mask sleep mode */ pci_write_config_dword(channel->pdev, 0x40, val & 0x3FFFFFFF); pci_read_config_byte(channel->pdev, PCI_CACHE_LINE_SIZE, &channel->h.cache_size); pci_read_config_dword(channel->pdev, PCI_COMMAND, &channel->h.command); t3e3_init(channel); err = request_irq(dev->irq, &t3e3_intr, IRQF_SHARED, dev->name, dev); if (err) { netdev_warn(channel->dev, "%s: could not get irq: %d\n", dev->name, dev->irq); goto unregister_dev; } pci_set_drvdata(pdev, channel); return 0; unregister_dev: unregister_hdlc_device(dev); free_dev: free_netdev(dev); free_regions: pci_release_regions(pdev); disable: pci_disable_device(pdev); return err; } static void t3e3_remove_card(struct pci_dev *pdev) { struct channel *channel0 = pci_get_drvdata(pdev); struct card *card = channel0->card; del_timer(&card->timer); if (has_two_ports(channel0->pdev)) { t3e3_remove_channel(&card->channels[1]); pci_dev_put(card->channels[1].pdev); } t3e3_remove_channel(channel0); kfree(card); } static int t3e3_init_card(struct pci_dev *pdev, const struct pci_device_id *ent) { /* pdev points to channel #0 */ struct pci_dev *pdev1 = NULL; struct card *card; int channels = 1, err; if (has_two_ports(pdev)) { while ((pdev1 = pci_get_subsys(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P1, pdev1))) if (pdev1->bus == pdev->bus && pdev1->devfn == pdev->devfn + 8 /* next device on the same bus */) break; /* found the second channel */ if (!pdev1) { dev_err(&pdev->dev, "Can't find the second channel\n"); return -EFAULT; } channels = 2; /* holds the reference for pdev1 */ } card = kzalloc(sizeof(struct card) + channels * sizeof(struct channel), GFP_KERNEL); if (!card) return -ENOBUFS; spin_lock_init(&card->bootrom_lock); card->bootrom_addr = pci_resource_start(pdev, 0); err = t3e3_init_channel(&card->channels[0], pdev, card); if (err) goto free_card; if (channels == 2) { err = t3e3_init_channel(&card->channels[1], pdev1, card); if (err) { t3e3_remove_channel(&card->channels[0]); goto free_card; } } /* start LED timer */ init_timer(&card->timer); card->timer.function = check_leds; card->timer.expires = jiffies + HZ / 10; card->timer.data = (unsigned long)card; add_timer(&card->timer); return 0; free_card: kfree(card); return err; } static struct pci_device_id t3e3_pci_tbl[] = { { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_T3E3, 0, 0, 0 }, { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P0, 0, 0, 0 }, /* channel 1 will be initialized after channel 0 */ { 0, } }; static struct pci_driver t3e3_pci_driver = { .name = "SBE T3E3", .id_table = t3e3_pci_tbl, .probe = t3e3_init_card, .remove = t3e3_remove_card, }; module_pci_driver(t3e3_pci_driver); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, t3e3_pci_tbl);