📄 ata_piix.c
字号:
* Set MW/UDMA mode for device, in host controller PCI config space. * * LOCKING: * None (inherited from caller). */static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev){ do_pata_set_dmamode(ap, adev, 0);}/** * ich_set_dmamode - Initialize host controller PATA DMA timings * @ap: Port whose timings we are configuring * @adev: um * * Set MW/UDMA mode for device, in host controller PCI config space. * * LOCKING: * None (inherited from caller). */static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev){ do_pata_set_dmamode(ap, adev, 1);}#ifdef CONFIG_PMstatic int piix_broken_suspend(void){ static const struct dmi_system_id sysids[] = { { .ident = "TECRA M3", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M3"), }, }, { .ident = "TECRA M3", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Tecra M3"), }, }, { .ident = "TECRA M4", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Tecra M4"), }, }, { .ident = "TECRA M5", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M5"), }, }, { .ident = "TECRA M7", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "TECRA M7"), }, }, { .ident = "TECRA A8", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A8"), }, }, { .ident = "Satellite R25", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite R25"), }, }, { .ident = "Satellite U200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U200"), }, }, { .ident = "Satellite U200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE U200"), }, }, { .ident = "Satellite Pro U200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE PRO U200"), }, }, { .ident = "Satellite U205", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U205"), }, }, { .ident = "SATELLITE U205", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE U205"), }, }, { .ident = "Portege M500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M500"), }, }, { } /* terminate list */ }; static const char *oemstrs[] = { "Tecra M3,", }; int i; if (dmi_check_system(sysids)) return 1; for (i = 0; i < ARRAY_SIZE(oemstrs); i++) if (dmi_find_device(DMI_DEV_TYPE_OEM_STRING, oemstrs[i], NULL)) return 1; return 0;}static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg){ struct ata_host *host = dev_get_drvdata(&pdev->dev); unsigned long flags; int rc = 0; rc = ata_host_suspend(host, mesg); if (rc) return rc; /* Some braindamaged ACPI suspend implementations expect the * controller to be awake on entry; otherwise, it burns cpu * cycles and power trying to do something to the sleeping * beauty. */ if (piix_broken_suspend() && mesg.event == PM_EVENT_SUSPEND) { pci_save_state(pdev); /* mark its power state as "unknown", since we don't * know if e.g. the BIOS will change its device state * when we suspend. */ if (pdev->current_state == PCI_D0) pdev->current_state = PCI_UNKNOWN; /* tell resume that it's waking up from broken suspend */ spin_lock_irqsave(&host->lock, flags); host->flags |= PIIX_HOST_BROKEN_SUSPEND; spin_unlock_irqrestore(&host->lock, flags); } else ata_pci_device_do_suspend(pdev, mesg); return 0;}static int piix_pci_device_resume(struct pci_dev *pdev){ struct ata_host *host = dev_get_drvdata(&pdev->dev); unsigned long flags; int rc; if (host->flags & PIIX_HOST_BROKEN_SUSPEND) { spin_lock_irqsave(&host->lock, flags); host->flags &= ~PIIX_HOST_BROKEN_SUSPEND; spin_unlock_irqrestore(&host->lock, flags); pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* PCI device wasn't disabled during suspend. Use * pci_reenable_device() to avoid affecting the enable * count. */ rc = pci_reenable_device(pdev); if (rc) dev_printk(KERN_ERR, &pdev->dev, "failed to enable " "device after resume (%d)\n", rc); } else rc = ata_pci_device_do_resume(pdev); if (rc == 0) ata_host_resume(host); return rc;}#endifstatic u8 piix_vmw_bmdma_status(struct ata_port *ap){ return ata_bmdma_status(ap) & ~ATA_DMA_ERR;}#define AHCI_PCI_BAR 5#define AHCI_GLOBAL_CTL 0x04#define AHCI_ENABLE (1 << 31)static int piix_disable_ahci(struct pci_dev *pdev){ void __iomem *mmio; u32 tmp; int rc = 0; /* BUG: pci_enable_device has not yet been called. This * works because this device is usually set up by BIOS. */ if (!pci_resource_start(pdev, AHCI_PCI_BAR) || !pci_resource_len(pdev, AHCI_PCI_BAR)) return 0; mmio = pci_iomap(pdev, AHCI_PCI_BAR, 64); if (!mmio) return -ENOMEM; tmp = ioread32(mmio + AHCI_GLOBAL_CTL); if (tmp & AHCI_ENABLE) { tmp &= ~AHCI_ENABLE; iowrite32(tmp, mmio + AHCI_GLOBAL_CTL); tmp = ioread32(mmio + AHCI_GLOBAL_CTL); if (tmp & AHCI_ENABLE) rc = -EIO; } pci_iounmap(pdev, mmio); return rc;}/** * piix_check_450nx_errata - Check for problem 450NX setup * @ata_dev: the PCI device to check * * Check for the present of 450NX errata #19 and errata #25. If * they are found return an error code so we can turn off DMA */static int __devinit piix_check_450nx_errata(struct pci_dev *ata_dev){ struct pci_dev *pdev = NULL; u16 cfg; int no_piix_dma = 0; while ((pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev)) != NULL) { /* Look for 450NX PXB. Check for problem configurations A PCI quirk checks bit 6 already */ pci_read_config_word(pdev, 0x41, &cfg); /* Only on the original revision: IDE DMA can hang */ if (pdev->revision == 0x00) no_piix_dma = 1; /* On all revisions below 5 PXB bus lock must be disabled for IDE */ else if (cfg & (1<<14) && pdev->revision < 5) no_piix_dma = 2; } if (no_piix_dma) dev_printk(KERN_WARNING, &ata_dev->dev, "450NX errata present, disabling IDE DMA.\n"); if (no_piix_dma == 2) dev_printk(KERN_WARNING, &ata_dev->dev, "A BIOS update may resolve this.\n"); return no_piix_dma;}static void __devinit piix_init_pcs(struct pci_dev *pdev, struct ata_port_info *pinfo, const struct piix_map_db *map_db){ u16 pcs, new_pcs; pci_read_config_word(pdev, ICH5_PCS, &pcs); new_pcs = pcs | map_db->port_enable; if (new_pcs != pcs) { DPRINTK("updating PCS from 0x%x to 0x%x\n", pcs, new_pcs); pci_write_config_word(pdev, ICH5_PCS, new_pcs); msleep(150); }}static void __devinit piix_init_sata_map(struct pci_dev *pdev, struct ata_port_info *pinfo, const struct piix_map_db *map_db){ struct piix_host_priv *hpriv = pinfo[0].private_data; const int *map; int i, invalid_map = 0; u8 map_value; pci_read_config_byte(pdev, ICH5_PMR, &map_value); map = map_db->map[map_value & map_db->mask]; dev_printk(KERN_INFO, &pdev->dev, "MAP ["); for (i = 0; i < 4; i++) { switch (map[i]) { case RV: invalid_map = 1; printk(" XX"); break; case NA: printk(" --"); break; case IDE: WARN_ON((i & 1) || map[i + 1] != IDE); pinfo[i / 2] = piix_port_info[ich_pata_100]; pinfo[i / 2].private_data = hpriv; i++; printk(" IDE IDE"); break; default: printk(" P%d", map[i]); if (i & 1) pinfo[i / 2].flags |= ATA_FLAG_SLAVE_POSS; break; } } printk(" ]\n"); if (invalid_map) dev_printk(KERN_ERR, &pdev->dev, "invalid MAP value %u\n", map_value); hpriv->map = map;}static void piix_iocfg_bit18_quirk(struct pci_dev *pdev){ static const struct dmi_system_id sysids[] = { { /* Clevo M570U sets IOCFG bit 18 if the cdrom * isn't used to boot the system which * disables the channel. */ .ident = "M570U", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Clevo Co."), DMI_MATCH(DMI_PRODUCT_NAME, "M570U"), }, }, { } /* terminate list */ }; u32 iocfg; if (!dmi_check_system(sysids)) return; /* The datasheet says that bit 18 is NOOP but certain systems * seem to use it to disable a channel. Clear the bit on the * affected systems. */ pci_read_config_dword(pdev, PIIX_IOCFG, &iocfg); if (iocfg & (1 << 18)) { dev_printk(KERN_INFO, &pdev->dev, "applying IOCFG bit18 quirk\n"); iocfg &= ~(1 << 18); pci_write_config_dword(pdev, PIIX_IOCFG, iocfg); }}/** * piix_init_one - Register PIIX ATA PCI device with kernel services * @pdev: PCI device to register * @ent: Entry in piix_pci_tbl matching with @pdev * * Called from kernel PCI layer. We probe for combined mode (sigh), * and then hand over control to libata, for it to do the rest. * * LOCKING: * Inherited from PCI layer (may sleep). * * RETURNS: * Zero on success, or -ERRNO value. */static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ static int printed_version; struct device *dev = &pdev->dev; struct ata_port_info port_info[2]; const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] }; struct piix_host_priv *hpriv; unsigned long port_flags; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* no hotplugging support (FIXME) */ if (!in_module_init) return -ENODEV; hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; port_info[0] = piix_port_info[ent->driver_data]; port_info[1] = piix_port_info[ent->driver_data]; port_info[0].private_data = hpriv; port_info[1].private_data = hpriv; port_flags = port_info[0].flags; if (port_flags & PIIX_FLAG_AHCI) { u8 tmp; pci_read_config_byte(pdev, PIIX_SCC, &tmp); if (tmp == PIIX_AHCI_DEVICE) { int rc = piix_disable_ahci(pdev); if (rc) return rc; } } /* Initialize SATA map */ if (port_flags & ATA_FLAG_SATA) { piix_init_sata_map(pdev, port_info, piix_map_db_table[ent->driver_data]); piix_init_pcs(pdev, port_info, piix_map_db_table[ent->driver_data]); } /* apply IOCFG bit18 quirk */ piix_iocfg_bit18_quirk(pdev); /* On ICH5, some BIOSen disable the interrupt using the * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3. * On ICH6, this bit has the same effect, but only when * MSI is disabled (and it is disabled, as we don't use * message-signalled interrupts currently). */ if (port_flags & PIIX_FLAG_CHECKINTR) pci_intx(pdev, 1); if (piix_check_450nx_errata(pdev)) { /* This writes into the master table but it does not really matter for this errata as we will apply it to all the PIIX devices on the board */ port_info[0].mwdma_mask = 0; port_info[0].udma_mask = 0; port_info[1].mwdma_mask = 0; port_info[1].udma_mask = 0; } return ata_pci_init_one(pdev, ppi);}static int __init piix_init(void){ int rc; DPRINTK("pci_register_driver\n"); rc = pci_register_driver(&piix_pci_driver); if (rc) return rc; in_module_init = 0; DPRINTK("done\n"); return 0;}static void __exit piix_exit(void){ pci_unregister_driver(&piix_pci_driver);}module_init(piix_init);module_exit(piix_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -