[Bugfix 2/3] eata: Implement PCI driver to manage eata PCI devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Previously the eata driver just grabs and accesses eata PCI devices
without implementing a PCI device driver, that causes troubles with
latest IRQ related

Commit 991de2e59090 ("PCI, x86: Implement pcibios_alloc_irq() and
pcibios_free_irq()") changes the way to allocate PCI legacy IRQ
for PCI devices on x86 platforms. Instead of allocating PCI legacy
IRQs when pcibios_enable_device() gets called, now pcibios_alloc_irq()
will be called by pci_device_probe() to allocate PCI legacy IRQs
when binding PCI drivers to PCI devices.

But the eata driver directly accesses PCI devices without implementing
corresponding PCI drivers, so pcibios_alloc_irq() won't be called for
those PCI devices and wrong IRQ number may be used to manage the PCI
device.

This patch implements a PCI device driver to manage eata PCI devices,
so eata driver could properly cooperate with the PCI core. It also
provides headroom for PCI hotplug with eata driver.

Signed-off-by: Jiang Liu <jiang.liu@xxxxxxxxxxxxxxx>
---
 drivers/scsi/eata.c |  170 ++++++++++++++++++++++-----------------------------
 1 file changed, 74 insertions(+), 96 deletions(-)

diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index b45d3b532b70..b92e6856f909 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -850,10 +850,6 @@ static unsigned long io_port[] = {
 	/* First ISA */
 	0x1f0,
 
-	/* Space for MAX_PCI ports possibly reported by PCI_BIOS */
-	SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
-	SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
-
 	/* MAX_EISA ports */
 	0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
 	0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
@@ -1024,60 +1020,13 @@ static int read_pio(unsigned long iobase, ushort * start, ushort * end)
 	return 0;
 }
 
-static struct pci_dev *get_pci_dev(unsigned long port_base)
-{
-#if defined(CONFIG_PCI)
-	unsigned int addr;
-	struct pci_dev *dev = NULL;
-
-	while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
-		addr = pci_resource_start(dev, 0);
-
-#if defined(DEBUG_PCI_DETECT)
-		printk("%s: get_pci_dev, bus %d, devfn 0x%x, addr 0x%x.\n",
-		       driver_name, dev->bus->number, dev->devfn, addr);
-#endif
-
-		/* we are in so much trouble for a pci hotplug system with this driver
-		 * anyway, so doing this at least lets people unload the driver and not
-		 * cause memory problems, but in general this is a bad thing to do (this
-		 * driver needs to be converted to the proper PCI api someday... */
-		pci_dev_put(dev);
-		if (addr + PCI_BASE_ADDRESS_0 == port_base)
-			return dev;
-	}
-#endif				/* end CONFIG_PCI */
-	return NULL;
-}
-
-static void enable_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
-	struct pci_dev *dev = NULL;
-
-	while ((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
-#if defined(DEBUG_PCI_DETECT)
-		printk("%s: enable_pci_ports, bus %d, devfn 0x%x.\n",
-		       driver_name, dev->bus->number, dev->devfn);
-#endif
-
-		if (pci_enable_device(dev))
-			printk
-			    ("%s: warning, pci_enable_device failed, bus %d devfn 0x%x.\n",
-			     driver_name, dev->bus->number, dev->devfn);
-	}
-
-#endif				/* end CONFIG_PCI */
-}
-
 static int port_detect(unsigned long port_base, unsigned int j,
-		struct scsi_host_template *tpnt)
+		       struct scsi_host_template *tpnt, struct pci_dev *pdev)
 {
 	unsigned char irq, dma_channel, subversion, i, is_pci = 0;
 	unsigned char protocol_rev;
 	struct eata_info info;
 	char *bus_type, dma_name[16];
-	struct pci_dev *pdev;
 	/* Allowed DMA channels for ISA (0 indicates reserved) */
 	unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
 	struct Scsi_Host *shost;
@@ -1199,15 +1148,8 @@ static int port_detect(unsigned long port_base, unsigned int j,
 		    ("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
 		     name, irq);
 
-	if (is_pci) {
-		pdev = get_pci_dev(port_base);
-		if (!pdev)
-			printk
-			    ("%s: warning, failed to get pci_dev structure.\n",
-			     name);
-	} else
-		pdev = NULL;
-
+	if (is_pci && !pdev)
+		printk("%s: warning, failed to get pci_dev structure.\n", name);
 	if (pdev && (irq != pdev->irq)) {
 		printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq,
 		       pdev->irq);
@@ -1510,14 +1452,17 @@ static int option_setup(char *str)
 }
 
 static unsigned int port_probe(unsigned long port_base,
-			       struct scsi_host_template *tpnt)
+			       struct scsi_host_template *tpnt,
+			       struct pci_dev *pdev)
 {
 	int id;
 
 	id = ida_simple_get(&eata_ida, 0, MAX_BOARDS, GFP_KERNEL);
 	if (id >= 0) {
 		set_bit(id, eata_board_bitmap);
-		if (port_detect(port_base, id, tpnt))
+		if (pdev)
+			dev_set_drvdata(&pdev->dev, (void *)(long)id);
+		if (port_detect(port_base, id, tpnt, pdev))
 			return id;
 		clear_bit(id, eata_board_bitmap);
 		ida_simple_remove(&eata_ida, id);
@@ -1526,42 +1471,81 @@ static unsigned int port_probe(unsigned long port_base,
 	return -1;
 }
 
-static void add_pci_ports(void)
-{
-#if defined(CONFIG_PCI)
-	unsigned int addr, k;
-	struct pci_dev *dev = NULL;
-
-	for (k = 0; k < MAX_PCI; k++) {
+#ifdef CONFIG_PCI
+static int eata2x_pci_device_count;
 
-		if (!(dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev)))
-			break;
+static int eata2x_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int i, ret = -ENXIO;
+	resource_size_t addr;
+	unsigned long port_base;
+	struct scsi_host_template *tpnt = (void *)id->driver_data;
 
-		if (pci_enable_device(dev)) {
+	if (pci_enable_device(dev)) {
 #if defined(DEBUG_PCI_DETECT)
-			printk
-			    ("%s: detect, bus %d, devfn 0x%x, pci_enable_device failed.\n",
-			     driver_name, dev->bus->number, dev->devfn);
+		pr_warn("%s: detect, bus %d, devfn 0x%x, pci_enable_device failed.\n",
+			driver_name, dev->bus->number, dev->devfn);
 #endif
+		goto out_error;
+	}
 
-			continue;
-		}
-
-		addr = pci_resource_start(dev, 0);
-
+	addr = pci_resource_start(dev, 0);
+	port_base = addr + PCI_BASE_ADDRESS_0;
 #if defined(DEBUG_PCI_DETECT)
-		printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
-		       driver_name, k, dev->bus->number, dev->devfn, addr);
+	printk("%s: detect, bus %d, devfn 0x%x, addr 0x%x.\n",
+	       driver_name, dev->bus->number, dev->devfn, (unsigned int)addr);
 #endif
 
-		/* Order addresses according to rev_scan value */
-		io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
-		    addr + PCI_BASE_ADDRESS_0;
+	if (setup_done) {
+		/*
+		 * Handle kernel or module parameter
+		 * . probe board if its port is specified by user
+		 * . otherwise ignore the board
+		 */
+		for (i = 1; i < MAX_INT_PARAM; i++)
+			if (io_port[i] == port_base) {
+				io_port[i] = SKIP;
+				break;
+			}
+		if (i >= MAX_INT_PARAM)
+			goto out_disable_device;
+	}
+	if (port_probe(port_base, tpnt, dev) >= 0) {
+		eata2x_pci_device_count++;
+		return 0;
 	}
 
-	pci_dev_put(dev);
-#endif				/* end CONFIG_PCI */
+out_disable_device:
+	pci_disable_device(dev);
+out_error:
+	return ret;
+}
+
+static struct pci_device_id eata2x_tbl[] = {
+	{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_SCSI << 8, PCI_ANY_ID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, eata2x_tbl);
+
+static struct pci_driver eata2x_pci_driver = {
+	.name		= "eata",
+	.id_table	= eata2x_tbl,
+	.probe		= eata2x_pci_probe,
+};
+
+static int eata2x_probe_pci_devices(struct scsi_host_template *tpnt)
+{
+	eata2x_tbl[0].driver_data = (kernel_ulong_t)tpnt;
+	if (pci_probe && pci_register_driver(&eata2x_pci_driver))
+		pr_warn("eata2x: failed to register PCI device driver.\n");
+	return eata2x_pci_device_count;
+}
+#else /* CONFIG_PCI */
+static inline int eata2x_probe_pci_devices(struct scsi_host_template *tpnt)
+{
+	return 0;
 }
+#endif /* CONFIG_PCI */
 
 static int eata2x_detect(struct scsi_host_template *tpnt)
 {
@@ -1592,16 +1576,10 @@ static int eata2x_detect(struct scsi_host_template *tpnt)
 				io_port[k] = SKIP;
 		}
 
-	if (pci_probe) {
-		if (!setup_done)
-			add_pci_ports();
-		else
-			enable_pci_ports();
-	}
-
+	count += eata2x_probe_pci_devices(tpnt);
 	for (k = 0; io_port[k]; k++)
 		if (io_port[k] != SKIP &&
-		    port_probe(io_port[k], tpnt) >= 0)
+		    port_probe(io_port[k], tpnt, NULL) >= 0)
 			count++;
 
 	return count;
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux