Commit | Line | Data |
---|---|---|
e39c75cf APR |
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 | |
e39c75cf | 14 | */ |
07b9733f | 15 | |
e39c75cf | 16 | #include <linux/ata.h> |
07b9733f | 17 | #include <linux/clk.h> |
e39c75cf | 18 | #include <linux/libata.h> |
07b9733f | 19 | #include <linux/module.h> |
ac316725 | 20 | #include <linux/mod_devicetable.h> |
e39c75cf | 21 | #include <linux/platform_device.h> |
e39c75cf APR |
22 | |
23 | #define DRV_NAME "pata_imx" | |
24 | ||
fab43e14 VZ |
25 | #define PATA_IMX_ATA_TIME_OFF 0x00 |
26 | #define PATA_IMX_ATA_TIME_ON 0x01 | |
27 | #define PATA_IMX_ATA_TIME_1 0x02 | |
28 | #define PATA_IMX_ATA_TIME_2W 0x03 | |
29 | #define PATA_IMX_ATA_TIME_2R 0x04 | |
30 | #define PATA_IMX_ATA_TIME_AX 0x05 | |
31 | #define PATA_IMX_ATA_TIME_PIO_RDX 0x06 | |
32 | #define PATA_IMX_ATA_TIME_4 0x07 | |
33 | #define PATA_IMX_ATA_TIME_9 0x08 | |
34 | ||
e39c75cf APR |
35 | #define PATA_IMX_ATA_CONTROL 0x24 |
36 | #define PATA_IMX_ATA_CTRL_FIFO_RST_B (1<<7) | |
37 | #define PATA_IMX_ATA_CTRL_ATA_RST_B (1<<6) | |
38 | #define PATA_IMX_ATA_CTRL_IORDY_EN (1<<0) | |
39 | #define PATA_IMX_ATA_INT_EN 0x2C | |
40 | #define PATA_IMX_ATA_INTR_ATA_INTRQ2 (1<<3) | |
41 | #define PATA_IMX_DRIVE_DATA 0xA0 | |
42 | #define PATA_IMX_DRIVE_CONTROL 0xD8 | |
43 | ||
fab43e14 VZ |
44 | static u32 pio_t4[] = { 30, 20, 15, 10, 10 }; |
45 | static u32 pio_t9[] = { 20, 15, 10, 10, 10 }; | |
46 | static u32 pio_tA[] = { 35, 35, 35, 35, 35 }; | |
47 | ||
e39c75cf APR |
48 | struct pata_imx_priv { |
49 | struct clk *clk; | |
50 | /* timings/interrupt/control regs */ | |
51b5539c | 51 | void __iomem *host_regs; |
e39c75cf APR |
52 | u32 ata_ctl; |
53 | }; | |
54 | ||
fab43e14 VZ |
55 | static void pata_imx_set_timing(struct ata_device *adev, |
56 | struct pata_imx_priv *priv) | |
57 | { | |
58 | struct ata_timing timing; | |
59 | unsigned long clkrate; | |
60 | u32 T, mode; | |
61 | ||
62 | clkrate = clk_get_rate(priv->clk); | |
63 | ||
64 | if (adev->pio_mode < XFER_PIO_0 || adev->pio_mode > XFER_PIO_4 || | |
65 | !clkrate) | |
66 | return; | |
67 | ||
68 | T = 1000000000 / clkrate; | |
69 | ata_timing_compute(adev, adev->pio_mode, &timing, T * 1000, 0); | |
70 | ||
71 | mode = adev->pio_mode - XFER_PIO_0; | |
72 | ||
73 | writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_OFF); | |
74 | writeb(3, priv->host_regs + PATA_IMX_ATA_TIME_ON); | |
75 | writeb(timing.setup, priv->host_regs + PATA_IMX_ATA_TIME_1); | |
76 | writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2W); | |
77 | writeb(timing.act8b, priv->host_regs + PATA_IMX_ATA_TIME_2R); | |
78 | writeb(1, priv->host_regs + PATA_IMX_ATA_TIME_PIO_RDX); | |
79 | ||
80 | writeb(pio_t4[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_4); | |
81 | writeb(pio_t9[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_9); | |
82 | writeb(pio_tA[mode] / T + 1, priv->host_regs + PATA_IMX_ATA_TIME_AX); | |
83 | } | |
84 | ||
65a443ea | 85 | static void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev) |
e39c75cf | 86 | { |
e39c75cf APR |
87 | struct pata_imx_priv *priv = ap->host->private_data; |
88 | u32 val; | |
89 | ||
fab43e14 VZ |
90 | pata_imx_set_timing(adev, priv); |
91 | ||
65a443ea VZ |
92 | val = __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); |
93 | if (ata_pio_need_iordy(adev)) | |
94 | val |= PATA_IMX_ATA_CTRL_IORDY_EN; | |
95 | else | |
96 | val &= ~PATA_IMX_ATA_CTRL_IORDY_EN; | |
97 | __raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL); | |
e39c75cf APR |
98 | } |
99 | ||
100 | static struct scsi_host_template pata_imx_sht = { | |
101 | ATA_PIO_SHT(DRV_NAME), | |
102 | }; | |
103 | ||
104 | static struct ata_port_operations pata_imx_port_ops = { | |
105 | .inherits = &ata_sff_port_ops, | |
23ebda2f | 106 | .sff_data_xfer = ata_sff_data_xfer32, |
e39c75cf | 107 | .cable_detect = ata_cable_unknown, |
65a443ea | 108 | .set_piomode = pata_imx_set_piomode, |
e39c75cf APR |
109 | }; |
110 | ||
111 | static void pata_imx_setup_port(struct ata_ioports *ioaddr) | |
112 | { | |
113 | /* Fixup the port shift for platforms that need it */ | |
114 | ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2); | |
115 | ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << 2); | |
116 | ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << 2); | |
117 | ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << 2); | |
118 | ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << 2); | |
119 | ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << 2); | |
120 | ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << 2); | |
121 | ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << 2); | |
122 | ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << 2); | |
123 | ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << 2); | |
124 | } | |
125 | ||
0ec24914 | 126 | static int pata_imx_probe(struct platform_device *pdev) |
e39c75cf APR |
127 | { |
128 | struct ata_host *host; | |
129 | struct ata_port *ap; | |
130 | struct pata_imx_priv *priv; | |
131 | int irq = 0; | |
132 | struct resource *io_res; | |
ff540d02 | 133 | int ret; |
e39c75cf | 134 | |
e39c75cf | 135 | irq = platform_get_irq(pdev, 0); |
3ef9cc31 FE |
136 | if (irq < 0) |
137 | return irq; | |
e39c75cf APR |
138 | |
139 | priv = devm_kzalloc(&pdev->dev, | |
140 | sizeof(struct pata_imx_priv), GFP_KERNEL); | |
141 | if (!priv) | |
142 | return -ENOMEM; | |
143 | ||
50f5a341 | 144 | priv->clk = devm_clk_get(&pdev->dev, NULL); |
e39c75cf APR |
145 | if (IS_ERR(priv->clk)) { |
146 | dev_err(&pdev->dev, "Failed to get clock\n"); | |
147 | return PTR_ERR(priv->clk); | |
148 | } | |
149 | ||
0475c947 FE |
150 | ret = clk_prepare_enable(priv->clk); |
151 | if (ret) | |
152 | return ret; | |
e39c75cf APR |
153 | |
154 | host = ata_host_alloc(&pdev->dev, 1); | |
ff540d02 SH |
155 | if (!host) { |
156 | ret = -ENOMEM; | |
157 | goto err; | |
158 | } | |
e39c75cf APR |
159 | |
160 | host->private_data = priv; | |
161 | ap = host->ports[0]; | |
162 | ||
163 | ap->ops = &pata_imx_port_ops; | |
b986723c | 164 | ap->pio_mask = ATA_PIO4; |
e39c75cf APR |
165 | ap->flags |= ATA_FLAG_SLAVE_POSS; |
166 | ||
b314fc77 FE |
167 | io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
168 | priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res); | |
13e8e78b BZ |
169 | if (IS_ERR(priv->host_regs)) { |
170 | ret = PTR_ERR(priv->host_regs); | |
ff540d02 | 171 | goto err; |
e39c75cf APR |
172 | } |
173 | ||
174 | ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA; | |
175 | ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL; | |
176 | ||
177 | ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; | |
178 | ||
179 | pata_imx_setup_port(&ap->ioaddr); | |
180 | ||
181 | ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx", | |
182 | (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA, | |
183 | (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL); | |
184 | ||
185 | /* deassert resets */ | |
186 | __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B | | |
187 | PATA_IMX_ATA_CTRL_ATA_RST_B, | |
188 | priv->host_regs + PATA_IMX_ATA_CONTROL); | |
189 | /* enable interrupts */ | |
190 | __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, | |
191 | priv->host_regs + PATA_IMX_ATA_INT_EN); | |
192 | ||
193 | /* activate */ | |
ff540d02 | 194 | ret = ata_host_activate(host, irq, ata_sff_interrupt, 0, |
e39c75cf APR |
195 | &pata_imx_sht); |
196 | ||
ff540d02 SH |
197 | if (ret) |
198 | goto err; | |
199 | ||
200 | return 0; | |
201 | err: | |
a18dada0 | 202 | clk_disable_unprepare(priv->clk); |
50f5a341 | 203 | |
ff540d02 | 204 | return ret; |
e39c75cf APR |
205 | } |
206 | ||
0ec24914 | 207 | static int pata_imx_remove(struct platform_device *pdev) |
e39c75cf | 208 | { |
d89995db | 209 | struct ata_host *host = platform_get_drvdata(pdev); |
e39c75cf APR |
210 | struct pata_imx_priv *priv = host->private_data; |
211 | ||
212 | ata_host_detach(host); | |
213 | ||
214 | __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); | |
215 | ||
a18dada0 | 216 | clk_disable_unprepare(priv->clk); |
e39c75cf APR |
217 | |
218 | return 0; | |
219 | } | |
220 | ||
58eb8cd5 | 221 | #ifdef CONFIG_PM_SLEEP |
e39c75cf APR |
222 | static int pata_imx_suspend(struct device *dev) |
223 | { | |
224 | struct ata_host *host = dev_get_drvdata(dev); | |
225 | struct pata_imx_priv *priv = host->private_data; | |
226 | int ret; | |
227 | ||
228 | ret = ata_host_suspend(host, PMSG_SUSPEND); | |
229 | if (!ret) { | |
230 | __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); | |
231 | priv->ata_ctl = | |
232 | __raw_readl(priv->host_regs + PATA_IMX_ATA_CONTROL); | |
a18dada0 | 233 | clk_disable_unprepare(priv->clk); |
e39c75cf APR |
234 | } |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
239 | static int pata_imx_resume(struct device *dev) | |
240 | { | |
241 | struct ata_host *host = dev_get_drvdata(dev); | |
242 | struct pata_imx_priv *priv = host->private_data; | |
243 | ||
0475c947 FE |
244 | int ret = clk_prepare_enable(priv->clk); |
245 | if (ret) | |
246 | return ret; | |
e39c75cf APR |
247 | |
248 | __raw_writel(priv->ata_ctl, priv->host_regs + PATA_IMX_ATA_CONTROL); | |
249 | ||
250 | __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, | |
251 | priv->host_regs + PATA_IMX_ATA_INT_EN); | |
252 | ||
253 | ata_host_resume(host); | |
254 | ||
255 | return 0; | |
256 | } | |
e39c75cf APR |
257 | #endif |
258 | ||
36888e95 FE |
259 | static SIMPLE_DEV_PM_OPS(pata_imx_pm_ops, pata_imx_suspend, pata_imx_resume); |
260 | ||
b9d15db2 SH |
261 | static const struct of_device_id imx_pata_dt_ids[] = { |
262 | { | |
263 | .compatible = "fsl,imx27-pata", | |
264 | }, { | |
265 | /* sentinel */ | |
266 | } | |
267 | }; | |
71ab1d58 | 268 | MODULE_DEVICE_TABLE(of, imx_pata_dt_ids); |
b9d15db2 | 269 | |
e39c75cf APR |
270 | static struct platform_driver pata_imx_driver = { |
271 | .probe = pata_imx_probe, | |
0ec24914 | 272 | .remove = pata_imx_remove, |
e39c75cf APR |
273 | .driver = { |
274 | .name = DRV_NAME, | |
b9d15db2 | 275 | .of_match_table = imx_pata_dt_ids, |
e39c75cf | 276 | .pm = &pata_imx_pm_ops, |
e39c75cf APR |
277 | }, |
278 | }; | |
279 | ||
99c8ea3e | 280 | module_platform_driver(pata_imx_driver); |
e39c75cf APR |
281 | |
282 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); | |
283 | MODULE_DESCRIPTION("low-level driver for iMX PATA"); | |
284 | MODULE_LICENSE("GPL"); | |
285 | MODULE_ALIAS("platform:" DRV_NAME); |