Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ec7df04 authored by Alan Cox's avatar Alan Cox Committed by Tejun Heo
Browse files

[PATCH] libata: rework legacy handling to remove much of the cruft



Kill host_set->next
Fix simplex support
Allow per platform setting of IDE legacy bases

Some of this can be tidied further later on, in particular all the
legacy port gunge belongs as a PCI quirk/PCI header decode to understand
the special legacy IDE rules in the PCI spec.

Longer term Jeff also wants to move the request_irq/free_irq out of core
which will make this even cleaner.

tj: folded in three followup patches - ata_piix-fix, broken-arch-fix
and fix-new-legacy-handling, and separated per-dev xfermask into
separate patch preceding this one.  Folded in fixes are...

* ata_piix-fix: fix build failure due to host_set->next removal
* broken-arch-fix: add missing include/asm-*/libata-portmap.h
* fix-new-legacy-handling:
	* In ata_pci_init_legacy_port(), probe_num was incorrectly
          incremented during initialization of the secondary port and
          probe_ent->n_ports was incorrectly fixed to 1.

	* Both legacy ports ended up having the same hard_port_no.

	* When printing port information, both legacy ports printed
	  the first irq.

Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
parent 37deecb5
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -932,8 +932,6 @@ static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)

static void piix_host_stop(struct ata_host_set *host_set)
{
	if (host_set->next == NULL)
		kfree(host_set->private_data);
	ata_host_stop(host_set);
}

+64 −71
Original line number Diff line number Diff line
@@ -867,44 +867,55 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int


static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
				struct ata_port_info *port, int port_num)
				struct ata_port_info **port, int port_mask)
{
	struct ata_probe_ent *probe_ent;
	unsigned long bmdma;
	unsigned long bmdma = pci_resource_start(pdev, 4);

	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port);
	int port_num = 0;

	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
	if (!probe_ent)
		return NULL;

	probe_ent->legacy_mode = 1;
	probe_ent->n_ports = 1;
	probe_ent->hard_port_no = port_num;
	probe_ent->private_data = port->private_data;
	probe_ent->hard_port_no = 0;
	probe_ent->private_data = port[0]->private_data;

	switch(port_num)
	{
		case 0:
	if (port_mask & ATA_PORT_PRIMARY) {
		probe_ent->irq = 14;
			probe_ent->port[0].cmd_addr = 0x1f0;
			probe_ent->port[0].altstatus_addr =
			probe_ent->port[0].ctl_addr = 0x3f6;
			break;
		case 1:
			probe_ent->irq = 15;
			probe_ent->port[0].cmd_addr = 0x170;
			probe_ent->port[0].altstatus_addr =
			probe_ent->port[0].ctl_addr = 0x376;
			break;
	}

	bmdma = pci_resource_start(pdev, 4);
	if (bmdma != 0) {
		bmdma += 8 * port_num;
		probe_ent->port[port_num].cmd_addr = ATA_PRIMARY_CMD;
		probe_ent->port[port_num].altstatus_addr =
		probe_ent->port[port_num].ctl_addr = ATA_PRIMARY_CTL;
		if (bmdma) {
			probe_ent->port[0].bmdma_addr = bmdma;
			if (inb(bmdma + 2) & 0x80)
				probe_ent->host_set_flags |= ATA_HOST_SIMPLEX;
		}
	ata_std_ports(&probe_ent->port[0]);
		ata_std_ports(&probe_ent->port[port_num]);
		port_num ++;
	}
	if (port_mask & ATA_PORT_SECONDARY) {
		if (port_num == 1)
			probe_ent->irq2 = 15;
		else {
			/* Secondary only. IRQ 15 only and "first" port is port 1 */
			probe_ent->irq = 15;
			probe_ent->hard_port_no = 1;
		}
		probe_ent->port[port_num].cmd_addr = ATA_SECONDARY_CMD;
		probe_ent->port[port_num].altstatus_addr =
		probe_ent->port[port_num].ctl_addr = ATA_SECONDARY_CTL;
		if (bmdma) {
			probe_ent->port[port_num].bmdma_addr = bmdma + 8;
			if (inb(bmdma + 10) & 0x80)
				probe_ent->host_set_flags |= ATA_HOST_SIMPLEX;
		}
		ata_std_ports(&probe_ent->port[port_num]);
		port_num ++;
	}

	probe_ent->n_ports = port_num;

	return probe_ent;
}
@@ -924,6 +935,10 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
 *	regions, sets the dma mask, enables bus master mode, and calls
 *	ata_device_add()
 *
 *	ASSUMPTION:
 *	Nobody makes a single channel controller that appears solely as
 *	the secondary legacy port on PCI.
 *
 *	LOCKING:
 *	Inherited from PCI layer (may sleep).
 *
@@ -934,7 +949,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
		      unsigned int n_ports)
{
	struct ata_probe_ent *probe_ent = NULL, *probe_ent2 = NULL;
	struct ata_probe_ent *probe_ent = NULL;
	struct ata_port_info *port[2];
	u8 tmp8, mask;
	unsigned int legacy_mode = 0;
@@ -983,35 +998,34 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
		goto err_out;
	}

	/* FIXME: Should use platform specific mappers for legacy port ranges */
	if (legacy_mode) {
		if (!request_region(0x1f0, 8, "libata")) {
		if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) {
			struct resource *conflict, res;
			res.start = 0x1f0;
			res.end = 0x1f0 + 8 - 1;
			res.start = ATA_PRIMARY_CMD;
			res.end = ATA_PRIMARY_CMD + 8 - 1;
			conflict = ____request_resource(&ioport_resource, &res);
			if (!strcmp(conflict->name, "libata"))
				legacy_mode |= (1 << 0);
				legacy_mode |= ATA_PORT_PRIMARY;
			else {
				disable_dev_on_err = 0;
				printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n");
				printk(KERN_WARNING "ata: 0x%0X IDE port busy\n", ATA_PRIMARY_CMD);
			}
		} else
			legacy_mode |= (1 << 0);
			legacy_mode |= ATA_PORT_PRIMARY;

		if (!request_region(0x170, 8, "libata")) {
		if (!request_region(ATA_SECONDARY_CMD, 8, "libata")) {
			struct resource *conflict, res;
			res.start = 0x170;
			res.end = 0x170 + 8 - 1;
			res.start = ATA_SECONDARY_CMD;
			res.end = ATA_SECONDARY_CMD + 8 - 1;
			conflict = ____request_resource(&ioport_resource, &res);
			if (!strcmp(conflict->name, "libata"))
				legacy_mode |= (1 << 1);
				legacy_mode |= ATA_PORT_SECONDARY;
			else {
				disable_dev_on_err = 0;
				printk(KERN_WARNING "ata: 0x170 IDE port busy\n");
				printk(KERN_WARNING "ata: 0x%X IDE port busy\n", ATA_SECONDARY_CMD);
			}
		} else
			legacy_mode |= (1 << 1);
			legacy_mode |= ATA_PORT_SECONDARY;
	}

	/* we have legacy mode, but all ports are unavailable */
@@ -1029,17 +1043,14 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
		goto err_out_regions;

	if (legacy_mode) {
		if (legacy_mode & (1 << 0))
			probe_ent = ata_pci_init_legacy_port(pdev, port[0], 0);
		if (legacy_mode & (1 << 1))
			probe_ent2 = ata_pci_init_legacy_port(pdev, port[1], 1);
		probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode);
	} else {
		if (n_ports == 2)
			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
		else
			probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY);
	}
	if (!probe_ent && !probe_ent2) {
	if (!probe_ent) {
		rc = -ENOMEM;
		goto err_out_regions;
	}
@@ -1047,35 +1058,17 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
	pci_set_master(pdev);

	/* FIXME: check ata_device_add return */
	if (legacy_mode) {
		struct device *dev = &pdev->dev;
		struct ata_host_set *host_set = NULL;

		if (legacy_mode & (1 << 0)) {
			ata_device_add(probe_ent);
			host_set = dev_get_drvdata(dev);
		}

		if (legacy_mode & (1 << 1)) {
			ata_device_add(probe_ent2);
			if (host_set) {
				host_set->next = dev_get_drvdata(dev);
				dev_set_drvdata(dev, host_set);
			}
		}
	} else
	ata_device_add(probe_ent);

	kfree(probe_ent);
	kfree(probe_ent2);

	return 0;

err_out_regions:
	if (legacy_mode & (1 << 0))
		release_region(0x1f0, 8);
	if (legacy_mode & (1 << 1))
		release_region(0x170, 8);
	if (legacy_mode & ATA_PORT_PRIMARY)
		release_region(ATA_PRIMARY_CMD, 8);
	if (legacy_mode & ATA_PORT_SECONDARY)
		release_region(ATA_SECONDARY_CMD, 8);
	pci_release_regions(pdev);
err_out:
	if (disable_dev_on_err)
+36 −23
Original line number Diff line number Diff line
@@ -5223,8 +5223,9 @@ void ata_port_init(struct ata_port *ap, struct ata_host_set *host_set,
	ap->host_set = host_set;
	ap->dev = ent->dev;
	ap->port_no = port_no;
	ap->hard_port_no =
		ent->legacy_mode ? ent->hard_port_no : port_no;
	ap->hard_port_no = port_no;
	if (ent->legacy_mode)
		ap->hard_port_no += ent->hard_port_no;
	ap->pio_mask = ent->pio_mask;
	ap->mwdma_mask = ent->mwdma_mask;
	ap->udma_mask = ent->udma_mask;
@@ -5400,6 +5401,7 @@ int ata_device_add(const struct ata_probe_ent *ent)
	ata_host_set_init(host_set, dev, ent->host_set_flags, ent->port_ops);
	host_set->n_ports = ent->n_ports;
	host_set->irq = ent->irq;
	host_set->irq2 = ent->irq2;
	host_set->mmio_base = ent->mmio_base;
	host_set->private_data = ent->private_data;

@@ -5407,11 +5409,16 @@ int ata_device_add(const struct ata_probe_ent *ent)
	for (i = 0; i < host_set->n_ports; i++) {
		struct ata_port *ap;
		unsigned long xfer_mode_mask;
		int irq_line = ent->irq;

		ap = ata_port_add(ent, host_set, i);
		if (!ap)
			goto err_out;

		/* Report the secondary IRQ for second channel legacy */
		if (i == 1 && ent->irq2)
			irq_line = ent->irq2;

		host_set->ports[i] = ap;
		xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) |
				(ap->mwdma_mask << ATA_SHIFT_MWDMA) |
@@ -5419,20 +5426,20 @@ int ata_device_add(const struct ata_probe_ent *ent)

		/* print per-port info to dmesg */
		ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%lX "
				"ctl 0x%lX bmdma 0x%lX irq %lu\n",
				"ctl 0x%lX bmdma 0x%lX irq %d\n",
				ap->flags & ATA_FLAG_SATA ? 'S' : 'P',
				ata_mode_string(xfer_mode_mask),
				ap->ioaddr.cmd_addr,
				ap->ioaddr.ctl_addr,
				ap->ioaddr.bmdma_addr,
				ent->irq);
				irq_line);

		ata_chk_status(ap);
		host_set->ops->irq_clear(ap);
		ata_eh_freeze_port(ap);	/* freeze port before requesting IRQ */
	}

	/* obtain irq, that is shared between channels */
	/* obtain irq, that may be shared between channels */
	rc = request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags,
			 DRV_NAME, host_set);
	if (rc) {
@@ -5441,6 +5448,21 @@ int ata_device_add(const struct ata_probe_ent *ent)
		goto err_out;
	}

	/* do we have a second IRQ for the other channel, eg legacy mode */
	if (ent->irq2) {
		/* We will get weird core code crashes later if this is true
		   so trap it now */
		BUG_ON(ent->irq == ent->irq2);

		rc = request_irq(ent->irq2, ent->port_ops->irq_handler, ent->irq_flags,
			 DRV_NAME, host_set);
		if (rc) {
			dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n",
				   ent->irq2, rc);
			goto err_out_free_irq;
		}
	}

	/* perform each probe synchronously */
	DPRINTK("probe begin\n");
	for (i = 0; i < host_set->n_ports; i++) {
@@ -5514,6 +5536,8 @@ int ata_device_add(const struct ata_probe_ent *ent)
	VPRINTK("EXIT, returning %u\n", ent->n_ports);
	return ent->n_ports; /* success */

err_out_free_irq:
	free_irq(ent->irq, host_set);
err_out:
	for (i = 0; i < host_set->n_ports; i++) {
		struct ata_port *ap = host_set->ports[i];
@@ -5605,6 +5629,8 @@ void ata_host_set_remove(struct ata_host_set *host_set)
		ata_port_detach(host_set->ports[i]);

	free_irq(host_set->irq, host_set);
	if (host_set->irq2)
		free_irq(host_set->irq2, host_set);

	for (i = 0; i < host_set->n_ports; i++) {
		struct ata_port *ap = host_set->ports[i];
@@ -5614,10 +5640,11 @@ void ata_host_set_remove(struct ata_host_set *host_set)
		if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) {
			struct ata_ioports *ioaddr = &ap->ioaddr;

			if (ioaddr->cmd_addr == 0x1f0)
				release_region(0x1f0, 8);
			else if (ioaddr->cmd_addr == 0x170)
				release_region(0x170, 8);
			/* FIXME: Add -ac IDE pci mods to remove these special cases */
			if (ioaddr->cmd_addr == ATA_PRIMARY_CMD)
				release_region(ATA_PRIMARY_CMD, 8);
			else if (ioaddr->cmd_addr == ATA_SECONDARY_CMD)
				release_region(ATA_SECONDARY_CMD, 8);
		}

		scsi_host_put(ap->host);
@@ -5735,11 +5762,8 @@ void ata_pci_remove_one (struct pci_dev *pdev)
{
	struct device *dev = pci_dev_to_dev(pdev);
	struct ata_host_set *host_set = dev_get_drvdata(dev);
	struct ata_host_set *host_set2 = host_set->next;

	ata_host_set_remove(host_set);
	if (host_set2)
		ata_host_set_remove(host_set2);

	pci_release_regions(pdev);
	pci_disable_device(pdev);
@@ -5807,14 +5831,6 @@ int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
	if (rc)
		return rc;

	if (host_set->next) {
		rc = ata_host_set_suspend(host_set->next, mesg);
		if (rc) {
			ata_host_set_resume(host_set);
			return rc;
		}
	}

	ata_pci_device_do_suspend(pdev, mesg);

	return 0;
@@ -5826,9 +5842,6 @@ int ata_pci_device_resume(struct pci_dev *pdev)

	ata_pci_device_do_resume(pdev);
	ata_host_set_resume(host_set);
	if (host_set->next)
		ata_host_set_resume(host_set->next);

	return 0;
}
#endif /* CONFIG_PCI */
+1 −0
Original line number Diff line number Diff line
#include <asm-generic/libata-portmap.h>
+12 −0
Original line number Diff line number Diff line
#ifndef __ASM_GENERIC_LIBATA_PORTMAP_H
#define __ASM_GENERIC_LIBATA_PORTMAP_H

#define ATA_PRIMARY_CMD		0x1F0
#define ATA_PRIMARY_CTL		0x3F6
#define ATA_PRIMARY_IRQ		14

#define ATA_SECONDARY_CMD	0x170
#define ATA_SECONDARY_CTL	0x376
#define ATA_SECONDARY_IRQ	15

#endif
Loading