All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Collins <ben.collins@ubuntu.com>
To: Alan <alan@lxorguk.ukuu.org.uk>
Cc: Ralf Baechle <ralf@linux-mips.org>, Andrew Morton <akpm@osdl.org>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH] PCI legacy resource fix
Date: Sat, 09 Dec 2006 03:12:11 -0500	[thread overview]
Message-ID: <1165651931.7443.377.camel@gullible> (raw)
In-Reply-To: <20061209024627.6bb11a58@localhost.localdomain>

On Sat, 2006-12-09 at 02:46 +0000, Alan wrote:
> > Checking the patch, my problem is that the old way, all BAR's were being
> > set at start = end = flags = 0. The patch makes it set all the BAR's to
> 
> Yes the old quirk used to blank the resources as the values on the chip
> are undefined and random. This gives you corrupt resource trees and needs
> hacks in the drivers as well
> 
> > the normal values. This is what it looks like in lspci, pre this patch:
> > 
> >         Region 0: I/O ports at <unassigned>
> >         Region 1: I/O ports at <unassigned>
> >         Region 2: I/O ports at <unassigned>
> >         Region 3: I/O ports at <unassigned>
> 
> Then your device is in legacy mode, or was disabled
>  
> > So my device is not running in compatibility mode, and should not have
> 
> The paste you have their shows that it almost certainly is in legacy mode.
> 
> > the BAR's set, as Alan's patch does.
> 
> Dump the class code and other bits during boot check how your device is
> seen (native v legacy/compatibility) and whether the fixup logic
> triggers. It should only trigger for legacy devices.

You're right, I was reading this backwards.

Thing is, if I disable the quirk, ata_piix works correctly.

So I think maybe the problem is the logic here. Having the PIIX quirk
pre-reserve SATA ports that are on legacy BAR's just doesn't seem to
make sense unless libata is going to recognize that. I think it's just
an accident that it worked before.

Looking at ata_pci_init_one(), it does check for this:

        if (legacy_mode) {
                if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) {
                        struct resource *conflict, res;
                        res.start = ATA_PRIMARY_CMD;
                        res.end = ATA_PRIMARY_CMD + 8 - 1;
                        conflict = ____request_resource(&ioport_resource, &res);
                        while (conflict->child)
                                conflict = ____request_resource(conflict, &res);
                        if (!strcmp(conflict->name, "libata"))
                                legacy_mode |= ATA_PORT_PRIMARY;

My controller is in legacy mode, however, it never gets to here because
of this call, just before this block of code:

        rc = pci_request_regions(pdev, DRV_NAME);
        if (rc) {
                disable_dev_on_err = 0;
                goto err_out;
        }

So, I wrote up this patch that made things work for me. It also should
make ata_pci_init_one() work with mixed legacy/native ports on the same
controller (if there are such things). My controller is port0=SATA,
port1=PATA.

diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 10ee22a..8897eba 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -852,11 +852,14 @@ #ifdef CONFIG_PCI
 struct ata_probe_ent *
 ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports)
 {
-	struct ata_probe_ent *probe_ent =
-		ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
+	struct ata_probe_ent *probe_ent;
 	int p = 0;
 	unsigned long bmdma;
 
+	if (!(ports & (ATA_PORT_PRIMARY|ATA_PORT_SECONDARY)))
+		return NULL;
+
+	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
 	if (!probe_ent)
 		return NULL;
 
@@ -906,6 +909,9 @@ static struct ata_probe_ent *ata_pci_ini
 	struct ata_probe_ent *probe_ent;
 	unsigned long bmdma = pci_resource_start(pdev, 4);
 
+	if (!(port_mask & (ATA_PORT_PRIMARY|ATA_PORT_SECONDARY)))
+		return NULL;
+
 	probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
 	if (!probe_ent)
 		return NULL;
@@ -979,10 +985,10 @@ static struct ata_probe_ent *ata_pci_ini
 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;
+	struct ata_probe_ent *probe_ent_legacy = NULL;
+	struct ata_probe_ent *probe_ent_native = NULL;
 	struct ata_port_info *port[2];
-	u8 mask;
-	unsigned int legacy_mode = 0;
+	unsigned int legacy_mode = 0, legacy_probe = 0, native_probe = 0;
 	int disable_dev_on_err = 1;
 	int rc;
 
@@ -1011,29 +1017,32 @@ int ata_pci_init_one (struct pci_dev *pd
 	if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
 		u8 tmp8;
 
-		/* TODO: What if one channel is in native mode ... */
 		pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8);
-		mask = (1 << 2) | (1 << 0);
-		if ((tmp8 & mask) != mask)
-			legacy_mode = (1 << 3);
+
+		if ((tmp8 & ATA_PORT_PRIMARY_LEGACY) == 0)
+			legacy_mode |= ATA_PORT_PRIMARY;
+		if ((tmp8 & ATA_PORT_SECONDARY_LEGACY) == 0)
+			legacy_mode |= ATA_PORT_SECONDARY;
+
 #if defined(CONFIG_NO_ATA_LEGACY)
 		/* Some platforms with PCI limits cannot address compat
 		   port space. In that case we punt if their firmware has
 		   left a device in compatibility mode */
 		if (legacy_mode) {
-			printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n");
+			printk(KERN_ERR "ata: Compatibility mode ATA is not " \
+					"supported on this platform, skipping.\n");
 			return -EOPNOTSUPP;
 		}
 #endif
 	}
 
