aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/pata_imx.c
diff options
context:
space:
mode:
authorArnaud Patard (Rtp) <arnaud.patard@rtp-net.org>2011-07-26 10:58:19 -0400
committerJeff Garzik <jgarzik@redhat.com>2011-08-18 23:57:58 -0400
commite39c75cf3e045c2fb3988770b207dfd09c30d4ac (patch)
tree128865b8ab5679735e43afd677d336f68a5acd44 /drivers/ata/pata_imx.c
parent6d0e194d2eefcaab6dbdca1f639748660144acb5 (diff)
ata: Add iMX pata support
Add basic support for pata on iMX. It has been tested only on imx51. SDMA support will probably be added later so this version supports only PIO. v2: - enable only when needed IORDY - use dev_get_drvdata v3: - add missing clk_put() calls - use platform_get_irq() - fix resume code to avoid disabling IORDY on resume v4: - Remove EXPERIMENTAL and switch to depends on ARCH_MXC - Use devm_kzalloc() - make clock a must-have - Use only 1 ioremap Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/pata_imx.c')
-rw-r--r--drivers/ata/pata_imx.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
new file mode 100644
index 000000000000..ca9d9caedfa3
--- /dev/null
+++ b/drivers/ata/pata_imx.c
@@ -0,0 +1,253 @@
1/*
2 * Freescale iMX PATA driver
3 *
4 * Copyright (C) 2011 Arnaud Patard <arnaud.patard@rtp-net.org>
5 *
6 * Based on pata_platform - Copyright (C) 2006 - 2007 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 *
12 * TODO:
13 * - dmaengine support
14 * - check if timing stuff needed
15 */
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/blkdev.h>
20#include <scsi/scsi_host.h>
21#include <linux/ata.h>
22#include <linux/libata.h>
23#include <linux/platform_device.h>
24#include <linux/clk.h>
25
26#define DRV_NAME "pata_imx"
27
28#define PATA_IMX_ATA_CONTROL 0x24
29#define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7)
30#define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6)
31#define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0)
32#define PATA_IMX_ATA_INT_EN 0x2C
33#define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3)
34#define PATA_IMX_DRIVE_DATA 0xA0
35#define PATA_IMX_DRIVE_CONTROL 0xD8
36
37struct pata_imx_priv {
38 struct clk *clk;
39 /* timings/interrupt/control regs */
40 u8 *host_regs;
41 u32 ata_ctl;
42};
43
44static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused)
45{
46 struct ata_device *dev;
47 struct ata_port *ap = link->ap;
48 struct pata_imx_priv *priv = ap->host->private_data;
49 u32 val;
50
51 ata_for_each_dev(dev, link, ENABLED) {
52 dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
53 dev->xfer_shift = ATA_SHIFT_PIO;
54 dev->flags |= ATA_DFLAG_PIO;
55
56 val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
57 if (ata_pio_need_iordy(dev))
58 val |= PATA_IMX_ATA_CTRL_IORDY_EN;
59 else
60 val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
61 __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
62
63 ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
64 }
65 return 0;
66}
67
68static struct scsi_host_template pata_imx_sht = {
69 ATA_PIO_SHT(DRV_NAME),
70};
71
72static struct ata_port_operations pata_imx_port_ops = {
73 .inherits = &ata_sff_port_ops,
74 .sff_data_xfer = ata_sff_data_xfer_noirq,
75 .cable_detect = ata_cable_unknown,
76 .set_mode = pata_imx_set_mode,
77};
78
79static void pata_imx_setup_port(struct ata_ioports *ioaddr)
80{
81 /* Fixup the port shift for platforms that need it */
82 ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2);
83 ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2);
84 ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2);
85 ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2);
86 ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2);
87 ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2);
88 ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2);
89 ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2);
90 ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2);
91 ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2);
92}
93
94static int __devinit pata_imx_probe(struct platform_device *pdev)
95{
96 struct ata_host *host;
97 struct ata_port *ap;
98 struct pata_imx_priv *priv;
99 int irq = 0;
100 struct resource *io_res;
101
102 io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
103 if (io_res == NULL)
104 return -EINVAL;
105
106 irq = platform_get_irq(pdev, 0);
107 if (irq <= 0)
108 return -EINVAL;
109
110 priv = devm_kzalloc(&pdev->dev,
111 sizeof(struct pata_imx_priv), GFP_KERNEL);
112 if (!priv)
113 return -ENOMEM;
114
115 priv->clk = clk_get(&pdev->dev, NULL);
116 if (IS_ERR(priv->clk)) {
117 dev_err(&pdev->dev, "Failed to get clock\n");
118 return PTR_ERR(priv->clk);
119 }
120
121 clk_enable(priv->clk);
122
123 host = ata_host_alloc(&pdev->dev, 1);
124 if (!host)
125 goto free_priv;
126
127 host->private_data = priv;
128 ap = host->ports[0];
129
130 ap->ops = &pata_imx_port_ops;
131 ap->pio_mask = ATA_PIO0;
132 ap->flags |= ATA_FLAG_SLAVE_POSS;
133
134 priv->host_regs = devm_ioremap(&pdev->dev, io_res->start,
135 resource_size(io_res));
136 if (!priv->host_regs) {
137 dev_err(&pdev->dev, "failed to map IO/CTL base\n");
138 goto free_priv;
139 }
140
141 ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA;
142 ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL;
143
144 ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
145
146 pata_imx_setup_port(&ap->ioaddr);
147
148 ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx",
149 (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA,
150 (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL);
151
152 /* deassert resets */
153 __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B |
154 PATA_IMX_ATA_CTRL_ATA_RST_B,
155 priv->host_regs + PATA_IMX_ATA_CONTROL);
156 /* enable interrupts */
157 __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
158 priv->host_regs + PATA_IMX_ATA_INT_EN);
159
160 /* activate */
161 return ata_host_activate(host, irq, ata_sff_interrupt, 0,
162 &pata_imx_sht);
163
164free_priv:
165 clk_disable(priv->clk);
166 clk_put(priv->clk);
167 return -ENOMEM;
168}
169
170static int __devexit pata_imx_remove(struct platform_device *pdev)
171{
172 struct ata_host *host = dev_get_drvdata(&pdev->dev);
173 struct pata_imx_priv *priv = host->private_data;
174
175 ata_host_detach(host);
176
177 __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
178
179 clk_disable(priv->clk);
180 clk_put(priv->clk);
181
182 return 0;
183}
184
185#ifdef CONFIG_PM
186static int pata_imx_suspend(struct device *dev)
187{
188 struct ata_host *host = dev_get_drvdata(dev);
189 struct pata_imx_priv *priv = host->private_data;
190 int ret;
191
192 ret = ata_host_suspend(host, PMSG_SUSPEND);
193 if (!ret) {
194 __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN);
195 priv->ata_ctl =
196 __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL);
197 clk_disable(priv->clk);
198 }
199
200 return ret;
201}
202
203static int pata_imx_resume(struct device *dev)
204{
205 struct ata_host *host = dev_get_drvdata(dev);
206 struct pata_imx_priv *priv = host->private_data;
207
208 clk_enable(priv->clk);
209
210 __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL);
211
212 __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2,
213 priv->host_regs + PATA_IMX_ATA_INT_EN);
214
215 ata_host_resume(host);
216
217 return 0;
218}
219
220static const struct dev_pm_ops pata_imx_pm_ops = {
221 .suspend = pata_imx_suspend,
222 .resume = pata_imx_resume,
223};
224#endif
225
226static struct platform_driver pata_imx_driver = {
227 .probe = pata_imx_probe,
228 .remove = __devexit_p(pata_imx_remove),
229 .driver = {
230 .name = DRV_NAME,
231 .owner = THIS_MODULE,
232#ifdef CONFIG_PM
233 .pm = &pata_imx_pm_ops,
234#endif
235 },
236};
237
238static int __init pata_imx_init(void)
239{
240 return platform_driver_register(&pata_imx_driver);
241}
242
243static void __exit pata_imx_exit(void)
244{
245 platform_driver_unregister(&pata_imx_driver);
246}
247module_init(pata_imx_init);
248module_exit(pata_imx_exit);
249
250MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
251MODULE_DESCRIPTION("low-level driver for iMX PATA");
252MODULE_LICENSE("GPL");
253MODULE_ALIAS("platform:" DRV_NAME);