linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tejun Heo <htejun@gmail.com>
To: Jeff Garzik <jeff@garzik.org>,
	IDE/ATA development list <linux-ide@vger.kernel.org>,
	Alan Cox <alan@lxorguk.ukuu.org.uk>
Subject: [PATCH 3/3] pata_amd: fix and improve cable detection
Date: Sat, 03 Nov 2007 00:22:24 +0900	[thread overview]
Message-ID: <472B40B0.6010702@gmail.com> (raw)
In-Reply-To: <472B4078.90009@gmail.com>

Cable detection on Nvidia PATA hosts is pathetic.  The current
nv_cable_detect() assumes that the native cable detection only
mistakes 80c as 40c but this isn't true.  On ASUS A8N-E, cable
register almost always says 80c is attached and it also manages to
trick the drive that the cable is 80c.

Also, BIOS checking, and before the get_acpi_cbl() update ACPI
checking too, in the current implementation were useless because they
read the setting _after_ reset is complete which forces the host into
PIO0, so they never triggered.

Reimplement nv_cable_detect() such that...

* BIOS setting is cached during controller initialization and restored
  on driver detach.

* BIOS and ACPI cable results are considered more important.  Native
  cable bits are used iff both BIOS and ACPI results are PATA_UNK.  If
  BIOS or ACPI indicates 80c, it's 80c.  If none indicates 80c, it's
  40c.

This change makes cable detection work correctly all the time on ASUS
A8N-E and should also work well on other NV PATA configurations.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/pata_amd.c |  112 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 90 insertions(+), 22 deletions(-)

Index: work/drivers/ata/pata_amd.c
===================================================================
--- work.orig/drivers/ata/pata_amd.c
+++ work/drivers/ata/pata_amd.c
@@ -255,26 +255,69 @@ static int nv_cable_detect(struct ata_po
 {
 	static const u8 bitmask[2] = {0x03, 0x0C};
 	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+	struct ata_device *dev;
+	char acpi_str[32] = "";
+	int native_cbl, bios_cbl, acpi_cbl, verdict;
+	u32 saved_udma, udma;
 	u8 ata66;
-	u16 udma;
-	int cbl;
 
+	/* native method, this is usually broken */
 	pci_read_config_byte(pdev, 0x52, &ata66);
 	if (ata66 & bitmask[ap->port_no])
-		cbl = ATA_CBL_PATA80;
+		native_cbl = ATA_CBL_PATA80;
 	else
-		cbl = ATA_CBL_PATA40;
+		native_cbl = ATA_CBL_PATA40;
 
- 	/* We now have to double check because the Nvidia boxes BIOS
- 	   doesn't always set the cable bits but does set mode bits */
- 	pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma);
- 	if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400)
-		cbl = ATA_CBL_PATA80;
-	/* And a triple check across suspend/resume with ACPI around */
-	if ((ap->pflags & ATA_PFLAG_INIT_GTM_VALID) &&
-	    ata_acpi_cbl(ap, &ap->acpi_init_gtm) == ATA_CBL_PATA80)
-		cbl = ATA_CBL_PATA80;
-	return cbl;
+	/* peek BIOS configuration */
+	bios_cbl = ATA_CBL_PATA_UNK;
+
+	saved_udma = udma = (unsigned long)ap->host->private_data;
+	if (ap->port_no == 0)
+		udma >>= 16;
+
+	ata_link_for_each_dev(dev, &ap->link) {
+		u32 tmp = udma;
+
+		if (dev->devno == 0)
+			tmp >>= 8;
+
+		if (!ata_dev_enabled(dev) || (tmp & 0xC0) != 0xC0)
+			continue;
+
+		if (tmp & 0x4)
+			bios_cbl = ATA_CBL_PATA80;
+		else
+			bios_cbl = ATA_CBL_PATA40;
+	}
+
+	/* and ACPI configuration */
+	acpi_cbl = ATA_CBL_PATA_UNK;
+	if (ap->pflags & ATA_PFLAG_INIT_GTM_VALID)
+		acpi_cbl = ata_acpi_cbl(ap, &ap->acpi_init_gtm);
+
+	/* Three values collected.  Native value is usually
+	 * untrustworthy.  Consider it iff both BIOS and ACPI values
+	 * are unknown; otherwise, choose higher of the two.
+	 */
+	if (bios_cbl == ATA_CBL_PATA_UNK && acpi_cbl == ATA_CBL_PATA_UNK)
+		verdict = native_cbl;
+	else if (bios_cbl == ATA_CBL_PATA80 || acpi_cbl == ATA_CBL_PATA80)
+		verdict = ATA_CBL_PATA80;
+	else
+		verdict = ATA_CBL_PATA40;
+
+	if (ap->pflags & ATA_PFLAG_INIT_GTM_VALID)
+		snprintf(acpi_str, sizeof(acpi_str), " (%u:%u:0x%x)",
+			 ap->acpi_init_gtm.drive[0].dma,
+			 ap->acpi_init_gtm.drive[1].dma,
+			 ap->acpi_init_gtm.flags);
+
+	ata_port_printk(ap, KERN_DEBUG, "nv_cable_detect: native=%d (0x%x) "
+			"BIOS=%d (0x%x) ACPI=%d%s verdict=%d\n",
+			native_cbl, ata66, bios_cbl, saved_udma, acpi_cbl,
+			acpi_str, verdict);
+
+	return verdict;
 }
 
 /**
@@ -314,6 +357,14 @@ static void nv133_set_dmamode(struct ata
 	timing_setup(ap, adev, 0x50, adev->dma_mode, 4);
 }
 
+static void nv_host_stop(struct ata_host *host)
+{
+	u32 udma = (unsigned long)host->private_data;
+
+	/* restore PCI config register 0x60 */
+	pci_write_config_dword(to_pci_dev(host->dev), 0x60, udma);
+}
+
 static struct scsi_host_template amd_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -495,6 +546,7 @@ static struct ata_port_operations nv100_
 	.irq_on		= ata_irq_on,
 
 	.port_start	= ata_sff_port_start,