-	rc = pci_request_regions(pdev, DRV_NAME);
+	rc = pci_request_region(pdev, 4, DRV_NAME);
 	if (rc) {
 		disable_dev_on_err = 0;
 		goto err_out;
 	}
 
-	if (legacy_mode) {
+	if (legacy_mode & ATA_PORT_PRIMARY) {
 		if (!request_region(ATA_PRIMARY_CMD, 8, "libata")) {
 			struct resource *conflict, res;
 			res.start = ATA_PRIMARY_CMD;
@@ -1042,7 +1051,7 @@ #endif
 			while (conflict->child)
 				conflict = ____request_resource(conflict, &res);
 			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_PRIMARY;
+				legacy_probe |= ATA_PORT_PRIMARY;
 			else {
 				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \
@@ -1051,8 +1060,19 @@ #endif
 						    conflict->name);
 			}
 		} else
-			legacy_mode |= ATA_PORT_PRIMARY;
+			legacy_probe |= ATA_PORT_PRIMARY;
+	} else {
+		if (!pci_request_region(pdev, 0, DRV_NAME)) {
+			if (!pci_request_region(pdev, 1, DRV_NAME))
+				native_probe |= ATA_PORT_PRIMARY;
+			else {
+				disable_dev_on_err = 0;
+				pci_release_region(pdev, 0);
+			}
+		}
+	}
 
+	if (legacy_mode & ATA_PORT_SECONDARY) {
 		if (!request_region(ATA_SECONDARY_CMD, 8, "libata")) {
 			struct resource *conflict, res;
 			res.start = ATA_SECONDARY_CMD;
@@ -1061,7 +1081,7 @@ #endif
 			while (conflict->child)
 				conflict = ____request_resource(conflict, &res);
 			if (!strcmp(conflict->name, "libata"))
-				legacy_mode |= ATA_PORT_SECONDARY;
+				legacy_probe |= ATA_PORT_SECONDARY;
 			else {
 				disable_dev_on_err = 0;
 				printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \
@@ -1070,11 +1090,20 @@ #endif
 						    conflict->name);
 			}
 		} else
-			legacy_mode |= ATA_PORT_SECONDARY;
-	}
+			legacy_probe |= ATA_PORT_SECONDARY;
+	} else if (n_ports > 1) {
+		if (!pci_request_region(pdev, 2, DRV_NAME)) {
+			if (!pci_request_region(pdev, 3, DRV_NAME))
+				native_probe = ATA_PORT_SECONDARY;
+			else {
+				disable_dev_on_err = 0;
+				pci_release_region(pdev, 2);
+			}
+                }
+        }
 
