📄 ahci.c
字号:
ahci_postreset);}static void ahci_post_internal_cmd(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ if (qc->flags & ATA_QCFLAG_FAILED) ahci_kick_engine(ap, 1);}static void ahci_pmp_attach(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); struct ahci_port_priv *pp = ap->private_data; u32 cmd; cmd = readl(port_mmio + PORT_CMD); cmd |= PORT_CMD_PMP; writel(cmd, port_mmio + PORT_CMD); pp->intr_mask |= PORT_IRQ_BAD_PMP; writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);}static void ahci_pmp_detach(struct ata_port *ap){ void __iomem *port_mmio = ahci_port_base(ap); struct ahci_port_priv *pp = ap->private_data; u32 cmd; cmd = readl(port_mmio + PORT_CMD); cmd &= ~PORT_CMD_PMP; writel(cmd, port_mmio + PORT_CMD); pp->intr_mask &= ~PORT_IRQ_BAD_PMP; writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);}static int ahci_port_resume(struct ata_port *ap){ ahci_power_up(ap); ahci_start_port(ap); if (ap->nr_pmp_links) ahci_pmp_attach(ap); else ahci_pmp_detach(ap); return 0;}#ifdef CONFIG_PMstatic int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg){ const char *emsg = NULL; int rc; rc = ahci_deinit_port(ap, &emsg); if (rc == 0) ahci_power_down(ap); else { ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); ahci_start_port(ap); } return rc;}static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg){ struct ata_host *host = dev_get_drvdata(&pdev->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 ctl; if (mesg.event == PM_EVENT_SUSPEND) { /* AHCI spec rev1.1 section 8.3.3: * Software must disable interrupts prior to requesting a * transition of the HBA to D3 state. */ ctl = readl(mmio + HOST_CTL); ctl &= ~HOST_IRQ_EN; writel(ctl, mmio + HOST_CTL); readl(mmio + HOST_CTL); /* flush */ } return ata_pci_device_suspend(pdev, mesg);}static int ahci_pci_device_resume(struct pci_dev *pdev){ struct ata_host *host = dev_get_drvdata(&pdev->dev); int rc; rc = ata_pci_device_do_resume(pdev); if (rc) return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { rc = ahci_reset_controller(host); if (rc) return rc; ahci_init_controller(host); } ata_host_resume(host); return 0;}#endifstatic int ahci_port_start(struct ata_port *ap){ struct device *dev = ap->host->dev; struct ahci_port_priv *pp; void *mem; dma_addr_t mem_dma; int rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) return -ENOMEM; rc = ata_pad_alloc(ap, dev); if (rc) return rc; mem = dmam_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); if (!mem) return -ENOMEM; memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); /* * First item in chunk of DMA memory: 32-slot command table, * 32 bytes each in size */ pp->cmd_slot = mem; pp->cmd_slot_dma = mem_dma; mem += AHCI_CMD_SLOT_SZ; mem_dma += AHCI_CMD_SLOT_SZ; /* * Second item: Received-FIS area */ pp->rx_fis = mem; pp->rx_fis_dma = mem_dma; mem += AHCI_RX_FIS_SZ; mem_dma += AHCI_RX_FIS_SZ; /* * Third item: data area for storing a single command * and its scatter-gather table */ pp->cmd_tbl = mem; pp->cmd_tbl_dma = mem_dma; /* * Save off initial list of interrupts to be enabled. * This could be changed later */ pp->intr_mask = DEF_PORT_IRQ; ap->private_data = pp; /* engage engines, captain */ return ahci_port_resume(ap);}static void ahci_port_stop(struct ata_port *ap){ const char *emsg = NULL; int rc; /* de-initialize port */ rc = ahci_deinit_port(ap, &emsg); if (rc) ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc);}static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac){ int rc; if (using_dac && !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { dev_printk(KERN_ERR, &pdev->dev, "64-bit DMA enable failed\n"); return rc; } } } else { rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { dev_printk(KERN_ERR, &pdev->dev, "32-bit DMA enable failed\n"); return rc; } rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { dev_printk(KERN_ERR, &pdev->dev, "32-bit consistent DMA enable failed\n"); return rc; } } return 0;}static void ahci_print_info(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]; u32 vers, cap, impl, speed; const char *speed_s; u16 cc; const char *scc_s; vers = readl(mmio + HOST_VERSION); cap = hpriv->cap; impl = hpriv->port_map; speed = (cap >> 20) & 0xf; if (speed == 1) speed_s = "1.5"; else if (speed == 2) speed_s = "3"; else speed_s = "?"; pci_read_config_word(pdev, 0x0a, &cc); if (cc == PCI_CLASS_STORAGE_IDE) scc_s = "IDE"; else if (cc == PCI_CLASS_STORAGE_SATA) scc_s = "SATA"; else if (cc == PCI_CLASS_STORAGE_RAID) scc_s = "RAID"; else scc_s = "unknown"; dev_printk(KERN_INFO, &pdev->dev, "AHCI %02x%02x.%02x%02x " "%u slots %u ports %s Gbps 0x%x impl %s mode\n" , (vers >> 24) & 0xff, (vers >> 16) & 0xff, (vers >> 8) & 0xff, vers & 0xff, ((cap >> 8) & 0x1f) + 1, (cap & 0x1f) + 1, speed_s, impl, scc_s); dev_printk(KERN_INFO, &pdev->dev, "flags: " "%s%s%s%s%s%s%s" "%s%s%s%s%s%s%s\n" , cap & (1 << 31) ? "64bit " : "", cap & (1 << 30) ? "ncq " : "", cap & (1 << 29) ? "sntf " : "", cap & (1 << 28) ? "ilck " : "", cap & (1 << 27) ? "stag " : "", cap & (1 << 26) ? "pm " : "", cap & (1 << 25) ? "led " : "", cap & (1 << 24) ? "clo " : "", cap & (1 << 19) ? "nz " : "", cap & (1 << 18) ? "only " : "", cap & (1 << 17) ? "pmp " : "", cap & (1 << 15) ? "pio " : "", cap & (1 << 14) ? "slum " : "", cap & (1 << 13) ? "part " : "" );}/* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is * hardwired to on-board SIMG 4726. The chipset is ICH8 and doesn't * support PMP and the 4726 either directly exports the device * attached to the first downstream port or acts as a hardware storage * controller and emulate a single ATA device (can be RAID 0/1 or some * other configuration). * * When there's no device attached to the first downstream port of the * 4726, "Config Disk" appears, which is a pseudo ATA device to * configure the 4726. However, ATA emulation of the device is very * lame. It doesn't send signature D2H Reg FIS after the initial * hardreset, pukes on SRST w/ PMP==0 and has bunch of other issues. * * The following function works around the problem by always using * hardreset on the port and not depending on receiving signature FIS * afterward. If signature FIS isn't received soon, ATA class is * assumed without follow-up softreset. */static void ahci_p5wdh_workaround(struct ata_host *host){ static struct dmi_system_id sysids[] = { { .ident = "P5W DH Deluxe", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTEK COMPUTER INC"), DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"), }, }, { } }; struct pci_dev *pdev = to_pci_dev(host->dev); if (pdev->bus->number == 0 && pdev->devfn == PCI_DEVFN(0x1f, 2) && dmi_check_system(sysids)) { struct ata_port *ap = host->ports[1]; dev_printk(KERN_INFO, &pdev->dev, "enabling ASUS P5W DH " "Deluxe on-board SIMG4726 workaround\n"); ap->ops = &ahci_p5wdh_ops; ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA; }}static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ static int printed_version; struct ata_port_info pi = ahci_port_info[ent->driver_data]; const struct ata_port_info *ppi[] = { &pi, NULL }; struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; struct ata_host *host; int i, rc; VPRINTK("ENTER\n"); WARN_ON(ATA_MAX_QUEUE > AHCI_MAX_CMDS); if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; rc = pcim_iomap_regions(pdev, 1 << AHCI_PCI_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == 0x2652 || pdev->device == 0x2653)) { u8 map; /* ICH6s share the same PCI ID for both piix and ahci * modes. Enabling ahci mode while MAP indicates * combined mode is a bad idea. Yield to ata_piix. */ pci_read_config_byte(pdev, ICH_MAP, &map); if (map & 0x3) { dev_printk(KERN_INFO, &pdev->dev, "controller is in " "combined mode, can't enable AHCI mode\n"); return -ENODEV; } } hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; hpriv->flags |= (unsigned long)pi.private_data; if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) pci_intx(pdev, 1); /* save initial config */ ahci_save_initial_config(pdev, hpriv); /* prepare host */ if (hpriv->cap & HOST_CAP_NCQ) pi.flags |= ATA_FLAG_NCQ; if (hpriv->cap & HOST_CAP_PMP) pi.flags |= ATA_FLAG_PMP; host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); if (!host) return -ENOMEM; host->iomap = pcim_iomap_table(pdev); host->private_data = hpriv; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; void __iomem *port_mmio = ahci_port_base(ap); ata_port_pbar_desc(ap, AHCI_PCI_BAR, -1, "abar"); ata_port_pbar_desc(ap, AHCI_PCI_BAR, 0x100 + ap->port_no * 0x80, "port"); /* set initial link pm policy */ ap->pm_policy = NOT_AVAILABLE; /* standard SATA port setup */ if (hpriv->port_map & (1 << i)) ap->ioaddr.cmd_addr = port_mmio; /* disabled/not-implemented port */ else ap->ops = &ata_dummy_port_ops; } /* apply workaround for ASUS P5W DH Deluxe mainboard */ ahci_p5wdh_workaround(host); /* initialize adapter */ rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); if (rc) return rc; rc = ahci_reset_controller(host); if (rc) return rc; ahci_init_controller(host); ahci_print_info(host); pci_set_master(pdev); return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, &ahci_sht);}static int __init ahci_init(void){ return pci_register_driver(&ahci_pci_driver);}static void __exit ahci_exit(void){ pci_unregister_driver(&ahci_pci_driver);}MODULE_AUTHOR("Jeff Garzik");MODULE_DESCRIPTION("AHCI SATA low-level driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);MODULE_VERSION(DRV_VERSION);module_init(ahci_init);module_exit(ahci_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -