18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/string.h>
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/export.h>
68c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
78c2ecf20Sopenharmony_ci#include <linux/ide.h>
88c2ecf20Sopenharmony_ci#include <linux/bitops.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic const char *udma_str[] =
118c2ecf20Sopenharmony_ci	 { "UDMA/16", "UDMA/25",  "UDMA/33",  "UDMA/44",
128c2ecf20Sopenharmony_ci	   "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" };
138c2ecf20Sopenharmony_cistatic const char *mwdma_str[] =
148c2ecf20Sopenharmony_ci	{ "MWDMA0", "MWDMA1", "MWDMA2", "MWDMA3", "MWDMA4" };
158c2ecf20Sopenharmony_cistatic const char *swdma_str[] =
168c2ecf20Sopenharmony_ci	{ "SWDMA0", "SWDMA1", "SWDMA2" };
178c2ecf20Sopenharmony_cistatic const char *pio_str[] =
188c2ecf20Sopenharmony_ci	{ "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5", "PIO6" };
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/**
218c2ecf20Sopenharmony_ci *	ide_xfer_verbose	-	return IDE mode names
228c2ecf20Sopenharmony_ci *	@mode: transfer mode
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *	Returns a constant string giving the name of the mode
258c2ecf20Sopenharmony_ci *	requested.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciconst char *ide_xfer_verbose(u8 mode)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	const char *s;
318c2ecf20Sopenharmony_ci	u8 i = mode & 0xf;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7)
348c2ecf20Sopenharmony_ci		s = udma_str[i];
358c2ecf20Sopenharmony_ci	else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_4)
368c2ecf20Sopenharmony_ci		s = mwdma_str[i];
378c2ecf20Sopenharmony_ci	else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2)
388c2ecf20Sopenharmony_ci		s = swdma_str[i];
398c2ecf20Sopenharmony_ci	else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_6)
408c2ecf20Sopenharmony_ci		s = pio_str[i & 0x7];
418c2ecf20Sopenharmony_ci	else if (mode == XFER_PIO_SLOW)
428c2ecf20Sopenharmony_ci		s = "PIO SLOW";
438c2ecf20Sopenharmony_ci	else
448c2ecf20Sopenharmony_ci		s = "XFER ERROR";
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return s;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_xfer_verbose);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/**
518c2ecf20Sopenharmony_ci *	ide_get_best_pio_mode	-	get PIO mode from drive
528c2ecf20Sopenharmony_ci *	@drive: drive to consider
538c2ecf20Sopenharmony_ci *	@mode_wanted: preferred mode
548c2ecf20Sopenharmony_ci *	@max_mode: highest allowed mode
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci *	This routine returns the recommended PIO settings for a given drive,
578c2ecf20Sopenharmony_ci *	based on the drive->id information and the ide_pio_blacklist[].
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *	Drive PIO mode is auto-selected if 255 is passed as mode_wanted.
608c2ecf20Sopenharmony_ci *	This is used by most chipset support modules when "auto-tuning".
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	u16 *id = drive->id;
668c2ecf20Sopenharmony_ci	int pio_mode = -1, overridden = 0;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (mode_wanted != 255)
698c2ecf20Sopenharmony_ci		return min_t(u8, mode_wanted, max_mode);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0)
728c2ecf20Sopenharmony_ci		pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (pio_mode != -1) {
758c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name);
768c2ecf20Sopenharmony_ci	} else {
778c2ecf20Sopenharmony_ci		pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8;
788c2ecf20Sopenharmony_ci		if (pio_mode > 2) {	/* 2 is maximum allowed tPIO value */
798c2ecf20Sopenharmony_ci			pio_mode = 2;
808c2ecf20Sopenharmony_ci			overridden = 1;
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		if (id[ATA_ID_FIELD_VALID] & 2) {	      /* ATA2? */
848c2ecf20Sopenharmony_ci			if (ata_id_is_cfa(id) && (id[ATA_ID_CFA_MODES] & 7))
858c2ecf20Sopenharmony_ci				pio_mode = 4 + min_t(int, 2,
868c2ecf20Sopenharmony_ci						     id[ATA_ID_CFA_MODES] & 7);
878c2ecf20Sopenharmony_ci			else if (ata_id_has_iordy(id)) {
888c2ecf20Sopenharmony_ci				if (id[ATA_ID_PIO_MODES] & 7) {
898c2ecf20Sopenharmony_ci					overridden = 0;
908c2ecf20Sopenharmony_ci					if (id[ATA_ID_PIO_MODES] & 4)
918c2ecf20Sopenharmony_ci						pio_mode = 5;
928c2ecf20Sopenharmony_ci					else if (id[ATA_ID_PIO_MODES] & 2)
938c2ecf20Sopenharmony_ci						pio_mode = 4;
948c2ecf20Sopenharmony_ci					else
958c2ecf20Sopenharmony_ci						pio_mode = 3;
968c2ecf20Sopenharmony_ci				}
978c2ecf20Sopenharmony_ci			}
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		if (overridden)
1018c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: tPIO > 2, assuming tPIO = 2\n",
1028c2ecf20Sopenharmony_ci					 drive->name);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (pio_mode > max_mode)
1068c2ecf20Sopenharmony_ci		pio_mode = max_mode;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return pio_mode;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint ide_pio_need_iordy(ide_drive_t *drive, const u8 pio)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * IORDY may lead to controller lock up on certain controllers
1158c2ecf20Sopenharmony_ci	 * if the port is not occupied.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (pio == 0 && (drive->hwif->port_flags & IDE_PFLAG_PROBING))
1188c2ecf20Sopenharmony_ci		return 0;
1198c2ecf20Sopenharmony_ci	return ata_id_pio_need_iordy(drive->id, pio);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_pio_need_iordy);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciint ide_set_pio_mode(ide_drive_t *drive, const u8 mode)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
1268c2ecf20Sopenharmony_ci	const struct ide_port_ops *port_ops = hwif->port_ops;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
1298c2ecf20Sopenharmony_ci		return 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (port_ops == NULL || port_ops->set_pio_mode == NULL)
1328c2ecf20Sopenharmony_ci		return -1;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * TODO: temporary hack for some legacy host drivers that didn't
1368c2ecf20Sopenharmony_ci	 * set transfer mode on the device in ->set_pio_mode method...
1378c2ecf20Sopenharmony_ci	 */
1388c2ecf20Sopenharmony_ci	if (port_ops->set_dma_mode == NULL) {
1398c2ecf20Sopenharmony_ci		drive->pio_mode = mode;
1408c2ecf20Sopenharmony_ci		port_ops->set_pio_mode(hwif, drive);
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
1458c2ecf20Sopenharmony_ci		if (ide_config_drive_speed(drive, mode))
1468c2ecf20Sopenharmony_ci			return -1;
1478c2ecf20Sopenharmony_ci		drive->pio_mode = mode;
1488c2ecf20Sopenharmony_ci		port_ops->set_pio_mode(hwif, drive);
1498c2ecf20Sopenharmony_ci		return 0;
1508c2ecf20Sopenharmony_ci	} else {
1518c2ecf20Sopenharmony_ci		drive->pio_mode = mode;
1528c2ecf20Sopenharmony_ci		port_ops->set_pio_mode(hwif, drive);
1538c2ecf20Sopenharmony_ci		return ide_config_drive_speed(drive, mode);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
1608c2ecf20Sopenharmony_ci	const struct ide_port_ops *port_ops = hwif->port_ops;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
1638c2ecf20Sopenharmony_ci		return 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (port_ops == NULL || port_ops->set_dma_mode == NULL)
1668c2ecf20Sopenharmony_ci		return -1;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
1698c2ecf20Sopenharmony_ci		if (ide_config_drive_speed(drive, mode))
1708c2ecf20Sopenharmony_ci			return -1;
1718c2ecf20Sopenharmony_ci		drive->dma_mode = mode;
1728c2ecf20Sopenharmony_ci		port_ops->set_dma_mode(hwif, drive);
1738c2ecf20Sopenharmony_ci		return 0;
1748c2ecf20Sopenharmony_ci	} else {
1758c2ecf20Sopenharmony_ci		drive->dma_mode = mode;
1768c2ecf20Sopenharmony_ci		port_ops->set_dma_mode(hwif, drive);
1778c2ecf20Sopenharmony_ci		return ide_config_drive_speed(drive, mode);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_set_dma_mode);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* req_pio == "255" for auto-tune */
1838c2ecf20Sopenharmony_civoid ide_set_pio(ide_drive_t *drive, u8 req_pio)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
1868c2ecf20Sopenharmony_ci	const struct ide_port_ops *port_ops = hwif->port_ops;
1878c2ecf20Sopenharmony_ci	u8 host_pio, pio;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
1908c2ecf20Sopenharmony_ci	    (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
1918c2ecf20Sopenharmony_ci		return;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	BUG_ON(hwif->pio_mask == 0x00);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	host_pio = fls(hwif->pio_mask) - 1;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	pio = ide_get_best_pio_mode(drive, req_pio, host_pio);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/*
2008c2ecf20Sopenharmony_ci	 * TODO:
2018c2ecf20Sopenharmony_ci	 * - report device max PIO mode
2028c2ecf20Sopenharmony_ci	 * - check req_pio != 255 against device max PIO mode
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: host max PIO%d wanted PIO%d%s selected PIO%d\n",
2058c2ecf20Sopenharmony_ci			  drive->name, host_pio, req_pio,
2068c2ecf20Sopenharmony_ci			  req_pio == 255 ? "(auto-tune)" : "", pio);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	(void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_set_pio);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/**
2138c2ecf20Sopenharmony_ci *	ide_rate_filter		-	filter transfer mode
2148c2ecf20Sopenharmony_ci *	@drive: IDE device
2158c2ecf20Sopenharmony_ci *	@speed: desired speed
2168c2ecf20Sopenharmony_ci *
2178c2ecf20Sopenharmony_ci *	Given the available transfer modes this function returns
2188c2ecf20Sopenharmony_ci *	the best available speed at or below the speed requested.
2198c2ecf20Sopenharmony_ci *
2208c2ecf20Sopenharmony_ci *	TODO: check device PIO capabilities
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
2268c2ecf20Sopenharmony_ci	u8 mode = ide_find_dma_mode(drive, speed);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (mode == 0) {
2298c2ecf20Sopenharmony_ci		if (hwif->pio_mask)
2308c2ecf20Sopenharmony_ci			mode = fls(hwif->pio_mask) - 1 + XFER_PIO_0;
2318c2ecf20Sopenharmony_ci		else
2328c2ecf20Sopenharmony_ci			mode = XFER_PIO_4;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/*	printk("%s: mode 0x%02x, speed 0x%02x\n", __func__, mode, speed); */
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return min(speed, mode);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/**
2418c2ecf20Sopenharmony_ci *	ide_set_xfer_rate	-	set transfer rate
2428c2ecf20Sopenharmony_ci *	@drive: drive to set
2438c2ecf20Sopenharmony_ci *	@rate: speed to attempt to set
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci *	General helper for setting the speed of an IDE device. This
2468c2ecf20Sopenharmony_ci *	function knows about user enforced limits from the configuration
2478c2ecf20Sopenharmony_ci *	which ->set_pio_mode/->set_dma_mode does not.
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ciint ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
2538c2ecf20Sopenharmony_ci	const struct ide_port_ops *port_ops = hwif->port_ops;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (port_ops == NULL || port_ops->set_dma_mode == NULL ||
2568c2ecf20Sopenharmony_ci	    (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
2578c2ecf20Sopenharmony_ci		return -1;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	rate = ide_rate_filter(drive, rate);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	BUG_ON(rate < XFER_PIO_0);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (rate >= XFER_PIO_0 && rate <= XFER_PIO_6)
2648c2ecf20Sopenharmony_ci		return ide_set_pio_mode(drive, rate);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return ide_set_dma_mode(drive, rate);
2678c2ecf20Sopenharmony_ci}
268