📄 libata-sff.c
字号:
/** * ata_bmdma_error_handler - Stock error handler for BMDMA controller * @ap: port to handle error for * * Stock error handler for BMDMA controller. * * LOCKING: * Kernel thread context (may sleep) */void ata_bmdma_error_handler(struct ata_port *ap){ ata_reset_fn_t hardreset; hardreset = NULL; if (sata_scr_valid(&ap->link)) hardreset = sata_std_hardreset; ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, ata_std_postreset);}/** * ata_bmdma_post_internal_cmd - Stock post_internal_cmd for * BMDMA controller * @qc: internal command to clean up * * LOCKING: * Kernel thread context (may sleep) */void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc){ if (qc->ap->ioaddr.bmdma_addr) ata_bmdma_stop(qc);}/** * ata_sff_port_start - Set port up for dma. * @ap: Port to initialize * * Called just after data structures for each port are * initialized. Allocates space for PRD table if the device * is DMA capable SFF. * * May be used as the port_start() entry in ata_port_operations. * * LOCKING: * Inherited from caller. */int ata_sff_port_start(struct ata_port *ap){ if (ap->ioaddr.bmdma_addr) return ata_port_start(ap); return 0;}#ifdef CONFIG_PCIstatic int ata_resources_present(struct pci_dev *pdev, int port){ int i; /* Check the PCI resources for this channel are enabled */ port = port * 2; for (i = 0; i < 2; i ++) { if (pci_resource_start(pdev, port + i) == 0 || pci_resource_len(pdev, port + i) == 0) return 0; } return 1;}/** * ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host * @host: target ATA host * * Acquire PCI BMDMA resources and initialize @host accordingly. * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: * 0 on success, -errno otherwise. */int ata_pci_init_bmdma(struct ata_host *host){ struct device *gdev = host->dev; struct pci_dev *pdev = to_pci_dev(gdev); int i, rc; /* No BAR4 allocation: No DMA */ if (pci_resource_start(pdev, 4) == 0) return 0; /* TODO: If we get no DMA mask we should fall back to PIO */ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; /* request and iomap DMA region */ rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME); if (rc) { dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n"); return -ENOMEM; } host->iomap = pcim_iomap_table(pdev); for (i = 0; i < 2; i++) { struct ata_port *ap = host->ports[i]; void __iomem *bmdma = host->iomap[4] + 8 * i; if (ata_port_is_dummy(ap)) continue; ap->ioaddr.bmdma_addr = bmdma; if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) && (ioread8(bmdma + 2) & 0x80)) host->flags |= ATA_HOST_SIMPLEX; ata_port_desc(ap, "bmdma 0x%llx", (unsigned long long)pci_resource_start(pdev, 4) + 8 * i); } return 0;}/** * ata_pci_init_sff_host - acquire native PCI ATA resources and init host * @host: target ATA host * * Acquire native PCI ATA resources for @host and initialize the * first two ports of @host accordingly. Ports marked dummy are * skipped and allocation failure makes the port dummy. * * Note that native PCI resources are valid even for legacy hosts * as we fix up pdev resources array early in boot, so this * function can be used for both native and legacy SFF hosts. * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: * 0 if at least one port is initialized, -ENODEV if no port is * available. */int ata_pci_init_sff_host(struct ata_host *host){ struct device *gdev = host->dev; struct pci_dev *pdev = to_pci_dev(gdev); unsigned int mask = 0; int i, rc; /* request, iomap BARs and init port addresses accordingly */ for (i = 0; i < 2; i++) { struct ata_port *ap = host->ports[i]; int base = i * 2; void __iomem * const *iomap; if (ata_port_is_dummy(ap)) continue; /* Discard disabled ports. Some controllers show * their unused channels this way. Disabled ports are * made dummy. */ if (!ata_resources_present(pdev, i)) { ap->ops = &ata_dummy_port_ops; continue; } rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME); if (rc) { dev_printk(KERN_WARNING, gdev, "failed to request/iomap BARs for port %d " "(errno=%d)\n", i, rc); if (rc == -EBUSY) pcim_pin_device(pdev); ap->ops = &ata_dummy_port_ops; continue; } host->iomap = iomap = pcim_iomap_table(pdev); ap->ioaddr.cmd_addr = iomap[base]; ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr = (void __iomem *) ((unsigned long)iomap[base + 1] | ATA_PCI_CTL_OFS); ata_std_ports(&ap->ioaddr); ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx", (unsigned long long)pci_resource_start(pdev, base), (unsigned long long)pci_resource_start(pdev, base + 1)); mask |= 1 << i; } if (!mask) { dev_printk(KERN_ERR, gdev, "no available native port\n"); return -ENODEV; } return 0;}/** * ata_pci_prepare_sff_host - helper to prepare native PCI ATA host * @pdev: target PCI device * @ppi: array of port_info, must be enough for two ports * @r_host: out argument for the initialized ATA host * * Helper to allocate ATA host for @pdev, acquire all native PCI * resources and initialize it accordingly in one go. * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: * 0 on success, -errno otherwise. */int ata_pci_prepare_sff_host(struct pci_dev *pdev, const struct ata_port_info * const * ppi, struct ata_host **r_host){ struct ata_host *host; int rc; if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) return -ENOMEM; host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); if (!host) { dev_printk(KERN_ERR, &pdev->dev, "failed to allocate ATA host\n"); rc = -ENOMEM; goto err_out; } rc = ata_pci_init_sff_host(host); if (rc) goto err_out; /* init DMA related stuff */ rc = ata_pci_init_bmdma(host); if (rc) goto err_bmdma; devres_remove_group(&pdev->dev, NULL); *r_host = host; return 0; err_bmdma: /* This is necessary because PCI and iomap resources are * merged and releasing the top group won't release the * acquired resources if some of those have been acquired * before entering this function. */ pcim_iounmap_regions(pdev, 0xf); err_out: devres_release_group(&pdev->dev, NULL); return rc;}/** * ata_pci_init_one - Initialize/register PCI IDE host controller * @pdev: Controller to be initialized * @ppi: array of port_info, must be enough for two ports * * This is a helper function which can be called from a driver's * xxx_init_one() probe function if the hardware uses traditional * IDE taskfile registers. * * This function calls pci_enable_device(), reserves its register * regions, sets the dma mask, enables bus master mode, and calls * ata_device_add() * * ASSUMPTION: * Nobody makes a single channel controller that appears solely as * the secondary legacy port on PCI. * * LOCKING: * Inherited from PCI layer (may sleep). * * RETURNS: * Zero on success, negative on errno-based value on error. */int ata_pci_init_one(struct pci_dev *pdev, const struct ata_port_info * const * ppi){ struct device *dev = &pdev->dev; const struct ata_port_info *pi = NULL; struct ata_host *host = NULL; u8 mask; int legacy_mode = 0; int i, rc; DPRINTK("ENTER\n"); /* look up the first valid port_info */ for (i = 0; i < 2 && ppi[i]; i++) { if (ppi[i]->port_ops != &ata_dummy_port_ops) { pi = ppi[i]; break; } } if (!pi) { dev_printk(KERN_ERR, &pdev->dev, "no valid port_info specified\n"); return -EINVAL; } if (!devres_open_group(dev, NULL, GFP_KERNEL)) return -ENOMEM; /* FIXME: Really for ATA it isn't safe because the device may be multi-purpose and we want to leave it alone if it was already enabled. Secondly for shared use as Arjan says we want refcounting Checking dev->is_enabled is insufficient as this is not set at boot for the primary video which is BIOS enabled */ rc = pcim_enable_device(pdev); if (rc) goto err_out; if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { u8 tmp8; /* TODO: What if one channel is in native mode ... */ pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); mask = (1 << 2) | (1 << 0); if ((tmp8 & mask) != mask) legacy_mode = 1;#if defined(CONFIG_NO_ATA_LEGACY) /* Some platforms with PCI limits cannot address compat port space. In that case we punt if their firmware has left a device in compatibility mode */ if (legacy_mode) { printk(KERN_ERR "ata: Compatibility mode ATA is not supported on this platform, skipping.\n"); rc = -EOPNOTSUPP; goto err_out; }#endif } /* prepare host */ rc = ata_pci_prepare_sff_host(pdev, ppi, &host); if (rc) goto err_out; pci_set_master(pdev); /* start host and request IRQ */ rc = ata_host_start(host); if (rc) goto err_out; if (!legacy_mode && pdev->irq) { /* We may have no IRQ assigned in which case we can poll. This shouldn't happen on a sane system but robustness is cheap in this case */ rc = devm_request_irq(dev, pdev->irq, pi->port_ops->irq_handler, IRQF_SHARED, DRV_NAME, host); if (rc) goto err_out; ata_port_desc(host->ports[0], "irq %d", pdev->irq); ata_port_desc(host->ports[1], "irq %d", pdev->irq); } else if (legacy_mode) { if (!ata_port_is_dummy(host->ports[0])) { rc = devm_request_irq(dev, ATA_PRIMARY_IRQ(pdev), pi->port_ops->irq_handler, IRQF_SHARED, DRV_NAME, host); if (rc) goto err_out; ata_port_desc(host->ports[0], "irq %d", ATA_PRIMARY_IRQ(pdev)); } if (!ata_port_is_dummy(host->ports[1])) { rc = devm_request_irq(dev, ATA_SECONDARY_IRQ(pdev), pi->port_ops->irq_handler, IRQF_SHARED, DRV_NAME, host); if (rc) goto err_out; ata_port_desc(host->ports[1], "irq %d", ATA_SECONDARY_IRQ(pdev)); } } /* register */ rc = ata_host_register(host, pi->sht); if (rc) goto err_out; devres_remove_group(dev, NULL); return 0;err_out: devres_release_group(dev, NULL); return rc;}/** * ata_pci_clear_simplex - attempt to kick device out of simplex * @pdev: PCI device * * Some PCI ATA devices report simplex mode but in fact can be told to * enter non simplex mode. This implements the necessary logic to * perform the task on such devices. Calling it on other devices will * have -undefined- behaviour. */int ata_pci_clear_simplex(struct pci_dev *pdev){ unsigned long bmdma = pci_resource_start(pdev, 4); u8 simplex; if (bmdma == 0) return -ENOENT; simplex = inb(bmdma + 0x02); outb(simplex & 0x60, bmdma + 0x02); simplex = inb(bmdma + 0x02); if (simplex & 0x80) return -EOPNOTSUPP; return 0;}unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer_mask){ /* Filter out DMA modes if the device has been configured by the BIOS as PIO only */ if (adev->link->ap->ioaddr.bmdma_addr == NULL) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); return xfer_mask;}#endif /* CONFIG_PCI */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -