18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/export.h> 58c2ecf20Sopenharmony_ci#include <linux/ide.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \ 88c2ecf20Sopenharmony_ci defined(CONFIG_PARISC) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) 98c2ecf20Sopenharmony_ci#include <asm/ide.h> 108c2ecf20Sopenharmony_ci#else 118c2ecf20Sopenharmony_ci#include <asm-generic/ide_iops.h> 128c2ecf20Sopenharmony_ci#endif 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * Conventional PIO operations for ATA devices 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic u8 ide_inb(unsigned long port) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci return (u8) inb(port); 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void ide_outb(u8 val, unsigned long port) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci outb(val, port); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * MMIO operations, typically used for SATA controllers 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic u8 ide_mm_inb(unsigned long port) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return (u8) readb((void __iomem *) port); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void ide_mm_outb(u8 value, unsigned long port) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci writeb(value, (void __iomem *) port); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid ide_exec_command(ide_hwif_t *hwif, u8 cmd) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_MMIO) 458c2ecf20Sopenharmony_ci writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); 468c2ecf20Sopenharmony_ci else 478c2ecf20Sopenharmony_ci outb(cmd, hwif->io_ports.command_addr); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_exec_command); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciu8 ide_read_status(ide_hwif_t *hwif) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_MMIO) 548c2ecf20Sopenharmony_ci return readb((void __iomem *)hwif->io_ports.status_addr); 558c2ecf20Sopenharmony_ci else 568c2ecf20Sopenharmony_ci return inb(hwif->io_ports.status_addr); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_read_status); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciu8 ide_read_altstatus(ide_hwif_t *hwif) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_MMIO) 638c2ecf20Sopenharmony_ci return readb((void __iomem *)hwif->io_ports.ctl_addr); 648c2ecf20Sopenharmony_ci else 658c2ecf20Sopenharmony_ci return inb(hwif->io_ports.ctl_addr); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_read_altstatus); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_civoid ide_write_devctl(ide_hwif_t *hwif, u8 ctl) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_MMIO) 728c2ecf20Sopenharmony_ci writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci outb(ctl, hwif->io_ports.ctl_addr); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_write_devctl); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_civoid ide_dev_select(ide_drive_t *drive) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 818c2ecf20Sopenharmony_ci u8 select = drive->select | ATA_DEVICE_OBS; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_MMIO) 848c2ecf20Sopenharmony_ci writeb(select, (void __iomem *)hwif->io_ports.device_addr); 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci outb(select, hwif->io_ports.device_addr); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_dev_select); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 938c2ecf20Sopenharmony_ci struct ide_io_ports *io_ports = &hwif->io_ports; 948c2ecf20Sopenharmony_ci void (*tf_outb)(u8 addr, unsigned long port); 958c2ecf20Sopenharmony_ci u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (mmio) 988c2ecf20Sopenharmony_ci tf_outb = ide_mm_outb; 998c2ecf20Sopenharmony_ci else 1008c2ecf20Sopenharmony_ci tf_outb = ide_outb; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (valid & IDE_VALID_FEATURE) 1038c2ecf20Sopenharmony_ci tf_outb(tf->feature, io_ports->feature_addr); 1048c2ecf20Sopenharmony_ci if (valid & IDE_VALID_NSECT) 1058c2ecf20Sopenharmony_ci tf_outb(tf->nsect, io_ports->nsect_addr); 1068c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAL) 1078c2ecf20Sopenharmony_ci tf_outb(tf->lbal, io_ports->lbal_addr); 1088c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAM) 1098c2ecf20Sopenharmony_ci tf_outb(tf->lbam, io_ports->lbam_addr); 1108c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAH) 1118c2ecf20Sopenharmony_ci tf_outb(tf->lbah, io_ports->lbah_addr); 1128c2ecf20Sopenharmony_ci if (valid & IDE_VALID_DEVICE) 1138c2ecf20Sopenharmony_ci tf_outb(tf->device, io_ports->device_addr); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_tf_load); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 1208c2ecf20Sopenharmony_ci struct ide_io_ports *io_ports = &hwif->io_ports; 1218c2ecf20Sopenharmony_ci u8 (*tf_inb)(unsigned long port); 1228c2ecf20Sopenharmony_ci u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (mmio) 1258c2ecf20Sopenharmony_ci tf_inb = ide_mm_inb; 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci tf_inb = ide_inb; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (valid & IDE_VALID_ERROR) 1308c2ecf20Sopenharmony_ci tf->error = tf_inb(io_ports->feature_addr); 1318c2ecf20Sopenharmony_ci if (valid & IDE_VALID_NSECT) 1328c2ecf20Sopenharmony_ci tf->nsect = tf_inb(io_ports->nsect_addr); 1338c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAL) 1348c2ecf20Sopenharmony_ci tf->lbal = tf_inb(io_ports->lbal_addr); 1358c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAM) 1368c2ecf20Sopenharmony_ci tf->lbam = tf_inb(io_ports->lbam_addr); 1378c2ecf20Sopenharmony_ci if (valid & IDE_VALID_LBAH) 1388c2ecf20Sopenharmony_ci tf->lbah = tf_inb(io_ports->lbah_addr); 1398c2ecf20Sopenharmony_ci if (valid & IDE_VALID_DEVICE) 1408c2ecf20Sopenharmony_ci tf->device = tf_inb(io_ports->device_addr); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_tf_read); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Some localbus EIDE interfaces require a special access sequence 1468c2ecf20Sopenharmony_ci * when using 32-bit I/O instructions to transfer data. We call this 1478c2ecf20Sopenharmony_ci * the "vlb_sync" sequence, which consists of three successive reads 1488c2ecf20Sopenharmony_ci * of the sector count register location, with interrupts disabled 1498c2ecf20Sopenharmony_ci * to ensure that the reads all happen together. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic void ata_vlb_sync(unsigned long port) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci (void)inb(port); 1548c2ecf20Sopenharmony_ci (void)inb(port); 1558c2ecf20Sopenharmony_ci (void)inb(port); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * This is used for most PIO data transfers *from* the IDE interface 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * These routines will round up any request for an odd number of bytes, 1628c2ecf20Sopenharmony_ci * so if an odd len is specified, be sure that there's at least one 1638c2ecf20Sopenharmony_ci * extra byte allocated for the buffer. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_civoid ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, 1668c2ecf20Sopenharmony_ci unsigned int len) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 1698c2ecf20Sopenharmony_ci struct ide_io_ports *io_ports = &hwif->io_ports; 1708c2ecf20Sopenharmony_ci unsigned long data_addr = io_ports->data_addr; 1718c2ecf20Sopenharmony_ci unsigned int words = (len + 1) >> 1; 1728c2ecf20Sopenharmony_ci u8 io_32bit = drive->io_32bit; 1738c2ecf20Sopenharmony_ci u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (io_32bit) { 1768c2ecf20Sopenharmony_ci unsigned long flags; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if ((io_32bit & 2) && !mmio) { 1798c2ecf20Sopenharmony_ci local_irq_save(flags); 1808c2ecf20Sopenharmony_ci ata_vlb_sync(io_ports->nsect_addr); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci words >>= 1; 1848c2ecf20Sopenharmony_ci if (mmio) 1858c2ecf20Sopenharmony_ci __ide_mm_insl((void __iomem *)data_addr, buf, words); 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci insl(data_addr, buf, words); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if ((io_32bit & 2) && !mmio) 1908c2ecf20Sopenharmony_ci local_irq_restore(flags); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (((len + 1) & 3) < 2) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci buf += len & ~3; 1968c2ecf20Sopenharmony_ci words = 1; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (mmio) 2008c2ecf20Sopenharmony_ci __ide_mm_insw((void __iomem *)data_addr, buf, words); 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci insw(data_addr, buf, words); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_input_data); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * This is used for most PIO data transfers *to* the IDE interface 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_civoid ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, 2108c2ecf20Sopenharmony_ci unsigned int len) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2138c2ecf20Sopenharmony_ci struct ide_io_ports *io_ports = &hwif->io_ports; 2148c2ecf20Sopenharmony_ci unsigned long data_addr = io_ports->data_addr; 2158c2ecf20Sopenharmony_ci unsigned int words = (len + 1) >> 1; 2168c2ecf20Sopenharmony_ci u8 io_32bit = drive->io_32bit; 2178c2ecf20Sopenharmony_ci u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (io_32bit) { 2208c2ecf20Sopenharmony_ci unsigned long flags; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if ((io_32bit & 2) && !mmio) { 2238c2ecf20Sopenharmony_ci local_irq_save(flags); 2248c2ecf20Sopenharmony_ci ata_vlb_sync(io_ports->nsect_addr); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci words >>= 1; 2288c2ecf20Sopenharmony_ci if (mmio) 2298c2ecf20Sopenharmony_ci __ide_mm_outsl((void __iomem *)data_addr, buf, words); 2308c2ecf20Sopenharmony_ci else 2318c2ecf20Sopenharmony_ci outsl(data_addr, buf, words); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if ((io_32bit & 2) && !mmio) 2348c2ecf20Sopenharmony_ci local_irq_restore(flags); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (((len + 1) & 3) < 2) 2378c2ecf20Sopenharmony_ci return; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci buf += len & ~3; 2408c2ecf20Sopenharmony_ci words = 1; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (mmio) 2448c2ecf20Sopenharmony_ci __ide_mm_outsw((void __iomem *)data_addr, buf, words); 2458c2ecf20Sopenharmony_ci else 2468c2ecf20Sopenharmony_ci outsw(data_addr, buf, words); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_output_data); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciconst struct ide_tp_ops default_tp_ops = { 2518c2ecf20Sopenharmony_ci .exec_command = ide_exec_command, 2528c2ecf20Sopenharmony_ci .read_status = ide_read_status, 2538c2ecf20Sopenharmony_ci .read_altstatus = ide_read_altstatus, 2548c2ecf20Sopenharmony_ci .write_devctl = ide_write_devctl, 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci .dev_select = ide_dev_select, 2578c2ecf20Sopenharmony_ci .tf_load = ide_tf_load, 2588c2ecf20Sopenharmony_ci .tf_read = ide_tf_read, 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci .input_data = ide_input_data, 2618c2ecf20Sopenharmony_ci .output_data = ide_output_data, 2628c2ecf20Sopenharmony_ci}; 263