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