18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * tpci200.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * driver for the TEWS TPCI-200 device
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2009-2012 CERN (www.cern.ch)
88c2ecf20Sopenharmony_ci * Author: Nicolas Serafini, EIC2 SA
98c2ecf20Sopenharmony_ci * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include "tpci200.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic const u16 tpci200_status_timeout[] = {
178c2ecf20Sopenharmony_ci	TPCI200_A_TIMEOUT,
188c2ecf20Sopenharmony_ci	TPCI200_B_TIMEOUT,
198c2ecf20Sopenharmony_ci	TPCI200_C_TIMEOUT,
208c2ecf20Sopenharmony_ci	TPCI200_D_TIMEOUT,
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic const u16 tpci200_status_error[] = {
248c2ecf20Sopenharmony_ci	TPCI200_A_ERROR,
258c2ecf20Sopenharmony_ci	TPCI200_B_ERROR,
268c2ecf20Sopenharmony_ci	TPCI200_C_ERROR,
278c2ecf20Sopenharmony_ci	TPCI200_D_ERROR,
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const size_t tpci200_space_size[IPACK_SPACE_COUNT] = {
318c2ecf20Sopenharmony_ci	[IPACK_IO_SPACE]    = TPCI200_IO_SPACE_SIZE,
328c2ecf20Sopenharmony_ci	[IPACK_ID_SPACE]    = TPCI200_ID_SPACE_SIZE,
338c2ecf20Sopenharmony_ci	[IPACK_INT_SPACE]   = TPCI200_INT_SPACE_SIZE,
348c2ecf20Sopenharmony_ci	[IPACK_MEM8_SPACE]  = TPCI200_MEM8_SPACE_SIZE,
358c2ecf20Sopenharmony_ci	[IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = {
398c2ecf20Sopenharmony_ci	[IPACK_IO_SPACE]    = TPCI200_IO_SPACE_INTERVAL,
408c2ecf20Sopenharmony_ci	[IPACK_ID_SPACE]    = TPCI200_ID_SPACE_INTERVAL,
418c2ecf20Sopenharmony_ci	[IPACK_INT_SPACE]   = TPCI200_INT_SPACE_INTERVAL,
428c2ecf20Sopenharmony_ci	[IPACK_MEM8_SPACE]  = TPCI200_MEM8_SPACE_INTERVAL,
438c2ecf20Sopenharmony_ci	[IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL,
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct tpci200_board *check_slot(struct ipack_device *dev)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (dev == NULL)
518c2ecf20Sopenharmony_ci		return NULL;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	tpci200 = dev_get_drvdata(dev->bus->parent);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (tpci200 == NULL) {
578c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "carrier board not found\n");
588c2ecf20Sopenharmony_ci		return NULL;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (dev->slot >= TPCI200_NB_SLOT) {
628c2ecf20Sopenharmony_ci		dev_info(&dev->dev,
638c2ecf20Sopenharmony_ci			 "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n",
648c2ecf20Sopenharmony_ci			 dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1);
658c2ecf20Sopenharmony_ci		return NULL;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return tpci200;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void tpci200_clear_mask(struct tpci200_board *tpci200,
728c2ecf20Sopenharmony_ci			       __le16 __iomem *addr, u16 mask)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	unsigned long flags;
758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tpci200->regs_lock, flags);
768c2ecf20Sopenharmony_ci	iowrite16(ioread16(addr) & (~mask), addr);
778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tpci200->regs_lock, flags);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void tpci200_set_mask(struct tpci200_board *tpci200,
818c2ecf20Sopenharmony_ci			     __le16 __iomem *addr, u16 mask)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	unsigned long flags;
848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tpci200->regs_lock, flags);
858c2ecf20Sopenharmony_ci	iowrite16(ioread16(addr) | mask, addr);
868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tpci200->regs_lock, flags);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void tpci200_unregister(struct tpci200_board *tpci200)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	free_irq(tpci200->info->pdev->irq, (void *) tpci200);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR);
968c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR);
978c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR);
988c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	pci_disable_device(tpci200->info->pdev);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void tpci200_enable_irq(struct tpci200_board *tpci200,
1048c2ecf20Sopenharmony_ci			       int islot)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	tpci200_set_mask(tpci200,
1078c2ecf20Sopenharmony_ci			&tpci200->info->interface_regs->control[islot],
1088c2ecf20Sopenharmony_ci			TPCI200_INT0_EN | TPCI200_INT1_EN);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void tpci200_disable_irq(struct tpci200_board *tpci200,
1128c2ecf20Sopenharmony_ci				int islot)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	tpci200_clear_mask(tpci200,
1158c2ecf20Sopenharmony_ci			&tpci200->info->interface_regs->control[islot],
1168c2ecf20Sopenharmony_ci			TPCI200_INT0_EN | TPCI200_INT1_EN);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	irqreturn_t ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!slot_irq)
1248c2ecf20Sopenharmony_ci		return -ENODEV;
1258c2ecf20Sopenharmony_ci	ret = slot_irq->handler(slot_irq->arg);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return ret;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic irqreturn_t tpci200_interrupt(int irq, void *dev_id)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id;
1338c2ecf20Sopenharmony_ci	struct slot_irq *slot_irq;
1348c2ecf20Sopenharmony_ci	irqreturn_t ret;
1358c2ecf20Sopenharmony_ci	u16 status_reg;
1368c2ecf20Sopenharmony_ci	int i;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Read status register */
1398c2ecf20Sopenharmony_ci	status_reg = ioread16(&tpci200->info->interface_regs->status);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Did we cause the interrupt? */
1428c2ecf20Sopenharmony_ci	if (!(status_reg & TPCI200_SLOT_INT_MASK))
1438c2ecf20Sopenharmony_ci		return IRQ_NONE;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* callback to the IRQ handler for the corresponding slot */
1468c2ecf20Sopenharmony_ci	rcu_read_lock();
1478c2ecf20Sopenharmony_ci	for (i = 0; i < TPCI200_NB_SLOT; i++) {
1488c2ecf20Sopenharmony_ci		if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i))))
1498c2ecf20Sopenharmony_ci			continue;
1508c2ecf20Sopenharmony_ci		slot_irq = rcu_dereference(tpci200->slots[i].irq);
1518c2ecf20Sopenharmony_ci		ret = tpci200_slot_irq(slot_irq);
1528c2ecf20Sopenharmony_ci		if (ret == -ENODEV) {
1538c2ecf20Sopenharmony_ci			dev_info(&tpci200->info->pdev->dev,
1548c2ecf20Sopenharmony_ci				 "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n",
1558c2ecf20Sopenharmony_ci				 tpci200->number, i);
1568c2ecf20Sopenharmony_ci			tpci200_disable_irq(tpci200, i);
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	rcu_read_unlock();
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic int tpci200_free_irq(struct ipack_device *dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct slot_irq *slot_irq;
1678c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	tpci200 = check_slot(dev);
1708c2ecf20Sopenharmony_ci	if (tpci200 == NULL)
1718c2ecf20Sopenharmony_ci		return -EINVAL;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&tpci200->mutex))
1748c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (tpci200->slots[dev->slot].irq == NULL) {
1778c2ecf20Sopenharmony_ci		mutex_unlock(&tpci200->mutex);
1788c2ecf20Sopenharmony_ci		return -EINVAL;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	tpci200_disable_irq(tpci200, dev->slot);
1828c2ecf20Sopenharmony_ci	slot_irq = tpci200->slots[dev->slot].irq;
1838c2ecf20Sopenharmony_ci	/* uninstall handler */
1848c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL);
1858c2ecf20Sopenharmony_ci	synchronize_rcu();
1868c2ecf20Sopenharmony_ci	kfree(slot_irq);
1878c2ecf20Sopenharmony_ci	mutex_unlock(&tpci200->mutex);
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int tpci200_request_irq(struct ipack_device *dev,
1928c2ecf20Sopenharmony_ci			       irqreturn_t (*handler)(void *), void *arg)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	int res = 0;
1958c2ecf20Sopenharmony_ci	struct slot_irq *slot_irq;
1968c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	tpci200 = check_slot(dev);
1998c2ecf20Sopenharmony_ci	if (tpci200 == NULL)
2008c2ecf20Sopenharmony_ci		return -EINVAL;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&tpci200->mutex))
2038c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (tpci200->slots[dev->slot].irq != NULL) {
2068c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
2078c2ecf20Sopenharmony_ci			"Slot [%d:%d] IRQ already registered !\n",
2088c2ecf20Sopenharmony_ci			dev->bus->bus_nr,
2098c2ecf20Sopenharmony_ci			dev->slot);
2108c2ecf20Sopenharmony_ci		res = -EINVAL;
2118c2ecf20Sopenharmony_ci		goto out_unlock;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL);
2158c2ecf20Sopenharmony_ci	if (slot_irq == NULL) {
2168c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
2178c2ecf20Sopenharmony_ci			"Slot [%d:%d] unable to allocate memory for IRQ !\n",
2188c2ecf20Sopenharmony_ci			dev->bus->bus_nr, dev->slot);
2198c2ecf20Sopenharmony_ci		res = -ENOMEM;
2208c2ecf20Sopenharmony_ci		goto out_unlock;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * WARNING: Setup Interrupt Vector in the IndustryPack device
2258c2ecf20Sopenharmony_ci	 * before an IRQ request.
2268c2ecf20Sopenharmony_ci	 * Read the User Manual of your IndustryPack device to know
2278c2ecf20Sopenharmony_ci	 * where to write the vector in memory.
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	slot_irq->handler = handler;
2308c2ecf20Sopenharmony_ci	slot_irq->arg = arg;
2318c2ecf20Sopenharmony_ci	slot_irq->holder = dev;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq);
2348c2ecf20Sopenharmony_ci	tpci200_enable_irq(tpci200, dev->slot);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ciout_unlock:
2378c2ecf20Sopenharmony_ci	mutex_unlock(&tpci200->mutex);
2388c2ecf20Sopenharmony_ci	return res;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int tpci200_register(struct tpci200_board *tpci200)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int i;
2448c2ecf20Sopenharmony_ci	int res;
2458c2ecf20Sopenharmony_ci	phys_addr_t ioidint_base;
2468c2ecf20Sopenharmony_ci	unsigned short slot_ctrl;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (pci_enable_device(tpci200->info->pdev) < 0)
2498c2ecf20Sopenharmony_ci		return -ENODEV;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Request IP interface register (Bar 2) */
2528c2ecf20Sopenharmony_ci	res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR,
2538c2ecf20Sopenharmony_ci				 "Carrier IP interface registers");
2548c2ecf20Sopenharmony_ci	if (res) {
2558c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
2568c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !",
2578c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
2588c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
2598c2ecf20Sopenharmony_ci		goto err_disable_device;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* Request IO ID INT space (Bar 3) */
2638c2ecf20Sopenharmony_ci	res = pci_request_region(tpci200->info->pdev,
2648c2ecf20Sopenharmony_ci				 TPCI200_IO_ID_INT_SPACES_BAR,
2658c2ecf20Sopenharmony_ci				 "Carrier IO ID INT space");
2668c2ecf20Sopenharmony_ci	if (res) {
2678c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
2688c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !",
2698c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
2708c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
2718c2ecf20Sopenharmony_ci		goto err_ip_interface_bar;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Request MEM8 space (Bar 5) */
2758c2ecf20Sopenharmony_ci	res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR,
2768c2ecf20Sopenharmony_ci				 "Carrier MEM8 space");
2778c2ecf20Sopenharmony_ci	if (res) {
2788c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
2798c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!",
2808c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
2818c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
2828c2ecf20Sopenharmony_ci		goto err_io_id_int_spaces_bar;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* Request MEM16 space (Bar 4) */
2868c2ecf20Sopenharmony_ci	res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR,
2878c2ecf20Sopenharmony_ci				 "Carrier MEM16 space");
2888c2ecf20Sopenharmony_ci	if (res) {
2898c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
2908c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!",
2918c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
2928c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
2938c2ecf20Sopenharmony_ci		goto err_mem8_space_bar;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Map internal tpci200 driver user space */
2978c2ecf20Sopenharmony_ci	tpci200->info->interface_regs =
2988c2ecf20Sopenharmony_ci		ioremap(pci_resource_start(tpci200->info->pdev,
2998c2ecf20Sopenharmony_ci					   TPCI200_IP_INTERFACE_BAR),
3008c2ecf20Sopenharmony_ci			TPCI200_IFACE_SIZE);
3018c2ecf20Sopenharmony_ci	if (!tpci200->info->interface_regs) {
3028c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
3038c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) failed to map driver user space!",
3048c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
3058c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
3068c2ecf20Sopenharmony_ci		res = -ENOMEM;
3078c2ecf20Sopenharmony_ci		goto err_mem16_space_bar;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Initialize lock that protects interface_regs */
3118c2ecf20Sopenharmony_ci	spin_lock_init(&tpci200->regs_lock);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	ioidint_base = pci_resource_start(tpci200->info->pdev,
3148c2ecf20Sopenharmony_ci					  TPCI200_IO_ID_INT_SPACES_BAR);
3158c2ecf20Sopenharmony_ci	tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF;
3168c2ecf20Sopenharmony_ci	tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF;
3178c2ecf20Sopenharmony_ci	tpci200->mod_mem[IPACK_INT_SPACE] =
3188c2ecf20Sopenharmony_ci		ioidint_base + TPCI200_INT_SPACE_OFF;
3198c2ecf20Sopenharmony_ci	tpci200->mod_mem[IPACK_MEM8_SPACE] =
3208c2ecf20Sopenharmony_ci		pci_resource_start(tpci200->info->pdev,
3218c2ecf20Sopenharmony_ci				   TPCI200_MEM8_SPACE_BAR);
3228c2ecf20Sopenharmony_ci	tpci200->mod_mem[IPACK_MEM16_SPACE] =
3238c2ecf20Sopenharmony_ci		pci_resource_start(tpci200->info->pdev,
3248c2ecf20Sopenharmony_ci				   TPCI200_MEM16_SPACE_BAR);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Set the default parameters of the slot
3278c2ecf20Sopenharmony_ci	 * INT0 disabled, level sensitive
3288c2ecf20Sopenharmony_ci	 * INT1 disabled, level sensitive
3298c2ecf20Sopenharmony_ci	 * error interrupt disabled
3308c2ecf20Sopenharmony_ci	 * timeout interrupt disabled
3318c2ecf20Sopenharmony_ci	 * recover time disabled
3328c2ecf20Sopenharmony_ci	 * clock rate 8 MHz
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	slot_ctrl = 0;
3358c2ecf20Sopenharmony_ci	for (i = 0; i < TPCI200_NB_SLOT; i++)
3368c2ecf20Sopenharmony_ci		writew(slot_ctrl, &tpci200->info->interface_regs->control[i]);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	res = request_irq(tpci200->info->pdev->irq,
3398c2ecf20Sopenharmony_ci			  tpci200_interrupt, IRQF_SHARED,
3408c2ecf20Sopenharmony_ci			  KBUILD_MODNAME, (void *) tpci200);
3418c2ecf20Sopenharmony_ci	if (res) {
3428c2ecf20Sopenharmony_ci		dev_err(&tpci200->info->pdev->dev,
3438c2ecf20Sopenharmony_ci			"(bn 0x%X, sn 0x%X) unable to register IRQ !",
3448c2ecf20Sopenharmony_ci			tpci200->info->pdev->bus->number,
3458c2ecf20Sopenharmony_ci			tpci200->info->pdev->devfn);
3468c2ecf20Sopenharmony_ci		goto err_interface_regs;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cierr_interface_regs:
3528c2ecf20Sopenharmony_ci	pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs);
3538c2ecf20Sopenharmony_cierr_mem16_space_bar:
3548c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR);
3558c2ecf20Sopenharmony_cierr_mem8_space_bar:
3568c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR);
3578c2ecf20Sopenharmony_cierr_io_id_int_spaces_bar:
3588c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR);
3598c2ecf20Sopenharmony_cierr_ip_interface_bar:
3608c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR);
3618c2ecf20Sopenharmony_cierr_disable_device:
3628c2ecf20Sopenharmony_ci	pci_disable_device(tpci200->info->pdev);
3638c2ecf20Sopenharmony_ci	return res;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int tpci200_get_clockrate(struct ipack_device *dev)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = check_slot(dev);
3698c2ecf20Sopenharmony_ci	__le16 __iomem *addr;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (!tpci200)
3728c2ecf20Sopenharmony_ci		return -ENODEV;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	addr = &tpci200->info->interface_regs->control[dev->slot];
3758c2ecf20Sopenharmony_ci	return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int tpci200_set_clockrate(struct ipack_device *dev, int mherz)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = check_slot(dev);
3818c2ecf20Sopenharmony_ci	__le16 __iomem *addr;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (!tpci200)
3848c2ecf20Sopenharmony_ci		return -ENODEV;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	addr = &tpci200->info->interface_regs->control[dev->slot];
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	switch (mherz) {
3898c2ecf20Sopenharmony_ci	case 8:
3908c2ecf20Sopenharmony_ci		tpci200_clear_mask(tpci200, addr, TPCI200_CLK32);
3918c2ecf20Sopenharmony_ci		break;
3928c2ecf20Sopenharmony_ci	case 32:
3938c2ecf20Sopenharmony_ci		tpci200_set_mask(tpci200, addr, TPCI200_CLK32);
3948c2ecf20Sopenharmony_ci		break;
3958c2ecf20Sopenharmony_ci	default:
3968c2ecf20Sopenharmony_ci		return -EINVAL;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int tpci200_get_error(struct ipack_device *dev)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = check_slot(dev);
4048c2ecf20Sopenharmony_ci	__le16 __iomem *addr;
4058c2ecf20Sopenharmony_ci	u16 mask;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (!tpci200)
4088c2ecf20Sopenharmony_ci		return -ENODEV;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	addr = &tpci200->info->interface_regs->status;
4118c2ecf20Sopenharmony_ci	mask = tpci200_status_error[dev->slot];
4128c2ecf20Sopenharmony_ci	return (ioread16(addr) & mask) ? 1 : 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int tpci200_get_timeout(struct ipack_device *dev)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = check_slot(dev);
4188c2ecf20Sopenharmony_ci	__le16 __iomem *addr;
4198c2ecf20Sopenharmony_ci	u16 mask;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (!tpci200)
4228c2ecf20Sopenharmony_ci		return -ENODEV;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	addr = &tpci200->info->interface_regs->status;
4258c2ecf20Sopenharmony_ci	mask = tpci200_status_timeout[dev->slot];
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return (ioread16(addr) & mask) ? 1 : 0;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int tpci200_reset_timeout(struct ipack_device *dev)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = check_slot(dev);
4338c2ecf20Sopenharmony_ci	__le16 __iomem *addr;
4348c2ecf20Sopenharmony_ci	u16 mask;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!tpci200)
4378c2ecf20Sopenharmony_ci		return -ENODEV;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	addr = &tpci200->info->interface_regs->status;
4408c2ecf20Sopenharmony_ci	mask = tpci200_status_timeout[dev->slot];
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	iowrite16(mask, addr);
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void tpci200_uninstall(struct tpci200_board *tpci200)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	tpci200_unregister(tpci200);
4498c2ecf20Sopenharmony_ci	kfree(tpci200->slots);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic const struct ipack_bus_ops tpci200_bus_ops = {
4538c2ecf20Sopenharmony_ci	.request_irq = tpci200_request_irq,
4548c2ecf20Sopenharmony_ci	.free_irq = tpci200_free_irq,
4558c2ecf20Sopenharmony_ci	.get_clockrate = tpci200_get_clockrate,
4568c2ecf20Sopenharmony_ci	.set_clockrate = tpci200_set_clockrate,
4578c2ecf20Sopenharmony_ci	.get_error     = tpci200_get_error,
4588c2ecf20Sopenharmony_ci	.get_timeout   = tpci200_get_timeout,
4598c2ecf20Sopenharmony_ci	.reset_timeout = tpci200_reset_timeout,
4608c2ecf20Sopenharmony_ci};
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int tpci200_install(struct tpci200_board *tpci200)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int res;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	tpci200->slots = kcalloc(TPCI200_NB_SLOT, sizeof(struct tpci200_slot),
4678c2ecf20Sopenharmony_ci				 GFP_KERNEL);
4688c2ecf20Sopenharmony_ci	if (tpci200->slots == NULL)
4698c2ecf20Sopenharmony_ci		return -ENOMEM;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	res = tpci200_register(tpci200);
4728c2ecf20Sopenharmony_ci	if (res) {
4738c2ecf20Sopenharmony_ci		kfree(tpci200->slots);
4748c2ecf20Sopenharmony_ci		tpci200->slots = NULL;
4758c2ecf20Sopenharmony_ci		return res;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	mutex_init(&tpci200->mutex);
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void tpci200_release_device(struct ipack_device *dev)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	kfree(dev);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int tpci200_create_device(struct tpci200_board *tpci200, int i)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	int ret;
4908c2ecf20Sopenharmony_ci	enum ipack_space space;
4918c2ecf20Sopenharmony_ci	struct ipack_device *dev =
4928c2ecf20Sopenharmony_ci		kzalloc(sizeof(struct ipack_device), GFP_KERNEL);
4938c2ecf20Sopenharmony_ci	if (!dev)
4948c2ecf20Sopenharmony_ci		return -ENOMEM;
4958c2ecf20Sopenharmony_ci	dev->slot = i;
4968c2ecf20Sopenharmony_ci	dev->bus = tpci200->info->ipack_bus;
4978c2ecf20Sopenharmony_ci	dev->release = tpci200_release_device;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	for (space = 0; space < IPACK_SPACE_COUNT; space++) {
5008c2ecf20Sopenharmony_ci		dev->region[space].start =
5018c2ecf20Sopenharmony_ci			tpci200->mod_mem[space]
5028c2ecf20Sopenharmony_ci			+ tpci200_space_interval[space] * i;
5038c2ecf20Sopenharmony_ci		dev->region[space].size = tpci200_space_size[space];
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	ret = ipack_device_init(dev);
5078c2ecf20Sopenharmony_ci	if (ret < 0) {
5088c2ecf20Sopenharmony_ci		ipack_put_device(dev);
5098c2ecf20Sopenharmony_ci		return ret;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	ret = ipack_device_add(dev);
5138c2ecf20Sopenharmony_ci	if (ret < 0)
5148c2ecf20Sopenharmony_ci		ipack_put_device(dev);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return ret;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int tpci200_pci_probe(struct pci_dev *pdev,
5208c2ecf20Sopenharmony_ci			     const struct pci_device_id *id)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	int ret, i;
5238c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200;
5248c2ecf20Sopenharmony_ci	u32 reg32;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL);
5278c2ecf20Sopenharmony_ci	if (!tpci200)
5288c2ecf20Sopenharmony_ci		return -ENOMEM;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL);
5318c2ecf20Sopenharmony_ci	if (!tpci200->info) {
5328c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5338c2ecf20Sopenharmony_ci		goto err_tpci200;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	pci_dev_get(pdev);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/* Obtain a mapping of the carrier's PCI configuration registers */
5398c2ecf20Sopenharmony_ci	ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR,
5408c2ecf20Sopenharmony_ci				 KBUILD_MODNAME " Configuration Memory");
5418c2ecf20Sopenharmony_ci	if (ret) {
5428c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory");
5438c2ecf20Sopenharmony_ci		ret = -EBUSY;
5448c2ecf20Sopenharmony_ci		goto err_tpci200_info;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci	tpci200->info->cfg_regs = ioremap(
5478c2ecf20Sopenharmony_ci			pci_resource_start(pdev, TPCI200_CFG_MEM_BAR),
5488c2ecf20Sopenharmony_ci			pci_resource_len(pdev, TPCI200_CFG_MEM_BAR));
5498c2ecf20Sopenharmony_ci	if (!tpci200->info->cfg_regs) {
5508c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to map PCI Configuration Memory");
5518c2ecf20Sopenharmony_ci		ret = -EFAULT;
5528c2ecf20Sopenharmony_ci		goto err_request_region;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* Disable byte swapping for 16 bit IP module access. This will ensure
5568c2ecf20Sopenharmony_ci	 * that the Industrypack big endian byte order is preserved by the
5578c2ecf20Sopenharmony_ci	 * carrier. */
5588c2ecf20Sopenharmony_ci	reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC);
5598c2ecf20Sopenharmony_ci	reg32 |= 1 << LAS_BIT_BIGENDIAN;
5608c2ecf20Sopenharmony_ci	iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC);
5638c2ecf20Sopenharmony_ci	reg32 |= 1 << LAS_BIT_BIGENDIAN;
5648c2ecf20Sopenharmony_ci	iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Save struct pci_dev pointer */
5678c2ecf20Sopenharmony_ci	tpci200->info->pdev = pdev;
5688c2ecf20Sopenharmony_ci	tpci200->info->id_table = (struct pci_device_id *)id;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* register the device and initialize it */
5718c2ecf20Sopenharmony_ci	ret = tpci200_install(tpci200);
5728c2ecf20Sopenharmony_ci	if (ret) {
5738c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "error during tpci200 install\n");
5748c2ecf20Sopenharmony_ci		ret = -ENODEV;
5758c2ecf20Sopenharmony_ci		goto err_cfg_regs;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* Register the carrier in the industry pack bus driver */
5798c2ecf20Sopenharmony_ci	tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev,
5808c2ecf20Sopenharmony_ci						      TPCI200_NB_SLOT,
5818c2ecf20Sopenharmony_ci						      &tpci200_bus_ops,
5828c2ecf20Sopenharmony_ci						      THIS_MODULE);
5838c2ecf20Sopenharmony_ci	if (!tpci200->info->ipack_bus) {
5848c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
5858c2ecf20Sopenharmony_ci			"error registering the carrier on ipack driver\n");
5868c2ecf20Sopenharmony_ci		ret = -EFAULT;
5878c2ecf20Sopenharmony_ci		goto err_tpci200_install;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* save the bus number given by ipack to logging purpose */
5918c2ecf20Sopenharmony_ci	tpci200->number = tpci200->info->ipack_bus->bus_nr;
5928c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, tpci200);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	for (i = 0; i < TPCI200_NB_SLOT; i++)
5958c2ecf20Sopenharmony_ci		tpci200_create_device(tpci200, i);
5968c2ecf20Sopenharmony_ci	return 0;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cierr_tpci200_install:
5998c2ecf20Sopenharmony_ci	tpci200_uninstall(tpci200);
6008c2ecf20Sopenharmony_cierr_cfg_regs:
6018c2ecf20Sopenharmony_ci	pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs);
6028c2ecf20Sopenharmony_cierr_request_region:
6038c2ecf20Sopenharmony_ci	pci_release_region(pdev, TPCI200_CFG_MEM_BAR);
6048c2ecf20Sopenharmony_cierr_tpci200_info:
6058c2ecf20Sopenharmony_ci	kfree(tpci200->info);
6068c2ecf20Sopenharmony_ci	pci_dev_put(pdev);
6078c2ecf20Sopenharmony_cierr_tpci200:
6088c2ecf20Sopenharmony_ci	kfree(tpci200);
6098c2ecf20Sopenharmony_ci	return ret;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic void __tpci200_pci_remove(struct tpci200_board *tpci200)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	ipack_bus_unregister(tpci200->info->ipack_bus);
6158c2ecf20Sopenharmony_ci	tpci200_uninstall(tpci200);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	pci_dev_put(tpci200->info->pdev);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	kfree(tpci200->info);
6248c2ecf20Sopenharmony_ci	kfree(tpci200);
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic void tpci200_pci_remove(struct pci_dev *dev)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct tpci200_board *tpci200 = pci_get_drvdata(dev);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	__tpci200_pci_remove(tpci200);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic const struct pci_device_id tpci200_idtable[] = {
6358c2ecf20Sopenharmony_ci	{ TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID,
6368c2ecf20Sopenharmony_ci	  TPCI200_SUBDEVICE_ID },
6378c2ecf20Sopenharmony_ci	{ 0, },
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tpci200_idtable);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic struct pci_driver tpci200_pci_drv = {
6438c2ecf20Sopenharmony_ci	.name = "tpci200",
6448c2ecf20Sopenharmony_ci	.id_table = tpci200_idtable,
6458c2ecf20Sopenharmony_ci	.probe = tpci200_pci_probe,
6468c2ecf20Sopenharmony_ci	.remove = tpci200_pci_remove,
6478c2ecf20Sopenharmony_ci};
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cimodule_pci_driver(tpci200_pci_drv);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TEWS TPCI-200 device driver");
6528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
653