📄 ahci.c
字号:
/* * if we came here with NOT_AVAILABLE, * it just means this is the first time we * have tried to enable - default to max performance, * and let the user go to lower power modes on request. */ ahci_disable_alpm(ap); return 0; case MIN_POWER: /* configure HBA to enter SLUMBER */ asp = PORT_CMD_ASP; break; case MEDIUM_POWER: /* configure HBA to enter PARTIAL */ asp = 0; break; default: return -EINVAL; } /* * Disable interrupts on Phy Ready. This keeps us from * getting woken up due to spurious phy ready interrupts * TBD - Hot plug should be done via polling now, is * that even supported? */ pp->intr_mask &= ~PORT_IRQ_PHYRDY; writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); /* * Set a flag to indicate that we should ignore all PhyRdy * state changes since these can happen now whenever we * change link state */ hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG; /* get the existing command bits */ cmd = readl(port_mmio + PORT_CMD); /* * Set ASP based on Policy */ cmd |= asp; /* * Setting this bit will instruct the HBA to aggressively * enter a lower power link state when it's appropriate and * based on the value set above for ASP */ cmd |= PORT_CMD_ALPE; /* write out new cmd value */ writel(cmd, port_mmio + PORT_CMD); cmd = readl(port_mmio + PORT_CMD); /* IPM bits should be set by libata-core */ return 0;}#ifdef CONFIG_PMstatic void ahci_power_down(struct ata_port *ap){ struct ahci_host_priv *hpriv = ap->host->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 cmd, scontrol; if (!(hpriv->cap & HOST_CAP_SSS)) return; /* put device into listen mode, first set PxSCTL.DET to 0 */ scontrol = readl(port_mmio + PORT_SCR_CTL); scontrol &= ~0xf; writel(scontrol, port_mmio + PORT_SCR_CTL); /* then set PxCMD.SUD to 0 */ cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; cmd &= ~PORT_CMD_SPIN_UP; writel(cmd, port_mmio + PORT_CMD);}#endifstatic void ahci_start_port(struct ata_port *ap){ /* enable FIS reception */ ahci_start_fis_rx(ap); /* enable DMA */ ahci_start_engine(ap);}static int ahci_deinit_port(struct ata_port *ap, const char **emsg){ int rc; /* disable DMA */ rc = ahci_stop_engine(ap); if (rc) { *emsg = "failed to stop engine"; return rc; } /* disable FIS reception */ rc = ahci_stop_fis_rx(ap); if (rc) { *emsg = "failed stop FIS RX"; return rc; } return 0;}static int ahci_reset_controller(struct ata_host *host){ struct pci_dev *pdev = to_pci_dev(host->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 tmp; /* we must be in AHCI mode, before using anything * AHCI-specific, such as HOST_RESET. */ tmp = readl(mmio + HOST_CTL); if (!(tmp & HOST_AHCI_EN)) { tmp |= HOST_AHCI_EN; writel(tmp, mmio + HOST_CTL); } /* global controller reset */ if ((tmp & HOST_RESET) == 0) { writel(tmp | HOST_RESET, mmio + HOST_CTL); readl(mmio + HOST_CTL); /* flush */ } /* reset must complete within 1 second, or * the hardware should be considered fried. */ ssleep(1); tmp = readl(mmio + HOST_CTL); if (tmp & HOST_RESET) { dev_printk(KERN_ERR, host->dev, "controller reset failed (0x%x)\n", tmp); return -EIO; } /* turn on AHCI mode */ writel(HOST_AHCI_EN, mmio + HOST_CTL); (void) readl(mmio + HOST_CTL); /* flush */ /* some registers might be cleared on reset. restore initial values */ ahci_restore_initial_config(host); if (pdev->vendor == PCI_VENDOR_ID_INTEL) { u16 tmp16; /* configure PCS */ pci_read_config_word(pdev, 0x92, &tmp16); tmp16 |= 0xf; pci_write_config_word(pdev, 0x92, tmp16); } return 0;}static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap, int port_no, void __iomem *mmio, void __iomem *port_mmio){ const char *emsg = NULL; int rc; u32 tmp; /* make sure port is not active */ rc = ahci_deinit_port(ap, &emsg); if (rc) dev_printk(KERN_WARNING, &pdev->dev, "%s (%d)\n", emsg, rc); /* clear SError */ tmp = readl(port_mmio + PORT_SCR_ERR); VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); writel(tmp, port_mmio + PORT_SCR_ERR); /* clear port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); writel(1 << port_no, mmio + HOST_IRQ_STAT);}static void ahci_init_controller(struct ata_host *host){ struct ahci_host_priv *hpriv = host->private_data; struct pci_dev *pdev = to_pci_dev(host->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; int i; void __iomem *port_mmio; u32 tmp; if (hpriv->flags & AHCI_HFLAG_MV_PATA) { port_mmio = __ahci_port_base(host, 4); writel(0, port_mmio + PORT_IRQ_MASK); /* clear port IRQ */ tmp = readl(port_mmio + PORT_IRQ_STAT); VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); if (tmp) writel(tmp, port_mmio + PORT_IRQ_STAT); } for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; port_mmio = ahci_port_base(ap); if (ata_port_is_dummy(ap)) continue; ahci_port_init(pdev, ap, i, mmio, port_mmio); } tmp = readl(mmio + HOST_CTL); VPRINTK("HOST_CTL 0x%x\n", tmp); writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); tmp = readl(mmio + HOST_CTL); VPRINTK("HOST_CTL 0x%x\n", tmp);}static unsigned int ahci_dev_classify(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); struct ata_taskfile tf; u32 tmp; tmp = readl(port_mmio + PORT_SIG); tf.lbah = (tmp >> 24) & 0xff; tf.lbam = (tmp >> 16) & 0xff; tf.lbal = (tmp >> 8) & 0xff; tf.nsect = (tmp) & 0xff; return ata_dev_classify(&tf);}static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, u32 opts){ dma_addr_t cmd_tbl_dma; cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ; pp->cmd_slot[tag].opts = cpu_to_le32(opts); pp->cmd_slot[tag].status = 0; pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff); pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);}static int ahci_kick_engine(struct ata_port *ap, int force_restart){ void __iomem *port_mmio = ap->ioaddr.cmd_addr; struct ahci_host_priv *hpriv = ap->host->private_data; u32 tmp; int busy, rc; /* do we need to kick the port? */ busy = ahci_check_status(ap) & (ATA_BUSY | ATA_DRQ); if (!busy && !force_restart) return 0; /* stop engine */ rc = ahci_stop_engine(ap); if (rc) goto out_restart; /* need to do CLO? */ if (!busy) { rc = 0; goto out_restart; } if (!(hpriv->cap & HOST_CAP_CLO)) { rc = -EOPNOTSUPP; goto out_restart; } /* perform CLO */ tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_CLO; writel(tmp, port_mmio + PORT_CMD); rc = 0; tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); if (tmp & PORT_CMD_CLO) rc = -EIO; /* restart engine */ out_restart: ahci_start_engine(ap); return rc;}static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, struct ata_taskfile *tf, int is_cmd, u16 flags, unsigned long timeout_msec){ const u32 cmd_fis_len = 5; /* five dwords */ struct ahci_port_priv *pp = ap->private_data; void __iomem *port_mmio = ahci_port_base(ap); u8 *fis = pp->cmd_tbl; u32 tmp; /* prep the command */ ata_tf_to_fis(tf, pmp, is_cmd, fis); ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12)); /* issue & wait */ writel(1, port_mmio + PORT_CMD_ISSUE); if (timeout_msec) { tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, timeout_msec); if (tmp & 0x1) { ahci_kick_engine(ap, 1); return -EBUSY; } } else readl(port_mmio + PORT_CMD_ISSUE); /* flush */ return 0;}static int ahci_do_softreset(struct ata_link *link, unsigned int *class, int pmp, unsigned long deadline){ struct ata_port *ap = link->ap; const char *reason = NULL; unsigned long now, msecs; struct ata_taskfile tf; int rc; DPRINTK("ENTER\n"); if (ata_link_offline(link)) { DPRINTK("PHY reports no device\n"); *class = ATA_DEV_NONE; return 0; } /* prepare for SRST (AHCI-1.1 10.4.1) */ rc = ahci_kick_engine(ap, 1); if (rc && rc != -EOPNOTSUPP) ata_link_printk(link, KERN_WARNING, "failed to reset engine (errno=%d)\n", rc); ata_tf_init(link->device, &tf); /* issue the first D2H Register FIS */ msecs = 0; now = jiffies; if (time_after(now, deadline)) msecs = jiffies_to_msecs(deadline - now); tf.ctl |= ATA_SRST; if (ahci_exec_polled_cmd(ap, pmp, &tf, 0, AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { rc = -EIO; reason = "1st FIS failed"; goto fail; } /* spec says at least 5us, but be generous and sleep for 1ms */ msleep(1); /* issue the second D2H Register FIS */ tf.ctl &= ~ATA_SRST; ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0); /* wait a while before checking status */ ata_wait_after_reset(ap, deadline); rc = ata_wait_ready(ap, deadline); /* link occupied, -ENODEV too is an error */ if (rc) { reason = "device not ready"; goto fail; } *class = ahci_dev_classify(ap); DPRINTK("EXIT, class=%u\n", *class); return 0; fail: ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason); return rc;}static int ahci_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ int pmp = 0; if (link->ap->flags & ATA_FLAG_PMP) pmp = SATA_PMP_CTRL_PORT; return ahci_do_softreset(link, class, pmp, deadline);}static int ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; int rc; DPRINTK("ENTER\n"); ahci_stop_engine(ap); /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(link->device, &tf); tf.command = 0x80; ata_tf_to_fis(&tf, 0, 0, d2h_fis); rc = sata_std_hardreset(link, class, deadline); ahci_start_engine(ap); if (rc == 0 && ata_link_online(link)) *class = ahci_dev_classify(ap); if (rc != -EAGAIN && *class == ATA_DEV_UNKNOWN) *class = ATA_DEV_NONE; DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); return rc;}static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ struct ata_port *ap = link->ap; u32 serror; int rc; DPRINTK("ENTER\n"); ahci_stop_engine(ap); rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline); /* vt8251 needs SError cleared for the port to operate */ ahci_scr_read(ap, SCR_ERROR, &serror); ahci_scr_write(ap, SCR_ERROR, serror); ahci_start_engine(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); /* vt8251 doesn't clear BSY on signature FIS reception,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -