diff options
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r-- | drivers/ata/libata-sff.c | 297 |
1 files changed, 38 insertions, 259 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index ce84805ba5f..6c289c7b132 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1,5 +1,5 @@ /* - * libata-bmdma.c - helper library for PCI IDE BMDMA + * libata-sff.c - helper library for PCI IDE BMDMA * * Maintained by: Jeff Garzik <jgarzik@pobox.com> * Please ALWAYS copy linux-ide@vger.kernel.org @@ -211,6 +211,8 @@ void ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) tf->hob_lbal = ioread8(ioaddr->lbal_addr); tf->hob_lbam = ioread8(ioaddr->lbam_addr); tf->hob_lbah = ioread8(ioaddr->lbah_addr); + iowrite8(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; } } @@ -604,13 +606,17 @@ int ata_pci_init_bmdma(struct ata_host *host) } /** - * ata_pci_init_native_host - acquire native ATA resources and init host + * 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). * @@ -618,7 +624,7 @@ int ata_pci_init_bmdma(struct ata_host *host) * 0 if at least one port is initialized, -ENODEV if no port is * available. */ -int ata_pci_init_native_host(struct ata_host *host) +int ata_pci_init_sff_host(struct ata_host *host) { struct device *gdev = host->dev; struct pci_dev *pdev = to_pci_dev(gdev); @@ -673,7 +679,7 @@ int ata_pci_init_native_host(struct ata_host *host) } /** - * ata_pci_prepare_native_host - helper to prepare native PCI ATA host + * 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 @@ -687,9 +693,9 @@ int ata_pci_init_native_host(struct ata_host *host) * RETURNS: * 0 on success, -errno otherwise. */ -int ata_pci_prepare_native_host(struct pci_dev *pdev, - const struct ata_port_info * const * ppi, - struct ata_host **r_host) +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; @@ -705,7 +711,7 @@ int ata_pci_prepare_native_host(struct pci_dev *pdev, goto err_out; } - rc = ata_pci_init_native_host(host); + rc = ata_pci_init_sff_host(host); if (rc) goto err_out; @@ -730,221 +736,6 @@ int ata_pci_prepare_native_host(struct pci_dev *pdev, return rc; } -struct ata_legacy_devres { - unsigned int mask; - unsigned long cmd_port[2]; - void __iomem * cmd_addr[2]; - void __iomem * ctl_addr[2]; - unsigned int irq[2]; - void * irq_dev_id[2]; -}; - -static void ata_legacy_free_irqs(struct ata_legacy_devres *legacy_dr) -{ - int i; - - for (i = 0; i < 2; i++) { - if (!legacy_dr->irq[i]) - continue; - - free_irq(legacy_dr->irq[i], legacy_dr->irq_dev_id[i]); - legacy_dr->irq[i] = 0; - legacy_dr->irq_dev_id[i] = NULL; - } -} - -static void ata_legacy_release(struct device *gdev, void *res) -{ - struct ata_legacy_devres *this = res; - int i; - - ata_legacy_free_irqs(this); - - for (i = 0; i < 2; i++) { - if (this->cmd_addr[i]) - ioport_unmap(this->cmd_addr[i]); - if (this->ctl_addr[i]) - ioport_unmap(this->ctl_addr[i]); - if (this->cmd_port[i]) - release_region(this->cmd_port[i], 8); - } -} - -static int ata_init_legacy_port(struct ata_port *ap, - struct ata_legacy_devres *legacy_dr) -{ - struct ata_host *host = ap->host; - int port_no = ap->port_no; - unsigned long cmd_port, ctl_port; - - if (port_no == 0) { - cmd_port = ATA_PRIMARY_CMD; - ctl_port = ATA_PRIMARY_CTL; - } else { - cmd_port = ATA_SECONDARY_CMD; - ctl_port = ATA_SECONDARY_CTL; - } - - /* request cmd_port */ - if (request_region(cmd_port, 8, "libata")) - legacy_dr->cmd_port[port_no] = cmd_port; - else { - dev_printk(KERN_WARNING, host->dev, - "0x%0lX IDE port busy\n", cmd_port); - return -EBUSY; - } - - /* iomap cmd and ctl ports */ - legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8); - legacy_dr->ctl_addr[port_no] = ioport_map(ctl_port, 1); - if (!legacy_dr->cmd_addr[port_no] || !legacy_dr->ctl_addr[port_no]) { - dev_printk(KERN_WARNING, host->dev, - "failed to map cmd/ctl ports\n"); - return -ENOMEM; - } - - /* init IO addresses */ - ap->ioaddr.cmd_addr = legacy_dr->cmd_addr[port_no]; - ap->ioaddr.altstatus_addr = legacy_dr->ctl_addr[port_no]; - ap->ioaddr.ctl_addr = legacy_dr->ctl_addr[port_no]; - ata_std_ports(&ap->ioaddr); - - return 0; -} - -/** - * ata_init_legacy_host - acquire legacy ATA resources and init ATA host - * @host: target ATA host - * @was_busy: out parameter, indicates whether any port was busy - * - * Acquire legacy ATA resources for the first two ports of @host - * and initialize it accordingly. Ports marked dummy are skipped - * and resource acquistion failure makes the port dummy. - * - * LOCKING: - * Inherited from calling layer (may sleep). - * - * RETURNS: - * 0 if at least one port is initialized, -ENODEV if no port is - * available. - */ -static int ata_init_legacy_host(struct ata_host *host, int *was_busy) -{ - struct device *gdev = host->dev; - struct ata_legacy_devres *legacy_dr; - int i, rc; - - if (!devres_open_group(gdev, NULL, GFP_KERNEL)) - return -ENOMEM; - - rc = -ENOMEM; - legacy_dr = devres_alloc(ata_legacy_release, sizeof(*legacy_dr), - GFP_KERNEL); - if (!legacy_dr) - goto err_out; - devres_add(gdev, legacy_dr); - - for (i = 0; i < 2; i++) { - if (ata_port_is_dummy(host->ports[i])) - continue; - - rc = ata_init_legacy_port(host->ports[i], legacy_dr); - if (rc == 0) - legacy_dr->mask |= 1 << i; - else { - if (rc == -EBUSY) - (*was_busy)++; - host->ports[i]->ops = &ata_dummy_port_ops; - } - } - - if (!legacy_dr->mask) { - dev_printk(KERN_ERR, gdev, "no available legacy port\n"); - return -ENODEV; - } - - devres_remove_group(gdev, NULL); - return 0; - - err_out: - devres_release_group(gdev, NULL); - return rc; -} - -/** - * ata_request_legacy_irqs - request legacy ATA IRQs - * @host: target ATA host - * @handler: array of IRQ handlers - * @irq_flags: array of IRQ flags - * @dev_id: array of IRQ dev_ids - * - * Request legacy IRQs for non-dummy legacy ports in @host. All - * IRQ parameters are passed as array to allow ports to have - * separate IRQ handlers. - * - * LOCKING: - * Inherited from calling layer (may sleep). - * - * RETURNS: - * 0 on success, -errno otherwise. - */ -static int ata_request_legacy_irqs(struct ata_host *host, - irq_handler_t const *handler, - const unsigned int *irq_flags, - void * const *dev_id) -{ - struct device *gdev = host->dev; - struct ata_legacy_devres *legacy_dr; - int i, rc; - - legacy_dr = devres_find(host->dev, ata_legacy_release, NULL, NULL); - BUG_ON(!legacy_dr); - - for (i = 0; i < 2; i++) { - unsigned int irq; - - /* FIXME: ATA_*_IRQ() should take generic device not pci_dev */ - if (i == 0) - irq = ATA_PRIMARY_IRQ(to_pci_dev(gdev)); - else - irq = ATA_SECONDARY_IRQ(to_pci_dev(gdev)); - - if (!(legacy_dr->mask & (1 << i))) - continue; - - if (!handler[i]) { - dev_printk(KERN_ERR, gdev, - "NULL handler specified for port %d\n", i); - rc = -EINVAL; - goto err_out; - } - - rc = request_irq(irq, handler[i], irq_flags[i], DRV_NAME, - dev_id[i]); - if (rc) { - dev_printk(KERN_ERR, gdev, - "irq %u request failed (errno=%d)\n", irq, rc); - goto err_out; - } - - /* record irq allocation in legacy_dr */ - legacy_dr->irq[i] = irq; - legacy_dr->irq_dev_id[i] = dev_id[i]; - - /* only used to print info */ - if (i == 0) - host->irq = irq; - else - host->irq2 = irq; - } - - return 0; - - err_out: - ata_legacy_free_irqs(legacy_dr); - return rc; -} - /** * ata_pci_init_one - Initialize/register PCI IDE host controller * @pdev: Controller to be initialized @@ -1029,35 +820,11 @@ int ata_pci_init_one(struct pci_dev *pdev, #endif } - /* alloc and init host */ - host = ata_host_alloc_pinfo(dev, ppi, 2); - if (!host) { - dev_printk(KERN_ERR, &pdev->dev, - "failed to allocate ATA host\n"); - rc = -ENOMEM; + /* prepare host */ + rc = ata_pci_prepare_sff_host(pdev, ppi, &host); + if (rc) goto err_out; - } - - if (!legacy_mode) { - rc = ata_pci_init_native_host(host); - if (rc) - goto err_out; - } else { - int was_busy = 0; - - rc = ata_init_legacy_host(host, &was_busy); - if (was_busy) - pcim_pin_device(pdev); - if (rc) - goto err_out; - - /* request respective PCI regions, may fail */ - rc = pci_request_region(pdev, 1, DRV_NAME); - rc = pci_request_region(pdev, 3, DRV_NAME); - } - /* init BMDMA, may fail */ - ata_pci_init_bmdma(host); pci_set_master(pdev); /* start host and request IRQ */ @@ -1065,19 +832,31 @@ int ata_pci_init_one(struct pci_dev *pdev, if (rc) goto err_out; - if (!legacy_mode) + if (!legacy_mode) { rc = devm_request_irq(dev, pdev->irq, pi->port_ops->irq_handler, IRQF_SHARED, DRV_NAME, host); - else { - irq_handler_t handler[2] = { host->ops->irq_handler, - host->ops->irq_handler }; - unsigned int irq_flags[2] = { IRQF_SHARED, IRQF_SHARED }; - void *dev_id[2] = { host, host }; + if (rc) + goto err_out; + host->irq = pdev->irq; + } else { + if (!ata_port_is_dummy(host->ports[0])) { + host->irq = ATA_PRIMARY_IRQ(pdev); + rc = devm_request_irq(dev, host->irq, + pi->port_ops->irq_handler, + IRQF_SHARED, DRV_NAME, host); + if (rc) + goto err_out; + } - rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id); + if (!ata_port_is_dummy(host->ports[1])) { + host->irq2 = ATA_SECONDARY_IRQ(pdev); + rc = devm_request_irq(dev, host->irq2, + pi->port_ops->irq_handler, + IRQF_SHARED, DRV_NAME, host); + if (rc) + goto err_out; + } } - if (rc) - goto err_out; /* register */ rc = ata_host_register(host, pi->sht); |