-	/* we have legacy mode, but all ports are unavailable */
-	if (legacy_mode == (1 << 3)) {
+	/* Didn't enable any ports */
+	if (!legacy_probe && !native_probe) {
 		rc = -EBUSY;
 		goto err_out_regions;
 	}
@@ -1087,38 +1116,58 @@ #endif
 	if (rc)
 		goto err_out_regions;
 
-	if (legacy_mode) {
-		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_ent_legacy = ata_pci_init_legacy_port(pdev, port, legacy_probe);
+	probe_ent_native = ata_pci_init_native_mode(pdev, port, native_probe);
+
+	if (!probe_ent_legacy && !probe_ent_native) {
 		rc = -ENOMEM;
 		goto err_out_regions;
 	}
 
 	pci_set_master(pdev);
 
-	if (!ata_device_add(probe_ent)) {
+	if (probe_ent_legacy) {
+		if (!ata_device_add(probe_ent_legacy)) {
+			printk(KERN_WARNING "ata: Failed to add legacy device(s)\n");
+			kfree(probe_ent_legacy);
+			probe_ent_legacy = NULL;
+		}
+	}
+	if (probe_ent_native) {
+		if (!ata_device_add(probe_ent_native)) {
+			printk(KERN_WARNING "ata: Failed to add native device(s)\n");
+			kfree(probe_ent_native);
+			probe_ent_native = NULL;
+		}
+	}
+
+	/* Did both fail? If so, no devices to see here. */
+	if (!probe_ent_native && !probe_ent_legacy) {
 		rc = -ENODEV;
-		goto err_out_ent;
+		goto err_out_regions;
 	}
 
-	kfree(probe_ent);
+	kfree(probe_ent_legacy);
+	kfree(probe_ent_native);
 
 	return 0;
 
-err_out_ent:
-	kfree(probe_ent);
 err_out_regions:
-	if (legacy_mode & ATA_PORT_PRIMARY)
+	if (legacy_probe & ATA_PORT_PRIMARY)
 		release_region(ATA_PRIMARY_CMD, 8);
-	if (legacy_mode & ATA_PORT_SECONDARY)
+	else if (native_probe & ATA_PORT_PRIMARY) {
+		pci_release_region(pdev, 0);
+		pci_release_region(pdev, 1);
+	}
+
+	if (legacy_probe & ATA_PORT_SECONDARY)
 		release_region(ATA_SECONDARY_CMD, 8);
-	pci_release_regions(pdev);
+	else if (native_probe & ATA_PORT_SECONDARY) {
+		pci_release_region(pdev, 2);
+		pci_release_region(pdev, 3);
+	}
+
+	pci_release_region(pdev, 4);
 err_out:
 	if (disable_dev_on_err)
 		pci_disable_device(pdev);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index ab27548..1f0a200 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -260,6 +260,10 @@ enum {
 	ATA_PORT_PRIMARY	= (1 << 0),
 	ATA_PORT_SECONDARY	= (1 << 1),
 
+	/* masks for port legacy mode */
+	ATA_PORT_PRIMARY_LEGACY	= (1 << 0),
+	ATA_PORT_SECONDARY_LEGACY = (1 << 2),
+
 	/* ering size */
 	ATA_ERING_SIZE		= 32,
 


  reply	other threads:[~2006-12-09  8:12 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-12-06 13:41 [PATCH] PCI legacy resource fix Ralf Baechle
2006-12-06 13:57 ` Alan
2006-12-09  0:46 ` Ben Collins
2006-12-09  1:25   ` Ralf Baechle
2006-12-09  2:13     ` Ben Collins
2006-12-09  2:46       ` Alan
2006-12-09  8:12         ` Ben Collins [this message]
2006-12-09 13:14           ` Alan
2006-12-09 15:50             ` Ben Collins
2006-12-09 13:15           ` Alan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1165651931.7443.377.camel@gullible \
    --to=ben.collins@ubuntu.com \
    --cc=akpm@osdl.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ralf@linux-mips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.