]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/ata/ahci_imx.c
ahci: imx: Pull out the clock enable/disable calls
[mirror_ubuntu-bionic-kernel.git] / drivers / ata / ahci_imx.c
CommitLineData
9e54eae2 1/*
8b789d89 2 * copyright (c) 2013 Freescale Semiconductor, Inc.
9e54eae2 3 * Freescale IMX AHCI SATA platform driver
9e54eae2
RZ
4 *
5 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/regmap.h>
24#include <linux/ahci_platform.h>
25#include <linux/of_device.h>
26#include <linux/mfd/syscon.h>
27#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
8b789d89 28#include <linux/libata.h>
9e54eae2
RZ
29#include "ahci.h"
30
31enum {
8b789d89
RZ
32 PORT_PHY_CTL = 0x178, /* Port0 PHY Control */
33 PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */
34 HOST_TIMER1MS = 0xe0, /* Timer 1-ms */
9e54eae2
RZ
35};
36
37struct imx_ahci_priv {
38 struct platform_device *ahci_pdev;
39 struct clk *sata_ref_clk;
40 struct clk *ahb_clk;
41 struct regmap *gpr;
8b789d89
RZ
42 bool no_device;
43 bool first_time;
44};
45
46static int ahci_imx_hotplug;
47module_param_named(hotplug, ahci_imx_hotplug, int, 0644);
48MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)");
49
8403e2ec
MV
50static int imx_sata_clock_enable(struct device *dev)
51{
52 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
53 int ret;
54
55 ret = clk_prepare_enable(imxpriv->sata_ref_clk);
56 if (ret < 0) {
57 dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret);
58 return ret;
59 }
60
61 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
62 IMX6Q_GPR13_SATA_MPLL_CLK_EN,
63 IMX6Q_GPR13_SATA_MPLL_CLK_EN);
64
65 usleep_range(1000, 2000);
66
67 return 0;
68}
69
70static void imx_sata_clock_disable(struct device *dev)
71{
72 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
73
74 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
75 IMX6Q_GPR13_SATA_MPLL_CLK_EN,
76 !IMX6Q_GPR13_SATA_MPLL_CLK_EN);
77 clk_disable_unprepare(imxpriv->sata_ref_clk);
78}
79
8b789d89
RZ
80static void ahci_imx_error_handler(struct ata_port *ap)
81{
82 u32 reg_val;
83 struct ata_device *dev;
84 struct ata_host *host = dev_get_drvdata(ap->dev);
85 struct ahci_host_priv *hpriv = host->private_data;
86 void __iomem *mmio = hpriv->mmio;
87 struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent);
88
89 ahci_error_handler(ap);
90
91 if (!(imxpriv->first_time) || ahci_imx_hotplug)
92 return;
93
94 imxpriv->first_time = false;
95
96 ata_for_each_dev(dev, &ap->link, ENABLED)
97 return;
98 /*
99 * Disable link to save power. An imx ahci port can't be recovered
100 * without full reset once the pddq mode is enabled making it
101 * impossible to use as part of libata LPM.
102 */
103 reg_val = readl(mmio + PORT_PHY_CTL);
104 writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL);
8403e2ec 105 imx_sata_clock_disable(ap->dev);
8b789d89
RZ
106 imxpriv->no_device = true;
107}
108
109static struct ata_port_operations ahci_imx_ops = {
110 .inherits = &ahci_platform_ops,
111 .error_handler = ahci_imx_error_handler,
112};
113
114static const struct ata_port_info ahci_imx_port_info = {
115 .flags = AHCI_FLAG_COMMON,
116 .pio_mask = ATA_PIO4,
117 .udma_mask = ATA_UDMA6,
118 .port_ops = &ahci_imx_ops,
9e54eae2
RZ
119};
120
121static int imx6q_sata_init(struct device *dev, void __iomem *mmio)
122{
123 int ret = 0;
124 unsigned int reg_val;
125 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
126
8403e2ec
MV
127 ret = imx_sata_clock_enable(dev);
128 if (ret < 0)
9e54eae2 129 return ret;
9e54eae2
RZ
130
131 /*
132 * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
133 * and IP vendor specific register HOST_TIMER1MS.
134 * Configure CAP_SSS (support stagered spin up).
135 * Implement the port0.
136 * Get the ahb clock rate, and configure the TIMER1MS register.
137 */
138 reg_val = readl(mmio + HOST_CAP);
139 if (!(reg_val & HOST_CAP_SSS)) {
140 reg_val |= HOST_CAP_SSS;
141 writel(reg_val, mmio + HOST_CAP);
142 }
143 reg_val = readl(mmio + HOST_PORTS_IMPL);
144 if (!(reg_val & 0x1)) {
145 reg_val |= 0x1;
146 writel(reg_val, mmio + HOST_PORTS_IMPL);
147 }
148
149 reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
150 writel(reg_val, mmio + HOST_TIMER1MS);
151
152 return 0;
153}
154
155static void imx6q_sata_exit(struct device *dev)
156{
8403e2ec 157 imx_sata_clock_disable(dev);
9e54eae2
RZ
158}
159
8b789d89
RZ
160static int imx_ahci_suspend(struct device *dev)
161{
162 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
163
164 /*
165 * If no_device is set, The CLKs had been gated off in the
166 * initialization so don't do it again here.
167 */
8403e2ec
MV
168 if (!imxpriv->no_device)
169 imx_sata_clock_disable(dev);
8b789d89
RZ
170
171 return 0;
172}
173
174static int imx_ahci_resume(struct device *dev)
175{
176 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
8403e2ec 177 int ret = 0;
8b789d89 178
8403e2ec
MV
179 if (!imxpriv->no_device)
180 ret = imx_sata_clock_enable(dev);
8b789d89 181
8403e2ec 182 return ret;
8b789d89
RZ
183}
184
9e54eae2
RZ
185static struct ahci_platform_data imx6q_sata_pdata = {
186 .init = imx6q_sata_init,
187 .exit = imx6q_sata_exit,
8b789d89
RZ
188 .ata_port_info = &ahci_imx_port_info,
189 .suspend = imx_ahci_suspend,
190 .resume = imx_ahci_resume,
9e54eae2
RZ
191};
192
193static const struct of_device_id imx_ahci_of_match[] = {
194 { .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata},
195 {},
196};
197MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
198
199static int imx_ahci_probe(struct platform_device *pdev)
200{
201 struct device *dev = &pdev->dev;
202 struct resource *mem, *irq, res[2];
203 const struct of_device_id *of_id;
204 const struct ahci_platform_data *pdata = NULL;
205 struct imx_ahci_priv *imxpriv;
206 struct device *ahci_dev;
207 struct platform_device *ahci_pdev;
208 int ret;
209
210 imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL);
211 if (!imxpriv) {
212 dev_err(dev, "can't alloc ahci_host_priv\n");
213 return -ENOMEM;
214 }
215
216 ahci_pdev = platform_device_alloc("ahci", -1);
217 if (!ahci_pdev)
218 return -ENODEV;
219
220 ahci_dev = &ahci_pdev->dev;
221 ahci_dev->parent = dev;
222
8b789d89
RZ
223 imxpriv->no_device = false;
224 imxpriv->first_time = true;
9e54eae2
RZ
225 imxpriv->ahb_clk = devm_clk_get(dev, "ahb");
226 if (IS_ERR(imxpriv->ahb_clk)) {
227 dev_err(dev, "can't get ahb clock.\n");
228 ret = PTR_ERR(imxpriv->ahb_clk);
229 goto err_out;
230 }
231
232 imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref");
233 if (IS_ERR(imxpriv->sata_ref_clk)) {
234 dev_err(dev, "can't get sata_ref clock.\n");
235 ret = PTR_ERR(imxpriv->sata_ref_clk);
236 goto err_out;
237 }
238
239 imxpriv->ahci_pdev = ahci_pdev;
240 platform_set_drvdata(pdev, imxpriv);
241
242 of_id = of_match_device(imx_ahci_of_match, dev);
243 if (of_id) {
244 pdata = of_id->data;
245 } else {
246 ret = -EINVAL;
247 goto err_out;
248 }
249
250 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
252 if (!mem || !irq) {
253 dev_err(dev, "no mmio/irq resource\n");
254 ret = -ENOMEM;
255 goto err_out;
256 }
257
258 res[0] = *mem;
259 res[1] = *irq;
260
261 ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32);
262 ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask;
263 ahci_dev->of_node = dev->of_node;
264
8403e2ec
MV
265 imxpriv->gpr =
266 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
267
268 if (IS_ERR(imxpriv->gpr)) {
269 dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n");
270 ret = PTR_ERR(imxpriv->gpr);
271 goto err_out;
272 }
273
274 /*
275 * Set PHY Paremeters, two steps to configure the GPR13,
276 * one write for rest of parameters, mask of first write
277 * is 0x07ffffff, and the other one write for setting
278 * the mpll_clk_en happens in imx_sata_clock_enable().
279 */
280 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
281 IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
282 IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK |
283 IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK |
284 IMX6Q_GPR13_SATA_SPD_MODE_MASK |
285 IMX6Q_GPR13_SATA_MPLL_SS_EN |
286 IMX6Q_GPR13_SATA_TX_ATTEN_MASK |
287 IMX6Q_GPR13_SATA_TX_BOOST_MASK |
288 IMX6Q_GPR13_SATA_TX_LVL_MASK |
289 IMX6Q_GPR13_SATA_MPLL_CLK_EN |
290 IMX6Q_GPR13_SATA_TX_EDGE_RATE,
291 IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB |
292 IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
293 IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
294 IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
295 IMX6Q_GPR13_SATA_MPLL_SS_EN |
296 IMX6Q_GPR13_SATA_TX_ATTEN_9_16 |
297 IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB |
298 IMX6Q_GPR13_SATA_TX_LVL_1_025_V);
299
9e54eae2
RZ
300 ret = platform_device_add_resources(ahci_pdev, res, 2);
301 if (ret)
302 goto err_out;
303
304 ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata));
305 if (ret)
306 goto err_out;
307
308 ret = platform_device_add(ahci_pdev);
309 if (ret) {
310err_out:
311 platform_device_put(ahci_pdev);
312 return ret;
313 }
314
315 return 0;
316}
317
318static int imx_ahci_remove(struct platform_device *pdev)
319{
320 struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev);
321 struct platform_device *ahci_pdev = imxpriv->ahci_pdev;
322
323 platform_device_unregister(ahci_pdev);
324 return 0;
325}
326
327static struct platform_driver imx_ahci_driver = {
328 .probe = imx_ahci_probe,
329 .remove = imx_ahci_remove,
330 .driver = {
331 .name = "ahci-imx",
332 .owner = THIS_MODULE,
333 .of_match_table = imx_ahci_of_match,
334 },
335};
336module_platform_driver(imx_ahci_driver);
337
338MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver");
339MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>");
340MODULE_LICENSE("GPL");
341MODULE_ALIAS("ahci:imx");