diff options
author | Tejun Heo <htejun@gmail.com> | 2006-07-26 02:59:26 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-07-29 04:01:31 -0400 |
commit | 0be0aa98985dfec42502c0d0af2a1baff9bdb19f (patch) | |
tree | 110f8d9419bc18370ddc410082e65a634d8a8109 /drivers/scsi | |
parent | 9f5920567bfabbd1be26112a31c44652b6587394 (diff) |
[PATCH] libata: improve driver initialization and deinitialization
Implement ahci_[de]init_port() and use it during initialization and
de-initialization. ahci_[de]init_port() are supersets of what used to
be done during driver [de-]initialization. This patch makes the
following behavior changes.
* Per-port IRQ mask is cleared on driver load as done in other
drivers. The mask will be configured properly during probe.
* During init_one(), HOST_IRQ_STAT is cleared after masking port IRQs
such that there is no race window.
* CMD_SPIN_UP is cleared during init_one() instead of being set. It
is set in port_start(). This is more consistent with overall
structure of initialization. Note that CMD_SPIN_UP simply controls
PHY activation.
* Slumber and staggered spin-up are handled properly.
* All init/deinit operations are done in step-by-step manner as
described in the spec instead of issued as single merged command.
Original implementation is from Zhao, Forrest <forrest.zhao@intel.com>
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Zhao, Forrest <forrest.zhao@intel.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/ahci.c | 202 |
1 files changed, 151 insertions, 51 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index e02b9c65287b..fb71fa7bc5de 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c | |||
@@ -92,7 +92,9 @@ enum { | |||
92 | HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ | 92 | HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ |
93 | 93 | ||
94 | /* HOST_CAP bits */ | 94 | /* HOST_CAP bits */ |
95 | HOST_CAP_SSC = (1 << 14), /* Slumber capable */ | ||
95 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ | 96 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ |
97 | HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ | ||
96 | HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ | 98 | HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ |
97 | HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ | 99 | HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ |
98 | 100 | ||
@@ -155,6 +157,7 @@ enum { | |||
155 | PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ | 157 | PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ |
156 | PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ | 158 | PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ |
157 | 159 | ||
160 | PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */ | ||
158 | PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ | 161 | PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ |
159 | PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ | 162 | PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ |
160 | PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ | 163 | PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ |
@@ -440,6 +443,135 @@ static int ahci_stop_engine(void __iomem *port_mmio) | |||
440 | return 0; | 443 | return 0; |
441 | } | 444 | } |
442 | 445 | ||
446 | static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, | ||
447 | dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) | ||
448 | { | ||
449 | u32 tmp; | ||
450 | |||
451 | /* set FIS registers */ | ||
452 | if (cap & HOST_CAP_64) | ||
453 | writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); | ||
454 | writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); | ||
455 | |||
456 | if (cap & HOST_CAP_64) | ||
457 | writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); | ||
458 | writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); | ||
459 | |||
460 | /* enable FIS reception */ | ||
461 | tmp = readl(port_mmio + PORT_CMD); | ||
462 | tmp |= PORT_CMD_FIS_RX; | ||
463 | writel(tmp, port_mmio + PORT_CMD); | ||
464 | |||
465 | /* flush */ | ||
466 | readl(port_mmio + PORT_CMD); | ||
467 | } | ||
468 | |||
469 | static int ahci_stop_fis_rx(void __iomem *port_mmio) | ||
470 | { | ||
471 | u32 tmp; | ||
472 | |||
473 | /* disable FIS reception */ | ||
474 | tmp = readl(port_mmio + PORT_CMD); | ||
475 | tmp &= ~PORT_CMD_FIS_RX; | ||
476 | writel(tmp, port_mmio + PORT_CMD); | ||
477 | |||
478 | /* wait for completion, spec says 500ms, give it 1000 */ | ||
479 | tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, | ||
480 | PORT_CMD_FIS_ON, 10, 1000); | ||
481 | if (tmp & PORT_CMD_FIS_ON) | ||
482 | return -EBUSY; | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static void ahci_power_up(void __iomem *port_mmio, u32 cap) | ||
488 | { | ||
489 | u32 cmd; | ||
490 | |||
491 | cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; | ||
492 | |||
493 | /* spin up device */ | ||
494 | if (cap & HOST_CAP_SSS) { | ||
495 | cmd |= PORT_CMD_SPIN_UP; | ||
496 | writel(cmd, port_mmio + PORT_CMD); | ||
497 | } | ||
498 | |||
499 | /* wake up link */ | ||
500 | writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); | ||
501 | } | ||
502 | |||
503 | static void ahci_power_down(void __iomem *port_mmio, u32 cap) | ||
504 | { | ||
505 | u32 cmd, scontrol; | ||
506 | |||
507 | cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; | ||
508 | |||
509 | if (cap & HOST_CAP_SSC) { | ||
510 | /* enable transitions to slumber mode */ | ||
511 | scontrol = readl(port_mmio + PORT_SCR_CTL); | ||
512 | if ((scontrol & 0x0f00) > 0x100) { | ||
513 | scontrol &= ~0xf00; | ||
514 | writel(scontrol, port_mmio + PORT_SCR_CTL); | ||
515 | } | ||
516 | |||
517 | /* put device into slumber mode */ | ||
518 | writel(cmd | PORT_CMD_ICC_SLUMBER, port_mmio + PORT_CMD); | ||
519 | |||
520 | /* wait for the transition to complete */ | ||
521 | ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_ICC_SLUMBER, | ||
522 | PORT_CMD_ICC_SLUMBER, 1, 50); | ||
523 | } | ||
524 | |||
525 | /* put device into listen mode */ | ||
526 | if (cap & HOST_CAP_SSS) { | ||
527 | /* first set PxSCTL.DET to 0 */ | ||
528 | scontrol = readl(port_mmio + PORT_SCR_CTL); | ||
529 | scontrol &= ~0xf; | ||
530 | writel(scontrol, port_mmio + PORT_SCR_CTL); | ||
531 | |||
532 | /* then set PxCMD.SUD to 0 */ | ||
533 | cmd &= ~PORT_CMD_SPIN_UP; | ||
534 | writel(cmd, port_mmio + PORT_CMD); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | static void ahci_init_port(void __iomem *port_mmio, u32 cap, | ||
539 | dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) | ||
540 | { | ||
541 | /* power up */ | ||
542 | ahci_power_up(port_mmio, cap); | ||
543 | |||
544 | /* enable FIS reception */ | ||
545 | ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); | ||
546 | |||
547 | /* enable DMA */ | ||
548 | ahci_start_engine(port_mmio); | ||
549 | } | ||
550 | |||
551 | static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) | ||
552 | { | ||
553 | int rc; | ||
554 | |||
555 | /* disable DMA */ | ||
556 | rc = ahci_stop_engine(port_mmio); | ||
557 | if (rc) { | ||
558 | *emsg = "failed to stop engine"; | ||
559 | return rc; | ||
560 | } | ||
561 | |||
562 | /* disable FIS reception */ | ||
563 | rc = ahci_stop_fis_rx(port_mmio); | ||
564 | if (rc) { | ||
565 | *emsg = "failed stop FIS RX"; | ||
566 | return rc; | ||
567 | } | ||
568 | |||
569 | /* put device into slumber mode */ | ||
570 | ahci_power_down(port_mmio, cap); | ||
571 | |||
572 | return 0; | ||
573 | } | ||
574 | |||
443 | static unsigned int ahci_dev_classify(struct ata_port *ap) | 575 | static unsigned int ahci_dev_classify(struct ata_port *ap) |
444 | { | 576 | { |
445 | void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; | 577 | void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; |
@@ -1037,20 +1169,8 @@ static int ahci_port_start(struct ata_port *ap) | |||
1037 | 1169 | ||
1038 | ap->private_data = pp; | 1170 | ap->private_data = pp; |
1039 | 1171 | ||
1040 | if (hpriv->cap & HOST_CAP_64) | 1172 | /* initialize port */ |
1041 | writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); | 1173 | ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); |
1042 | writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); | ||
1043 | readl(port_mmio + PORT_LST_ADDR); /* flush */ | ||
1044 | |||
1045 | if (hpriv->cap & HOST_CAP_64) | ||
1046 | writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); | ||
1047 | writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); | ||
1048 | readl(port_mmio + PORT_FIS_ADDR); /* flush */ | ||
1049 | |||
1050 | writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | | ||
1051 | PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | | ||
1052 | PORT_CMD_START, port_mmio + PORT_CMD); | ||
1053 | readl(port_mmio + PORT_CMD); /* flush */ | ||
1054 | 1174 | ||
1055 | return 0; | 1175 | return 0; |
1056 | } | 1176 | } |
@@ -1058,20 +1178,17 @@ static int ahci_port_start(struct ata_port *ap) | |||
1058 | static void ahci_port_stop(struct ata_port *ap) | 1178 | static void ahci_port_stop(struct ata_port *ap) |
1059 | { | 1179 | { |
1060 | struct device *dev = ap->host_set->dev; | 1180 | struct device *dev = ap->host_set->dev; |
1181 | struct ahci_host_priv *hpriv = ap->host_set->private_data; | ||
1061 | struct ahci_port_priv *pp = ap->private_data; | 1182 | struct ahci_port_priv *pp = ap->private_data; |
1062 | void __iomem *mmio = ap->host_set->mmio_base; | 1183 | void __iomem *mmio = ap->host_set->mmio_base; |
1063 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); | 1184 | void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); |
1064 | u32 tmp; | 1185 | const char *emsg = NULL; |
1065 | 1186 | int rc; | |
1066 | tmp = readl(port_mmio + PORT_CMD); | ||
1067 | tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); | ||
1068 | writel(tmp, port_mmio + PORT_CMD); | ||
1069 | readl(port_mmio + PORT_CMD); /* flush */ | ||
1070 | 1187 | ||
1071 | /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so | 1188 | /* de-initialize port */ |
1072 | * this is slightly incorrect. | 1189 | rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); |
1073 | */ | 1190 | if (rc) |
1074 | msleep(500); | 1191 | ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); |
1075 | 1192 | ||
1076 | ap->private_data = NULL; | 1193 | ap->private_data = NULL; |
1077 | dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, | 1194 | dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, |
@@ -1099,7 +1216,7 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) | |||
1099 | struct pci_dev *pdev = to_pci_dev(probe_ent->dev); | 1216 | struct pci_dev *pdev = to_pci_dev(probe_ent->dev); |
1100 | void __iomem *mmio = probe_ent->mmio_base; | 1217 | void __iomem *mmio = probe_ent->mmio_base; |
1101 | u32 tmp, cap_save; | 1218 | u32 tmp, cap_save; |
1102 | unsigned int i, j, using_dac; | 1219 | unsigned int i, using_dac; |
1103 | int rc; | 1220 | int rc; |
1104 | void __iomem *port_mmio; | 1221 | void __iomem *port_mmio; |
1105 | 1222 | ||
@@ -1175,6 +1292,8 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) | |||
1175 | } | 1292 | } |
1176 | 1293 | ||
1177 | for (i = 0; i < probe_ent->n_ports; i++) { | 1294 | for (i = 0; i < probe_ent->n_ports; i++) { |
1295 | const char *emsg = NULL; | ||
1296 | |||
1178 | #if 0 /* BIOSen initialize this incorrectly */ | 1297 | #if 0 /* BIOSen initialize this incorrectly */ |
1179 | if (!(hpriv->port_map & (1 << i))) | 1298 | if (!(hpriv->port_map & (1 << i))) |
1180 | continue; | 1299 | continue; |
@@ -1187,43 +1306,24 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) | |||
1187 | (unsigned long) mmio, i); | 1306 | (unsigned long) mmio, i); |
1188 | 1307 | ||
1189 | /* make sure port is not active */ | 1308 | /* make sure port is not active */ |
1190 | tmp = readl(port_mmio + PORT_CMD); | 1309 | rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); |
1191 | VPRINTK("PORT_CMD 0x%x\n", tmp); | 1310 | if (rc) |
1192 | if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | | 1311 | dev_printk(KERN_WARNING, &pdev->dev, |
1193 | PORT_CMD_FIS_RX | PORT_CMD_START)) { | 1312 | "%s (%d)\n", emsg, rc); |
1194 | tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | | ||
1195 | PORT_CMD_FIS_RX | PORT_CMD_START); | ||
1196 | writel(tmp, port_mmio + PORT_CMD); | ||
1197 | readl(port_mmio + PORT_CMD); /* flush */ | ||
1198 | |||
1199 | /* spec says 500 msecs for each bit, so | ||
1200 | * this is slightly incorrect. | ||
1201 | */ | ||
1202 | msleep(500); | ||
1203 | } | ||
1204 | |||
1205 | writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD); | ||
1206 | |||
1207 | j = 0; | ||
1208 | while (j < 100) { | ||
1209 | msleep(10); | ||
1210 | tmp = readl(port_mmio + PORT_SCR_STAT); | ||
1211 | if ((tmp & 0xf) == 0x3) | ||
1212 | break; | ||
1213 | j++; | ||
1214 | } | ||
1215 | 1313 | ||
1314 | /* clear SError */ | ||
1216 | tmp = readl(port_mmio + PORT_SCR_ERR); | 1315 | tmp = readl(port_mmio + PORT_SCR_ERR); |
1217 | VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); | 1316 | VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); |
1218 | writel(tmp, port_mmio + PORT_SCR_ERR); | 1317 | writel(tmp, port_mmio + PORT_SCR_ERR); |
1219 | 1318 | ||
1220 | /* ack any pending irq events for this port */ | 1319 | /* clear & turn off port IRQ */ |
1221 | tmp = readl(port_mmio + PORT_IRQ_STAT); | 1320 | tmp = readl(port_mmio + PORT_IRQ_STAT); |
1222 | VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); | 1321 | VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); |
1223 | if (tmp) | 1322 | if (tmp) |
1224 | writel(tmp, port_mmio + PORT_IRQ_STAT); | 1323 | writel(tmp, port_mmio + PORT_IRQ_STAT); |
1225 | 1324 | ||
1226 | writel(1 << i, mmio + HOST_IRQ_STAT); | 1325 | writel(1 << i, mmio + HOST_IRQ_STAT); |
1326 | writel(0, port_mmio + PORT_IRQ_MASK); | ||
1227 | } | 1327 | } |
1228 | 1328 | ||
1229 | tmp = readl(mmio + HOST_CTL); | 1329 | tmp = readl(mmio + HOST_CTL); |