+	.host_stop	= nv_host_stop,
 };
 
 static struct ata_port_operations nv133_port_ops = {
@@ -528,6 +580,7 @@ static struct ata_port_operations nv133_
 	.irq_on		= ata_irq_on,
 
 	.port_start	= ata_sff_port_start,
+	.host_stop	= nv_host_stop,
 };
 
 static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -614,7 +667,8 @@ static int amd_init_one(struct pci_dev *
 			.port_ops = &amd100_port_ops
 		}
 	};
-	const struct ata_port_info *ppi[] = { NULL, NULL };
+	struct ata_port_info pi;
+	const struct ata_port_info *ppi[] = { &pi, NULL };
 	static int printed_version;
 	int type = id->driver_data;
 	u8 fifo;
@@ -628,6 +682,19 @@ static int amd_init_one(struct pci_dev *
 	if (type == 1 && pdev->revision > 0x7)
 		type = 2;
 
+	/* Serenade ? */
+	if (type == 5 && pdev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
+			 pdev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
+		type = 6;	/* UDMA 100 only */
+
+	/*
+	 * Okay, type is determined now.  Apply type-specific workarounds.
+	 */
+	pi = info[type];
+
+	if (type < 3)
+		ata_pci_clear_simplex(pdev);
+
 	/* Check for AMD7411 */
 	if (type == 3)
 		/* FIFO is broken */
@@ -635,16 +702,17 @@ static int amd_init_one(struct pci_dev *
 	else
 		pci_write_config_byte(pdev, 0x41, fifo | 0xF0);
 
-	/* Serenade ? */
-	if (type == 5 && pdev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
-			 pdev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
-		type = 6;	/* UDMA 100 only */
+	/* Cable detection on Nvidia chips doesn't work too well,
+	 * cache BIOS programmed UDMA mode.
+	 */
+	if (type == 7 || type == 8) {
+		u32 udma;
 
-	if (type < 3)
-		ata_pci_clear_simplex(pdev);
+		pci_read_config_dword(pdev, 0x60, &udma);
+		pi.private_data = (void *)(unsigned long)udma;
+	}
 
 	/* And fire it up */
-	ppi[0] = &info[type];
 	return ata_pci_init_one(pdev, ppi);
 }
 

  reply	other threads:[~2007-11-02 15:22 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-02 15:20 [PATCH 1/3] libata: implement dev->acpi_init_gtm Tejun Heo
2007-11-02 15:21 ` [PATCH 2/3] libata: extend ata_acpi_cbl_80wire() and fix cable detection in pata_via and pata_amd Tejun Heo
2007-11-02 15:22   ` Tejun Heo [this message]
2007-11-02 15:44     ` [PATCH 3/3] pata_amd: fix and improve cable detection Alan Cox
2007-11-02 22:22       ` Tejun Heo
2007-11-03  0:10         ` Alan Cox
2007-11-03  0:35           ` Tejun Heo
2007-11-03  0:42           ` Tejun Heo
2007-11-02 15:42   ` [PATCH 2/3] libata: extend ata_acpi_cbl_80wire() and fix cable detection in pata_via and pata_amd Alan Cox
2007-11-02 22:18     ` Tejun Heo
2007-11-02 23:45       ` Alan Cox
2007-11-03  0:46         ` Tejun Heo
2007-11-03  1:12           ` Alan Cox
2007-11-03  1:16             ` Tejun Heo
2007-11-03  1:23               ` Alan Cox
2007-11-03  7:03                 ` Tejun Heo
2007-11-03  0:57     ` Bartlomiej Zolnierkiewicz
2007-11-03  1:12       ` Bartlomiej Zolnierkiewicz
2007-11-02 15:36 ` [PATCH 1/3] libata: implement dev->acpi_init_gtm Jeff Garzik
2007-11-02 22:12   ` Tejun Heo
2007-11-03  7:10     ` Tejun Heo
2007-11-03 12:33       ` Jeff Garzik
2007-11-02 15:44 ` Alan Cox
2007-11-02 22:08   ` Tejun Heo

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=472B40B0.6010702@gmail.com \
    --to=htejun@gmail.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=jeff@garzik.org \
    --cc=linux-ide@vger.kernel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).