diff options
Diffstat (limited to 'drivers/ide/ide-io-std.c')
-rw-r--r-- | drivers/ide/ide-io-std.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c new file mode 100644 index 000000000..94bdcf1ea --- /dev/null +++ b/drivers/ide/ide-io-std.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> + +#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \ + defined(CONFIG_PARISC) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) +#include <asm/ide.h> +#else +#include <asm-generic/ide_iops.h> +#endif + +/* + * Conventional PIO operations for ATA devices + */ + +static u8 ide_inb(unsigned long port) +{ + return (u8) inb(port); +} + +static void ide_outb(u8 val, unsigned long port) +{ + outb(val, port); +} + +/* + * MMIO operations, typically used for SATA controllers + */ + +static u8 ide_mm_inb(unsigned long port) +{ + return (u8) readb((void __iomem *) port); +} + +static void ide_mm_outb(u8 value, unsigned long port) +{ + writeb(value, (void __iomem *) port); +} + +void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + else + outb(cmd, hwif->io_ports.command_addr); +} +EXPORT_SYMBOL_GPL(ide_exec_command); + +u8 ide_read_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.status_addr); + else + return inb(hwif->io_ports.status_addr); +} +EXPORT_SYMBOL_GPL(ide_read_status); + +u8 ide_read_altstatus(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.ctl_addr); + else + return inb(hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_read_altstatus); + +void ide_write_devctl(ide_hwif_t *hwif, u8 ctl) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + else + outb(ctl, hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_write_devctl); + +void ide_dev_select(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 select = drive->select | ATA_DEVICE_OBS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(select, (void __iomem *)hwif->io_ports.device_addr); + else + outb(select, hwif->io_ports.device_addr); +} +EXPORT_SYMBOL_GPL(ide_dev_select); + +void ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + void (*tf_outb)(u8 addr, unsigned long port); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (mmio) + tf_outb = ide_mm_outb; + else + tf_outb = ide_outb; + + if (valid & IDE_VALID_FEATURE) + tf_outb(tf->feature, io_ports->feature_addr); + if (valid & IDE_VALID_NSECT) + tf_outb(tf->nsect, io_ports->nsect_addr); + if (valid & IDE_VALID_LBAL) + tf_outb(tf->lbal, io_ports->lbal_addr); + if (valid & IDE_VALID_LBAM) + tf_outb(tf->lbam, io_ports->lbam_addr); + if (valid & IDE_VALID_LBAH) + tf_outb(tf->lbah, io_ports->lbah_addr); + if (valid & IDE_VALID_DEVICE) + tf_outb(tf->device, io_ports->device_addr); +} +EXPORT_SYMBOL_GPL(ide_tf_load); + +void ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + u8 (*tf_inb)(unsigned long port); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (mmio) + tf_inb = ide_mm_inb; + else + tf_inb = ide_inb; + + if (valid & IDE_VALID_ERROR) + tf->error = tf_inb(io_ports->feature_addr); + if (valid & IDE_VALID_NSECT) + tf->nsect = tf_inb(io_ports->nsect_addr); + if (valid & IDE_VALID_LBAL) + tf->lbal = tf_inb(io_ports->lbal_addr); + if (valid & IDE_VALID_LBAM) + tf->lbam = tf_inb(io_ports->lbam_addr); + if (valid & IDE_VALID_LBAH) + tf->lbah = tf_inb(io_ports->lbah_addr); + if (valid & IDE_VALID_DEVICE) + tf->device = tf_inb(io_ports->device_addr); +} +EXPORT_SYMBOL_GPL(ide_tf_read); + +/* + * Some localbus EIDE interfaces require a special access sequence + * when using 32-bit I/O instructions to transfer data. We call this + * the "vlb_sync" sequence, which consists of three successive reads + * of the sector count register location, with interrupts disabled + * to ensure that the reads all happen together. + */ +static void ata_vlb_sync(unsigned long port) +{ + (void)inb(port); + (void)inb(port); + (void)inb(port); +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + * + * These routines will round up any request for an odd number of bytes, + * so if an odd len is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; + u8 io_32bit = drive->io_32bit; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (io_32bit) { + unsigned long flags; + + if ((io_32bit & 2) && !mmio) { + local_irq_save(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + + words >>= 1; + if (mmio) + __ide_mm_insl((void __iomem *)data_addr, buf, words); + else + insl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) + local_irq_restore(flags); + + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; + } + + if (mmio) + __ide_mm_insw((void __iomem *)data_addr, buf, words); + else + insw(data_addr, buf, words); +} +EXPORT_SYMBOL_GPL(ide_input_data); + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +void ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; + u8 io_32bit = drive->io_32bit; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (io_32bit) { + unsigned long flags; + + if ((io_32bit & 2) && !mmio) { + local_irq_save(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + + words >>= 1; + if (mmio) + __ide_mm_outsl((void __iomem *)data_addr, buf, words); + else + outsl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) + local_irq_restore(flags); + + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; + } + + if (mmio) + __ide_mm_outsw((void __iomem *)data_addr, buf, words); + else + outsw(data_addr, buf, words); +} +EXPORT_SYMBOL_GPL(ide_output_data); + +const struct ide_tp_ops default_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; |