]>
Commit | Line | Data |
---|---|---|
2b230b77 SS |
1 | From 093f4fdd74f29031d79be747c65b95fb16601a92 Mon Sep 17 00:00:00 2001 |
2 | From: Richard Zhu <r65037@freescale.com> | |
3 | Date: Wed, 24 Jul 2013 06:15:29 +0000 | |
4 | Subject: [PATCH 2/2] ahci_imx: add ahci sata support on imx platforms | |
5 | ||
6 | imx6q contains one Synopsys AHCI SATA controller, But it can't share | |
7 | ahci_platform driver with other controllers because there are some | |
8 | misalignments of the generic AHCI controller - the bits definitions of | |
9 | the HBA registers, the Vendor Specific registers, the AHCI PHY clock | |
10 | and the AHCI signals adjustment window(GPR13 register). | |
11 | ||
12 | - CAP_SSS(bit20) of the HOST_CAP is writable, default value is '0', | |
13 | should be configured to be '1' | |
14 | ||
15 | - bit0 (only one AHCI SATA port on imx6q) of the HOST_PORTS_IMPL | |
16 | should be set to be '1'.(default 0) | |
17 | ||
18 | - One Vendor Specific register HOST_TIMER1MS(offset:0xe0) should be | |
19 | configured regarding to the frequency of AHB bus clock. | |
20 | ||
21 | - Configurations of the AHCI PHY clock, and the signal parameters of | |
22 | the GPR13 | |
23 | ||
24 | Setup its own ahci sata driver, contained the imx6q specific | |
25 | initialized codes, re-use the generic ahci_platform driver, and keep | |
26 | the generic ahci_platform driver clean as much as possible. | |
27 | ||
28 | tj: patch description reformatted | |
29 | ||
30 | Signed-off-by: Richard Zhu <r65037@freescale.com> | |
31 | Reviewed-by: Shawn Guo <shawn.guo@linaro.org> | |
32 | Signed-off-by: Tejun Heo <tj@kernel.org> | |
33 | --- | |
34 | drivers/ata/Kconfig | 9 ++ | |
35 | drivers/ata/Makefile | 1 + | |
36 | drivers/ata/ahci_imx.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++ | |
37 | 3 files changed, 246 insertions(+) | |
38 | create mode 100644 drivers/ata/ahci_imx.c | |
39 | ||
40 | diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig | |
41 | index 80dc988..cbf7a16 100644 | |
42 | --- a/drivers/ata/Kconfig | |
43 | +++ b/drivers/ata/Kconfig | |
44 | @@ -97,6 +97,15 @@ config SATA_AHCI_PLATFORM | |
45 | ||
46 | If unsure, say N. | |
47 | ||
48 | +config AHCI_IMX | |
49 | + tristate "Freescale i.MX AHCI SATA support" | |
50 | + depends on SATA_AHCI_PLATFORM | |
51 | + help | |
52 | + This option enables support for the Freescale i.MX SoC's | |
53 | + onboard AHCI SATA. | |
54 | + | |
55 | + If unsure, say N. | |
56 | + | |
57 | config SATA_FSL | |
58 | tristate "Freescale 3.0Gbps SATA support" | |
59 | depends on FSL_SOC | |
60 | diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile | |
61 | index c04d0fd..46518c6 100644 | |
62 | --- a/drivers/ata/Makefile | |
63 | +++ b/drivers/ata/Makefile | |
64 | @@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o | |
65 | obj-$(CONFIG_SATA_SIL24) += sata_sil24.o | |
66 | obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o | |
67 | obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o | |
68 | +obj-$(CONFIG_AHCI_IMX) += ahci_imx.o | |
69 | ||
70 | # SFF w/ custom DMA | |
71 | obj-$(CONFIG_PDC_ADMA) += pdc_adma.o | |
72 | diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c | |
73 | new file mode 100644 | |
74 | index 0000000..58debb0 | |
75 | --- /dev/null | |
76 | +++ b/drivers/ata/ahci_imx.c | |
77 | @@ -0,0 +1,236 @@ | |
78 | +/* | |
79 | + * Freescale IMX AHCI SATA platform driver | |
80 | + * Copyright 2013 Freescale Semiconductor, Inc. | |
81 | + * | |
82 | + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov | |
83 | + * | |
84 | + * This program is free software; you can redistribute it and/or modify it | |
85 | + * under the terms and conditions of the GNU General Public License, | |
86 | + * version 2, as published by the Free Software Foundation. | |
87 | + * | |
88 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
89 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
90 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
91 | + * more details. | |
92 | + * | |
93 | + * You should have received a copy of the GNU General Public License along with | |
94 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
95 | + */ | |
96 | + | |
97 | +#include <linux/kernel.h> | |
98 | +#include <linux/module.h> | |
99 | +#include <linux/platform_device.h> | |
100 | +#include <linux/regmap.h> | |
101 | +#include <linux/ahci_platform.h> | |
102 | +#include <linux/of_device.h> | |
103 | +#include <linux/mfd/syscon.h> | |
104 | +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | |
105 | +#include "ahci.h" | |
106 | + | |
107 | +enum { | |
108 | + HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ | |
109 | +}; | |
110 | + | |
111 | +struct imx_ahci_priv { | |
112 | + struct platform_device *ahci_pdev; | |
113 | + struct clk *sata_ref_clk; | |
114 | + struct clk *ahb_clk; | |
115 | + struct regmap *gpr; | |
116 | +}; | |
117 | + | |
118 | +static int imx6q_sata_init(struct device *dev, void __iomem *mmio) | |
119 | +{ | |
120 | + int ret = 0; | |
121 | + unsigned int reg_val; | |
122 | + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); | |
123 | + | |
124 | + imxpriv->gpr = | |
125 | + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); | |
126 | + if (IS_ERR(imxpriv->gpr)) { | |
127 | + dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n"); | |
128 | + return PTR_ERR(imxpriv->gpr); | |
129 | + } | |
130 | + | |
131 | + ret = clk_prepare_enable(imxpriv->sata_ref_clk); | |
132 | + if (ret < 0) { | |
133 | + dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret); | |
134 | + return ret; | |
135 | + } | |
136 | + | |
137 | + /* | |
138 | + * set PHY Paremeters, two steps to configure the GPR13, | |
139 | + * one write for rest of parameters, mask of first write | |
140 | + * is 0x07fffffd, and the other one write for setting | |
141 | + * the mpll_clk_en. | |
142 | + */ | |
143 | + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | |
144 | + | IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | |
145 | + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | |
146 | + | IMX6Q_GPR13_SATA_SPD_MODE_MASK | |
147 | + | IMX6Q_GPR13_SATA_MPLL_SS_EN | |
148 | + | IMX6Q_GPR13_SATA_TX_ATTEN_MASK | |
149 | + | IMX6Q_GPR13_SATA_TX_BOOST_MASK | |
150 | + | IMX6Q_GPR13_SATA_TX_LVL_MASK | |
151 | + | IMX6Q_GPR13_SATA_TX_EDGE_RATE | |
152 | + , IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | |
153 | + | IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | |
154 | + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | |
155 | + | IMX6Q_GPR13_SATA_SPD_MODE_3P0G | |
156 | + | IMX6Q_GPR13_SATA_MPLL_SS_EN | |
157 | + | IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | |
158 | + | IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | |
159 | + | IMX6Q_GPR13_SATA_TX_LVL_1_025_V); | |
160 | + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, | |
161 | + IMX6Q_GPR13_SATA_MPLL_CLK_EN); | |
162 | + usleep_range(100, 200); | |
163 | + | |
164 | + /* | |
165 | + * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, | |
166 | + * and IP vendor specific register HOST_TIMER1MS. | |
167 | + * Configure CAP_SSS (support stagered spin up). | |
168 | + * Implement the port0. | |
169 | + * Get the ahb clock rate, and configure the TIMER1MS register. | |
170 | + */ | |
171 | + reg_val = readl(mmio + HOST_CAP); | |
172 | + if (!(reg_val & HOST_CAP_SSS)) { | |
173 | + reg_val |= HOST_CAP_SSS; | |
174 | + writel(reg_val, mmio + HOST_CAP); | |
175 | + } | |
176 | + reg_val = readl(mmio + HOST_PORTS_IMPL); | |
177 | + if (!(reg_val & 0x1)) { | |
178 | + reg_val |= 0x1; | |
179 | + writel(reg_val, mmio + HOST_PORTS_IMPL); | |
180 | + } | |
181 | + | |
182 | + reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; | |
183 | + writel(reg_val, mmio + HOST_TIMER1MS); | |
184 | + | |
185 | + return 0; | |
186 | +} | |
187 | + | |
188 | +static void imx6q_sata_exit(struct device *dev) | |
189 | +{ | |
190 | + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); | |
191 | + | |
192 | + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, | |
193 | + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); | |
194 | + clk_disable_unprepare(imxpriv->sata_ref_clk); | |
195 | +} | |
196 | + | |
197 | +static struct ahci_platform_data imx6q_sata_pdata = { | |
198 | + .init = imx6q_sata_init, | |
199 | + .exit = imx6q_sata_exit, | |
200 | +}; | |
201 | + | |
202 | +static const struct of_device_id imx_ahci_of_match[] = { | |
203 | + { .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata}, | |
204 | + {}, | |
205 | +}; | |
206 | +MODULE_DEVICE_TABLE(of, imx_ahci_of_match); | |
207 | + | |
208 | +static int imx_ahci_probe(struct platform_device *pdev) | |
209 | +{ | |
210 | + struct device *dev = &pdev->dev; | |
211 | + struct resource *mem, *irq, res[2]; | |
212 | + const struct of_device_id *of_id; | |
213 | + const struct ahci_platform_data *pdata = NULL; | |
214 | + struct imx_ahci_priv *imxpriv; | |
215 | + struct device *ahci_dev; | |
216 | + struct platform_device *ahci_pdev; | |
217 | + int ret; | |
218 | + | |
219 | + imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); | |
220 | + if (!imxpriv) { | |
221 | + dev_err(dev, "can't alloc ahci_host_priv\n"); | |
222 | + return -ENOMEM; | |
223 | + } | |
224 | + | |
225 | + ahci_pdev = platform_device_alloc("ahci", -1); | |
226 | + if (!ahci_pdev) | |
227 | + return -ENODEV; | |
228 | + | |
229 | + ahci_dev = &ahci_pdev->dev; | |
230 | + ahci_dev->parent = dev; | |
231 | + | |
232 | + imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); | |
233 | + if (IS_ERR(imxpriv->ahb_clk)) { | |
234 | + dev_err(dev, "can't get ahb clock.\n"); | |
235 | + ret = PTR_ERR(imxpriv->ahb_clk); | |
236 | + goto err_out; | |
237 | + } | |
238 | + | |
239 | + imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); | |
240 | + if (IS_ERR(imxpriv->sata_ref_clk)) { | |
241 | + dev_err(dev, "can't get sata_ref clock.\n"); | |
242 | + ret = PTR_ERR(imxpriv->sata_ref_clk); | |
243 | + goto err_out; | |
244 | + } | |
245 | + | |
246 | + imxpriv->ahci_pdev = ahci_pdev; | |
247 | + platform_set_drvdata(pdev, imxpriv); | |
248 | + | |
249 | + of_id = of_match_device(imx_ahci_of_match, dev); | |
250 | + if (of_id) { | |
251 | + pdata = of_id->data; | |
252 | + } else { | |
253 | + ret = -EINVAL; | |
254 | + goto err_out; | |
255 | + } | |
256 | + | |
257 | + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
258 | + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
259 | + if (!mem || !irq) { | |
260 | + dev_err(dev, "no mmio/irq resource\n"); | |
261 | + ret = -ENOMEM; | |
262 | + goto err_out; | |
263 | + } | |
264 | + | |
265 | + res[0] = *mem; | |
266 | + res[1] = *irq; | |
267 | + | |
268 | + ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32); | |
269 | + ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask; | |
270 | + ahci_dev->of_node = dev->of_node; | |
271 | + | |
272 | + ret = platform_device_add_resources(ahci_pdev, res, 2); | |
273 | + if (ret) | |
274 | + goto err_out; | |
275 | + | |
276 | + ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); | |
277 | + if (ret) | |
278 | + goto err_out; | |
279 | + | |
280 | + ret = platform_device_add(ahci_pdev); | |
281 | + if (ret) { | |
282 | +err_out: | |
283 | + platform_device_put(ahci_pdev); | |
284 | + return ret; | |
285 | + } | |
286 | + | |
287 | + return 0; | |
288 | +} | |
289 | + | |
290 | +static int imx_ahci_remove(struct platform_device *pdev) | |
291 | +{ | |
292 | + struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev); | |
293 | + struct platform_device *ahci_pdev = imxpriv->ahci_pdev; | |
294 | + | |
295 | + platform_device_unregister(ahci_pdev); | |
296 | + return 0; | |
297 | +} | |
298 | + | |
299 | +static struct platform_driver imx_ahci_driver = { | |
300 | + .probe = imx_ahci_probe, | |
301 | + .remove = imx_ahci_remove, | |
302 | + .driver = { | |
303 | + .name = "ahci-imx", | |
304 | + .owner = THIS_MODULE, | |
305 | + .of_match_table = imx_ahci_of_match, | |
306 | + }, | |
307 | +}; | |
308 | +module_platform_driver(imx_ahci_driver); | |
309 | + | |
310 | +MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); | |
311 | +MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>"); | |
312 | +MODULE_LICENSE("GPL"); | |
313 | +MODULE_ALIAS("ahci:imx"); | |
314 | -- | |
315 | 1.7.10.4 | |
316 |