1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2*0419bf0fSAditya Srivastava /* 305e5027eSGreg Kroah-Hartman * driver for the TEWS TPCI-200 device 405e5027eSGreg Kroah-Hartman * 505e5027eSGreg Kroah-Hartman * Copyright (C) 2009-2012 CERN (www.cern.ch) 605e5027eSGreg Kroah-Hartman * Author: Nicolas Serafini, EIC2 SA 705e5027eSGreg Kroah-Hartman * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> 805e5027eSGreg Kroah-Hartman */ 905e5027eSGreg Kroah-Hartman 1005e5027eSGreg Kroah-Hartman #include <linux/module.h> 1105e5027eSGreg Kroah-Hartman #include <linux/slab.h> 1205e5027eSGreg Kroah-Hartman #include "tpci200.h" 1305e5027eSGreg Kroah-Hartman 1405e5027eSGreg Kroah-Hartman static const u16 tpci200_status_timeout[] = { 1505e5027eSGreg Kroah-Hartman TPCI200_A_TIMEOUT, 1605e5027eSGreg Kroah-Hartman TPCI200_B_TIMEOUT, 1705e5027eSGreg Kroah-Hartman TPCI200_C_TIMEOUT, 1805e5027eSGreg Kroah-Hartman TPCI200_D_TIMEOUT, 1905e5027eSGreg Kroah-Hartman }; 2005e5027eSGreg Kroah-Hartman 2105e5027eSGreg Kroah-Hartman static const u16 tpci200_status_error[] = { 2205e5027eSGreg Kroah-Hartman TPCI200_A_ERROR, 2305e5027eSGreg Kroah-Hartman TPCI200_B_ERROR, 2405e5027eSGreg Kroah-Hartman TPCI200_C_ERROR, 2505e5027eSGreg Kroah-Hartman TPCI200_D_ERROR, 2605e5027eSGreg Kroah-Hartman }; 2705e5027eSGreg Kroah-Hartman 2805e5027eSGreg Kroah-Hartman static const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { 2905e5027eSGreg Kroah-Hartman [IPACK_IO_SPACE] = TPCI200_IO_SPACE_SIZE, 3005e5027eSGreg Kroah-Hartman [IPACK_ID_SPACE] = TPCI200_ID_SPACE_SIZE, 3105e5027eSGreg Kroah-Hartman [IPACK_INT_SPACE] = TPCI200_INT_SPACE_SIZE, 3205e5027eSGreg Kroah-Hartman [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_SIZE, 3305e5027eSGreg Kroah-Hartman [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, 3405e5027eSGreg Kroah-Hartman }; 3505e5027eSGreg Kroah-Hartman 3605e5027eSGreg Kroah-Hartman static const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { 3705e5027eSGreg Kroah-Hartman [IPACK_IO_SPACE] = TPCI200_IO_SPACE_INTERVAL, 3805e5027eSGreg Kroah-Hartman [IPACK_ID_SPACE] = TPCI200_ID_SPACE_INTERVAL, 3905e5027eSGreg Kroah-Hartman [IPACK_INT_SPACE] = TPCI200_INT_SPACE_INTERVAL, 4005e5027eSGreg Kroah-Hartman [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_INTERVAL, 4105e5027eSGreg Kroah-Hartman [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, 4205e5027eSGreg Kroah-Hartman }; 4305e5027eSGreg Kroah-Hartman 4405e5027eSGreg Kroah-Hartman static struct tpci200_board *check_slot(struct ipack_device *dev) 4505e5027eSGreg Kroah-Hartman { 4605e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200; 4705e5027eSGreg Kroah-Hartman 4805e5027eSGreg Kroah-Hartman if (dev == NULL) 4905e5027eSGreg Kroah-Hartman return NULL; 5005e5027eSGreg Kroah-Hartman 5105e5027eSGreg Kroah-Hartman 5205e5027eSGreg Kroah-Hartman tpci200 = dev_get_drvdata(dev->bus->parent); 5305e5027eSGreg Kroah-Hartman 5405e5027eSGreg Kroah-Hartman if (tpci200 == NULL) { 5505e5027eSGreg Kroah-Hartman dev_info(&dev->dev, "carrier board not found\n"); 5605e5027eSGreg Kroah-Hartman return NULL; 5705e5027eSGreg Kroah-Hartman } 5805e5027eSGreg Kroah-Hartman 5905e5027eSGreg Kroah-Hartman if (dev->slot >= TPCI200_NB_SLOT) { 6005e5027eSGreg Kroah-Hartman dev_info(&dev->dev, 6105e5027eSGreg Kroah-Hartman "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", 6205e5027eSGreg Kroah-Hartman dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); 6305e5027eSGreg Kroah-Hartman return NULL; 6405e5027eSGreg Kroah-Hartman } 6505e5027eSGreg Kroah-Hartman 6605e5027eSGreg Kroah-Hartman return tpci200; 6705e5027eSGreg Kroah-Hartman } 6805e5027eSGreg Kroah-Hartman 6905e5027eSGreg Kroah-Hartman static void tpci200_clear_mask(struct tpci200_board *tpci200, 7005e5027eSGreg Kroah-Hartman __le16 __iomem *addr, u16 mask) 7105e5027eSGreg Kroah-Hartman { 7205e5027eSGreg Kroah-Hartman unsigned long flags; 7305e5027eSGreg Kroah-Hartman spin_lock_irqsave(&tpci200->regs_lock, flags); 7405e5027eSGreg Kroah-Hartman iowrite16(ioread16(addr) & (~mask), addr); 7505e5027eSGreg Kroah-Hartman spin_unlock_irqrestore(&tpci200->regs_lock, flags); 7605e5027eSGreg Kroah-Hartman } 7705e5027eSGreg Kroah-Hartman 7805e5027eSGreg Kroah-Hartman static void tpci200_set_mask(struct tpci200_board *tpci200, 7905e5027eSGreg Kroah-Hartman __le16 __iomem *addr, u16 mask) 8005e5027eSGreg Kroah-Hartman { 8105e5027eSGreg Kroah-Hartman unsigned long flags; 8205e5027eSGreg Kroah-Hartman spin_lock_irqsave(&tpci200->regs_lock, flags); 8305e5027eSGreg Kroah-Hartman iowrite16(ioread16(addr) | mask, addr); 8405e5027eSGreg Kroah-Hartman spin_unlock_irqrestore(&tpci200->regs_lock, flags); 8505e5027eSGreg Kroah-Hartman } 8605e5027eSGreg Kroah-Hartman 8705e5027eSGreg Kroah-Hartman static void tpci200_unregister(struct tpci200_board *tpci200) 8805e5027eSGreg Kroah-Hartman { 8905e5027eSGreg Kroah-Hartman free_irq(tpci200->info->pdev->irq, (void *) tpci200); 9005e5027eSGreg Kroah-Hartman 9105e5027eSGreg Kroah-Hartman pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); 9205e5027eSGreg Kroah-Hartman pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); 9305e5027eSGreg Kroah-Hartman 9405e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); 9505e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); 9605e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); 9705e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); 9805e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); 9905e5027eSGreg Kroah-Hartman 10005e5027eSGreg Kroah-Hartman pci_disable_device(tpci200->info->pdev); 10105e5027eSGreg Kroah-Hartman pci_dev_put(tpci200->info->pdev); 10205e5027eSGreg Kroah-Hartman } 10305e5027eSGreg Kroah-Hartman 10405e5027eSGreg Kroah-Hartman static void tpci200_enable_irq(struct tpci200_board *tpci200, 10505e5027eSGreg Kroah-Hartman int islot) 10605e5027eSGreg Kroah-Hartman { 10705e5027eSGreg Kroah-Hartman tpci200_set_mask(tpci200, 10805e5027eSGreg Kroah-Hartman &tpci200->info->interface_regs->control[islot], 10905e5027eSGreg Kroah-Hartman TPCI200_INT0_EN | TPCI200_INT1_EN); 11005e5027eSGreg Kroah-Hartman } 11105e5027eSGreg Kroah-Hartman 11205e5027eSGreg Kroah-Hartman static void tpci200_disable_irq(struct tpci200_board *tpci200, 11305e5027eSGreg Kroah-Hartman int islot) 11405e5027eSGreg Kroah-Hartman { 11505e5027eSGreg Kroah-Hartman tpci200_clear_mask(tpci200, 11605e5027eSGreg Kroah-Hartman &tpci200->info->interface_regs->control[islot], 11705e5027eSGreg Kroah-Hartman TPCI200_INT0_EN | TPCI200_INT1_EN); 11805e5027eSGreg Kroah-Hartman } 11905e5027eSGreg Kroah-Hartman 12005e5027eSGreg Kroah-Hartman static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) 12105e5027eSGreg Kroah-Hartman { 12205e5027eSGreg Kroah-Hartman irqreturn_t ret; 12305e5027eSGreg Kroah-Hartman 12405e5027eSGreg Kroah-Hartman if (!slot_irq) 12505e5027eSGreg Kroah-Hartman return -ENODEV; 12605e5027eSGreg Kroah-Hartman ret = slot_irq->handler(slot_irq->arg); 12705e5027eSGreg Kroah-Hartman 12805e5027eSGreg Kroah-Hartman return ret; 12905e5027eSGreg Kroah-Hartman } 13005e5027eSGreg Kroah-Hartman 13105e5027eSGreg Kroah-Hartman static irqreturn_t tpci200_interrupt(int irq, void *dev_id) 13205e5027eSGreg Kroah-Hartman { 13305e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; 13405e5027eSGreg Kroah-Hartman struct slot_irq *slot_irq; 13505e5027eSGreg Kroah-Hartman irqreturn_t ret; 13605e5027eSGreg Kroah-Hartman u16 status_reg; 13705e5027eSGreg Kroah-Hartman int i; 13805e5027eSGreg Kroah-Hartman 13905e5027eSGreg Kroah-Hartman /* Read status register */ 14005e5027eSGreg Kroah-Hartman status_reg = ioread16(&tpci200->info->interface_regs->status); 14105e5027eSGreg Kroah-Hartman 14205e5027eSGreg Kroah-Hartman /* Did we cause the interrupt? */ 14305e5027eSGreg Kroah-Hartman if (!(status_reg & TPCI200_SLOT_INT_MASK)) 14405e5027eSGreg Kroah-Hartman return IRQ_NONE; 14505e5027eSGreg Kroah-Hartman 14605e5027eSGreg Kroah-Hartman /* callback to the IRQ handler for the corresponding slot */ 14705e5027eSGreg Kroah-Hartman rcu_read_lock(); 14805e5027eSGreg Kroah-Hartman for (i = 0; i < TPCI200_NB_SLOT; i++) { 14905e5027eSGreg Kroah-Hartman if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) 15005e5027eSGreg Kroah-Hartman continue; 15105e5027eSGreg Kroah-Hartman slot_irq = rcu_dereference(tpci200->slots[i].irq); 15205e5027eSGreg Kroah-Hartman ret = tpci200_slot_irq(slot_irq); 15305e5027eSGreg Kroah-Hartman if (ret == -ENODEV) { 15405e5027eSGreg Kroah-Hartman dev_info(&tpci200->info->pdev->dev, 15505e5027eSGreg Kroah-Hartman "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", 15605e5027eSGreg Kroah-Hartman tpci200->number, i); 15705e5027eSGreg Kroah-Hartman tpci200_disable_irq(tpci200, i); 15805e5027eSGreg Kroah-Hartman } 15905e5027eSGreg Kroah-Hartman } 16005e5027eSGreg Kroah-Hartman rcu_read_unlock(); 16105e5027eSGreg Kroah-Hartman 16205e5027eSGreg Kroah-Hartman return IRQ_HANDLED; 16305e5027eSGreg Kroah-Hartman } 16405e5027eSGreg Kroah-Hartman 16505e5027eSGreg Kroah-Hartman static int tpci200_free_irq(struct ipack_device *dev) 16605e5027eSGreg Kroah-Hartman { 16705e5027eSGreg Kroah-Hartman struct slot_irq *slot_irq; 16805e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200; 16905e5027eSGreg Kroah-Hartman 17005e5027eSGreg Kroah-Hartman tpci200 = check_slot(dev); 17105e5027eSGreg Kroah-Hartman if (tpci200 == NULL) 17205e5027eSGreg Kroah-Hartman return -EINVAL; 17305e5027eSGreg Kroah-Hartman 17405e5027eSGreg Kroah-Hartman if (mutex_lock_interruptible(&tpci200->mutex)) 17505e5027eSGreg Kroah-Hartman return -ERESTARTSYS; 17605e5027eSGreg Kroah-Hartman 17705e5027eSGreg Kroah-Hartman if (tpci200->slots[dev->slot].irq == NULL) { 17805e5027eSGreg Kroah-Hartman mutex_unlock(&tpci200->mutex); 17905e5027eSGreg Kroah-Hartman return -EINVAL; 18005e5027eSGreg Kroah-Hartman } 18105e5027eSGreg Kroah-Hartman 18205e5027eSGreg Kroah-Hartman tpci200_disable_irq(tpci200, dev->slot); 18305e5027eSGreg Kroah-Hartman slot_irq = tpci200->slots[dev->slot].irq; 18405e5027eSGreg Kroah-Hartman /* uninstall handler */ 18505e5027eSGreg Kroah-Hartman RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); 18605e5027eSGreg Kroah-Hartman synchronize_rcu(); 18705e5027eSGreg Kroah-Hartman kfree(slot_irq); 18805e5027eSGreg Kroah-Hartman mutex_unlock(&tpci200->mutex); 18905e5027eSGreg Kroah-Hartman return 0; 19005e5027eSGreg Kroah-Hartman } 19105e5027eSGreg Kroah-Hartman 19205e5027eSGreg Kroah-Hartman static int tpci200_request_irq(struct ipack_device *dev, 19305e5027eSGreg Kroah-Hartman irqreturn_t (*handler)(void *), void *arg) 19405e5027eSGreg Kroah-Hartman { 19505e5027eSGreg Kroah-Hartman int res = 0; 19605e5027eSGreg Kroah-Hartman struct slot_irq *slot_irq; 19705e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200; 19805e5027eSGreg Kroah-Hartman 19905e5027eSGreg Kroah-Hartman tpci200 = check_slot(dev); 20005e5027eSGreg Kroah-Hartman if (tpci200 == NULL) 20105e5027eSGreg Kroah-Hartman return -EINVAL; 20205e5027eSGreg Kroah-Hartman 20305e5027eSGreg Kroah-Hartman if (mutex_lock_interruptible(&tpci200->mutex)) 20405e5027eSGreg Kroah-Hartman return -ERESTARTSYS; 20505e5027eSGreg Kroah-Hartman 20605e5027eSGreg Kroah-Hartman if (tpci200->slots[dev->slot].irq != NULL) { 20705e5027eSGreg Kroah-Hartman dev_err(&dev->dev, 20805e5027eSGreg Kroah-Hartman "Slot [%d:%d] IRQ already registered !\n", 20905e5027eSGreg Kroah-Hartman dev->bus->bus_nr, 21005e5027eSGreg Kroah-Hartman dev->slot); 21105e5027eSGreg Kroah-Hartman res = -EINVAL; 21205e5027eSGreg Kroah-Hartman goto out_unlock; 21305e5027eSGreg Kroah-Hartman } 21405e5027eSGreg Kroah-Hartman 21505e5027eSGreg Kroah-Hartman slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); 21605e5027eSGreg Kroah-Hartman if (slot_irq == NULL) { 21705e5027eSGreg Kroah-Hartman dev_err(&dev->dev, 21805e5027eSGreg Kroah-Hartman "Slot [%d:%d] unable to allocate memory for IRQ !\n", 21905e5027eSGreg Kroah-Hartman dev->bus->bus_nr, dev->slot); 22005e5027eSGreg Kroah-Hartman res = -ENOMEM; 22105e5027eSGreg Kroah-Hartman goto out_unlock; 22205e5027eSGreg Kroah-Hartman } 22305e5027eSGreg Kroah-Hartman 22405e5027eSGreg Kroah-Hartman /* 22505e5027eSGreg Kroah-Hartman * WARNING: Setup Interrupt Vector in the IndustryPack device 22605e5027eSGreg Kroah-Hartman * before an IRQ request. 22705e5027eSGreg Kroah-Hartman * Read the User Manual of your IndustryPack device to know 22805e5027eSGreg Kroah-Hartman * where to write the vector in memory. 22905e5027eSGreg Kroah-Hartman */ 23005e5027eSGreg Kroah-Hartman slot_irq->handler = handler; 23105e5027eSGreg Kroah-Hartman slot_irq->arg = arg; 23205e5027eSGreg Kroah-Hartman slot_irq->holder = dev; 23305e5027eSGreg Kroah-Hartman 23405e5027eSGreg Kroah-Hartman rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); 23505e5027eSGreg Kroah-Hartman tpci200_enable_irq(tpci200, dev->slot); 23605e5027eSGreg Kroah-Hartman 23705e5027eSGreg Kroah-Hartman out_unlock: 23805e5027eSGreg Kroah-Hartman mutex_unlock(&tpci200->mutex); 23905e5027eSGreg Kroah-Hartman return res; 24005e5027eSGreg Kroah-Hartman } 24105e5027eSGreg Kroah-Hartman 24205e5027eSGreg Kroah-Hartman static int tpci200_register(struct tpci200_board *tpci200) 24305e5027eSGreg Kroah-Hartman { 24405e5027eSGreg Kroah-Hartman int i; 24505e5027eSGreg Kroah-Hartman int res; 24605e5027eSGreg Kroah-Hartman phys_addr_t ioidint_base; 24705e5027eSGreg Kroah-Hartman unsigned short slot_ctrl; 24805e5027eSGreg Kroah-Hartman 24905e5027eSGreg Kroah-Hartman if (pci_enable_device(tpci200->info->pdev) < 0) 25005e5027eSGreg Kroah-Hartman return -ENODEV; 25105e5027eSGreg Kroah-Hartman 25205e5027eSGreg Kroah-Hartman /* Request IP interface register (Bar 2) */ 25305e5027eSGreg Kroah-Hartman res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, 25405e5027eSGreg Kroah-Hartman "Carrier IP interface registers"); 25505e5027eSGreg Kroah-Hartman if (res) { 25605e5027eSGreg Kroah-Hartman dev_err(&tpci200->info->pdev->dev, 25705e5027eSGreg Kroah-Hartman "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", 25805e5027eSGreg Kroah-Hartman tpci200->info->pdev->bus->number, 25905e5027eSGreg Kroah-Hartman tpci200->info->pdev->devfn); 26005e5027eSGreg Kroah-Hartman goto out_disable_pci; 26105e5027eSGreg Kroah-Hartman } 26205e5027eSGreg Kroah-Hartman 26305e5027eSGreg Kroah-Hartman /* Request IO ID INT space (Bar 3) */ 26405e5027eSGreg Kroah-Hartman res = pci_request_region(tpci200->info->pdev, 26505e5027eSGreg Kroah-Hartman TPCI200_IO_ID_INT_SPACES_BAR, 26605e5027eSGreg Kroah-Hartman "Carrier IO ID INT space"); 26705e5027eSGreg Kroah-Hartman if (res) { 26805e5027eSGreg Kroah-Hartman dev_err(&tpci200->info->pdev->dev, 26905e5027eSGreg Kroah-Hartman "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", 27005e5027eSGreg Kroah-Hartman tpci200->info->pdev->bus->number, 27105e5027eSGreg Kroah-Hartman tpci200->info->pdev->devfn); 27205e5027eSGreg Kroah-Hartman goto out_release_ip_space; 27305e5027eSGreg Kroah-Hartman } 27405e5027eSGreg Kroah-Hartman 27505e5027eSGreg Kroah-Hartman /* Request MEM8 space (Bar 5) */ 27605e5027eSGreg Kroah-Hartman res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, 27705e5027eSGreg Kroah-Hartman "Carrier MEM8 space"); 27805e5027eSGreg Kroah-Hartman if (res) { 27905e5027eSGreg Kroah-Hartman dev_err(&tpci200->info->pdev->dev, 28005e5027eSGreg Kroah-Hartman "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", 28105e5027eSGreg Kroah-Hartman tpci200->info->pdev->bus->number, 28205e5027eSGreg Kroah-Hartman tpci200->info->pdev->devfn); 28305e5027eSGreg Kroah-Hartman goto out_release_ioid_int_space; 28405e5027eSGreg Kroah-Hartman } 28505e5027eSGreg Kroah-Hartman 28605e5027eSGreg Kroah-Hartman /* Request MEM16 space (Bar 4) */ 28705e5027eSGreg Kroah-Hartman res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, 28805e5027eSGreg Kroah-Hartman "Carrier MEM16 space"); 28905e5027eSGreg Kroah-Hartman if (res) { 29005e5027eSGreg Kroah-Hartman dev_err(&tpci200->info->pdev->dev, 29105e5027eSGreg Kroah-Hartman "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", 29205e5027eSGreg Kroah-Hartman tpci200->info->pdev->bus->number, 29305e5027eSGreg Kroah-Hartman tpci200->info->pdev->devfn); 29405e5027eSGreg Kroah-Hartman goto out_release_mem8_space; 29505e5027eSGreg Kroah-Hartman } 29605e5027eSGreg Kroah-Hartman 29705e5027eSGreg Kroah-Hartman /* Map internal tpci200 driver user space */ 29805e5027eSGreg Kroah-Hartman tpci200->info->interface_regs = 2994bdc0d67SChristoph Hellwig ioremap(pci_resource_start(tpci200->info->pdev, 30005e5027eSGreg Kroah-Hartman TPCI200_IP_INTERFACE_BAR), 30105e5027eSGreg Kroah-Hartman TPCI200_IFACE_SIZE); 30243986798SZhouyang Jia if (!tpci200->info->interface_regs) { 30343986798SZhouyang Jia dev_err(&tpci200->info->pdev->dev, 30443986798SZhouyang Jia "(bn 0x%X, sn 0x%X) failed to map driver user space!", 30543986798SZhouyang Jia tpci200->info->pdev->bus->number, 30643986798SZhouyang Jia tpci200->info->pdev->devfn); 30713331747SWei Yongjun res = -ENOMEM; 30843986798SZhouyang Jia goto out_release_mem8_space; 30943986798SZhouyang Jia } 31005e5027eSGreg Kroah-Hartman 31105e5027eSGreg Kroah-Hartman /* Initialize lock that protects interface_regs */ 31205e5027eSGreg Kroah-Hartman spin_lock_init(&tpci200->regs_lock); 31305e5027eSGreg Kroah-Hartman 31405e5027eSGreg Kroah-Hartman ioidint_base = pci_resource_start(tpci200->info->pdev, 31505e5027eSGreg Kroah-Hartman TPCI200_IO_ID_INT_SPACES_BAR); 31605e5027eSGreg Kroah-Hartman tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; 31705e5027eSGreg Kroah-Hartman tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; 31805e5027eSGreg Kroah-Hartman tpci200->mod_mem[IPACK_INT_SPACE] = 31905e5027eSGreg Kroah-Hartman ioidint_base + TPCI200_INT_SPACE_OFF; 32005e5027eSGreg Kroah-Hartman tpci200->mod_mem[IPACK_MEM8_SPACE] = 32105e5027eSGreg Kroah-Hartman pci_resource_start(tpci200->info->pdev, 32205e5027eSGreg Kroah-Hartman TPCI200_MEM8_SPACE_BAR); 32305e5027eSGreg Kroah-Hartman tpci200->mod_mem[IPACK_MEM16_SPACE] = 32405e5027eSGreg Kroah-Hartman pci_resource_start(tpci200->info->pdev, 32505e5027eSGreg Kroah-Hartman TPCI200_MEM16_SPACE_BAR); 32605e5027eSGreg Kroah-Hartman 32705e5027eSGreg Kroah-Hartman /* Set the default parameters of the slot 32805e5027eSGreg Kroah-Hartman * INT0 disabled, level sensitive 32905e5027eSGreg Kroah-Hartman * INT1 disabled, level sensitive 33005e5027eSGreg Kroah-Hartman * error interrupt disabled 33105e5027eSGreg Kroah-Hartman * timeout interrupt disabled 33205e5027eSGreg Kroah-Hartman * recover time disabled 33305e5027eSGreg Kroah-Hartman * clock rate 8 MHz 33405e5027eSGreg Kroah-Hartman */ 33505e5027eSGreg Kroah-Hartman slot_ctrl = 0; 33605e5027eSGreg Kroah-Hartman for (i = 0; i < TPCI200_NB_SLOT; i++) 33705e5027eSGreg Kroah-Hartman writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); 33805e5027eSGreg Kroah-Hartman 33905e5027eSGreg Kroah-Hartman res = request_irq(tpci200->info->pdev->irq, 34005e5027eSGreg Kroah-Hartman tpci200_interrupt, IRQF_SHARED, 34105e5027eSGreg Kroah-Hartman KBUILD_MODNAME, (void *) tpci200); 34205e5027eSGreg Kroah-Hartman if (res) { 34305e5027eSGreg Kroah-Hartman dev_err(&tpci200->info->pdev->dev, 34405e5027eSGreg Kroah-Hartman "(bn 0x%X, sn 0x%X) unable to register IRQ !", 34505e5027eSGreg Kroah-Hartman tpci200->info->pdev->bus->number, 34605e5027eSGreg Kroah-Hartman tpci200->info->pdev->devfn); 34705e5027eSGreg Kroah-Hartman goto out_release_ioid_int_space; 34805e5027eSGreg Kroah-Hartman } 34905e5027eSGreg Kroah-Hartman 35005e5027eSGreg Kroah-Hartman return 0; 35105e5027eSGreg Kroah-Hartman 35205e5027eSGreg Kroah-Hartman out_release_mem8_space: 35305e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); 35405e5027eSGreg Kroah-Hartman out_release_ioid_int_space: 35505e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); 35605e5027eSGreg Kroah-Hartman out_release_ip_space: 35705e5027eSGreg Kroah-Hartman pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); 35805e5027eSGreg Kroah-Hartman out_disable_pci: 35905e5027eSGreg Kroah-Hartman pci_disable_device(tpci200->info->pdev); 36005e5027eSGreg Kroah-Hartman return res; 36105e5027eSGreg Kroah-Hartman } 36205e5027eSGreg Kroah-Hartman 36305e5027eSGreg Kroah-Hartman static int tpci200_get_clockrate(struct ipack_device *dev) 36405e5027eSGreg Kroah-Hartman { 36505e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = check_slot(dev); 36605e5027eSGreg Kroah-Hartman __le16 __iomem *addr; 36705e5027eSGreg Kroah-Hartman 36805e5027eSGreg Kroah-Hartman if (!tpci200) 36905e5027eSGreg Kroah-Hartman return -ENODEV; 37005e5027eSGreg Kroah-Hartman 37105e5027eSGreg Kroah-Hartman addr = &tpci200->info->interface_regs->control[dev->slot]; 37205e5027eSGreg Kroah-Hartman return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; 37305e5027eSGreg Kroah-Hartman } 37405e5027eSGreg Kroah-Hartman 37505e5027eSGreg Kroah-Hartman static int tpci200_set_clockrate(struct ipack_device *dev, int mherz) 37605e5027eSGreg Kroah-Hartman { 37705e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = check_slot(dev); 37805e5027eSGreg Kroah-Hartman __le16 __iomem *addr; 37905e5027eSGreg Kroah-Hartman 38005e5027eSGreg Kroah-Hartman if (!tpci200) 38105e5027eSGreg Kroah-Hartman return -ENODEV; 38205e5027eSGreg Kroah-Hartman 38305e5027eSGreg Kroah-Hartman addr = &tpci200->info->interface_regs->control[dev->slot]; 38405e5027eSGreg Kroah-Hartman 38505e5027eSGreg Kroah-Hartman switch (mherz) { 38605e5027eSGreg Kroah-Hartman case 8: 38705e5027eSGreg Kroah-Hartman tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); 38805e5027eSGreg Kroah-Hartman break; 38905e5027eSGreg Kroah-Hartman case 32: 39005e5027eSGreg Kroah-Hartman tpci200_set_mask(tpci200, addr, TPCI200_CLK32); 39105e5027eSGreg Kroah-Hartman break; 39205e5027eSGreg Kroah-Hartman default: 39305e5027eSGreg Kroah-Hartman return -EINVAL; 39405e5027eSGreg Kroah-Hartman } 39505e5027eSGreg Kroah-Hartman return 0; 39605e5027eSGreg Kroah-Hartman } 39705e5027eSGreg Kroah-Hartman 39805e5027eSGreg Kroah-Hartman static int tpci200_get_error(struct ipack_device *dev) 39905e5027eSGreg Kroah-Hartman { 40005e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = check_slot(dev); 40105e5027eSGreg Kroah-Hartman __le16 __iomem *addr; 40205e5027eSGreg Kroah-Hartman u16 mask; 40305e5027eSGreg Kroah-Hartman 40405e5027eSGreg Kroah-Hartman if (!tpci200) 40505e5027eSGreg Kroah-Hartman return -ENODEV; 40605e5027eSGreg Kroah-Hartman 40705e5027eSGreg Kroah-Hartman addr = &tpci200->info->interface_regs->status; 40805e5027eSGreg Kroah-Hartman mask = tpci200_status_error[dev->slot]; 40905e5027eSGreg Kroah-Hartman return (ioread16(addr) & mask) ? 1 : 0; 41005e5027eSGreg Kroah-Hartman } 41105e5027eSGreg Kroah-Hartman 41205e5027eSGreg Kroah-Hartman static int tpci200_get_timeout(struct ipack_device *dev) 41305e5027eSGreg Kroah-Hartman { 41405e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = check_slot(dev); 41505e5027eSGreg Kroah-Hartman __le16 __iomem *addr; 41605e5027eSGreg Kroah-Hartman u16 mask; 41705e5027eSGreg Kroah-Hartman 41805e5027eSGreg Kroah-Hartman if (!tpci200) 41905e5027eSGreg Kroah-Hartman return -ENODEV; 42005e5027eSGreg Kroah-Hartman 42105e5027eSGreg Kroah-Hartman addr = &tpci200->info->interface_regs->status; 42205e5027eSGreg Kroah-Hartman mask = tpci200_status_timeout[dev->slot]; 42305e5027eSGreg Kroah-Hartman 42405e5027eSGreg Kroah-Hartman return (ioread16(addr) & mask) ? 1 : 0; 42505e5027eSGreg Kroah-Hartman } 42605e5027eSGreg Kroah-Hartman 42705e5027eSGreg Kroah-Hartman static int tpci200_reset_timeout(struct ipack_device *dev) 42805e5027eSGreg Kroah-Hartman { 42905e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = check_slot(dev); 43005e5027eSGreg Kroah-Hartman __le16 __iomem *addr; 43105e5027eSGreg Kroah-Hartman u16 mask; 43205e5027eSGreg Kroah-Hartman 43305e5027eSGreg Kroah-Hartman if (!tpci200) 43405e5027eSGreg Kroah-Hartman return -ENODEV; 43505e5027eSGreg Kroah-Hartman 43605e5027eSGreg Kroah-Hartman addr = &tpci200->info->interface_regs->status; 43705e5027eSGreg Kroah-Hartman mask = tpci200_status_timeout[dev->slot]; 43805e5027eSGreg Kroah-Hartman 43905e5027eSGreg Kroah-Hartman iowrite16(mask, addr); 44005e5027eSGreg Kroah-Hartman return 0; 44105e5027eSGreg Kroah-Hartman } 44205e5027eSGreg Kroah-Hartman 44305e5027eSGreg Kroah-Hartman static void tpci200_uninstall(struct tpci200_board *tpci200) 44405e5027eSGreg Kroah-Hartman { 44505e5027eSGreg Kroah-Hartman tpci200_unregister(tpci200); 44605e5027eSGreg Kroah-Hartman kfree(tpci200->slots); 44705e5027eSGreg Kroah-Hartman } 44805e5027eSGreg Kroah-Hartman 44905e5027eSGreg Kroah-Hartman static const struct ipack_bus_ops tpci200_bus_ops = { 45005e5027eSGreg Kroah-Hartman .request_irq = tpci200_request_irq, 45105e5027eSGreg Kroah-Hartman .free_irq = tpci200_free_irq, 45205e5027eSGreg Kroah-Hartman .get_clockrate = tpci200_get_clockrate, 45305e5027eSGreg Kroah-Hartman .set_clockrate = tpci200_set_clockrate, 45405e5027eSGreg Kroah-Hartman .get_error = tpci200_get_error, 45505e5027eSGreg Kroah-Hartman .get_timeout = tpci200_get_timeout, 45605e5027eSGreg Kroah-Hartman .reset_timeout = tpci200_reset_timeout, 45705e5027eSGreg Kroah-Hartman }; 45805e5027eSGreg Kroah-Hartman 45905e5027eSGreg Kroah-Hartman static int tpci200_install(struct tpci200_board *tpci200) 46005e5027eSGreg Kroah-Hartman { 46105e5027eSGreg Kroah-Hartman int res; 46205e5027eSGreg Kroah-Hartman 4636396bb22SKees Cook tpci200->slots = kcalloc(TPCI200_NB_SLOT, sizeof(struct tpci200_slot), 4646396bb22SKees Cook GFP_KERNEL); 46505e5027eSGreg Kroah-Hartman if (tpci200->slots == NULL) 46605e5027eSGreg Kroah-Hartman return -ENOMEM; 46705e5027eSGreg Kroah-Hartman 46805e5027eSGreg Kroah-Hartman res = tpci200_register(tpci200); 46905e5027eSGreg Kroah-Hartman if (res) { 47005e5027eSGreg Kroah-Hartman kfree(tpci200->slots); 47105e5027eSGreg Kroah-Hartman tpci200->slots = NULL; 47205e5027eSGreg Kroah-Hartman return res; 47305e5027eSGreg Kroah-Hartman } 47405e5027eSGreg Kroah-Hartman 47505e5027eSGreg Kroah-Hartman mutex_init(&tpci200->mutex); 47605e5027eSGreg Kroah-Hartman return 0; 47705e5027eSGreg Kroah-Hartman } 47805e5027eSGreg Kroah-Hartman 47905e5027eSGreg Kroah-Hartman static void tpci200_release_device(struct ipack_device *dev) 48005e5027eSGreg Kroah-Hartman { 48105e5027eSGreg Kroah-Hartman kfree(dev); 48205e5027eSGreg Kroah-Hartman } 48305e5027eSGreg Kroah-Hartman 48405e5027eSGreg Kroah-Hartman static int tpci200_create_device(struct tpci200_board *tpci200, int i) 48505e5027eSGreg Kroah-Hartman { 486e926301bSSamuel Iglesias Gonsalvez int ret; 48705e5027eSGreg Kroah-Hartman enum ipack_space space; 48805e5027eSGreg Kroah-Hartman struct ipack_device *dev = 48905e5027eSGreg Kroah-Hartman kzalloc(sizeof(struct ipack_device), GFP_KERNEL); 49005e5027eSGreg Kroah-Hartman if (!dev) 49105e5027eSGreg Kroah-Hartman return -ENOMEM; 49205e5027eSGreg Kroah-Hartman dev->slot = i; 49305e5027eSGreg Kroah-Hartman dev->bus = tpci200->info->ipack_bus; 49405e5027eSGreg Kroah-Hartman dev->release = tpci200_release_device; 49505e5027eSGreg Kroah-Hartman 49605e5027eSGreg Kroah-Hartman for (space = 0; space < IPACK_SPACE_COUNT; space++) { 49705e5027eSGreg Kroah-Hartman dev->region[space].start = 49805e5027eSGreg Kroah-Hartman tpci200->mod_mem[space] 49905e5027eSGreg Kroah-Hartman + tpci200_space_interval[space] * i; 50005e5027eSGreg Kroah-Hartman dev->region[space].size = tpci200_space_size[space]; 50105e5027eSGreg Kroah-Hartman } 502e926301bSSamuel Iglesias Gonsalvez 503e926301bSSamuel Iglesias Gonsalvez ret = ipack_device_init(dev); 504e926301bSSamuel Iglesias Gonsalvez if (ret < 0) { 505e926301bSSamuel Iglesias Gonsalvez ipack_put_device(dev); 506e926301bSSamuel Iglesias Gonsalvez return ret; 507e926301bSSamuel Iglesias Gonsalvez } 508e926301bSSamuel Iglesias Gonsalvez 509e926301bSSamuel Iglesias Gonsalvez ret = ipack_device_add(dev); 510e926301bSSamuel Iglesias Gonsalvez if (ret < 0) 511e926301bSSamuel Iglesias Gonsalvez ipack_put_device(dev); 512e926301bSSamuel Iglesias Gonsalvez 513e926301bSSamuel Iglesias Gonsalvez return ret; 51405e5027eSGreg Kroah-Hartman } 51505e5027eSGreg Kroah-Hartman 51605e5027eSGreg Kroah-Hartman static int tpci200_pci_probe(struct pci_dev *pdev, 51705e5027eSGreg Kroah-Hartman const struct pci_device_id *id) 51805e5027eSGreg Kroah-Hartman { 51905e5027eSGreg Kroah-Hartman int ret, i; 52005e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200; 52105e5027eSGreg Kroah-Hartman u32 reg32; 52205e5027eSGreg Kroah-Hartman 52305e5027eSGreg Kroah-Hartman tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); 52405e5027eSGreg Kroah-Hartman if (!tpci200) 52505e5027eSGreg Kroah-Hartman return -ENOMEM; 52605e5027eSGreg Kroah-Hartman 52705e5027eSGreg Kroah-Hartman tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); 52805e5027eSGreg Kroah-Hartman if (!tpci200->info) { 52905e5027eSGreg Kroah-Hartman ret = -ENOMEM; 53005e5027eSGreg Kroah-Hartman goto out_err_info; 53105e5027eSGreg Kroah-Hartman } 53205e5027eSGreg Kroah-Hartman 53305e5027eSGreg Kroah-Hartman pci_dev_get(pdev); 53405e5027eSGreg Kroah-Hartman 53505e5027eSGreg Kroah-Hartman /* Obtain a mapping of the carrier's PCI configuration registers */ 53605e5027eSGreg Kroah-Hartman ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, 53705e5027eSGreg Kroah-Hartman KBUILD_MODNAME " Configuration Memory"); 53805e5027eSGreg Kroah-Hartman if (ret) { 53905e5027eSGreg Kroah-Hartman dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); 54005e5027eSGreg Kroah-Hartman ret = -EBUSY; 54105e5027eSGreg Kroah-Hartman goto out_err_pci_request; 54205e5027eSGreg Kroah-Hartman } 5434bdc0d67SChristoph Hellwig tpci200->info->cfg_regs = ioremap( 54405e5027eSGreg Kroah-Hartman pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), 54505e5027eSGreg Kroah-Hartman pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); 54605e5027eSGreg Kroah-Hartman if (!tpci200->info->cfg_regs) { 54705e5027eSGreg Kroah-Hartman dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); 54805e5027eSGreg Kroah-Hartman ret = -EFAULT; 54905e5027eSGreg Kroah-Hartman goto out_err_ioremap; 55005e5027eSGreg Kroah-Hartman } 55105e5027eSGreg Kroah-Hartman 55205e5027eSGreg Kroah-Hartman /* Disable byte swapping for 16 bit IP module access. This will ensure 55305e5027eSGreg Kroah-Hartman * that the Industrypack big endian byte order is preserved by the 55405e5027eSGreg Kroah-Hartman * carrier. */ 55505e5027eSGreg Kroah-Hartman reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); 55605e5027eSGreg Kroah-Hartman reg32 |= 1 << LAS_BIT_BIGENDIAN; 55705e5027eSGreg Kroah-Hartman iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); 55805e5027eSGreg Kroah-Hartman 55905e5027eSGreg Kroah-Hartman reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); 56005e5027eSGreg Kroah-Hartman reg32 |= 1 << LAS_BIT_BIGENDIAN; 56105e5027eSGreg Kroah-Hartman iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); 56205e5027eSGreg Kroah-Hartman 56305e5027eSGreg Kroah-Hartman /* Save struct pci_dev pointer */ 56405e5027eSGreg Kroah-Hartman tpci200->info->pdev = pdev; 56505e5027eSGreg Kroah-Hartman tpci200->info->id_table = (struct pci_device_id *)id; 56605e5027eSGreg Kroah-Hartman 56705e5027eSGreg Kroah-Hartman /* register the device and initialize it */ 56805e5027eSGreg Kroah-Hartman ret = tpci200_install(tpci200); 56905e5027eSGreg Kroah-Hartman if (ret) { 57005e5027eSGreg Kroah-Hartman dev_err(&pdev->dev, "error during tpci200 install\n"); 57105e5027eSGreg Kroah-Hartman ret = -ENODEV; 57205e5027eSGreg Kroah-Hartman goto out_err_install; 57305e5027eSGreg Kroah-Hartman } 57405e5027eSGreg Kroah-Hartman 57505e5027eSGreg Kroah-Hartman /* Register the carrier in the industry pack bus driver */ 57605e5027eSGreg Kroah-Hartman tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, 57705e5027eSGreg Kroah-Hartman TPCI200_NB_SLOT, 57836c53b3cSFederico Vaga &tpci200_bus_ops, 57936c53b3cSFederico Vaga THIS_MODULE); 58005e5027eSGreg Kroah-Hartman if (!tpci200->info->ipack_bus) { 58105e5027eSGreg Kroah-Hartman dev_err(&pdev->dev, 58205e5027eSGreg Kroah-Hartman "error registering the carrier on ipack driver\n"); 58305e5027eSGreg Kroah-Hartman ret = -EFAULT; 58405e5027eSGreg Kroah-Hartman goto out_err_bus_register; 58505e5027eSGreg Kroah-Hartman } 58605e5027eSGreg Kroah-Hartman 58705e5027eSGreg Kroah-Hartman /* save the bus number given by ipack to logging purpose */ 58805e5027eSGreg Kroah-Hartman tpci200->number = tpci200->info->ipack_bus->bus_nr; 58905e5027eSGreg Kroah-Hartman dev_set_drvdata(&pdev->dev, tpci200); 59005e5027eSGreg Kroah-Hartman 59105e5027eSGreg Kroah-Hartman for (i = 0; i < TPCI200_NB_SLOT; i++) 59205e5027eSGreg Kroah-Hartman tpci200_create_device(tpci200, i); 59305e5027eSGreg Kroah-Hartman return 0; 59405e5027eSGreg Kroah-Hartman 59505e5027eSGreg Kroah-Hartman out_err_bus_register: 59605e5027eSGreg Kroah-Hartman tpci200_uninstall(tpci200); 5979272e5d0SLv Yunlong /* tpci200->info->cfg_regs is unmapped in tpci200_uninstall */ 5989272e5d0SLv Yunlong tpci200->info->cfg_regs = NULL; 59905e5027eSGreg Kroah-Hartman out_err_install: 6009272e5d0SLv Yunlong if (tpci200->info->cfg_regs) 60105e5027eSGreg Kroah-Hartman iounmap(tpci200->info->cfg_regs); 60205e5027eSGreg Kroah-Hartman out_err_ioremap: 60305e5027eSGreg Kroah-Hartman pci_release_region(pdev, TPCI200_CFG_MEM_BAR); 60405e5027eSGreg Kroah-Hartman out_err_pci_request: 60505e5027eSGreg Kroah-Hartman pci_dev_put(pdev); 60605e5027eSGreg Kroah-Hartman kfree(tpci200->info); 60705e5027eSGreg Kroah-Hartman out_err_info: 60805e5027eSGreg Kroah-Hartman kfree(tpci200); 60905e5027eSGreg Kroah-Hartman return ret; 61005e5027eSGreg Kroah-Hartman } 61105e5027eSGreg Kroah-Hartman 61205e5027eSGreg Kroah-Hartman static void __tpci200_pci_remove(struct tpci200_board *tpci200) 61305e5027eSGreg Kroah-Hartman { 61405e5027eSGreg Kroah-Hartman ipack_bus_unregister(tpci200->info->ipack_bus); 61505e5027eSGreg Kroah-Hartman tpci200_uninstall(tpci200); 61605e5027eSGreg Kroah-Hartman 61705e5027eSGreg Kroah-Hartman kfree(tpci200->info); 61805e5027eSGreg Kroah-Hartman kfree(tpci200); 61905e5027eSGreg Kroah-Hartman } 62005e5027eSGreg Kroah-Hartman 62129c35442SBill Pemberton static void tpci200_pci_remove(struct pci_dev *dev) 62205e5027eSGreg Kroah-Hartman { 62305e5027eSGreg Kroah-Hartman struct tpci200_board *tpci200 = pci_get_drvdata(dev); 62405e5027eSGreg Kroah-Hartman 62505e5027eSGreg Kroah-Hartman __tpci200_pci_remove(tpci200); 62605e5027eSGreg Kroah-Hartman } 62705e5027eSGreg Kroah-Hartman 6287426d29eSBenoit Taine static const struct pci_device_id tpci200_idtable[] = { 62905e5027eSGreg Kroah-Hartman { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, 63005e5027eSGreg Kroah-Hartman TPCI200_SUBDEVICE_ID }, 63105e5027eSGreg Kroah-Hartman { 0, }, 63205e5027eSGreg Kroah-Hartman }; 63305e5027eSGreg Kroah-Hartman 63405e5027eSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, tpci200_idtable); 63505e5027eSGreg Kroah-Hartman 63605e5027eSGreg Kroah-Hartman static struct pci_driver tpci200_pci_drv = { 63705e5027eSGreg Kroah-Hartman .name = "tpci200", 63805e5027eSGreg Kroah-Hartman .id_table = tpci200_idtable, 63905e5027eSGreg Kroah-Hartman .probe = tpci200_pci_probe, 640c5dee46cSBill Pemberton .remove = tpci200_pci_remove, 64105e5027eSGreg Kroah-Hartman }; 64205e5027eSGreg Kroah-Hartman 64305e5027eSGreg Kroah-Hartman module_pci_driver(tpci200_pci_drv); 64405e5027eSGreg Kroah-Hartman 64505e5027eSGreg Kroah-Hartman MODULE_DESCRIPTION("TEWS TPCI-200 device driver"); 64605e5027eSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 647