18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <linux/gfp.h>
58c2ecf20Sopenharmony_ci#include <linux/ide.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ciDEFINE_MUTEX(ide_setting_mtx);
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ciide_devset_get(io_32bit, io_32bit);
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic int set_io_32bit(ide_drive_t *drive, int arg)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT)
148c2ecf20Sopenharmony_ci		return -EPERM;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1))
178c2ecf20Sopenharmony_ci		return -EINVAL;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	drive->io_32bit = arg;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	return 0;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int set_ksettings(ide_drive_t *drive, int arg)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 1)
298c2ecf20Sopenharmony_ci		return -EINVAL;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (arg)
328c2ecf20Sopenharmony_ci		drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS;
338c2ecf20Sopenharmony_ci	else
348c2ecf20Sopenharmony_ci		drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return 0;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int set_using_dma(ide_drive_t *drive, int arg)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_IDEDMA
448c2ecf20Sopenharmony_ci	int err = -EPERM;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 1)
478c2ecf20Sopenharmony_ci		return -EINVAL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (ata_id_has_dma(drive->id) == 0)
508c2ecf20Sopenharmony_ci		goto out;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (drive->hwif->dma_ops == NULL)
538c2ecf20Sopenharmony_ci		goto out;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	err = 0;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (arg) {
588c2ecf20Sopenharmony_ci		if (ide_set_dma(drive))
598c2ecf20Sopenharmony_ci			err = -EIO;
608c2ecf20Sopenharmony_ci	} else
618c2ecf20Sopenharmony_ci		ide_dma_off(drive);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciout:
648c2ecf20Sopenharmony_ci	return err;
658c2ecf20Sopenharmony_ci#else
668c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 1)
678c2ecf20Sopenharmony_ci		return -EINVAL;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return -EPERM;
708c2ecf20Sopenharmony_ci#endif
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/*
748c2ecf20Sopenharmony_ci * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_cistatic int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	switch (req_pio) {
798c2ecf20Sopenharmony_ci	case 202:
808c2ecf20Sopenharmony_ci	case 201:
818c2ecf20Sopenharmony_ci	case 200:
828c2ecf20Sopenharmony_ci	case 102:
838c2ecf20Sopenharmony_ci	case 101:
848c2ecf20Sopenharmony_ci	case 100:
858c2ecf20Sopenharmony_ci		return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
868c2ecf20Sopenharmony_ci	case 9:
878c2ecf20Sopenharmony_ci	case 8:
888c2ecf20Sopenharmony_ci		return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
898c2ecf20Sopenharmony_ci	case 7:
908c2ecf20Sopenharmony_ci	case 6:
918c2ecf20Sopenharmony_ci		return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
928c2ecf20Sopenharmony_ci	default:
938c2ecf20Sopenharmony_ci		return 0;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int set_pio_mode(ide_drive_t *drive, int arg)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
1008c2ecf20Sopenharmony_ci	const struct ide_port_ops *port_ops = hwif->port_ops;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 255)
1038c2ecf20Sopenharmony_ci		return -EINVAL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
1068c2ecf20Sopenharmony_ci	    (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
1078c2ecf20Sopenharmony_ci		return -ENOSYS;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (set_pio_mode_abuse(drive->hwif, arg)) {
1108c2ecf20Sopenharmony_ci		drive->pio_mode = arg + XFER_PIO_0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (arg == 8 || arg == 9) {
1138c2ecf20Sopenharmony_ci			unsigned long flags;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			/* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
1168c2ecf20Sopenharmony_ci			spin_lock_irqsave(&hwif->lock, flags);
1178c2ecf20Sopenharmony_ci			port_ops->set_pio_mode(hwif, drive);
1188c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&hwif->lock, flags);
1198c2ecf20Sopenharmony_ci		} else
1208c2ecf20Sopenharmony_ci			port_ops->set_pio_mode(hwif, drive);
1218c2ecf20Sopenharmony_ci	} else {
1228c2ecf20Sopenharmony_ci		int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		ide_set_pio(drive, arg);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
1278c2ecf20Sopenharmony_ci			if (keep_dma)
1288c2ecf20Sopenharmony_ci				ide_dma_on(drive);
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int set_unmaskirq(ide_drive_t *drive, int arg)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	if (drive->dev_flags & IDE_DFLAG_NO_UNMASK)
1408c2ecf20Sopenharmony_ci		return -EPERM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (arg < 0 || arg > 1)
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (arg)
1468c2ecf20Sopenharmony_ci		drive->dev_flags |= IDE_DFLAG_UNMASK;
1478c2ecf20Sopenharmony_ci	else
1488c2ecf20Sopenharmony_ci		drive->dev_flags &= ~IDE_DFLAG_UNMASK;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciide_ext_devset_rw_sync(io_32bit, io_32bit);
1548c2ecf20Sopenharmony_ciide_ext_devset_rw_sync(keepsettings, ksettings);
1558c2ecf20Sopenharmony_ciide_ext_devset_rw_sync(unmaskirq, unmaskirq);
1568c2ecf20Sopenharmony_ciide_ext_devset_rw_sync(using_dma, using_dma);
1578c2ecf20Sopenharmony_ci__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciint ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
1608c2ecf20Sopenharmony_ci		       int arg)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct request_queue *q = drive->queue;
1638c2ecf20Sopenharmony_ci	struct request *rq;
1648c2ecf20Sopenharmony_ci	int ret = 0;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (!(setting->flags & DS_SYNC))
1678c2ecf20Sopenharmony_ci		return setting->set(drive, arg);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	rq = blk_get_request(q, REQ_OP_DRV_IN, 0);
1708c2ecf20Sopenharmony_ci	ide_req(rq)->type = ATA_PRIV_MISC;
1718c2ecf20Sopenharmony_ci	scsi_req(rq)->cmd_len = 5;
1728c2ecf20Sopenharmony_ci	scsi_req(rq)->cmd[0] = REQ_DEVSET_EXEC;
1738c2ecf20Sopenharmony_ci	*(int *)&scsi_req(rq)->cmd[1] = arg;
1748c2ecf20Sopenharmony_ci	ide_req(rq)->special = setting->set;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	blk_execute_rq(q, NULL, rq, 0);
1778c2ecf20Sopenharmony_ci	ret = scsi_req(rq)->result;
1788c2ecf20Sopenharmony_ci	blk_put_request(rq);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciide_startstop_t ide_do_devset(ide_drive_t *drive, struct request *rq)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int err, (*setfunc)(ide_drive_t *, int) = ide_req(rq)->special;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	err = setfunc(drive, *(int *)&scsi_req(rq)->cmd[1]);
1888c2ecf20Sopenharmony_ci	if (err)
1898c2ecf20Sopenharmony_ci		scsi_req(rq)->result = err;
1908c2ecf20Sopenharmony_ci	ide_complete_rq(drive, 0, blk_rq_bytes(rq));
1918c2ecf20Sopenharmony_ci	return ide_stopped;
1928c2ecf20Sopenharmony_ci}
193