18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Freescale iMX PATA driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Arnaud Patard <arnaud.patard@rtp-net.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on pata_platform - Copyright (C) 2006 - 2007 Paul Mundt 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 108c2ecf20Sopenharmony_ci * for more details. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * TODO: 138c2ecf20Sopenharmony_ci * - dmaengine support 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/ata.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/libata.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRV_NAME "pata_imx" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_OFF 0x00 268c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_ON 0x01 278c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_1 0x02 288c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_2W 0x03 298c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_2R 0x04 308c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_AX 0x05 318c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_PIO_RDX 0x06 328c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_4 0x07 338c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_TIME_9 0x08 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_CONTROL 0x24 368c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7) 378c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6) 388c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0) 398c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_INT_EN 0x2C 408c2ecf20Sopenharmony_ci#define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3) 418c2ecf20Sopenharmony_ci#define PATA_IMX_DRIVE_DATA 0xA0 428c2ecf20Sopenharmony_ci#define PATA_IMX_DRIVE_CONTROL 0xD8 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic u32 pio_t4[] = { 30, 20, 15, 10, 10 }; 458c2ecf20Sopenharmony_cistatic u32 pio_t9[] = { 20, 15, 10, 10, 10 }; 468c2ecf20Sopenharmony_cistatic u32 pio_tA[] = { 35, 35, 35, 35, 35 }; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct pata_imx_priv { 498c2ecf20Sopenharmony_ci struct clk *clk; 508c2ecf20Sopenharmony_ci /* timings/interrupt/control regs */ 518c2ecf20Sopenharmony_ci void __iomem *host_regs; 528c2ecf20Sopenharmony_ci u32 ata_ctl; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void pata_imx_set_timing(struct ata_device *adev, 568c2ecf20Sopenharmony_ci struct pata_imx_priv *priv) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct ata_timing timing; 598c2ecf20Sopenharmony_ci unsigned long clkrate; 608c2ecf20Sopenharmony_ci u32 T, mode; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci clkrate = clk_get_rate(priv->clk); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (adev->pio_mode < XFER_PIO_0 || adev->pio_mode > XFER_PIO_4 || 658c2ecf20Sopenharmony_ci !clkrate) 668c2ecf20Sopenharmony_ci return; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci T = 1000000000 / clkrate; 698c2ecf20Sopenharmony_ci ata_timing_compute(adev, adev->pio_mode, &timing, T * 1000, 0); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mode = adev->pio_mode - XFER_PIO_0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_OFF); 748c2ecf20Sopenharmony_ci writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_ON); 758c2ecf20Sopenharmony_ci writeb(timing.setup, priv->host_regs + PATA_IMX_ATA_TIME_1); 768c2ecf20Sopenharmony_ci writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2W); 778c2ecf20Sopenharmony_ci writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2R); 788c2ecf20Sopenharmony_ci writeb(1, priv->host_regs + PATA_IMX_ATA_TIME_PIO_RDX); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci writeb(pio_t4[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_4); 818c2ecf20Sopenharmony_ci writeb(pio_t9[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_9); 828c2ecf20Sopenharmony_ci writeb(pio_tA[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_AX); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct pata_imx_priv *priv = ap->host->private_data; 888c2ecf20Sopenharmony_ci u32 val; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci pata_imx_set_timing(adev, priv); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); 938c2ecf20Sopenharmony_ci if (ata_pio_need_iordy(adev)) 948c2ecf20Sopenharmony_ci val |= PATA_IMX_ATA_CTRL_IORDY_EN; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci val &= ~PATA_IMX_ATA_CTRL_IORDY_EN; 978c2ecf20Sopenharmony_ci __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct scsi_host_template pata_imx_sht = { 1018c2ecf20Sopenharmony_ci ATA_PIO_SHT(DRV_NAME), 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct ata_port_operations pata_imx_port_ops = { 1058c2ecf20Sopenharmony_ci .inherits = &ata_sff_port_ops, 1068c2ecf20Sopenharmony_ci .sff_data_xfer = ata_sff_data_xfer32, 1078c2ecf20Sopenharmony_ci .cable_detect = ata_cable_unknown, 1088c2ecf20Sopenharmony_ci .set_piomode = pata_imx_set_piomode, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void pata_imx_setup_port(struct ata_ioports *ioaddr) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci /* Fixup the port shift for platforms that need it */ 1148c2ecf20Sopenharmony_ci ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2); 1158c2ecf20Sopenharmony_ci ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2); 1168c2ecf20Sopenharmony_ci ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2); 1178c2ecf20Sopenharmony_ci ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2); 1188c2ecf20Sopenharmony_ci ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2); 1198c2ecf20Sopenharmony_ci ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2); 1208c2ecf20Sopenharmony_ci ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2); 1218c2ecf20Sopenharmony_ci ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2); 1228c2ecf20Sopenharmony_ci ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2); 1238c2ecf20Sopenharmony_ci ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int pata_imx_probe(struct platform_device *pdev) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct ata_host *host; 1298c2ecf20Sopenharmony_ci struct ata_port *ap; 1308c2ecf20Sopenharmony_ci struct pata_imx_priv *priv; 1318c2ecf20Sopenharmony_ci int irq = 0; 1328c2ecf20Sopenharmony_ci struct resource *io_res; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 1368c2ecf20Sopenharmony_ci if (irq < 0) 1378c2ecf20Sopenharmony_ci return irq; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, 1408c2ecf20Sopenharmony_ci sizeof(struct pata_imx_priv), GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (!priv) 1428c2ecf20Sopenharmony_ci return -ENOMEM; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, NULL); 1458c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 1468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 1478c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci host = ata_host_alloc(&pdev->dev, 1); 1558c2ecf20Sopenharmony_ci if (!host) { 1568c2ecf20Sopenharmony_ci ret = -ENOMEM; 1578c2ecf20Sopenharmony_ci goto err; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci host->private_data = priv; 1618c2ecf20Sopenharmony_ci ap = host->ports[0]; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ap->ops = &pata_imx_port_ops; 1648c2ecf20Sopenharmony_ci ap->pio_mask = ATA_PIO4; 1658c2ecf20Sopenharmony_ci ap->flags |= ATA_FLAG_SLAVE_POSS; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1688c2ecf20Sopenharmony_ci priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res); 1698c2ecf20Sopenharmony_ci if (IS_ERR(priv->host_regs)) { 1708c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->host_regs); 1718c2ecf20Sopenharmony_ci goto err; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA; 1758c2ecf20Sopenharmony_ci ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci pata_imx_setup_port(&ap->ioaddr); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx", 1828c2ecf20Sopenharmony_ci (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA, 1838c2ecf20Sopenharmony_ci (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* deassert resets */ 1868c2ecf20Sopenharmony_ci __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B | 1878c2ecf20Sopenharmony_ci PATA_IMX_ATA_CTRL_ATA_RST_B, 1888c2ecf20Sopenharmony_ci priv->host_regs + PATA_IMX_ATA_CONTROL); 1898c2ecf20Sopenharmony_ci /* enable interrupts */ 1908c2ecf20Sopenharmony_ci __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, 1918c2ecf20Sopenharmony_ci priv->host_regs + PATA_IMX_ATA_INT_EN); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* activate */ 1948c2ecf20Sopenharmony_ci ret = ata_host_activate(host, irq, ata_sff_interrupt, 0, 1958c2ecf20Sopenharmony_ci &pata_imx_sht); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (ret) 1988c2ecf20Sopenharmony_ci goto err; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_cierr: 2028c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int pata_imx_remove(struct platform_device *pdev) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct ata_host *host = platform_get_drvdata(pdev); 2108c2ecf20Sopenharmony_ci struct pata_imx_priv *priv = host->private_data; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ata_host_detach(host); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2228c2ecf20Sopenharmony_cistatic int pata_imx_suspend(struct device *dev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct ata_host *host = dev_get_drvdata(dev); 2258c2ecf20Sopenharmony_ci struct pata_imx_priv *priv = host->private_data; 2268c2ecf20Sopenharmony_ci int ret; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = ata_host_suspend(host, PMSG_SUSPEND); 2298c2ecf20Sopenharmony_ci if (!ret) { 2308c2ecf20Sopenharmony_ci __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); 2318c2ecf20Sopenharmony_ci priv->ata_ctl = 2328c2ecf20Sopenharmony_ci __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); 2338c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int pata_imx_resume(struct device *dev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct ata_host *host = dev_get_drvdata(dev); 2428c2ecf20Sopenharmony_ci struct pata_imx_priv *priv = host->private_data; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci int ret = clk_prepare_enable(priv->clk); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, 2518c2ecf20Sopenharmony_ci priv->host_regs + PATA_IMX_ATA_INT_EN); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ata_host_resume(host); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci#endif 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pata_imx_pm_ops, pata_imx_suspend, pata_imx_resume); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic const struct of_device_id imx_pata_dt_ids[] = { 2628c2ecf20Sopenharmony_ci { 2638c2ecf20Sopenharmony_ci .compatible = "fsl,imx27-pata", 2648c2ecf20Sopenharmony_ci }, { 2658c2ecf20Sopenharmony_ci /* sentinel */ 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_pata_dt_ids); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct platform_driver pata_imx_driver = { 2718c2ecf20Sopenharmony_ci .probe = pata_imx_probe, 2728c2ecf20Sopenharmony_ci .remove = pata_imx_remove, 2738c2ecf20Sopenharmony_ci .driver = { 2748c2ecf20Sopenharmony_ci .name = DRV_NAME, 2758c2ecf20Sopenharmony_ci .of_match_table = imx_pata_dt_ids, 2768c2ecf20Sopenharmony_ci .pm = &pata_imx_pm_ops, 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cimodule_platform_driver(pata_imx_driver); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); 2838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("low-level driver for iMX PATA"); 2848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2858c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 286