162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * driver for the TEWS TPCI-200 device 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2012 CERN (www.cern.ch) 662306a36Sopenharmony_ci * Author: Nicolas Serafini, EIC2 SA 762306a36Sopenharmony_ci * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include "tpci200.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic const u16 tpci200_status_timeout[] = { 1562306a36Sopenharmony_ci TPCI200_A_TIMEOUT, 1662306a36Sopenharmony_ci TPCI200_B_TIMEOUT, 1762306a36Sopenharmony_ci TPCI200_C_TIMEOUT, 1862306a36Sopenharmony_ci TPCI200_D_TIMEOUT, 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const u16 tpci200_status_error[] = { 2262306a36Sopenharmony_ci TPCI200_A_ERROR, 2362306a36Sopenharmony_ci TPCI200_B_ERROR, 2462306a36Sopenharmony_ci TPCI200_C_ERROR, 2562306a36Sopenharmony_ci TPCI200_D_ERROR, 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { 2962306a36Sopenharmony_ci [IPACK_IO_SPACE] = TPCI200_IO_SPACE_SIZE, 3062306a36Sopenharmony_ci [IPACK_ID_SPACE] = TPCI200_ID_SPACE_SIZE, 3162306a36Sopenharmony_ci [IPACK_INT_SPACE] = TPCI200_INT_SPACE_SIZE, 3262306a36Sopenharmony_ci [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_SIZE, 3362306a36Sopenharmony_ci [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { 3762306a36Sopenharmony_ci [IPACK_IO_SPACE] = TPCI200_IO_SPACE_INTERVAL, 3862306a36Sopenharmony_ci [IPACK_ID_SPACE] = TPCI200_ID_SPACE_INTERVAL, 3962306a36Sopenharmony_ci [IPACK_INT_SPACE] = TPCI200_INT_SPACE_INTERVAL, 4062306a36Sopenharmony_ci [IPACK_MEM8_SPACE] = TPCI200_MEM8_SPACE_INTERVAL, 4162306a36Sopenharmony_ci [IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct tpci200_board *check_slot(struct ipack_device *dev) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct tpci200_board *tpci200; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (dev == NULL) 4962306a36Sopenharmony_ci return NULL; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci tpci200 = dev_get_drvdata(dev->bus->parent); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (tpci200 == NULL) { 5562306a36Sopenharmony_ci dev_info(&dev->dev, "carrier board not found\n"); 5662306a36Sopenharmony_ci return NULL; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (dev->slot >= TPCI200_NB_SLOT) { 6062306a36Sopenharmony_ci dev_info(&dev->dev, 6162306a36Sopenharmony_ci "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", 6262306a36Sopenharmony_ci dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return tpci200; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void tpci200_clear_mask(struct tpci200_board *tpci200, 7062306a36Sopenharmony_ci __le16 __iomem *addr, u16 mask) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned long flags; 7362306a36Sopenharmony_ci spin_lock_irqsave(&tpci200->regs_lock, flags); 7462306a36Sopenharmony_ci iowrite16(ioread16(addr) & (~mask), addr); 7562306a36Sopenharmony_ci spin_unlock_irqrestore(&tpci200->regs_lock, flags); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void tpci200_set_mask(struct tpci200_board *tpci200, 7962306a36Sopenharmony_ci __le16 __iomem *addr, u16 mask) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci unsigned long flags; 8262306a36Sopenharmony_ci spin_lock_irqsave(&tpci200->regs_lock, flags); 8362306a36Sopenharmony_ci iowrite16(ioread16(addr) | mask, addr); 8462306a36Sopenharmony_ci spin_unlock_irqrestore(&tpci200->regs_lock, flags); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void tpci200_unregister(struct tpci200_board *tpci200) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci free_irq(tpci200->info->pdev->irq, (void *) tpci200); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); 9462306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); 9562306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); 9662306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pci_disable_device(tpci200->info->pdev); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void tpci200_enable_irq(struct tpci200_board *tpci200, 10262306a36Sopenharmony_ci int islot) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci tpci200_set_mask(tpci200, 10562306a36Sopenharmony_ci &tpci200->info->interface_regs->control[islot], 10662306a36Sopenharmony_ci TPCI200_INT0_EN | TPCI200_INT1_EN); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void tpci200_disable_irq(struct tpci200_board *tpci200, 11062306a36Sopenharmony_ci int islot) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci tpci200_clear_mask(tpci200, 11362306a36Sopenharmony_ci &tpci200->info->interface_regs->control[islot], 11462306a36Sopenharmony_ci TPCI200_INT0_EN | TPCI200_INT1_EN); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci irqreturn_t ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!slot_irq) 12262306a36Sopenharmony_ci return -ENODEV; 12362306a36Sopenharmony_ci ret = slot_irq->handler(slot_irq->arg); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic irqreturn_t tpci200_interrupt(int irq, void *dev_id) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; 13162306a36Sopenharmony_ci struct slot_irq *slot_irq; 13262306a36Sopenharmony_ci irqreturn_t ret; 13362306a36Sopenharmony_ci u16 status_reg; 13462306a36Sopenharmony_ci int i; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Read status register */ 13762306a36Sopenharmony_ci status_reg = ioread16(&tpci200->info->interface_regs->status); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Did we cause the interrupt? */ 14062306a36Sopenharmony_ci if (!(status_reg & TPCI200_SLOT_INT_MASK)) 14162306a36Sopenharmony_ci return IRQ_NONE; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* callback to the IRQ handler for the corresponding slot */ 14462306a36Sopenharmony_ci rcu_read_lock(); 14562306a36Sopenharmony_ci for (i = 0; i < TPCI200_NB_SLOT; i++) { 14662306a36Sopenharmony_ci if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) 14762306a36Sopenharmony_ci continue; 14862306a36Sopenharmony_ci slot_irq = rcu_dereference(tpci200->slots[i].irq); 14962306a36Sopenharmony_ci ret = tpci200_slot_irq(slot_irq); 15062306a36Sopenharmony_ci if (ret == -ENODEV) { 15162306a36Sopenharmony_ci dev_info(&tpci200->info->pdev->dev, 15262306a36Sopenharmony_ci "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", 15362306a36Sopenharmony_ci tpci200->number, i); 15462306a36Sopenharmony_ci tpci200_disable_irq(tpci200, i); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci rcu_read_unlock(); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return IRQ_HANDLED; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int tpci200_free_irq(struct ipack_device *dev) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct slot_irq *slot_irq; 16562306a36Sopenharmony_ci struct tpci200_board *tpci200; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci tpci200 = check_slot(dev); 16862306a36Sopenharmony_ci if (tpci200 == NULL) 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (mutex_lock_interruptible(&tpci200->mutex)) 17262306a36Sopenharmony_ci return -ERESTARTSYS; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (tpci200->slots[dev->slot].irq == NULL) { 17562306a36Sopenharmony_ci mutex_unlock(&tpci200->mutex); 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci tpci200_disable_irq(tpci200, dev->slot); 18062306a36Sopenharmony_ci slot_irq = tpci200->slots[dev->slot].irq; 18162306a36Sopenharmony_ci /* uninstall handler */ 18262306a36Sopenharmony_ci RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); 18362306a36Sopenharmony_ci synchronize_rcu(); 18462306a36Sopenharmony_ci kfree(slot_irq); 18562306a36Sopenharmony_ci mutex_unlock(&tpci200->mutex); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int tpci200_request_irq(struct ipack_device *dev, 19062306a36Sopenharmony_ci irqreturn_t (*handler)(void *), void *arg) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int res = 0; 19362306a36Sopenharmony_ci struct slot_irq *slot_irq; 19462306a36Sopenharmony_ci struct tpci200_board *tpci200; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci tpci200 = check_slot(dev); 19762306a36Sopenharmony_ci if (tpci200 == NULL) 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (mutex_lock_interruptible(&tpci200->mutex)) 20162306a36Sopenharmony_ci return -ERESTARTSYS; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (tpci200->slots[dev->slot].irq != NULL) { 20462306a36Sopenharmony_ci dev_err(&dev->dev, 20562306a36Sopenharmony_ci "Slot [%d:%d] IRQ already registered !\n", 20662306a36Sopenharmony_ci dev->bus->bus_nr, 20762306a36Sopenharmony_ci dev->slot); 20862306a36Sopenharmony_ci res = -EINVAL; 20962306a36Sopenharmony_ci goto out_unlock; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); 21362306a36Sopenharmony_ci if (slot_irq == NULL) { 21462306a36Sopenharmony_ci dev_err(&dev->dev, 21562306a36Sopenharmony_ci "Slot [%d:%d] unable to allocate memory for IRQ !\n", 21662306a36Sopenharmony_ci dev->bus->bus_nr, dev->slot); 21762306a36Sopenharmony_ci res = -ENOMEM; 21862306a36Sopenharmony_ci goto out_unlock; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * WARNING: Setup Interrupt Vector in the IndustryPack device 22362306a36Sopenharmony_ci * before an IRQ request. 22462306a36Sopenharmony_ci * Read the User Manual of your IndustryPack device to know 22562306a36Sopenharmony_ci * where to write the vector in memory. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci slot_irq->handler = handler; 22862306a36Sopenharmony_ci slot_irq->arg = arg; 22962306a36Sopenharmony_ci slot_irq->holder = dev; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); 23262306a36Sopenharmony_ci tpci200_enable_irq(tpci200, dev->slot); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciout_unlock: 23562306a36Sopenharmony_ci mutex_unlock(&tpci200->mutex); 23662306a36Sopenharmony_ci return res; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int tpci200_register(struct tpci200_board *tpci200) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci int i; 24262306a36Sopenharmony_ci int res; 24362306a36Sopenharmony_ci phys_addr_t ioidint_base; 24462306a36Sopenharmony_ci unsigned short slot_ctrl; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (pci_enable_device(tpci200->info->pdev) < 0) 24762306a36Sopenharmony_ci return -ENODEV; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Request IP interface register (Bar 2) */ 25062306a36Sopenharmony_ci res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, 25162306a36Sopenharmony_ci "Carrier IP interface registers"); 25262306a36Sopenharmony_ci if (res) { 25362306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 25462306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", 25562306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 25662306a36Sopenharmony_ci tpci200->info->pdev->devfn); 25762306a36Sopenharmony_ci goto err_disable_device; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Request IO ID INT space (Bar 3) */ 26162306a36Sopenharmony_ci res = pci_request_region(tpci200->info->pdev, 26262306a36Sopenharmony_ci TPCI200_IO_ID_INT_SPACES_BAR, 26362306a36Sopenharmony_ci "Carrier IO ID INT space"); 26462306a36Sopenharmony_ci if (res) { 26562306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 26662306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", 26762306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 26862306a36Sopenharmony_ci tpci200->info->pdev->devfn); 26962306a36Sopenharmony_ci goto err_ip_interface_bar; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Request MEM8 space (Bar 5) */ 27362306a36Sopenharmony_ci res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, 27462306a36Sopenharmony_ci "Carrier MEM8 space"); 27562306a36Sopenharmony_ci if (res) { 27662306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 27762306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", 27862306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 27962306a36Sopenharmony_ci tpci200->info->pdev->devfn); 28062306a36Sopenharmony_ci goto err_io_id_int_spaces_bar; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Request MEM16 space (Bar 4) */ 28462306a36Sopenharmony_ci res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, 28562306a36Sopenharmony_ci "Carrier MEM16 space"); 28662306a36Sopenharmony_ci if (res) { 28762306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 28862306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", 28962306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 29062306a36Sopenharmony_ci tpci200->info->pdev->devfn); 29162306a36Sopenharmony_ci goto err_mem8_space_bar; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Map internal tpci200 driver user space */ 29562306a36Sopenharmony_ci tpci200->info->interface_regs = 29662306a36Sopenharmony_ci ioremap(pci_resource_start(tpci200->info->pdev, 29762306a36Sopenharmony_ci TPCI200_IP_INTERFACE_BAR), 29862306a36Sopenharmony_ci TPCI200_IFACE_SIZE); 29962306a36Sopenharmony_ci if (!tpci200->info->interface_regs) { 30062306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 30162306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) failed to map driver user space!", 30262306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 30362306a36Sopenharmony_ci tpci200->info->pdev->devfn); 30462306a36Sopenharmony_ci res = -ENOMEM; 30562306a36Sopenharmony_ci goto err_mem16_space_bar; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Initialize lock that protects interface_regs */ 30962306a36Sopenharmony_ci spin_lock_init(&tpci200->regs_lock); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ioidint_base = pci_resource_start(tpci200->info->pdev, 31262306a36Sopenharmony_ci TPCI200_IO_ID_INT_SPACES_BAR); 31362306a36Sopenharmony_ci tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; 31462306a36Sopenharmony_ci tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; 31562306a36Sopenharmony_ci tpci200->mod_mem[IPACK_INT_SPACE] = 31662306a36Sopenharmony_ci ioidint_base + TPCI200_INT_SPACE_OFF; 31762306a36Sopenharmony_ci tpci200->mod_mem[IPACK_MEM8_SPACE] = 31862306a36Sopenharmony_ci pci_resource_start(tpci200->info->pdev, 31962306a36Sopenharmony_ci TPCI200_MEM8_SPACE_BAR); 32062306a36Sopenharmony_ci tpci200->mod_mem[IPACK_MEM16_SPACE] = 32162306a36Sopenharmony_ci pci_resource_start(tpci200->info->pdev, 32262306a36Sopenharmony_ci TPCI200_MEM16_SPACE_BAR); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Set the default parameters of the slot 32562306a36Sopenharmony_ci * INT0 disabled, level sensitive 32662306a36Sopenharmony_ci * INT1 disabled, level sensitive 32762306a36Sopenharmony_ci * error interrupt disabled 32862306a36Sopenharmony_ci * timeout interrupt disabled 32962306a36Sopenharmony_ci * recover time disabled 33062306a36Sopenharmony_ci * clock rate 8 MHz 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci slot_ctrl = 0; 33362306a36Sopenharmony_ci for (i = 0; i < TPCI200_NB_SLOT; i++) 33462306a36Sopenharmony_ci writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci res = request_irq(tpci200->info->pdev->irq, 33762306a36Sopenharmony_ci tpci200_interrupt, IRQF_SHARED, 33862306a36Sopenharmony_ci KBUILD_MODNAME, (void *) tpci200); 33962306a36Sopenharmony_ci if (res) { 34062306a36Sopenharmony_ci dev_err(&tpci200->info->pdev->dev, 34162306a36Sopenharmony_ci "(bn 0x%X, sn 0x%X) unable to register IRQ !", 34262306a36Sopenharmony_ci tpci200->info->pdev->bus->number, 34362306a36Sopenharmony_ci tpci200->info->pdev->devfn); 34462306a36Sopenharmony_ci goto err_interface_regs; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cierr_interface_regs: 35062306a36Sopenharmony_ci pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); 35162306a36Sopenharmony_cierr_mem16_space_bar: 35262306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); 35362306a36Sopenharmony_cierr_mem8_space_bar: 35462306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); 35562306a36Sopenharmony_cierr_io_id_int_spaces_bar: 35662306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); 35762306a36Sopenharmony_cierr_ip_interface_bar: 35862306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); 35962306a36Sopenharmony_cierr_disable_device: 36062306a36Sopenharmony_ci pci_disable_device(tpci200->info->pdev); 36162306a36Sopenharmony_ci return res; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int tpci200_get_clockrate(struct ipack_device *dev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct tpci200_board *tpci200 = check_slot(dev); 36762306a36Sopenharmony_ci __le16 __iomem *addr; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!tpci200) 37062306a36Sopenharmony_ci return -ENODEV; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci addr = &tpci200->info->interface_regs->control[dev->slot]; 37362306a36Sopenharmony_ci return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int tpci200_set_clockrate(struct ipack_device *dev, int mherz) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct tpci200_board *tpci200 = check_slot(dev); 37962306a36Sopenharmony_ci __le16 __iomem *addr; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!tpci200) 38262306a36Sopenharmony_ci return -ENODEV; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci addr = &tpci200->info->interface_regs->control[dev->slot]; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci switch (mherz) { 38762306a36Sopenharmony_ci case 8: 38862306a36Sopenharmony_ci tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci case 32: 39162306a36Sopenharmony_ci tpci200_set_mask(tpci200, addr, TPCI200_CLK32); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci default: 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int tpci200_get_error(struct ipack_device *dev) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct tpci200_board *tpci200 = check_slot(dev); 40262306a36Sopenharmony_ci __le16 __iomem *addr; 40362306a36Sopenharmony_ci u16 mask; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!tpci200) 40662306a36Sopenharmony_ci return -ENODEV; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci addr = &tpci200->info->interface_regs->status; 40962306a36Sopenharmony_ci mask = tpci200_status_error[dev->slot]; 41062306a36Sopenharmony_ci return (ioread16(addr) & mask) ? 1 : 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int tpci200_get_timeout(struct ipack_device *dev) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct tpci200_board *tpci200 = check_slot(dev); 41662306a36Sopenharmony_ci __le16 __iomem *addr; 41762306a36Sopenharmony_ci u16 mask; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!tpci200) 42062306a36Sopenharmony_ci return -ENODEV; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci addr = &tpci200->info->interface_regs->status; 42362306a36Sopenharmony_ci mask = tpci200_status_timeout[dev->slot]; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return (ioread16(addr) & mask) ? 1 : 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int tpci200_reset_timeout(struct ipack_device *dev) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct tpci200_board *tpci200 = check_slot(dev); 43162306a36Sopenharmony_ci __le16 __iomem *addr; 43262306a36Sopenharmony_ci u16 mask; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!tpci200) 43562306a36Sopenharmony_ci return -ENODEV; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci addr = &tpci200->info->interface_regs->status; 43862306a36Sopenharmony_ci mask = tpci200_status_timeout[dev->slot]; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci iowrite16(mask, addr); 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void tpci200_uninstall(struct tpci200_board *tpci200) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci tpci200_unregister(tpci200); 44762306a36Sopenharmony_ci kfree(tpci200->slots); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic const struct ipack_bus_ops tpci200_bus_ops = { 45162306a36Sopenharmony_ci .request_irq = tpci200_request_irq, 45262306a36Sopenharmony_ci .free_irq = tpci200_free_irq, 45362306a36Sopenharmony_ci .get_clockrate = tpci200_get_clockrate, 45462306a36Sopenharmony_ci .set_clockrate = tpci200_set_clockrate, 45562306a36Sopenharmony_ci .get_error = tpci200_get_error, 45662306a36Sopenharmony_ci .get_timeout = tpci200_get_timeout, 45762306a36Sopenharmony_ci .reset_timeout = tpci200_reset_timeout, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int tpci200_install(struct tpci200_board *tpci200) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int res; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci tpci200->slots = kcalloc(TPCI200_NB_SLOT, sizeof(struct tpci200_slot), 46562306a36Sopenharmony_ci GFP_KERNEL); 46662306a36Sopenharmony_ci if (tpci200->slots == NULL) 46762306a36Sopenharmony_ci return -ENOMEM; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci res = tpci200_register(tpci200); 47062306a36Sopenharmony_ci if (res) { 47162306a36Sopenharmony_ci kfree(tpci200->slots); 47262306a36Sopenharmony_ci tpci200->slots = NULL; 47362306a36Sopenharmony_ci return res; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci mutex_init(&tpci200->mutex); 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void tpci200_release_device(struct ipack_device *dev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci kfree(dev); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int tpci200_create_device(struct tpci200_board *tpci200, int i) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci int ret; 48862306a36Sopenharmony_ci enum ipack_space space; 48962306a36Sopenharmony_ci struct ipack_device *dev = 49062306a36Sopenharmony_ci kzalloc(sizeof(struct ipack_device), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!dev) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci dev->slot = i; 49462306a36Sopenharmony_ci dev->bus = tpci200->info->ipack_bus; 49562306a36Sopenharmony_ci dev->release = tpci200_release_device; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci for (space = 0; space < IPACK_SPACE_COUNT; space++) { 49862306a36Sopenharmony_ci dev->region[space].start = 49962306a36Sopenharmony_ci tpci200->mod_mem[space] 50062306a36Sopenharmony_ci + tpci200_space_interval[space] * i; 50162306a36Sopenharmony_ci dev->region[space].size = tpci200_space_size[space]; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci ret = ipack_device_init(dev); 50562306a36Sopenharmony_ci if (ret < 0) { 50662306a36Sopenharmony_ci ipack_put_device(dev); 50762306a36Sopenharmony_ci return ret; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = ipack_device_add(dev); 51162306a36Sopenharmony_ci if (ret < 0) 51262306a36Sopenharmony_ci ipack_put_device(dev); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int tpci200_pci_probe(struct pci_dev *pdev, 51862306a36Sopenharmony_ci const struct pci_device_id *id) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci int ret, i; 52162306a36Sopenharmony_ci struct tpci200_board *tpci200; 52262306a36Sopenharmony_ci u32 reg32; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); 52562306a36Sopenharmony_ci if (!tpci200) 52662306a36Sopenharmony_ci return -ENOMEM; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); 52962306a36Sopenharmony_ci if (!tpci200->info) { 53062306a36Sopenharmony_ci ret = -ENOMEM; 53162306a36Sopenharmony_ci goto err_tpci200; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci pci_dev_get(pdev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Obtain a mapping of the carrier's PCI configuration registers */ 53762306a36Sopenharmony_ci ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, 53862306a36Sopenharmony_ci KBUILD_MODNAME " Configuration Memory"); 53962306a36Sopenharmony_ci if (ret) { 54062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); 54162306a36Sopenharmony_ci ret = -EBUSY; 54262306a36Sopenharmony_ci goto err_tpci200_info; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci tpci200->info->cfg_regs = ioremap( 54562306a36Sopenharmony_ci pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), 54662306a36Sopenharmony_ci pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); 54762306a36Sopenharmony_ci if (!tpci200->info->cfg_regs) { 54862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); 54962306a36Sopenharmony_ci ret = -EFAULT; 55062306a36Sopenharmony_ci goto err_request_region; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Disable byte swapping for 16 bit IP module access. This will ensure 55462306a36Sopenharmony_ci * that the Industrypack big endian byte order is preserved by the 55562306a36Sopenharmony_ci * carrier. */ 55662306a36Sopenharmony_ci reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); 55762306a36Sopenharmony_ci reg32 |= 1 << LAS_BIT_BIGENDIAN; 55862306a36Sopenharmony_ci iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); 56162306a36Sopenharmony_ci reg32 |= 1 << LAS_BIT_BIGENDIAN; 56262306a36Sopenharmony_ci iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Save struct pci_dev pointer */ 56562306a36Sopenharmony_ci tpci200->info->pdev = pdev; 56662306a36Sopenharmony_ci tpci200->info->id_table = (struct pci_device_id *)id; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* register the device and initialize it */ 56962306a36Sopenharmony_ci ret = tpci200_install(tpci200); 57062306a36Sopenharmony_ci if (ret) { 57162306a36Sopenharmony_ci dev_err(&pdev->dev, "error during tpci200 install\n"); 57262306a36Sopenharmony_ci ret = -ENODEV; 57362306a36Sopenharmony_ci goto err_cfg_regs; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Register the carrier in the industry pack bus driver */ 57762306a36Sopenharmony_ci tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, 57862306a36Sopenharmony_ci TPCI200_NB_SLOT, 57962306a36Sopenharmony_ci &tpci200_bus_ops, 58062306a36Sopenharmony_ci THIS_MODULE); 58162306a36Sopenharmony_ci if (!tpci200->info->ipack_bus) { 58262306a36Sopenharmony_ci dev_err(&pdev->dev, 58362306a36Sopenharmony_ci "error registering the carrier on ipack driver\n"); 58462306a36Sopenharmony_ci ret = -EFAULT; 58562306a36Sopenharmony_ci goto err_tpci200_install; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* save the bus number given by ipack to logging purpose */ 58962306a36Sopenharmony_ci tpci200->number = tpci200->info->ipack_bus->bus_nr; 59062306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, tpci200); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (i = 0; i < TPCI200_NB_SLOT; i++) 59362306a36Sopenharmony_ci tpci200_create_device(tpci200, i); 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cierr_tpci200_install: 59762306a36Sopenharmony_ci tpci200_uninstall(tpci200); 59862306a36Sopenharmony_cierr_cfg_regs: 59962306a36Sopenharmony_ci pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); 60062306a36Sopenharmony_cierr_request_region: 60162306a36Sopenharmony_ci pci_release_region(pdev, TPCI200_CFG_MEM_BAR); 60262306a36Sopenharmony_cierr_tpci200_info: 60362306a36Sopenharmony_ci kfree(tpci200->info); 60462306a36Sopenharmony_ci pci_dev_put(pdev); 60562306a36Sopenharmony_cierr_tpci200: 60662306a36Sopenharmony_ci kfree(tpci200); 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void __tpci200_pci_remove(struct tpci200_board *tpci200) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci ipack_bus_unregister(tpci200->info->ipack_bus); 61362306a36Sopenharmony_ci tpci200_uninstall(tpci200); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci pci_dev_put(tpci200->info->pdev); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci kfree(tpci200->info); 62262306a36Sopenharmony_ci kfree(tpci200); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void tpci200_pci_remove(struct pci_dev *dev) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct tpci200_board *tpci200 = pci_get_drvdata(dev); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci __tpci200_pci_remove(tpci200); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic const struct pci_device_id tpci200_idtable[] = { 63362306a36Sopenharmony_ci { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, 63462306a36Sopenharmony_ci TPCI200_SUBDEVICE_ID }, 63562306a36Sopenharmony_ci { 0, }, 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tpci200_idtable); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic struct pci_driver tpci200_pci_drv = { 64162306a36Sopenharmony_ci .name = "tpci200", 64262306a36Sopenharmony_ci .id_table = tpci200_idtable, 64362306a36Sopenharmony_ci .probe = tpci200_pci_probe, 64462306a36Sopenharmony_ci .remove = tpci200_pci_remove, 64562306a36Sopenharmony_ci}; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cimodule_pci_driver(tpci200_pci_drv); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ciMODULE_DESCRIPTION("TEWS TPCI-200 device driver"); 65062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 651