X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fata%2Flibata-sff.c;h=60cd4b179766e9683e82b39552c3a5546afa4b44;hb=fa2fc7f4813bfec1ae3232d49e3befbd601e8a6f;hp=fa1c22c7b38fef986e0029c139a471e53667459b;hpb=4031826b3ca40982880f6b9f2282c7d7fad60d77;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index fa1c22c..60cd4b1 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 * Please ALWAYS copy linux-ide@vger.kernel.org @@ -64,46 +64,6 @@ u8 ata_irq_on(struct ata_port *ap) return tmp; } -u8 ata_dummy_irq_on (struct ata_port *ap) { return 0; } - -/** - * ata_irq_ack - Acknowledge a device interrupt. - * @ap: Port on which interrupts are enabled. - * - * Wait up to 10 ms for legacy IDE device to become idle (BUSY - * or BUSY+DRQ clear). Obtain dma status and port status from - * device. Clear the interrupt. Return port status. - * - * LOCKING: - */ - -u8 ata_irq_ack(struct ata_port *ap, unsigned int chk_drq) -{ - unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY; - u8 host_stat = 0, post_stat = 0, status; - - status = ata_busy_wait(ap, bits, 1000); - if (status & bits) - if (ata_msg_err(ap)) - printk(KERN_ERR "abnormal status 0x%X\n", status); - - if (ap->ioaddr.bmdma_addr) { - /* get controller status; clear intr, err bits */ - host_stat = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - iowrite8(host_stat | ATA_DMA_INTR | ATA_DMA_ERR, - ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - - post_stat = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); - } - if (ata_msg_intr(ap)) - printk(KERN_INFO "%s: irq ack: host_stat 0x%X, new host_stat 0x%X, drv_stat 0x%X\n", - __FUNCTION__, - host_stat, post_stat, status); - return status; -} - -u8 ata_dummy_irq_ack(struct ata_port *ap, unsigned int chk_drq) { return 0; } - /** * ata_tf_load - send taskfile registers to host controller * @ap: Port to which output is sent @@ -187,7 +147,9 @@ void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) * @tf: ATA taskfile register set for storing input * * Reads ATA taskfile registers for currently-selected device - * into @tf. + * into @tf. Assumes the device has a fully SFF compliant task file + * layout and behaviour. If you device does not (eg has a different + * status method) then you will need to provide a replacement tf_read * * LOCKING: * Inherited from caller. @@ -211,6 +173,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; } } @@ -286,7 +250,7 @@ void ata_bmdma_setup(struct ata_queued_cmd *qc) * LOCKING: * spin_lock_irqsave(host lock) */ -void ata_bmdma_start (struct ata_queued_cmd *qc) +void ata_bmdma_start(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; u8 dmactl; @@ -295,7 +259,7 @@ void ata_bmdma_start (struct ata_queued_cmd *qc) dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); iowrite8(dmactl | ATA_DMA_START, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - /* Strictly, one may wish to issue a readb() here, to + /* Strictly, one may wish to issue an ioread8() here, to * flush the mmio write. However, control also passes * to the hardware at this point, and it will interrupt * us when we are to resume control. So, in effect, @@ -305,6 +269,9 @@ void ata_bmdma_start (struct ata_queued_cmd *qc) * is expected, so I think it is best to not add a readb() * without first all the MMIO ATA cards/mobos. * Or maybe I'm just being paranoid. + * + * FIXME: The posting of this write means I/O starts are + * unneccessarily delayed for MMIO */ } @@ -440,7 +407,7 @@ void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset, unsigned long flags; int thaw = 0; - qc = __ata_qc_from_tag(ap, ap->active_tag); + qc = __ata_qc_from_tag(ap, ap->link.active_tag); if (qc && !(qc->flags & ATA_QCFLAG_FAILED)) qc = NULL; @@ -450,7 +417,7 @@ void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ap->hsm_task_state = HSM_ST_IDLE; if (qc && (qc->tf.protocol == ATA_PROT_DMA || - qc->tf.protocol == ATA_PROT_ATAPI_DMA)) { + qc->tf.protocol == ATAPI_PROT_DMA)) { u8 host_stat; host_stat = ap->ops->bmdma_status(ap); @@ -495,7 +462,7 @@ void ata_bmdma_error_handler(struct ata_port *ap) ata_reset_fn_t hardreset; hardreset = NULL; - if (sata_scr_valid(ap)) + if (sata_scr_valid(&ap->link)) hardreset = sata_std_hardreset; ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, @@ -571,6 +538,10 @@ int ata_pci_init_bmdma(struct ata_host *host) 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) @@ -580,7 +551,7 @@ int ata_pci_init_bmdma(struct ata_host *host) return rc; /* request and iomap DMA region */ - rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME); + rc = pcim_iomap_regions(pdev, 1 << 4, dev_driver_string(gdev)); if (rc) { dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n"); return -ENOMEM; @@ -598,19 +569,26 @@ int ata_pci_init_bmdma(struct ata_host *host) 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_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 +596,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); @@ -643,7 +621,8 @@ int ata_pci_init_native_host(struct ata_host *host) continue; } - rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME); + rc = pcim_iomap_regions(pdev, 0x3 << base, + dev_driver_string(gdev)); if (rc) { dev_printk(KERN_WARNING, gdev, "failed to request/iomap BARs for port %d " @@ -661,6 +640,10 @@ int ata_pci_init_native_host(struct ata_host *host) ((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; } @@ -673,7 +656,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 +670,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 +688,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,218 +713,96 @@ 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 + * ata_pci_activate_sff_host - start SFF host, request IRQ and register it + * @host: target SFF ATA host + * @irq_handler: irq_handler used when requesting IRQ(s) + * @sht: scsi_host_template to use when registering the host * - * 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. + * This is the counterpart of ata_host_activate() for SFF ATA + * hosts. This separate helper is necessary because SFF hosts + * use two separate interrupts in legacy mode. * * LOCKING: * Inherited from calling layer (may sleep). * * RETURNS: - * 0 if at least one port is initialized, -ENODEV if no port is - * available. + * 0 on success, -errno otherwise. */ -static int ata_init_legacy_host(struct ata_host *host, int *was_busy) +int ata_pci_activate_sff_host(struct ata_host *host, + irq_handler_t irq_handler, + struct scsi_host_template *sht) { - struct device *gdev = host->dev; - struct ata_legacy_devres *legacy_dr; - int i, rc; - - if (!devres_open_group(gdev, NULL, GFP_KERNEL)) - return -ENOMEM; + struct device *dev = host->dev; + struct pci_dev *pdev = to_pci_dev(dev); + const char *drv_name = dev_driver_string(host->dev); + int legacy_mode = 0, rc; - 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); + rc = ata_host_start(host); + if (rc) + return rc; - for (i = 0; i < 2; i++) { - if (ata_port_is_dummy(host->ports[i])) - continue; + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + u8 tmp8, mask; - 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; + /* 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"); + return -EOPNOTSUPP; } +#endif } - 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; - } + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return -ENOMEM; - 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; + if (!legacy_mode && pdev->irq) { + rc = devm_request_irq(dev, pdev->irq, irq_handler, + IRQF_SHARED, drv_name, host); + if (rc) + goto 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), + irq_handler, IRQF_SHARED, + drv_name, host); + if (rc) + goto out; + + ata_port_desc(host->ports[0], "irq %d", + ATA_PRIMARY_IRQ(pdev)); } - /* record irq allocation in legacy_dr */ - legacy_dr->irq[i] = irq; - legacy_dr->irq_dev_id[i] = dev_id[i]; + if (!ata_port_is_dummy(host->ports[1])) { + rc = devm_request_irq(dev, ATA_SECONDARY_IRQ(pdev), + irq_handler, IRQF_SHARED, + drv_name, host); + if (rc) + goto out; - /* only used to print info */ - if (i == 0) - host->irq = irq; - else - host->irq2 = irq; + ata_port_desc(host->ports[1], "irq %d", + ATA_SECONDARY_IRQ(pdev)); + } } - return 0; + rc = ata_host_register(host, sht); + out: + if (rc == 0) + devres_remove_group(dev, NULL); + else + devres_release_group(dev, NULL); - err_out: - ata_legacy_free_irqs(legacy_dr); return rc; } @@ -974,8 +835,6 @@ int ata_pci_init_one(struct pci_dev *pdev, 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"); @@ -997,99 +856,24 @@ int ata_pci_init_one(struct pci_dev *pdev, 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; + goto 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 - } - - /* 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; - 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 */ - rc = ata_host_start(host); + /* prepare and activate SFF host */ + rc = ata_pci_prepare_sff_host(pdev, ppi, &host); if (rc) - goto err_out; - - if (!legacy_mode) { - rc = devm_request_irq(dev, pdev->irq, pi->port_ops->irq_handler, - IRQF_SHARED, DRV_NAME, host); - host->irq = pdev->irq; - } 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 }; - - rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id); - } - if (rc) - goto err_out; + goto out; - /* register */ - rc = ata_host_register(host, pi->sht); - if (rc) - goto err_out; - - devres_remove_group(dev, NULL); - return 0; + pci_set_master(pdev); + rc = ata_pci_activate_sff_host(host, pi->port_ops->irq_handler, + pi->sht); + out: + if (rc == 0) + devres_remove_group(&pdev->dev, NULL); + else + devres_release_group(&pdev->dev, NULL); -err_out: - devres_release_group(dev, NULL); return rc; } @@ -1098,7 +882,7 @@ err_out: * @pdev: PCI device * * Some PCI ATA devices report simplex mode but in fact can be told to - * enter non simplex mode. This implements the neccessary logic 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. */ @@ -1124,7 +908,7 @@ unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer /* Filter out DMA modes if the device has been configured by the BIOS as PIO only */ - if (adev->ap->ioaddr.bmdma_addr == 0) + if (adev->link->ap->ioaddr.bmdma_addr == NULL) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); return xfer_mask; }