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