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