xref: /openbmc/linux/drivers/ata/pata_falcon.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
17f1d5c9dSBartlomiej Zolnierkiewicz // SPDX-License-Identifier: GPL-2.0
27f1d5c9dSBartlomiej Zolnierkiewicz 
37e11aabdSBartlomiej Zolnierkiewicz /*
47e11aabdSBartlomiej Zolnierkiewicz  * Atari Falcon PATA controller driver
57e11aabdSBartlomiej Zolnierkiewicz  *
67e11aabdSBartlomiej Zolnierkiewicz  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
77e11aabdSBartlomiej Zolnierkiewicz  *		http://www.samsung.com
87e11aabdSBartlomiej Zolnierkiewicz  *
97e11aabdSBartlomiej Zolnierkiewicz  * Based on falconide.c:
107e11aabdSBartlomiej Zolnierkiewicz  *
117e11aabdSBartlomiej Zolnierkiewicz  *     Created 12 Jul 1997 by Geert Uytterhoeven
127e11aabdSBartlomiej Zolnierkiewicz  */
137e11aabdSBartlomiej Zolnierkiewicz 
147e11aabdSBartlomiej Zolnierkiewicz #include <linux/kernel.h>
157e11aabdSBartlomiej Zolnierkiewicz #include <linux/module.h>
167e11aabdSBartlomiej Zolnierkiewicz #include <linux/init.h>
177e11aabdSBartlomiej Zolnierkiewicz #include <linux/blkdev.h>
187e11aabdSBartlomiej Zolnierkiewicz #include <linux/delay.h>
197e11aabdSBartlomiej Zolnierkiewicz #include <scsi/scsi_host.h>
207e11aabdSBartlomiej Zolnierkiewicz #include <scsi/scsi_cmnd.h>
217e11aabdSBartlomiej Zolnierkiewicz #include <linux/ata.h>
227e11aabdSBartlomiej Zolnierkiewicz #include <linux/libata.h>
237e11aabdSBartlomiej Zolnierkiewicz #include <linux/mm.h>
247e11aabdSBartlomiej Zolnierkiewicz #include <linux/interrupt.h>
257e11aabdSBartlomiej Zolnierkiewicz #include <linux/platform_device.h>
267e11aabdSBartlomiej Zolnierkiewicz 
277e11aabdSBartlomiej Zolnierkiewicz #include <asm/setup.h>
287e11aabdSBartlomiej Zolnierkiewicz #include <asm/atarihw.h>
297e11aabdSBartlomiej Zolnierkiewicz #include <asm/atariints.h>
307e11aabdSBartlomiej Zolnierkiewicz #include <asm/atari_stdma.h>
317e11aabdSBartlomiej Zolnierkiewicz 
327e11aabdSBartlomiej Zolnierkiewicz #define DRV_NAME "pata_falcon"
337e11aabdSBartlomiej Zolnierkiewicz #define DRV_VERSION "0.1.0"
347e11aabdSBartlomiej Zolnierkiewicz 
35*8847d42dSMichael Schmitz static int pata_falcon_swap_mask;
36*8847d42dSMichael Schmitz 
37*8847d42dSMichael Schmitz module_param_named(data_swab, pata_falcon_swap_mask, int, 0444);
38*8847d42dSMichael Schmitz MODULE_PARM_DESC(data_swab, "Data byte swap enable/disable bitmap (0x1==drive1, 0x2==drive2, 0x4==drive3, 0x8==drive4, default==0)");
39*8847d42dSMichael Schmitz 
4025df73d9SBart Van Assche static const struct scsi_host_template pata_falcon_sht = {
417e11aabdSBartlomiej Zolnierkiewicz 	ATA_PIO_SHT(DRV_NAME),
427e11aabdSBartlomiej Zolnierkiewicz };
437e11aabdSBartlomiej Zolnierkiewicz 
pata_falcon_data_xfer(struct ata_queued_cmd * qc,unsigned char * buf,unsigned int buflen,int rw)447e11aabdSBartlomiej Zolnierkiewicz static unsigned int pata_falcon_data_xfer(struct ata_queued_cmd *qc,
457e11aabdSBartlomiej Zolnierkiewicz 					  unsigned char *buf,
467e11aabdSBartlomiej Zolnierkiewicz 					  unsigned int buflen, int rw)
477e11aabdSBartlomiej Zolnierkiewicz {
487e11aabdSBartlomiej Zolnierkiewicz 	struct ata_device *dev = qc->dev;
497e11aabdSBartlomiej Zolnierkiewicz 	struct ata_port *ap = dev->link->ap;
507e11aabdSBartlomiej Zolnierkiewicz 	void __iomem *data_addr = ap->ioaddr.data_addr;
517e11aabdSBartlomiej Zolnierkiewicz 	unsigned int words = buflen >> 1;
527e11aabdSBartlomiej Zolnierkiewicz 	struct scsi_cmnd *cmd = qc->scsicmd;
537e11aabdSBartlomiej Zolnierkiewicz 	bool swap = 1;
547e11aabdSBartlomiej Zolnierkiewicz 
55c8329cd5SBart Van Assche 	if (dev->class == ATA_DEV_ATA && cmd &&
56c8329cd5SBart Van Assche 	    !blk_rq_is_passthrough(scsi_cmd_to_rq(cmd)))
57*8847d42dSMichael Schmitz 		swap = (uintptr_t)ap->private_data & BIT(dev->devno);
587e11aabdSBartlomiej Zolnierkiewicz 
597e11aabdSBartlomiej Zolnierkiewicz 	/* Transfer multiple of 2 bytes */
607e11aabdSBartlomiej Zolnierkiewicz 	if (rw == READ) {
617e11aabdSBartlomiej Zolnierkiewicz 		if (swap)
625fad5077SFinn Thain 			raw_insw_swapw(data_addr, (u16 *)buf, words);
637e11aabdSBartlomiej Zolnierkiewicz 		else
645fad5077SFinn Thain 			raw_insw(data_addr, (u16 *)buf, words);
657e11aabdSBartlomiej Zolnierkiewicz 	} else {
667e11aabdSBartlomiej Zolnierkiewicz 		if (swap)
675fad5077SFinn Thain 			raw_outsw_swapw(data_addr, (u16 *)buf, words);
687e11aabdSBartlomiej Zolnierkiewicz 		else
695fad5077SFinn Thain 			raw_outsw(data_addr, (u16 *)buf, words);
707e11aabdSBartlomiej Zolnierkiewicz 	}
717e11aabdSBartlomiej Zolnierkiewicz 
727e11aabdSBartlomiej Zolnierkiewicz 	/* Transfer trailing byte, if any. */
737e11aabdSBartlomiej Zolnierkiewicz 	if (unlikely(buflen & 0x01)) {
747e11aabdSBartlomiej Zolnierkiewicz 		unsigned char pad[2] = { };
757e11aabdSBartlomiej Zolnierkiewicz 
767e11aabdSBartlomiej Zolnierkiewicz 		/* Point buf to the tail of buffer */
777e11aabdSBartlomiej Zolnierkiewicz 		buf += buflen - 1;
787e11aabdSBartlomiej Zolnierkiewicz 
797e11aabdSBartlomiej Zolnierkiewicz 		if (rw == READ) {
807e11aabdSBartlomiej Zolnierkiewicz 			if (swap)
815fad5077SFinn Thain 				raw_insw_swapw(data_addr, (u16 *)pad, 1);
827e11aabdSBartlomiej Zolnierkiewicz 			else
835fad5077SFinn Thain 				raw_insw(data_addr, (u16 *)pad, 1);
847e11aabdSBartlomiej Zolnierkiewicz 			*buf = pad[0];
857e11aabdSBartlomiej Zolnierkiewicz 		} else {
867e11aabdSBartlomiej Zolnierkiewicz 			pad[0] = *buf;
877e11aabdSBartlomiej Zolnierkiewicz 			if (swap)
885fad5077SFinn Thain 				raw_outsw_swapw(data_addr, (u16 *)pad, 1);
897e11aabdSBartlomiej Zolnierkiewicz 			else
905fad5077SFinn Thain 				raw_outsw(data_addr, (u16 *)pad, 1);
917e11aabdSBartlomiej Zolnierkiewicz 		}
927e11aabdSBartlomiej Zolnierkiewicz 		words++;
937e11aabdSBartlomiej Zolnierkiewicz 	}
947e11aabdSBartlomiej Zolnierkiewicz 
957e11aabdSBartlomiej Zolnierkiewicz 	return words << 1;
967e11aabdSBartlomiej Zolnierkiewicz }
977e11aabdSBartlomiej Zolnierkiewicz 
987e11aabdSBartlomiej Zolnierkiewicz /*
997e11aabdSBartlomiej Zolnierkiewicz  * Provide our own set_mode() as we don't want to change anything that has
1007e11aabdSBartlomiej Zolnierkiewicz  * already been configured..
1017e11aabdSBartlomiej Zolnierkiewicz  */
pata_falcon_set_mode(struct ata_link * link,struct ata_device ** unused)1027e11aabdSBartlomiej Zolnierkiewicz static int pata_falcon_set_mode(struct ata_link *link,
1037e11aabdSBartlomiej Zolnierkiewicz 				struct ata_device **unused)
1047e11aabdSBartlomiej Zolnierkiewicz {
1057e11aabdSBartlomiej Zolnierkiewicz 	struct ata_device *dev;
1067e11aabdSBartlomiej Zolnierkiewicz 
1077e11aabdSBartlomiej Zolnierkiewicz 	ata_for_each_dev(dev, link, ENABLED) {
1087e11aabdSBartlomiej Zolnierkiewicz 		/* We don't really care */
1097e11aabdSBartlomiej Zolnierkiewicz 		dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
1107e11aabdSBartlomiej Zolnierkiewicz 		dev->xfer_shift = ATA_SHIFT_PIO;
1117e11aabdSBartlomiej Zolnierkiewicz 		dev->flags |= ATA_DFLAG_PIO;
1127e11aabdSBartlomiej Zolnierkiewicz 		ata_dev_info(dev, "configured for PIO\n");
1137e11aabdSBartlomiej Zolnierkiewicz 	}
1147e11aabdSBartlomiej Zolnierkiewicz 	return 0;
1157e11aabdSBartlomiej Zolnierkiewicz }
1167e11aabdSBartlomiej Zolnierkiewicz 
1177e11aabdSBartlomiej Zolnierkiewicz static struct ata_port_operations pata_falcon_ops = {
1187e11aabdSBartlomiej Zolnierkiewicz 	.inherits	= &ata_sff_port_ops,
1197e11aabdSBartlomiej Zolnierkiewicz 	.sff_data_xfer	= pata_falcon_data_xfer,
1207e11aabdSBartlomiej Zolnierkiewicz 	.cable_detect	= ata_cable_unknown,
1217e11aabdSBartlomiej Zolnierkiewicz 	.set_mode	= pata_falcon_set_mode,
1227e11aabdSBartlomiej Zolnierkiewicz };
1237e11aabdSBartlomiej Zolnierkiewicz 
pata_falcon_init_one(struct platform_device * pdev)1245ed0794cSMichael Schmitz static int __init pata_falcon_init_one(struct platform_device *pdev)
1257e11aabdSBartlomiej Zolnierkiewicz {
12644b1fbc0SFinn Thain 	struct resource *base_mem_res, *ctl_mem_res;
12744b1fbc0SFinn Thain 	struct resource *base_res, *ctl_res, *irq_res;
1287e11aabdSBartlomiej Zolnierkiewicz 	struct ata_host *host;
1297e11aabdSBartlomiej Zolnierkiewicz 	struct ata_port *ap;
1308a1f00b7SMichael Schmitz 	void __iomem *base, *ctl_base;
131*8847d42dSMichael Schmitz 	int mask_shift = 0; /* Q40 & Falcon default */
1328a1f00b7SMichael Schmitz 	int irq = 0, io_offset = 1, reg_shift = 2; /* Falcon defaults */
1337e11aabdSBartlomiej Zolnierkiewicz 
13444b1fbc0SFinn Thain 	dev_info(&pdev->dev, "Atari Falcon and Q40/Q60 PATA controller\n");
1355ed0794cSMichael Schmitz 
13644b1fbc0SFinn Thain 	base_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
13744b1fbc0SFinn Thain 	if (base_res && !devm_request_region(&pdev->dev, base_res->start,
13844b1fbc0SFinn Thain 					   resource_size(base_res), DRV_NAME)) {
1395ed0794cSMichael Schmitz 		dev_err(&pdev->dev, "resources busy\n");
1407e11aabdSBartlomiej Zolnierkiewicz 		return -EBUSY;
1417e11aabdSBartlomiej Zolnierkiewicz 	}
1427e11aabdSBartlomiej Zolnierkiewicz 
14344b1fbc0SFinn Thain 	ctl_res = platform_get_resource(pdev, IORESOURCE_IO, 1);
14444b1fbc0SFinn Thain 	if (ctl_res && !devm_request_region(&pdev->dev, ctl_res->start,
14544b1fbc0SFinn Thain 					    resource_size(ctl_res), DRV_NAME)) {
14644b1fbc0SFinn Thain 		dev_err(&pdev->dev, "resources busy\n");
14744b1fbc0SFinn Thain 		return -EBUSY;
14844b1fbc0SFinn Thain 	}
14944b1fbc0SFinn Thain 
15044b1fbc0SFinn Thain 	base_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
15144b1fbc0SFinn Thain 	if (!base_mem_res)
15244b1fbc0SFinn Thain 		return -ENODEV;
15344b1fbc0SFinn Thain 	if (!devm_request_mem_region(&pdev->dev, base_mem_res->start,
15444b1fbc0SFinn Thain 				     resource_size(base_mem_res), DRV_NAME)) {
15544b1fbc0SFinn Thain 		dev_err(&pdev->dev, "resources busy\n");
15644b1fbc0SFinn Thain 		return -EBUSY;
15744b1fbc0SFinn Thain 	}
15844b1fbc0SFinn Thain 
15944b1fbc0SFinn Thain 	ctl_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
16044b1fbc0SFinn Thain 	if (!ctl_mem_res)
16144b1fbc0SFinn Thain 		return -ENODEV;
16244b1fbc0SFinn Thain 
1637e11aabdSBartlomiej Zolnierkiewicz 	/* allocate host */
1647e11aabdSBartlomiej Zolnierkiewicz 	host = ata_host_alloc(&pdev->dev, 1);
1657e11aabdSBartlomiej Zolnierkiewicz 	if (!host)
1667e11aabdSBartlomiej Zolnierkiewicz 		return -ENOMEM;
1677e11aabdSBartlomiej Zolnierkiewicz 	ap = host->ports[0];
1687e11aabdSBartlomiej Zolnierkiewicz 
1697e11aabdSBartlomiej Zolnierkiewicz 	ap->ops = &pata_falcon_ops;
1707e11aabdSBartlomiej Zolnierkiewicz 	ap->pio_mask = ATA_PIO4;
1717e11aabdSBartlomiej Zolnierkiewicz 	ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
1727e11aabdSBartlomiej Zolnierkiewicz 
17344b1fbc0SFinn Thain 	/* N.B. this assumes data_addr will be used for word-sized I/O only */
1748a1f00b7SMichael Schmitz 	ap->ioaddr.data_addr = (void __iomem *)base_mem_res->start;
1757e11aabdSBartlomiej Zolnierkiewicz 
1768a1f00b7SMichael Schmitz 	if (base_res) {		/* only Q40 has IO resources */
1778a1f00b7SMichael Schmitz 		io_offset = 0x10000;
1788a1f00b7SMichael Schmitz 		reg_shift = 0;
1798a1f00b7SMichael Schmitz 		base = (void __iomem *)base_res->start;
1808a1f00b7SMichael Schmitz 		ctl_base = (void __iomem *)ctl_res->start;
1818a1f00b7SMichael Schmitz 	} else {
1828a1f00b7SMichael Schmitz 		base = (void __iomem *)base_mem_res->start;
1838a1f00b7SMichael Schmitz 		ctl_base = (void __iomem *)ctl_mem_res->start;
1848a1f00b7SMichael Schmitz 	}
1857e11aabdSBartlomiej Zolnierkiewicz 
1868a1f00b7SMichael Schmitz 	ap->ioaddr.error_addr	= base + io_offset + (1 << reg_shift);
1878a1f00b7SMichael Schmitz 	ap->ioaddr.feature_addr	= base + io_offset + (1 << reg_shift);
1888a1f00b7SMichael Schmitz 	ap->ioaddr.nsect_addr	= base + io_offset + (2 << reg_shift);
1898a1f00b7SMichael Schmitz 	ap->ioaddr.lbal_addr	= base + io_offset + (3 << reg_shift);
1908a1f00b7SMichael Schmitz 	ap->ioaddr.lbam_addr	= base + io_offset + (4 << reg_shift);
1918a1f00b7SMichael Schmitz 	ap->ioaddr.lbah_addr	= base + io_offset + (5 << reg_shift);
1928a1f00b7SMichael Schmitz 	ap->ioaddr.device_addr	= base + io_offset + (6 << reg_shift);
1938a1f00b7SMichael Schmitz 	ap->ioaddr.status_addr	= base + io_offset + (7 << reg_shift);
1948a1f00b7SMichael Schmitz 	ap->ioaddr.command_addr	= base + io_offset + (7 << reg_shift);
1958a1f00b7SMichael Schmitz 
1968a1f00b7SMichael Schmitz 	ap->ioaddr.altstatus_addr	= ctl_base + io_offset;
1978a1f00b7SMichael Schmitz 	ap->ioaddr.ctl_addr		= ctl_base + io_offset;
1988a1f00b7SMichael Schmitz 
1998a1f00b7SMichael Schmitz 	ata_port_desc(ap, "cmd %px ctl %px data %px",
2008a1f00b7SMichael Schmitz 		      base, ctl_base, ap->ioaddr.data_addr);
20144b1fbc0SFinn Thain 
202*8847d42dSMichael Schmitz 	if (pdev->id > 0)
203*8847d42dSMichael Schmitz 		mask_shift = 2;
204*8847d42dSMichael Schmitz 	ap->private_data = (void *)(uintptr_t)(pata_falcon_swap_mask >> mask_shift);
205*8847d42dSMichael Schmitz 
20644b1fbc0SFinn Thain 	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
20744b1fbc0SFinn Thain 	if (irq_res && irq_res->start > 0) {
20844b1fbc0SFinn Thain 		irq = irq_res->start;
20944b1fbc0SFinn Thain 	} else {
21044b1fbc0SFinn Thain 		ap->flags |= ATA_FLAG_PIO_POLLING;
21144b1fbc0SFinn Thain 		ata_port_desc(ap, "no IRQ, using PIO polling");
21244b1fbc0SFinn Thain 	}
2137e11aabdSBartlomiej Zolnierkiewicz 
2147e11aabdSBartlomiej Zolnierkiewicz 	/* activate */
21544b1fbc0SFinn Thain 	return ata_host_activate(host, irq, irq ? ata_sff_interrupt : NULL,
21644b1fbc0SFinn Thain 				 IRQF_SHARED, &pata_falcon_sht);
2177e11aabdSBartlomiej Zolnierkiewicz }
2187e11aabdSBartlomiej Zolnierkiewicz 
pata_falcon_remove_one(struct platform_device * pdev)2195ed0794cSMichael Schmitz static int __exit pata_falcon_remove_one(struct platform_device *pdev)
2205ed0794cSMichael Schmitz {
2215ed0794cSMichael Schmitz 	struct ata_host *host = platform_get_drvdata(pdev);
2225ed0794cSMichael Schmitz 
2235ed0794cSMichael Schmitz 	ata_host_detach(host);
2245ed0794cSMichael Schmitz 
2255ed0794cSMichael Schmitz 	return 0;
2265ed0794cSMichael Schmitz }
2275ed0794cSMichael Schmitz 
2285ed0794cSMichael Schmitz static struct platform_driver pata_falcon_driver = {
2295ed0794cSMichael Schmitz 	.remove = __exit_p(pata_falcon_remove_one),
2305ed0794cSMichael Schmitz 	.driver   = {
2315ed0794cSMichael Schmitz 		.name	= "atari-falcon-ide",
2325ed0794cSMichael Schmitz 	},
2335ed0794cSMichael Schmitz };
2345ed0794cSMichael Schmitz 
2355ed0794cSMichael Schmitz module_platform_driver_probe(pata_falcon_driver, pata_falcon_init_one);
2367e11aabdSBartlomiej Zolnierkiewicz 
2377e11aabdSBartlomiej Zolnierkiewicz MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
2387e11aabdSBartlomiej Zolnierkiewicz MODULE_DESCRIPTION("low-level driver for Atari Falcon PATA");
2397f1d5c9dSBartlomiej Zolnierkiewicz MODULE_LICENSE("GPL v2");
2405ed0794cSMichael Schmitz MODULE_ALIAS("platform:atari-falcon-ide");
2417e11aabdSBartlomiej Zolnierkiewicz MODULE_VERSION(DRV_VERSION);
242