| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * tpci200.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * driver for the TEWS TPCI-200 device | 
					
						
							| 
									
										
										
										
											2012-11-16 16:19:58 +01:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2009-2012 CERN (www.cern.ch) | 
					
						
							|  |  |  |  * Author: Nicolas Serafini, EIC2 SA | 
					
						
							|  |  |  |  * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License as published by the Free | 
					
						
							| 
									
										
										
										
											2012-05-11 10:17:14 +02:00
										 |  |  |  * Software Foundation; version 2 of the License. | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2012-10-22 09:36:48 +02:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | #include "tpci200.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:30 +02:00
										 |  |  | static const u16 tpci200_status_timeout[] = { | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	TPCI200_A_TIMEOUT, | 
					
						
							|  |  |  | 	TPCI200_B_TIMEOUT, | 
					
						
							|  |  |  | 	TPCI200_C_TIMEOUT, | 
					
						
							|  |  |  | 	TPCI200_D_TIMEOUT, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:30 +02:00
										 |  |  | static const u16 tpci200_status_error[] = { | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	TPCI200_A_ERROR, | 
					
						
							|  |  |  | 	TPCI200_B_ERROR, | 
					
						
							|  |  |  | 	TPCI200_C_ERROR, | 
					
						
							|  |  |  | 	TPCI200_D_ERROR, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:31 +02:00
										 |  |  | static const size_t tpci200_space_size[IPACK_SPACE_COUNT] = { | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 	[IPACK_IO_SPACE]    = TPCI200_IO_SPACE_SIZE, | 
					
						
							|  |  |  | 	[IPACK_ID_SPACE]    = TPCI200_ID_SPACE_SIZE, | 
					
						
							|  |  |  | 	[IPACK_INT_SPACE]   = TPCI200_INT_SPACE_SIZE, | 
					
						
							|  |  |  | 	[IPACK_MEM8_SPACE]  = TPCI200_MEM8_SPACE_SIZE, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | 	[IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_SIZE, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:31 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const size_t tpci200_space_interval[IPACK_SPACE_COUNT] = { | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 	[IPACK_IO_SPACE]    = TPCI200_IO_SPACE_INTERVAL, | 
					
						
							|  |  |  | 	[IPACK_ID_SPACE]    = TPCI200_ID_SPACE_INTERVAL, | 
					
						
							|  |  |  | 	[IPACK_INT_SPACE]   = TPCI200_INT_SPACE_INTERVAL, | 
					
						
							|  |  |  | 	[IPACK_MEM8_SPACE]  = TPCI200_MEM8_SPACE_INTERVAL, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | 	[IPACK_MEM16_SPACE] = TPCI200_MEM16_SPACE_INTERVAL, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:31 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static struct tpci200_board *check_slot(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 	if (dev == NULL) | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 09:39:04 +02:00
										 |  |  | 	tpci200 = dev_get_drvdata(dev->bus->parent); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (tpci200 == NULL) { | 
					
						
							|  |  |  | 		dev_info(&dev->dev, "carrier board not found\n"); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->slot >= TPCI200_NB_SLOT) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_info(&dev->dev, | 
					
						
							|  |  |  | 			 "Slot [%d:%d] doesn't exist! Last tpci200 slot is %d.\n", | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:25 +02:00
										 |  |  | 			 dev->bus->bus_nr, dev->slot, TPCI200_NB_SLOT-1); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return tpci200; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:23 +02:00
										 |  |  | static void tpci200_clear_mask(struct tpci200_board *tpci200, | 
					
						
							|  |  |  | 			       __le16 __iomem *addr, u16 mask) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:33 +02:00
										 |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	spin_lock_irqsave(&tpci200->regs_lock, flags); | 
					
						
							|  |  |  | 	iowrite16(ioread16(addr) & (~mask), addr); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&tpci200->regs_lock, flags); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:23 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpci200_set_mask(struct tpci200_board *tpci200, | 
					
						
							|  |  |  | 			     __le16 __iomem *addr, u16 mask) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:33 +02:00
										 |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	spin_lock_irqsave(&tpci200->regs_lock, flags); | 
					
						
							|  |  |  | 	iowrite16(ioread16(addr) | mask, addr); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&tpci200->regs_lock, flags); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:23 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static void tpci200_unregister(struct tpci200_board *tpci200) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	free_irq(tpci200->info->pdev->irq, (void *) tpci200); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs); | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:04 +02:00
										 |  |  | 	pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); | 
					
						
							|  |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:04 +02:00
										 |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pci_disable_device(tpci200->info->pdev); | 
					
						
							|  |  |  | 	pci_dev_put(tpci200->info->pdev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:36 +02:00
										 |  |  | static void tpci200_enable_irq(struct tpci200_board *tpci200, | 
					
						
							|  |  |  | 			       int islot) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	tpci200_set_mask(tpci200, | 
					
						
							|  |  |  | 			&tpci200->info->interface_regs->control[islot], | 
					
						
							|  |  |  | 			TPCI200_INT0_EN | TPCI200_INT1_EN); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tpci200_disable_irq(struct tpci200_board *tpci200, | 
					
						
							|  |  |  | 				int islot) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	tpci200_clear_mask(tpci200, | 
					
						
							|  |  |  | 			&tpci200->info->interface_regs->control[islot], | 
					
						
							|  |  |  | 			TPCI200_INT0_EN | TPCI200_INT1_EN); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:25 +02:00
										 |  |  | static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	irqreturn_t ret; | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	if (!slot_irq) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	ret = slot_irq->handler(slot_irq->arg); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:25 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static irqreturn_t tpci200_interrupt(int irq, void *dev_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:13 +02:00
										 |  |  | 	struct slot_irq *slot_irq; | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	irqreturn_t ret; | 
					
						
							|  |  |  | 	u16 status_reg; | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Read status register */ | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	status_reg = ioread16(&tpci200->info->interface_regs->status); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	/* Did we cause the interrupt? */ | 
					
						
							|  |  |  | 	if (!(status_reg & TPCI200_SLOT_INT_MASK)) | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:34 +02:00
										 |  |  | 		return IRQ_NONE; | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* callback to the IRQ handler for the corresponding slot */ | 
					
						
							|  |  |  | 	rcu_read_lock(); | 
					
						
							|  |  |  | 	for (i = 0; i < TPCI200_NB_SLOT; i++) { | 
					
						
							|  |  |  | 		if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i)))) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		slot_irq = rcu_dereference(tpci200->slots[i].irq); | 
					
						
							|  |  |  | 		ret = tpci200_slot_irq(slot_irq); | 
					
						
							|  |  |  | 		if (ret == -ENODEV) { | 
					
						
							|  |  |  | 			dev_info(&tpci200->info->pdev->dev, | 
					
						
							|  |  |  | 				 "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n", | 
					
						
							|  |  |  | 				 tpci200->number, i); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:36 +02:00
										 |  |  | 			tpci200_disable_irq(tpci200, i); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:34 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:35 +02:00
										 |  |  | 	rcu_read_unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:37 +02:00
										 |  |  | static int tpci200_free_irq(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct slot_irq *slot_irq; | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tpci200 = check_slot(dev); | 
					
						
							|  |  |  | 	if (tpci200 == NULL) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&tpci200->mutex)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (tpci200->slots[dev->slot].irq == NULL) { | 
					
						
							|  |  |  | 		mutex_unlock(&tpci200->mutex); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tpci200_disable_irq(tpci200, dev->slot); | 
					
						
							|  |  |  | 	slot_irq = tpci200->slots[dev->slot].irq; | 
					
						
							|  |  |  | 	/* uninstall handler */ | 
					
						
							|  |  |  | 	RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL); | 
					
						
							|  |  |  | 	synchronize_rcu(); | 
					
						
							|  |  |  | 	kfree(slot_irq); | 
					
						
							|  |  |  | 	mutex_unlock(&tpci200->mutex); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 12:32:21 +02:00
										 |  |  | static int tpci200_request_irq(struct ipack_device *dev, | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:38 +02:00
										 |  |  | 			       irqreturn_t (*handler)(void *), void *arg) | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:37 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int res = 0; | 
					
						
							|  |  |  | 	struct slot_irq *slot_irq; | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tpci200 = check_slot(dev); | 
					
						
							|  |  |  | 	if (tpci200 == NULL) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mutex_lock_interruptible(&tpci200->mutex)) | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (tpci200->slots[dev->slot].irq != NULL) { | 
					
						
							|  |  |  | 		dev_err(&dev->dev, | 
					
						
							| 
									
										
										
										
											2012-11-11 22:41:13 +02:00
										 |  |  | 			"Slot [%d:%d] IRQ already registered !\n", | 
					
						
							|  |  |  | 			dev->bus->bus_nr, | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:37 +02:00
										 |  |  | 			dev->slot); | 
					
						
							|  |  |  | 		res = -EINVAL; | 
					
						
							|  |  |  | 		goto out_unlock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (slot_irq == NULL) { | 
					
						
							|  |  |  | 		dev_err(&dev->dev, | 
					
						
							|  |  |  | 			"Slot [%d:%d] unable to allocate memory for IRQ !\n", | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:25 +02:00
										 |  |  | 			dev->bus->bus_nr, dev->slot); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:37 +02:00
										 |  |  | 		res = -ENOMEM; | 
					
						
							|  |  |  | 		goto out_unlock; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * WARNING: Setup Interrupt Vector in the IndustryPack device | 
					
						
							|  |  |  | 	 * before an IRQ request. | 
					
						
							|  |  |  | 	 * Read the User Manual of your IndustryPack device to know | 
					
						
							|  |  |  | 	 * where to write the vector in memory. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	slot_irq->handler = handler; | 
					
						
							|  |  |  | 	slot_irq->arg = arg; | 
					
						
							|  |  |  | 	slot_irq->holder = dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq); | 
					
						
							|  |  |  | 	tpci200_enable_irq(tpci200, dev->slot); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_unlock: | 
					
						
							|  |  |  | 	mutex_unlock(&tpci200->mutex); | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static int tpci200_register(struct tpci200_board *tpci200) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-23 15:54:40 +02:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	int res; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:28 +02:00
										 |  |  | 	phys_addr_t ioidint_base; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	unsigned short slot_ctrl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pci_enable_device(tpci200->info->pdev) < 0) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Request IP interface register (Bar 2) */ | 
					
						
							|  |  |  | 	res = pci_request_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR, | 
					
						
							|  |  |  | 				 "Carrier IP interface registers"); | 
					
						
							|  |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_err(&tpci200->info->pdev->dev, | 
					
						
							|  |  |  | 			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 2 !", | 
					
						
							|  |  |  | 			tpci200->info->pdev->bus->number, | 
					
						
							|  |  |  | 			tpci200->info->pdev->devfn); | 
					
						
							| 
									
										
										
										
											2012-05-23 15:54:40 +02:00
										 |  |  | 		goto out_disable_pci; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Request IO ID INT space (Bar 3) */ | 
					
						
							|  |  |  | 	res = pci_request_region(tpci200->info->pdev, | 
					
						
							|  |  |  | 				 TPCI200_IO_ID_INT_SPACES_BAR, | 
					
						
							|  |  |  | 				 "Carrier IO ID INT space"); | 
					
						
							|  |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_err(&tpci200->info->pdev->dev, | 
					
						
							|  |  |  | 			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 3 !", | 
					
						
							|  |  |  | 			tpci200->info->pdev->bus->number, | 
					
						
							|  |  |  | 			tpci200->info->pdev->devfn); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 		goto out_release_ip_space; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 	/* Request MEM8 space (Bar 5) */ | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	res = pci_request_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 				 "Carrier MEM8 space"); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_err(&tpci200->info->pdev->dev, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 5!", | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 			tpci200->info->pdev->bus->number, | 
					
						
							|  |  |  | 			tpci200->info->pdev->devfn); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 		goto out_release_ioid_int_space; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | 	/* Request MEM16 space (Bar 4) */ | 
					
						
							|  |  |  | 	res = pci_request_region(tpci200->info->pdev, TPCI200_MEM16_SPACE_BAR, | 
					
						
							|  |  |  | 				 "Carrier MEM16 space"); | 
					
						
							|  |  |  | 	if (res) { | 
					
						
							|  |  |  | 		dev_err(&tpci200->info->pdev->dev, | 
					
						
							|  |  |  | 			"(bn 0x%X, sn 0x%X) failed to allocate PCI resource for BAR 4!", | 
					
						
							|  |  |  | 			tpci200->info->pdev->bus->number, | 
					
						
							|  |  |  | 			tpci200->info->pdev->devfn); | 
					
						
							|  |  |  | 		goto out_release_mem8_space; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	/* Map internal tpci200 driver user space */ | 
					
						
							|  |  |  | 	tpci200->info->interface_regs = | 
					
						
							| 
									
										
										
										
											2012-07-20 09:39:05 +02:00
										 |  |  | 		ioremap_nocache(pci_resource_start(tpci200->info->pdev, | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 					   TPCI200_IP_INTERFACE_BAR), | 
					
						
							|  |  |  | 			TPCI200_IFACE_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:33 +02:00
										 |  |  | 	/* Initialize lock that protects interface_regs */ | 
					
						
							|  |  |  | 	spin_lock_init(&tpci200->regs_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	ioidint_base = pci_resource_start(tpci200->info->pdev, | 
					
						
							|  |  |  | 					  TPCI200_IO_ID_INT_SPACES_BAR); | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:29 +02:00
										 |  |  | 	tpci200->mod_mem[IPACK_IO_SPACE] = ioidint_base + TPCI200_IO_SPACE_OFF; | 
					
						
							|  |  |  | 	tpci200->mod_mem[IPACK_ID_SPACE] = ioidint_base + TPCI200_ID_SPACE_OFF; | 
					
						
							|  |  |  | 	tpci200->mod_mem[IPACK_INT_SPACE] = | 
					
						
							|  |  |  | 		ioidint_base + TPCI200_INT_SPACE_OFF; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:36 +02:00
										 |  |  | 	tpci200->mod_mem[IPACK_MEM8_SPACE] = | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:29 +02:00
										 |  |  | 		pci_resource_start(tpci200->info->pdev, | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:30 +02:00
										 |  |  | 				   TPCI200_MEM8_SPACE_BAR); | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | 	tpci200->mod_mem[IPACK_MEM16_SPACE] = | 
					
						
							|  |  |  | 		pci_resource_start(tpci200->info->pdev, | 
					
						
							|  |  |  | 				   TPCI200_MEM16_SPACE_BAR); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Set the default parameters of the slot
 | 
					
						
							|  |  |  | 	 * INT0 disabled, level sensitive | 
					
						
							|  |  |  | 	 * INT1 disabled, level sensitive | 
					
						
							|  |  |  | 	 * error interrupt disabled | 
					
						
							|  |  |  | 	 * timeout interrupt disabled | 
					
						
							|  |  |  | 	 * recover time disabled | 
					
						
							|  |  |  | 	 * clock rate 8 MHz | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	slot_ctrl = 0; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:35 +02:00
										 |  |  | 	for (i = 0; i < TPCI200_NB_SLOT; i++) | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:54 +02:00
										 |  |  | 		writew(slot_ctrl, &tpci200->info->interface_regs->control[i]); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	res = request_irq(tpci200->info->pdev->irq, | 
					
						
							|  |  |  | 			  tpci200_interrupt, IRQF_SHARED, | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:07 +02:00
										 |  |  | 			  KBUILD_MODNAME, (void *) tpci200); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_err(&tpci200->info->pdev->dev, | 
					
						
							|  |  |  | 			"(bn 0x%X, sn 0x%X) unable to register IRQ !", | 
					
						
							|  |  |  | 			tpci200->info->pdev->bus->number, | 
					
						
							|  |  |  | 			tpci200->info->pdev->devfn); | 
					
						
							| 
									
										
										
										
											2012-05-23 15:54:40 +02:00
										 |  |  | 		goto out_release_ioid_int_space; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:37 +02:00
										 |  |  | out_release_mem8_space: | 
					
						
							|  |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | out_release_ioid_int_space: | 
					
						
							|  |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR); | 
					
						
							|  |  |  | out_release_ip_space: | 
					
						
							|  |  |  | 	pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR); | 
					
						
							|  |  |  | out_disable_pci: | 
					
						
							|  |  |  | 	pci_disable_device(tpci200->info->pdev); | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | static int tpci200_get_clockrate(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = check_slot(dev); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:46 +02:00
										 |  |  | 	__le16 __iomem *addr; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = &tpci200->info->interface_regs->control[dev->slot]; | 
					
						
							|  |  |  | 	return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_set_clockrate(struct ipack_device *dev, int mherz) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = check_slot(dev); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:46 +02:00
										 |  |  | 	__le16 __iomem *addr; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = &tpci200->info->interface_regs->control[dev->slot]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (mherz) { | 
					
						
							|  |  |  | 	case 8: | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:23 +02:00
										 |  |  | 		tpci200_clear_mask(tpci200, addr, TPCI200_CLK32); | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case 32: | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:23 +02:00
										 |  |  | 		tpci200_set_mask(tpci200, addr, TPCI200_CLK32); | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_get_error(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = check_slot(dev); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:46 +02:00
										 |  |  | 	__le16 __iomem *addr; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	u16 mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = &tpci200->info->interface_regs->status; | 
					
						
							|  |  |  | 	mask = tpci200_status_error[dev->slot]; | 
					
						
							|  |  |  | 	return (ioread16(addr) & mask) ? 1 : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_get_timeout(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = check_slot(dev); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:46 +02:00
										 |  |  | 	__le16 __iomem *addr; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	u16 mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = &tpci200->info->interface_regs->status; | 
					
						
							|  |  |  | 	mask = tpci200_status_timeout[dev->slot]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (ioread16(addr) & mask) ? 1 : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_reset_timeout(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct tpci200_board *tpci200 = check_slot(dev); | 
					
						
							| 
									
										
										
										
											2012-09-12 14:55:46 +02:00
										 |  |  | 	__le16 __iomem *addr; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	u16 mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = &tpci200->info->interface_regs->status; | 
					
						
							|  |  |  | 	mask = tpci200_status_timeout[dev->slot]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iowrite16(mask, addr); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static void tpci200_uninstall(struct tpci200_board *tpci200) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	tpci200_unregister(tpci200); | 
					
						
							|  |  |  | 	kfree(tpci200->slots); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 11:14:01 -07:00
										 |  |  | static const struct ipack_bus_ops tpci200_bus_ops = { | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	.request_irq = tpci200_request_irq, | 
					
						
							|  |  |  | 	.free_irq = tpci200_free_irq, | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	.get_clockrate = tpci200_get_clockrate, | 
					
						
							|  |  |  | 	.set_clockrate = tpci200_set_clockrate, | 
					
						
							|  |  |  | 	.get_error     = tpci200_get_error, | 
					
						
							|  |  |  | 	.get_timeout   = tpci200_get_timeout, | 
					
						
							|  |  |  | 	.reset_timeout = tpci200_reset_timeout, | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_install(struct tpci200_board *tpci200) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tpci200->slots = kzalloc( | 
					
						
							|  |  |  | 		TPCI200_NB_SLOT * sizeof(struct tpci200_slot), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2012-06-07 10:24:54 +02:00
										 |  |  | 	if (tpci200->slots == NULL) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	res = tpci200_register(tpci200); | 
					
						
							| 
									
										
										
										
											2012-06-07 10:24:54 +02:00
										 |  |  | 	if (res) { | 
					
						
							|  |  |  | 		kfree(tpci200->slots); | 
					
						
							|  |  |  | 		tpci200->slots = NULL; | 
					
						
							|  |  |  | 		return res; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mutex_init(&tpci200->mutex); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:26 +02:00
										 |  |  | static void tpci200_release_device(struct ipack_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kfree(dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tpci200_create_device(struct tpci200_board *tpci200, int i) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-08 09:21:47 +01:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:31 +02:00
										 |  |  | 	enum ipack_space space; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:26 +02:00
										 |  |  | 	struct ipack_device *dev = | 
					
						
							|  |  |  | 		kzalloc(sizeof(struct ipack_device), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	dev->slot = i; | 
					
						
							|  |  |  | 	dev->bus = tpci200->info->ipack_bus; | 
					
						
							|  |  |  | 	dev->release = tpci200_release_device; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (space = 0; space < IPACK_SPACE_COUNT; space++) { | 
					
						
							|  |  |  | 		dev->region[space].start = | 
					
						
							|  |  |  | 			tpci200->mod_mem[space] | 
					
						
							|  |  |  | 			+ tpci200_space_interval[space] * i; | 
					
						
							|  |  |  | 		dev->region[space].size = tpci200_space_size[space]; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-03-08 09:21:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = ipack_device_init(dev); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		ipack_put_device(dev); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = ipack_device_add(dev); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		ipack_put_device(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:26 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-07 08:50:54 -07:00
										 |  |  | static int tpci200_pci_probe(struct pci_dev *pdev, | 
					
						
							|  |  |  | 			     const struct pci_device_id *id) | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-23 15:54:40 +02:00
										 |  |  | 	int ret, i; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	struct tpci200_board *tpci200; | 
					
						
							| 
									
										
										
										
											2012-09-11 13:34:56 +02:00
										 |  |  | 	u32 reg32; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!tpci200) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!tpci200->info) { | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:06 +02:00
										 |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto out_err_info; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:05 +02:00
										 |  |  | 	pci_dev_get(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:07 +02:00
										 |  |  | 	/* Obtain a mapping of the carrier's PCI configuration registers */ | 
					
						
							|  |  |  | 	ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR, | 
					
						
							|  |  |  | 				 KBUILD_MODNAME " Configuration Memory"); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory"); | 
					
						
							|  |  |  | 		ret = -EBUSY; | 
					
						
							|  |  |  | 		goto out_err_pci_request; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tpci200->info->cfg_regs = ioremap_nocache( | 
					
						
							|  |  |  | 			pci_resource_start(pdev, TPCI200_CFG_MEM_BAR), | 
					
						
							|  |  |  | 			pci_resource_len(pdev, TPCI200_CFG_MEM_BAR)); | 
					
						
							|  |  |  | 	if (!tpci200->info->cfg_regs) { | 
					
						
							|  |  |  | 		dev_err(&pdev->dev, "Failed to map PCI Configuration Memory"); | 
					
						
							|  |  |  | 		ret = -EFAULT; | 
					
						
							|  |  |  | 		goto out_err_ioremap; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable byte swapping for 16 bit IP module access. This will ensure
 | 
					
						
							|  |  |  | 	 * that the Industrypack big endian byte order is preserved by the | 
					
						
							|  |  |  | 	 * carrier. */ | 
					
						
							|  |  |  | 	reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC); | 
					
						
							|  |  |  | 	reg32 |= 1 << LAS_BIT_BIGENDIAN; | 
					
						
							|  |  |  | 	iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC); | 
					
						
							|  |  |  | 	reg32 |= 1 << LAS_BIT_BIGENDIAN; | 
					
						
							|  |  |  | 	iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	/* Save struct pci_dev pointer */ | 
					
						
							|  |  |  | 	tpci200->info->pdev = pdev; | 
					
						
							|  |  |  | 	tpci200->info->id_table = (struct pci_device_id *)id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* register the device and initialize it */ | 
					
						
							|  |  |  | 	ret = tpci200_install(tpci200); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:06 +02:00
										 |  |  | 		dev_err(&pdev->dev, "error during tpci200 install\n"); | 
					
						
							|  |  |  | 		ret = -ENODEV; | 
					
						
							|  |  |  | 		goto out_err_install; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:05 +02:00
										 |  |  | 	/* Register the carrier in the industry pack bus driver */ | 
					
						
							|  |  |  | 	tpci200->info->ipack_bus = ipack_bus_register(&pdev->dev, | 
					
						
							|  |  |  | 						      TPCI200_NB_SLOT, | 
					
						
							|  |  |  | 						      &tpci200_bus_ops); | 
					
						
							|  |  |  | 	if (!tpci200->info->ipack_bus) { | 
					
						
							| 
									
										
										
										
											2012-05-25 13:08:12 +02:00
										 |  |  | 		dev_err(&pdev->dev, | 
					
						
							|  |  |  | 			"error registering the carrier on ipack driver\n"); | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:06 +02:00
										 |  |  | 		ret = -EFAULT; | 
					
						
							|  |  |  | 		goto out_err_bus_register; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	/* save the bus number given by ipack to logging purpose */ | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:05 +02:00
										 |  |  | 	tpci200->number = tpci200->info->ipack_bus->bus_nr; | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	dev_set_drvdata(&pdev->dev, tpci200); | 
					
						
							| 
									
										
										
										
											2012-05-23 15:54:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < TPCI200_NB_SLOT; i++) | 
					
						
							| 
									
										
										
										
											2012-09-27 12:37:26 +02:00
										 |  |  | 		tpci200_create_device(tpci200, i); | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:06 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_err_bus_register: | 
					
						
							|  |  |  | 	tpci200_uninstall(tpci200); | 
					
						
							|  |  |  | out_err_install: | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:07 +02:00
										 |  |  | 	iounmap(tpci200->info->cfg_regs); | 
					
						
							|  |  |  | out_err_ioremap: | 
					
						
							|  |  |  | 	pci_release_region(pdev, TPCI200_CFG_MEM_BAR); | 
					
						
							|  |  |  | out_err_pci_request: | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:05 +02:00
										 |  |  | 	pci_dev_put(pdev); | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:06 +02:00
										 |  |  | 	kfree(tpci200->info); | 
					
						
							|  |  |  | out_err_info: | 
					
						
							|  |  |  | 	kfree(tpci200); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __tpci200_pci_remove(struct tpci200_board *tpci200) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:05 +02:00
										 |  |  | 	ipack_bus_unregister(tpci200->info->ipack_bus); | 
					
						
							| 
									
										
										
										
											2012-09-11 13:35:10 +02:00
										 |  |  | 	tpci200_uninstall(tpci200); | 
					
						
							| 
									
										
										
										
											2012-09-04 17:01:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-18 11:10:05 +02:00
										 |  |  | 	kfree(tpci200->info); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	kfree(tpci200); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 13:26:39 -05:00
										 |  |  | static void tpci200_pci_remove(struct pci_dev *dev) | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 09:39:04 +02:00
										 |  |  | 	struct tpci200_board *tpci200 = pci_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 09:39:04 +02:00
										 |  |  | 	__tpci200_pci_remove(tpci200); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 10:24:58 +02:00
										 |  |  | static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = { | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 	{ TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, | 
					
						
							|  |  |  | 	  TPCI200_SUBDEVICE_ID }, | 
					
						
							|  |  |  | 	{ 0, }, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 10:24:58 +02:00
										 |  |  | MODULE_DEVICE_TABLE(pci, tpci200_idtable); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | static struct pci_driver tpci200_pci_drv = { | 
					
						
							|  |  |  | 	.name = "tpci200", | 
					
						
							|  |  |  | 	.id_table = tpci200_idtable, | 
					
						
							| 
									
										
										
										
											2012-09-07 08:50:54 -07:00
										 |  |  | 	.probe = tpci200_pci_probe, | 
					
						
							| 
									
										
										
										
											2012-11-19 13:21:31 -05:00
										 |  |  | 	.remove = tpci200_pci_remove, | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-18 23:18:46 +08:00
										 |  |  | module_pci_driver(tpci200_pci_drv); | 
					
						
							| 
									
										
										
										
											2012-05-09 15:27:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION("TEWS TPCI-200 device driver"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |