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