18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Generic platform device PATA driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2006 - 2007  Paul Mundt
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based on pata_pcmcia:
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *   Copyright 2005-2006 Red Hat Inc, all rights reserved.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
118c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
128c2ecf20Sopenharmony_ci * for more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
178c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
188c2ecf20Sopenharmony_ci#include <linux/ata.h>
198c2ecf20Sopenharmony_ci#include <linux/libata.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/ata_platform.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define DRV_NAME "pata_platform"
248c2ecf20Sopenharmony_ci#define DRV_VERSION "1.2"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int pio_mask = 1;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Provide our own set_mode() as we don't want to change anything that has
308c2ecf20Sopenharmony_ci * already been configured..
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistatic int pata_platform_set_mode(struct ata_link *link, struct ata_device **unused)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct ata_device *dev;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	ata_for_each_dev(dev, link, ENABLED) {
378c2ecf20Sopenharmony_ci		/* We don't really care */
388c2ecf20Sopenharmony_ci		dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
398c2ecf20Sopenharmony_ci		dev->xfer_shift = ATA_SHIFT_PIO;
408c2ecf20Sopenharmony_ci		dev->flags |= ATA_DFLAG_PIO;
418c2ecf20Sopenharmony_ci		ata_dev_info(dev, "configured for PIO\n");
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct scsi_host_template pata_platform_sht = {
478c2ecf20Sopenharmony_ci	ATA_PIO_SHT(DRV_NAME),
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void pata_platform_setup_port(struct ata_ioports *ioaddr,
518c2ecf20Sopenharmony_ci				     unsigned int shift)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	/* Fixup the port shift for platforms that need it */
548c2ecf20Sopenharmony_ci	ioaddr->data_addr	= ioaddr->cmd_addr + (ATA_REG_DATA    << shift);
558c2ecf20Sopenharmony_ci	ioaddr->error_addr	= ioaddr->cmd_addr + (ATA_REG_ERR     << shift);
568c2ecf20Sopenharmony_ci	ioaddr->feature_addr	= ioaddr->cmd_addr + (ATA_REG_FEATURE << shift);
578c2ecf20Sopenharmony_ci	ioaddr->nsect_addr	= ioaddr->cmd_addr + (ATA_REG_NSECT   << shift);
588c2ecf20Sopenharmony_ci	ioaddr->lbal_addr	= ioaddr->cmd_addr + (ATA_REG_LBAL    << shift);
598c2ecf20Sopenharmony_ci	ioaddr->lbam_addr	= ioaddr->cmd_addr + (ATA_REG_LBAM    << shift);
608c2ecf20Sopenharmony_ci	ioaddr->lbah_addr	= ioaddr->cmd_addr + (ATA_REG_LBAH    << shift);
618c2ecf20Sopenharmony_ci	ioaddr->device_addr	= ioaddr->cmd_addr + (ATA_REG_DEVICE  << shift);
628c2ecf20Sopenharmony_ci	ioaddr->status_addr	= ioaddr->cmd_addr + (ATA_REG_STATUS  << shift);
638c2ecf20Sopenharmony_ci	ioaddr->command_addr	= ioaddr->cmd_addr + (ATA_REG_CMD     << shift);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci *	__pata_platform_probe		-	attach a platform interface
688c2ecf20Sopenharmony_ci *	@dev: device
698c2ecf20Sopenharmony_ci *	@io_res: Resource representing I/O base
708c2ecf20Sopenharmony_ci *	@ctl_res: Resource representing CTL base
718c2ecf20Sopenharmony_ci *	@irq_res: Resource representing IRQ and its flags
728c2ecf20Sopenharmony_ci *	@ioport_shift: I/O port shift
738c2ecf20Sopenharmony_ci *	@__pio_mask: PIO mask
748c2ecf20Sopenharmony_ci *	@sht: scsi_host_template to use when registering
758c2ecf20Sopenharmony_ci *	@use16bit: Flag to indicate 16-bit IO instead of 32-bit
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci *	Register a platform bus IDE interface. Such interfaces are PIO and we
788c2ecf20Sopenharmony_ci *	assume do not support IRQ sharing.
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci *	Platform devices are expected to contain at least 2 resources per port:
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci *		- I/O Base (IORESOURCE_IO or IORESOURCE_MEM)
838c2ecf20Sopenharmony_ci *		- CTL Base (IORESOURCE_IO or IORESOURCE_MEM)
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci *	and optionally:
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci *		- IRQ	   (IORESOURCE_IRQ)
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci *	If the base resources are both mem types, the ioremap() is handled
908c2ecf20Sopenharmony_ci *	here. For IORESOURCE_IO, it's assumed that there's no remapping
918c2ecf20Sopenharmony_ci *	necessary.
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci *	If no IRQ resource is present, PIO polling mode is used instead.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ciint __pata_platform_probe(struct device *dev, struct resource *io_res,
968c2ecf20Sopenharmony_ci			  struct resource *ctl_res, struct resource *irq_res,
978c2ecf20Sopenharmony_ci			  unsigned int ioport_shift, int __pio_mask,
988c2ecf20Sopenharmony_ci			  struct scsi_host_template *sht, bool use16bit)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct ata_host *host;
1018c2ecf20Sopenharmony_ci	struct ata_port *ap;
1028c2ecf20Sopenharmony_ci	unsigned int mmio;
1038c2ecf20Sopenharmony_ci	int irq = 0;
1048c2ecf20Sopenharmony_ci	int irq_flags = 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/*
1078c2ecf20Sopenharmony_ci	 * Check for MMIO
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	mmio = (( io_res->flags == IORESOURCE_MEM) &&
1108c2ecf20Sopenharmony_ci		(ctl_res->flags == IORESOURCE_MEM));
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * And the IRQ
1148c2ecf20Sopenharmony_ci	 */
1158c2ecf20Sopenharmony_ci	if (irq_res && irq_res->start > 0) {
1168c2ecf20Sopenharmony_ci		irq = irq_res->start;
1178c2ecf20Sopenharmony_ci		irq_flags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/*
1218c2ecf20Sopenharmony_ci	 * Now that that's out of the way, wire up the port..
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	host = ata_host_alloc(dev, 1);
1248c2ecf20Sopenharmony_ci	if (!host)
1258c2ecf20Sopenharmony_ci		return -ENOMEM;
1268c2ecf20Sopenharmony_ci	ap = host->ports[0];
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ap->ops = devm_kzalloc(dev, sizeof(*ap->ops), GFP_KERNEL);
1298c2ecf20Sopenharmony_ci	ap->ops->inherits = &ata_sff_port_ops;
1308c2ecf20Sopenharmony_ci	ap->ops->cable_detect = ata_cable_unknown;
1318c2ecf20Sopenharmony_ci	ap->ops->set_mode = pata_platform_set_mode;
1328c2ecf20Sopenharmony_ci	if (use16bit)
1338c2ecf20Sopenharmony_ci		ap->ops->sff_data_xfer = ata_sff_data_xfer;
1348c2ecf20Sopenharmony_ci	else
1358c2ecf20Sopenharmony_ci		ap->ops->sff_data_xfer = ata_sff_data_xfer32;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	ap->pio_mask = __pio_mask;
1388c2ecf20Sopenharmony_ci	ap->flags |= ATA_FLAG_SLAVE_POSS;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * Use polling mode if there's no IRQ
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	if (!irq) {
1448c2ecf20Sopenharmony_ci		ap->flags |= ATA_FLAG_PIO_POLLING;
1458c2ecf20Sopenharmony_ci		ata_port_desc(ap, "no IRQ, using PIO polling");
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * Handle the MMIO case
1508c2ecf20Sopenharmony_ci	 */
1518c2ecf20Sopenharmony_ci	if (mmio) {
1528c2ecf20Sopenharmony_ci		ap->ioaddr.cmd_addr = devm_ioremap(dev, io_res->start,
1538c2ecf20Sopenharmony_ci				resource_size(io_res));
1548c2ecf20Sopenharmony_ci		ap->ioaddr.ctl_addr = devm_ioremap(dev, ctl_res->start,
1558c2ecf20Sopenharmony_ci				resource_size(ctl_res));
1568c2ecf20Sopenharmony_ci	} else {
1578c2ecf20Sopenharmony_ci		ap->ioaddr.cmd_addr = devm_ioport_map(dev, io_res->start,
1588c2ecf20Sopenharmony_ci				resource_size(io_res));
1598c2ecf20Sopenharmony_ci		ap->ioaddr.ctl_addr = devm_ioport_map(dev, ctl_res->start,
1608c2ecf20Sopenharmony_ci				resource_size(ctl_res));
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) {
1638c2ecf20Sopenharmony_ci		dev_err(dev, "failed to map IO/CTL base\n");
1648c2ecf20Sopenharmony_ci		return -ENOMEM;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	pata_platform_setup_port(&ap->ioaddr, ioport_shift);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	ata_port_desc(ap, "%s cmd 0x%llx ctl 0x%llx", mmio ? "mmio" : "ioport",
1728c2ecf20Sopenharmony_ci		      (unsigned long long)io_res->start,
1738c2ecf20Sopenharmony_ci		      (unsigned long long)ctl_res->start);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* activate */
1768c2ecf20Sopenharmony_ci	return ata_host_activate(host, irq, irq ? ata_sff_interrupt : NULL,
1778c2ecf20Sopenharmony_ci				 irq_flags, sht);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__pata_platform_probe);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int pata_platform_probe(struct platform_device *pdev)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct resource *io_res;
1848c2ecf20Sopenharmony_ci	struct resource *ctl_res;
1858c2ecf20Sopenharmony_ci	struct resource *irq_res;
1868c2ecf20Sopenharmony_ci	struct pata_platform_info *pp_info = dev_get_platdata(&pdev->dev);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * Simple resource validation ..
1908c2ecf20Sopenharmony_ci	 */
1918c2ecf20Sopenharmony_ci	if ((pdev->num_resources != 3) && (pdev->num_resources != 2)) {
1928c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid number of resources\n");
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/*
1978c2ecf20Sopenharmony_ci	 * Get the I/O base first
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	io_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
2008c2ecf20Sopenharmony_ci	if (io_res == NULL) {
2018c2ecf20Sopenharmony_ci		io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2028c2ecf20Sopenharmony_ci		if (unlikely(io_res == NULL))
2038c2ecf20Sopenharmony_ci			return -EINVAL;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * Then the CTL base
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	ctl_res = platform_get_resource(pdev, IORESOURCE_IO, 1);
2108c2ecf20Sopenharmony_ci	if (ctl_res == NULL) {
2118c2ecf20Sopenharmony_ci		ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
2128c2ecf20Sopenharmony_ci		if (unlikely(ctl_res == NULL))
2138c2ecf20Sopenharmony_ci			return -EINVAL;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/*
2178c2ecf20Sopenharmony_ci	 * And the IRQ
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res,
2228c2ecf20Sopenharmony_ci				     pp_info ? pp_info->ioport_shift : 0,
2238c2ecf20Sopenharmony_ci				     pio_mask, &pata_platform_sht, false);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic struct platform_driver pata_platform_driver = {
2278c2ecf20Sopenharmony_ci	.probe		= pata_platform_probe,
2288c2ecf20Sopenharmony_ci	.remove		= ata_platform_remove_one,
2298c2ecf20Sopenharmony_ci	.driver = {
2308c2ecf20Sopenharmony_ci		.name		= DRV_NAME,
2318c2ecf20Sopenharmony_ci	},
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cimodule_platform_driver(pata_platform_driver);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cimodule_param(pio_mask, int, 0);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mundt");
2398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for platform device ATA");
2408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2418c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
2428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME);
243