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 | */ |
15 | |
16 | #include <linux/ata.h> |
17 | #include <linux/clk.h> |
18 | #include <linux/libata.h> |
19 | #include <linux/module.h> |
20 | #include <linux/mod_devicetable.h> |
21 | #include <linux/platform_device.h> |
22 | |
23 | #define DRV_NAME "pata_imx" |
24 | |
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 | |
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 | |
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 | |
48 | struct pata_imx_priv { |
49 | struct clk *clk; |
50 | /* timings/interrupt/control regs */ |
51 | void __iomem *host_regs; |
52 | u32 ata_ctl; |
53 | }; |
54 | |
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(clk: 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(val: 3, addr: priv->host_regs + PATA_IMX_ATA_TIME_OFF); |
74 | writeb(val: 3, addr: priv->host_regs + PATA_IMX_ATA_TIME_ON); |
75 | writeb(val: timing.setup, addr: priv->host_regs + PATA_IMX_ATA_TIME_1); |
76 | writeb(val: timing.act8b, addr: priv->host_regs + PATA_IMX_ATA_TIME_2W); |
77 | writeb(val: timing.act8b, addr: priv->host_regs + PATA_IMX_ATA_TIME_2R); |
78 | writeb(val: 1, addr: priv->host_regs + PATA_IMX_ATA_TIME_PIO_RDX); |
79 | |
80 | writeb(val: pio_t4[mode] / T + 1, addr: priv->host_regs + PATA_IMX_ATA_TIME_4); |
81 | writeb(val: pio_t9[mode] / T + 1, addr: priv->host_regs + PATA_IMX_ATA_TIME_9); |
82 | writeb(val: pio_tA[mode] / T + 1, addr: priv->host_regs + PATA_IMX_ATA_TIME_AX); |
83 | } |
84 | |
85 | static void pata_imx_set_piomode(struct ata_port *ap, struct ata_device *adev) |
86 | { |
87 | struct pata_imx_priv *priv = ap->host->private_data; |
88 | u32 val; |
89 | |
90 | pata_imx_set_timing(adev, priv); |
91 | |
92 | val = __raw_readl(addr: 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, addr: priv->host_regs + PATA_IMX_ATA_CONTROL); |
98 | } |
99 | |
100 | static const 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, |
106 | .sff_data_xfer = ata_sff_data_xfer32, |
107 | .cable_detect = ata_cable_unknown, |
108 | .set_piomode = pata_imx_set_piomode, |
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 | |
126 | static int pata_imx_probe(struct platform_device *pdev) |
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; |
133 | int ret; |
134 | |
135 | irq = platform_get_irq(pdev, 0); |
136 | if (irq < 0) |
137 | return irq; |
138 | |
139 | priv = devm_kzalloc(dev: &pdev->dev, |
140 | size: sizeof(struct pata_imx_priv), GFP_KERNEL); |
141 | if (!priv) |
142 | return -ENOMEM; |
143 | |
144 | priv->clk = devm_clk_get_enabled(dev: &pdev->dev, NULL); |
145 | if (IS_ERR(ptr: priv->clk)) { |
146 | dev_err(&pdev->dev, "Failed to get and enable clock\n" ); |
147 | return PTR_ERR(ptr: priv->clk); |
148 | } |
149 | |
150 | host = ata_host_alloc(dev: &pdev->dev, max_ports: 1); |
151 | if (!host) |
152 | return -ENOMEM; |
153 | |
154 | host->private_data = priv; |
155 | ap = host->ports[0]; |
156 | |
157 | ap->ops = &pata_imx_port_ops; |
158 | ap->pio_mask = ATA_PIO4; |
159 | ap->flags |= ATA_FLAG_SLAVE_POSS; |
160 | |
161 | priv->host_regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &io_res); |
162 | if (IS_ERR(ptr: priv->host_regs)) |
163 | return PTR_ERR(ptr: priv->host_regs); |
164 | |
165 | ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA; |
166 | ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL; |
167 | |
168 | ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; |
169 | |
170 | pata_imx_setup_port(ioaddr: &ap->ioaddr); |
171 | |
172 | ata_port_desc(ap, fmt: "cmd 0x%llx ctl 0x%llx" , |
173 | (unsigned long long)io_res->start + PATA_IMX_DRIVE_DATA, |
174 | (unsigned long long)io_res->start + PATA_IMX_DRIVE_CONTROL); |
175 | |
176 | /* deassert resets */ |
177 | __raw_writel(PATA_IMX_ATA_CTRL_FIFO_RST_B | |
178 | PATA_IMX_ATA_CTRL_ATA_RST_B, |
179 | addr: priv->host_regs + PATA_IMX_ATA_CONTROL); |
180 | /* enable interrupts */ |
181 | __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, |
182 | addr: priv->host_regs + PATA_IMX_ATA_INT_EN); |
183 | |
184 | /* activate */ |
185 | ret = ata_host_activate(host, irq, irq_handler: ata_sff_interrupt, irq_flags: 0, |
186 | sht: &pata_imx_sht); |
187 | |
188 | if (ret) |
189 | return ret; |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static void pata_imx_remove(struct platform_device *pdev) |
195 | { |
196 | struct ata_host *host = platform_get_drvdata(pdev); |
197 | struct pata_imx_priv *priv = host->private_data; |
198 | |
199 | ata_host_detach(host); |
200 | |
201 | __raw_writel(val: 0, addr: priv->host_regs + PATA_IMX_ATA_INT_EN); |
202 | } |
203 | |
204 | #ifdef CONFIG_PM_SLEEP |
205 | static int pata_imx_suspend(struct device *dev) |
206 | { |
207 | struct ata_host *host = dev_get_drvdata(dev); |
208 | struct pata_imx_priv *priv = host->private_data; |
209 | |
210 | ata_host_suspend(host, PMSG_SUSPEND); |
211 | |
212 | __raw_writel(val: 0, addr: priv->host_regs + PATA_IMX_ATA_INT_EN); |
213 | priv->ata_ctl = __raw_readl(addr: priv->host_regs + PATA_IMX_ATA_CONTROL); |
214 | clk_disable_unprepare(clk: priv->clk); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static int pata_imx_resume(struct device *dev) |
220 | { |
221 | struct ata_host *host = dev_get_drvdata(dev); |
222 | struct pata_imx_priv *priv = host->private_data; |
223 | |
224 | int ret = clk_prepare_enable(clk: priv->clk); |
225 | if (ret) |
226 | return ret; |
227 | |
228 | __raw_writel(val: priv->ata_ctl, addr: priv->host_regs + PATA_IMX_ATA_CONTROL); |
229 | |
230 | __raw_writel(PATA_IMX_ATA_INTR_ATA_INTRQ2, |
231 | addr: priv->host_regs + PATA_IMX_ATA_INT_EN); |
232 | |
233 | ata_host_resume(host); |
234 | |
235 | return 0; |
236 | } |
237 | #endif |
238 | |
239 | static SIMPLE_DEV_PM_OPS(pata_imx_pm_ops, pata_imx_suspend, pata_imx_resume); |
240 | |
241 | static const struct of_device_id imx_pata_dt_ids[] = { |
242 | { |
243 | .compatible = "fsl,imx27-pata" , |
244 | }, { |
245 | /* sentinel */ |
246 | } |
247 | }; |
248 | MODULE_DEVICE_TABLE(of, imx_pata_dt_ids); |
249 | |
250 | static struct platform_driver pata_imx_driver = { |
251 | .probe = pata_imx_probe, |
252 | .remove_new = pata_imx_remove, |
253 | .driver = { |
254 | .name = DRV_NAME, |
255 | .of_match_table = imx_pata_dt_ids, |
256 | .pm = &pata_imx_pm_ops, |
257 | }, |
258 | }; |
259 | |
260 | module_platform_driver(pata_imx_driver); |
261 | |
262 | MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>" ); |
263 | MODULE_DESCRIPTION("low-level driver for iMX PATA" ); |
264 | MODULE_LICENSE("GPL" ); |
265 | MODULE_ALIAS("platform:" DRV_NAME); |
266 | |