aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/ahci.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2008-02-06 01:13:51 -0500
committerJeff Garzik <jeff@garzik.org>2008-02-06 06:59:27 -0500
commit837f5f8fb98d4357d49e9631c9ee2815f3c328ca (patch)
tree25c24b611fd66b0599d8145716513339d42a7bb3 /drivers/ata/ahci.c
parentf351b2d638c3cb0b95adde3549b7bfaf3f991dfa (diff)
ahci: fix CAP.NP and PI handling
AHCI uses CAP.NP to indicate the number of ports and PI to tell which ports are enabled. The only requirement is that the number of ports indicated by CAP.NP should equal or be higher than the number of enabled ports in PI. CAP.NP and PI carry duplicate information and there have been some interesting cases. Some early AHCI controllers didn't set PI at all and just implement from port 0 to CAP.NP. An ICH8 board which wired four out of six available ports had 3 (4 ports) for CAP.NP and 0x33 for PI. While ESB2 has less bits set in PI than the value in CAP.NP. Till now, ahci driver assumed that PI is invalid if it doesn't match CAP.NP exactly. This violates AHCI standard and the driver ends up accessing unmimplemented ports on ESB2. This patch updates CAP.NP and PI handling such that PI can have less number of bits set than indicated in CAP.NP and the highest port is determined as the maximum port of what CAP.NP and PI indicate. Signed-off-by: Tejun Heo <htejun@gmail.com> Cc: Jan Beulich <jbeulich@novell.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r--drivers/ata/ahci.c35
1 files changed, 19 insertions, 16 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 27c8d56111c2..29e71bddd6ff 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -679,24 +679,20 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
679 679
680 /* cross check port_map and cap.n_ports */ 680 /* cross check port_map and cap.n_ports */
681 if (port_map) { 681 if (port_map) {
682 u32 tmp_port_map = port_map; 682 int map_ports = 0;
683 int n_ports = ahci_nr_ports(cap);
684 683
685 for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { 684 for (i = 0; i < AHCI_MAX_PORTS; i++)
686 if (tmp_port_map & (1 << i)) { 685 if (port_map & (1 << i))
687 n_ports--; 686 map_ports++;
688 tmp_port_map &= ~(1 << i);
689 }
690 }
691 687
692 /* If n_ports and port_map are inconsistent, whine and 688 /* If PI has more ports than n_ports, whine, clear
693 * clear port_map and let it be generated from n_ports. 689 * port_map and let it be generated from n_ports.
694 */ 690 */
695 if (n_ports || tmp_port_map) { 691 if (map_ports > ahci_nr_ports(cap)) {
696 dev_printk(KERN_WARNING, &pdev->dev, 692 dev_printk(KERN_WARNING, &pdev->dev,
697 "nr_ports (%u) and implemented port map " 693 "implemented port map (0x%x) contains more "
698 "(0x%x) don't match, using nr_ports\n", 694 "ports than nr_ports (%u), using nr_ports\n",
699 ahci_nr_ports(cap), port_map); 695 port_map, ahci_nr_ports(cap));
700 port_map = 0; 696 port_map = 0;
701 } 697 }
702 } 698 }
@@ -2201,7 +2197,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
2201 struct device *dev = &pdev->dev; 2197 struct device *dev = &pdev->dev;
2202 struct ahci_host_priv *hpriv; 2198 struct ahci_host_priv *hpriv;
2203 struct ata_host *host; 2199 struct ata_host *host;
2204 int i, rc; 2200 int n_ports, i, rc;
2205 2201
2206 VPRINTK("ENTER\n"); 2202 VPRINTK("ENTER\n");
2207 2203
@@ -2255,7 +2251,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
2255 if (hpriv->cap & HOST_CAP_PMP) 2251 if (hpriv->cap & HOST_CAP_PMP)
2256 pi.flags |= ATA_FLAG_PMP; 2252 pi.flags |= ATA_FLAG_PMP;
2257 2253
2258 host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); 2254 /* CAP.NP sometimes indicate the index of the last enabled
2255 * port, at other times, that of the last possible port, so
2256 * determining the maximum port number requires looking at
2257 * both CAP.NP and port_map.
2258 */
2259 n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
2260
2261 host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
2259 if (!host) 2262 if (!host)
2260 return -ENOMEM; 2263 return -ENOMEM;
2261 host->iomap = pcim_iomap_table(pdev); 2264 host->iomap = pcim_iomap_table(pdev);