📄 ahci.c
字号:
{ PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */ { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */ { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb600 }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb600 }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb600 }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb600 }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb600 }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb600 }, /* ATI SB700/800 */ /* VIA */ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ /* NVIDIA */ { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci }, /* MCP79 */ /* SiS */ { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */ { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */ /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, { } /* terminate list */};static struct pci_driver ahci_pci_driver = { .name = DRV_NAME, .id_table = ahci_pci_tbl, .probe = ahci_init_one, .remove = ata_pci_remove_one,#ifdef CONFIG_PM .suspend = ahci_pci_device_suspend, .resume = ahci_pci_device_resume,#endif};static inline int ahci_nr_ports(u32 cap){ return (cap & 0x1f) + 1;}static inline void __iomem *__ahci_port_base(struct ata_host *host, unsigned int port_no){ void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; return mmio + 0x100 + (port_no * 0x80);}static inline void __iomem *ahci_port_base(struct ata_port *ap){ return __ahci_port_base(ap->host, ap->port_no);}/** * ahci_save_initial_config - Save and fixup initial config values * @pdev: target PCI device * @hpriv: host private area to store config values * * Some registers containing configuration info might be setup by * BIOS and might be cleared on reset. This function saves the * initial values of those registers into @hpriv such that they * can be restored after controller reset. * * If inconsistent, config values are fixed up by this function. * * LOCKING: * None. */static void ahci_save_initial_config(struct pci_dev *pdev, struct ahci_host_priv *hpriv){ void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; u32 cap, port_map; int i; /* Values prefixed with saved_ are written back to host after * reset. Values without are used for driver operation. */ hpriv->saved_cap = cap = readl(mmio + HOST_CAP); hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); /* some chips have errata preventing 64bit use */ if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) { dev_printk(KERN_INFO, &pdev->dev, "controller can't do 64bit DMA, forcing 32bit\n"); cap &= ~HOST_CAP_64; } if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) { dev_printk(KERN_INFO, &pdev->dev, "controller can't do NCQ, turning off CAP_NCQ\n"); cap &= ~HOST_CAP_NCQ; } if ((cap && HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) { dev_printk(KERN_INFO, &pdev->dev, "controller can't do PMP, turning off CAP_PMP\n"); cap &= ~HOST_CAP_PMP; } /* * Temporary Marvell 6145 hack: PATA port presence * is asserted through the standard AHCI port * presence register, as bit 4 (counting from 0) */ if (hpriv->flags & AHCI_HFLAG_MV_PATA) { dev_printk(KERN_ERR, &pdev->dev, "MV_AHCI HACK: port_map %x -> %x\n", hpriv->port_map, hpriv->port_map & 0xf); port_map &= 0xf; } /* cross check port_map and cap.n_ports */ if (port_map) { u32 tmp_port_map = port_map; int n_ports = ahci_nr_ports(cap); for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { if (tmp_port_map & (1 << i)) { n_ports--; tmp_port_map &= ~(1 << i); } } /* If n_ports and port_map are inconsistent, whine and * clear port_map and let it be generated from n_ports. */ if (n_ports || tmp_port_map) { dev_printk(KERN_WARNING, &pdev->dev, "nr_ports (%u) and implemented port map " "(0x%x) don't match, using nr_ports\n", ahci_nr_ports(cap), port_map); port_map = 0; } } /* fabricate port_map from cap.nr_ports */ if (!port_map) { port_map = (1 << ahci_nr_ports(cap)) - 1; dev_printk(KERN_WARNING, &pdev->dev, "forcing PORTS_IMPL to 0x%x\n", port_map); /* write the fixed up value to the PI register */ hpriv->saved_port_map = port_map; } /* record values to use during operation */ hpriv->cap = cap; hpriv->port_map = port_map;}/** * ahci_restore_initial_config - Restore initial config * @host: target ATA host * * Restore initial config stored by ahci_save_initial_config(). * * LOCKING: * None. */static void ahci_restore_initial_config(struct ata_host *host){ struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; writel(hpriv->saved_cap, mmio + HOST_CAP); writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */}static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg){ static const int offset[] = { [SCR_STATUS] = PORT_SCR_STAT, [SCR_CONTROL] = PORT_SCR_CTL, [SCR_ERROR] = PORT_SCR_ERR, [SCR_ACTIVE] = PORT_SCR_ACT, [SCR_NOTIFICATION] = PORT_SCR_NTF, }; struct ahci_host_priv *hpriv = ap->host->private_data; if (sc_reg < ARRAY_SIZE(offset) && (sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF))) return offset[sc_reg]; return 0;}static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val){ void __iomem *port_mmio = ahci_port_base(ap); int offset = ahci_scr_offset(ap, sc_reg); if (offset) { *val = readl(port_mmio + offset); return 0; } return -EINVAL;}static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val){ void __iomem *port_mmio = ahci_port_base(ap); int offset = ahci_scr_offset(ap, sc_reg); if (offset) { writel(val, port_mmio + offset); return 0; } return -EINVAL;}static void ahci_start_engine(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* start DMA */ tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); readl(port_mmio + PORT_CMD); /* flush */}static int ahci_stop_engine(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; tmp = readl(port_mmio + PORT_CMD); /* check if the HBA is idle */ if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) return 0; /* setting HBA to idle */ tmp &= ~PORT_CMD_START; writel(tmp, port_mmio + PORT_CMD); /* wait for engine to stop. This could be as long as 500 msec */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500); if (tmp & PORT_CMD_LIST_ON) return -EIO; return 0;}static void ahci_start_fis_rx(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; u32 tmp; /* set FIS registers */ if (hpriv->cap & HOST_CAP_64) writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); if (hpriv->cap & HOST_CAP_64) writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); /* enable FIS reception */ tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_FIS_RX; writel(tmp, port_mmio + PORT_CMD); /* flush */ readl(port_mmio + PORT_CMD);}static int ahci_stop_fis_rx(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* disable FIS reception */ tmp = readl(port_mmio + PORT_CMD); tmp &= ~PORT_CMD_FIS_RX; writel(tmp, port_mmio + PORT_CMD); /* wait for completion, spec says 500ms, give it 1000 */ tmp = ata_wait_register(port_mmio + PORT_CMD, PORT_CMD_FIS_ON, PORT_CMD_FIS_ON, 10, 1000); if (tmp & PORT_CMD_FIS_ON) return -EBUSY; return 0;}static void ahci_power_up(struct ata_port *ap){ struct ahci_host_priv *hpriv = ap->host->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 cmd; cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; /* spin up device */ if (hpriv->cap & HOST_CAP_SSS) { cmd |= PORT_CMD_SPIN_UP; writel(cmd, port_mmio + PORT_CMD); } /* wake up link */ writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);}static void ahci_disable_alpm(struct ata_port *ap){ struct ahci_host_priv *hpriv = ap->host->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 cmd; struct ahci_port_priv *pp = ap->private_data; /* IPM bits should be disabled by libata-core */ /* get the existing command bits */ cmd = readl(port_mmio + PORT_CMD); /* disable ALPM and ASP */ cmd &= ~PORT_CMD_ASP; cmd &= ~PORT_CMD_ALPE; /* force the interface back to active */ cmd |= PORT_CMD_ICC_ACTIVE; /* write out new cmd value */ writel(cmd, port_mmio + PORT_CMD); cmd = readl(port_mmio + PORT_CMD); /* wait 10ms to be sure we've come out of any low power state */ msleep(10); /* clear out any PhyRdy stuff from interrupt status */ writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); /* go ahead and clean out PhyRdy Change from Serror too */ ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); /* * Clear flag to indicate that we should ignore all PhyRdy * state changes */ hpriv->flags &= ~AHCI_HFLAG_NO_HOTPLUG; /* * Enable interrupts on Phy Ready. */ pp->intr_mask |= PORT_IRQ_PHYRDY; writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); /* * don't change the link pm policy - we can be called * just to turn of link pm temporarily */}static int ahci_enable_alpm(struct ata_port *ap, enum link_pm policy){ struct ahci_host_priv *hpriv = ap->host->private_data; void __iomem *port_mmio = ahci_port_base(ap); u32 cmd; struct ahci_port_priv *pp = ap->private_data; u32 asp; /* Make sure the host is capable of link power management */ if (!(hpriv->cap & HOST_CAP_ALPM)) return -EINVAL; switch (policy) { case MAX_PERFORMANCE: case NOT_AVAILABLE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -