Implement the following legacy ATA init helpers. * ata_legacy_acquire_resources() : acquire legacy ATA resources * ata_legacy_release_resources() : release legacy ATA resources * ata_legacy_init_ports() : init legacy port addresses * ata_legacy_request_irqs() : request legacy ATA IRQs * ata_legacy_free_irqs() : free legacy ATA IRQs These helpers can be used independently or called implictly from other bus helpers which has support for legacy ATA device for compatibility (e.g. PCI). Signed-off-by: Tejun Heo <htejun@xxxxxxxxx> --- drivers/ata/Kconfig | 5 + drivers/ata/Makefile | 3 + drivers/ata/libata-legacy.c | 241 +++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 11 ++ include/linux/libata.h | 15 ++- 5 files changed, 273 insertions(+), 2 deletions(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 2109d75..1a98559 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -15,6 +15,11 @@ config ATA that "speaks" the ATA protocol, also called ATA controller), because you will be asked for it. +config ATA_LEGACY + bool + depends on ATA + default n + config SATA_AHCI tristate "AHCI SATA support" depends on ATA && PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index c17ae9c..61d3e76 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -19,3 +19,6 @@ obj-$(CONFIG_PDC_ADMA) += pdc_adma.o libata-objs := libata-core.o libata-scsi.o libata-bmdma.o libata-eh.o +ifeq ($(CONFIG_ATA_LEGACY),y) +libata-objs += libata-legacy.o +endif diff --git a/drivers/ata/libata-legacy.c b/drivers/ata/libata-legacy.c new file mode 100644 index 0000000..00b70b0 --- /dev/null +++ b/drivers/ata/libata-legacy.c @@ -0,0 +1,241 @@ +/* + * libata-legacy.c - helper library for legacy ATA + * + * Maintained by: Jeff Garzik <jgarzik@xxxxxxxxx> + * Please ALWAYS copy linux-ide@xxxxxxxxxxxxxxx + * on emails. + * + * Copyright 2006 Tejun Heo <htejun@xxxxxxxxx> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available from http://www.t13.org/ and + * http://www.sata-io.org/ + * + */ + +#include <linux/kernel.h> +#include <linux/libata.h> + +#include "libata.h" + +static unsigned long ata_legacy_addr(int port, int sel) +{ + if (port == 0) { + if (sel == 0) + return ATA_PRIMARY_CMD; + else + return ATA_PRIMARY_CTL; + } else { + if (sel == 0) + return ATA_SECONDARY_CMD; + else + return ATA_SECONDARY_CTL; + } +} + +static int ata_legacy_acquire_port(struct ata_port *ap) +{ + struct ata_host *host = ap->host; + unsigned long cmd_addr = ata_legacy_addr(ap->port_no, 0); + + if (request_region(cmd_addr, 8, "libata") != NULL) + host->legacy_flags |= ATA_LEGACY_RES_PRI << ap->port_no; + else { + struct resource *conflict, res; + + res.start = cmd_addr; + res.end = cmd_addr + 8 - 1; + conflict = ____request_resource(&ioport_resource, &res); + + if (strcmp(conflict->name, "libata")) { + printk(KERN_WARNING "ata: 0x%0lX IDE port busy\n", + cmd_addr); + host->flags |= ATA_HOST_DEV_BUSY; + return -EBUSY; + } + printk("ata: 0x%0lX IDE port preallocated\n", cmd_addr); + } + + return 0; +} + +/** + * ata_legacy_acquire_resources - acquire legacy ATA resources + * @host: target ATA host + * @p_reason: out arg for error message (can be NULL) + * + * Acquire legacy ATA resources for ports specified by + * ATA_PORT_PRIMARY/SECONDARY mask in host->legacy_flags. If the + * port is busy, this function makes the port dummy instead of + * failing. This is to allow legacy ports to be driven by other + * drivers. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_legacy_acquire_resources(struct ata_host *host, const char **p_reason) +{ + int i; + + for (i = 0; i < 2; i++) { + if (!(host->legacy_flags & (ATA_PORT_PRIMARY << i))) + continue; + BUG_ON(i >= host->n_ports); + + if (ata_legacy_acquire_port(host->ports[i])) + host->ports[i]->ops = &ata_dummy_port_ops; + } + + return 0; +} + +/** + * ata_legacy_release_resources - release legacy ATA resources + * @host: target ATA host + * + * Free all legacy ATA resources @host is holding. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ +void ata_legacy_release_resources(struct ata_host *host) +{ + int i; + + for (i = 0; i < 2; i++) { + if (host->legacy_flags & (ATA_LEGACY_RES_PRI << i)) { + release_region(ata_legacy_addr(i, 0), 8); + host->legacy_flags &= ~(ATA_LEGACY_RES_PRI << i); + } + } +} + +/** + * ata_legacy_init_ports - initialize legacy ATA port addresses + * @host: target ATA host + * + * Initialize legacy ATA port addresses for non-dummy legacy + * ports in @host. + * + * LOCKING: + * Inherited from calling layer. + */ +void ata_legacy_init_ports(struct ata_host *host) +{ + int i; + + for (i = 0; i < 2; i++) { + struct ata_port *ap = host->ports[i]; + + if (host->legacy_flags & (ATA_PORT_PRIMARY << i) && + !ata_port_is_dummy(ap)) { + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned long cmd_addr = ata_legacy_addr(i, 0); + unsigned long ctl_addr = ata_legacy_addr(i, 1); + + ioaddr->cmd_addr = cmd_addr; + ioaddr->altstatus_addr = ctl_addr; + ioaddr->ctl_addr = ctl_addr; + ata_std_ports(ioaddr); + } + } +} + +/** + * ata_legacy_request_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 + * @p_reason: out arg for error message (can be NULL) + * + * 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. + */ +int ata_legacy_request_irqs(struct ata_host *host, + irqreturn_t (* const * handler)(int, void *, struct pt_regs *), + const unsigned int *irq_flags, void * const *dev_id, + const char **p_reason) +{ + const unsigned int legacy_irq[] = { 14, 15 }; + const char *reason; + int i, rc; + + for (i = 0; i < 2; i++) { + struct ata_port *ap = host->ports[i]; + unsigned int irq = legacy_irq[i]; + + if (!(host->legacy_flags & (ATA_PORT_PRIMARY << i)) || + ata_port_is_dummy(ap)) + continue; + + if (!handler[i]) { + reason = "NULL handler"; + rc = -EINVAL; + goto err; + } + + rc = ata_host_request_irq_marker(host, irq, + handler[i], irq_flags[i], dev_id[i], + ata_legacy_request_irqs, &reason); + if (rc) + goto err; + + /* only for info printing */ + if (i == 0) + host->irq = irq; + else + host->irq2 = irq; + } + + return 0; + + err: + ata_legacy_free_irqs(host); + if (p_reason) + *p_reason = reason; + return rc; +} + +/** + * ata_legacy_free_irqs - free legacy ATA IRQs + * @host: target ATA host + * + * Free all legacy ATA IRQs of @host. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ +void ata_legacy_free_irqs(struct ata_host *host) +{ + ata_host_free_irqs_marker(host, ata_legacy_request_irqs); +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1d24254..0601c4a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -117,6 +117,17 @@ extern void ata_schedule_scsi_eh(struct extern void ata_scsi_dev_rescan(void *data); extern int ata_bus_probe(struct ata_port *ap); +/* libata-legacy.c */ +extern int ata_legacy_acquire_resources(struct ata_host *host, + const char **p_reason); +extern void ata_legacy_release_resources(struct ata_host *host); +extern void ata_legacy_init_ports(struct ata_host *host); +extern int ata_legacy_request_irqs(struct ata_host *host, + irqreturn_t (* const * handler)(int, void *, struct pt_regs *), + const unsigned int *irq_flags, void * const *dev_id, + const char **p_reason); +extern void ata_legacy_free_irqs(struct ata_host *host); + /* libata-eh.c */ extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); diff --git a/include/linux/libata.h b/include/linux/libata.h index b050517..9535f54 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -200,8 +200,16 @@ enum { ATA_QCFLAG_EH_SCHEDULED = (1 << 18), /* EH scheduled (obsolete) */ /* host set flags */ - ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host only */ - + ATA_HOST_SIMPLEX = (1 << 0), /* host is simplex, one DMA channel per host only */ + ATA_HOST_DEV_BUSY = (1 << 1), /* host device was busy on init */ + + /* legacy flags */ + /* BIT 0 and 1 are reserved for ATA_PORT_PRIMARY and SECONDARY */ + ATA_LEGACY_MASK = (1 << 0) | (1 << 1), + + ATA_LEGACY_RES_PRI = (1 << 8), + ATA_LEGACY_RES_SEC = (1 << 9), + /* various lengths of time */ ATA_TMOUT_BOOT = 30 * HZ, /* heuristic */ ATA_TMOUT_BOOT_QUICK = 7 * HZ, /* heuristic */ @@ -376,8 +384,11 @@ struct ata_host { void *private_data; const struct ata_port_operations *ops; unsigned long flags; + int simplex_claimed; /* Keep seperate in case we ever need to do this locked */ + unsigned int legacy_flags; + struct list_head irq_list; /* list of acquired irqs */ struct ata_port *ports[0]; }; -- 1.4.1.1 - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html