Skip to content

Commit 9e54eae

Browse files
Richard Zhuhtejun
Richard Zhu
authored andcommitted
ahci_imx: add ahci sata support on imx platforms
imx6q contains one Synopsys AHCI SATA controller, But it can't share ahci_platform driver with other controllers because there are some misalignments of the generic AHCI controller - the bits definitions of the HBA registers, the Vendor Specific registers, the AHCI PHY clock and the AHCI signals adjustment window(GPR13 register). - CAP_SSS(bit20) of the HOST_CAP is writable, default value is '0', should be configured to be '1' - bit0 (only one AHCI SATA port on imx6q) of the HOST_PORTS_IMPL should be set to be '1'.(default 0) - One Vendor Specific register HOST_TIMER1MS(offset:0xe0) should be configured regarding to the frequency of AHB bus clock. - Configurations of the AHCI PHY clock, and the signal parameters of the GPR13 Setup its own ahci sata driver, contained the imx6q specific initialized codes, re-use the generic ahci_platform driver, and keep the generic ahci_platform driver clean as much as possible. tj: patch description reformatted Signed-off-by: Richard Zhu <[email protected]> Reviewed-by: Shawn Guo <[email protected]> Signed-off-by: Tejun Heo <[email protected]>
1 parent 6a6c21e commit 9e54eae

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

drivers/ata/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ config SATA_AHCI_PLATFORM
9797

9898
If unsure, say N.
9999

100+
config AHCI_IMX
101+
tristate "Freescale i.MX AHCI SATA support"
102+
depends on SATA_AHCI_PLATFORM
103+
help
104+
This option enables support for the Freescale i.MX SoC's
105+
onboard AHCI SATA.
106+
107+
If unsure, say N.
108+
100109
config SATA_FSL
101110
tristate "Freescale 3.0Gbps SATA support"
102111
depends on FSL_SOC

drivers/ata/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
1010
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
1111
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
1212
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
13+
obj-$(CONFIG_AHCI_IMX) += ahci_imx.o
1314

1415
# SFF w/ custom DMA
1516
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o

drivers/ata/ahci_imx.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* Freescale IMX AHCI SATA platform driver
3+
* Copyright 2013 Freescale Semiconductor, Inc.
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>
28+
#include "ahci.h"
29+
30+
enum {
31+
HOST_TIMER1MS = 0xe0, /* Timer 1-ms */
32+
};
33+
34+
struct imx_ahci_priv {
35+
struct platform_device *ahci_pdev;
36+
struct clk *sata_ref_clk;
37+
struct clk *ahb_clk;
38+
struct regmap *gpr;
39+
};
40+
41+
static int imx6q_sata_init(struct device *dev, void __iomem *mmio)
42+
{
43+
int ret = 0;
44+
unsigned int reg_val;
45+
struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
46+
47+
imxpriv->gpr =
48+
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
49+
if (IS_ERR(imxpriv->gpr)) {
50+
dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n");
51+
return PTR_ERR(imxpriv->gpr);
52+
}
53+
54+
ret = clk_prepare_enable(imxpriv->sata_ref_clk);
55+
if (ret < 0) {
56+
dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret);
57+
return ret;
58+
}
59+
60+
/*
61+
* set PHY Paremeters, two steps to configure the GPR13,
62+
* one write for rest of parameters, mask of first write
63+
* is 0x07fffffd, and the other one write for setting
64+
* the mpll_clk_en.
65+
*/
66+
regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK
67+
| IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK
68+
| IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK
69+
| IMX6Q_GPR13_SATA_SPD_MODE_MASK
70+
| IMX6Q_GPR13_SATA_MPLL_SS_EN
71+
| IMX6Q_GPR13_SATA_TX_ATTEN_MASK
72+
| IMX6Q_GPR13_SATA_TX_BOOST_MASK
73+
| IMX6Q_GPR13_SATA_TX_LVL_MASK
74+
| IMX6Q_GPR13_SATA_TX_EDGE_RATE
75+
, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB
76+
| IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M
77+
| IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F
78+
| IMX6Q_GPR13_SATA_SPD_MODE_3P0G
79+
| IMX6Q_GPR13_SATA_MPLL_SS_EN
80+
| IMX6Q_GPR13_SATA_TX_ATTEN_9_16
81+
| IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB
82+
| IMX6Q_GPR13_SATA_TX_LVL_1_025_V);
83+
regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN,
84+
IMX6Q_GPR13_SATA_MPLL_CLK_EN);
85+
usleep_range(100, 200);
86+
87+
/*
88+
* Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
89+
* and IP vendor specific register HOST_TIMER1MS.
90+
* Configure CAP_SSS (support stagered spin up).
91+
* Implement the port0.
92+
* Get the ahb clock rate, and configure the TIMER1MS register.
93+
*/
94+
reg_val = readl(mmio + HOST_CAP);
95+
if (!(reg_val & HOST_CAP_SSS)) {
96+
reg_val |= HOST_CAP_SSS;
97+
writel(reg_val, mmio + HOST_CAP);
98+
}
99+
reg_val = readl(mmio + HOST_PORTS_IMPL);
100+
if (!(reg_val & 0x1)) {
101+
reg_val |= 0x1;
102+
writel(reg_val, mmio + HOST_PORTS_IMPL);
103+
}
104+
105+
reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
106+
writel(reg_val, mmio + HOST_TIMER1MS);
107+
108+
return 0;
109+
}
110+
111+
static void imx6q_sata_exit(struct device *dev)
112+
{
113+
struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent);
114+
115+
regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN,
116+
!IMX6Q_GPR13_SATA_MPLL_CLK_EN);
117+
clk_disable_unprepare(imxpriv->sata_ref_clk);
118+
}
119+
120+
static struct ahci_platform_data imx6q_sata_pdata = {
121+
.init = imx6q_sata_init,
122+
.exit = imx6q_sata_exit,
123+
};
124+
125+
static const struct of_device_id imx_ahci_of_match[] = {
126+
{ .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata},
127+
{},
128+
};
129+
MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
130+
131+
static int imx_ahci_probe(struct platform_device *pdev)
132+
{
133+
struct device *dev = &pdev->dev;
134+
struct resource *mem, *irq, res[2];
135+
const struct of_device_id *of_id;
136+
const struct ahci_platform_data *pdata = NULL;
137+
struct imx_ahci_priv *imxpriv;
138+
struct device *ahci_dev;
139+
struct platform_device *ahci_pdev;
140+
int ret;
141+
142+
imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL);
143+
if (!imxpriv) {
144+
dev_err(dev, "can't alloc ahci_host_priv\n");
145+
return -ENOMEM;
146+
}
147+
148+
ahci_pdev = platform_device_alloc("ahci", -1);
149+
if (!ahci_pdev)
150+
return -ENODEV;
151+
152+
ahci_dev = &ahci_pdev->dev;
153+
ahci_dev->parent = dev;
154+
155+
imxpriv->ahb_clk = devm_clk_get(dev, "ahb");
156+
if (IS_ERR(imxpriv->ahb_clk)) {
157+
dev_err(dev, "can't get ahb clock.\n");
158+
ret = PTR_ERR(imxpriv->ahb_clk);
159+
goto err_out;
160+
}
161+
162+
imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref");
163+
if (IS_ERR(imxpriv->sata_ref_clk)) {
164+
dev_err(dev, "can't get sata_ref clock.\n");
165+
ret = PTR_ERR(imxpriv->sata_ref_clk);
166+
goto err_out;
167+
}
168+
169+
imxpriv->ahci_pdev = ahci_pdev;
170+
platform_set_drvdata(pdev, imxpriv);
171+
172+
of_id = of_match_device(imx_ahci_of_match, dev);
173+
if (of_id) {
174+
pdata = of_id->data;
175+
} else {
176+
ret = -EINVAL;
177+
goto err_out;
178+
}
179+
180+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
181+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
182+
if (!mem || !irq) {
183+
dev_err(dev, "no mmio/irq resource\n");
184+
ret = -ENOMEM;
185+
goto err_out;
186+
}
187+
188+
res[0] = *mem;
189+
res[1] = *irq;
190+
191+
ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32);
192+
ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask;
193+
ahci_dev->of_node = dev->of_node;
194+
195+
ret = platform_device_add_resources(ahci_pdev, res, 2);
196+
if (ret)
197+
goto err_out;
198+
199+
ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata));
200+
if (ret)
201+
goto err_out;
202+
203+
ret = platform_device_add(ahci_pdev);
204+
if (ret) {
205+
err_out:
206+
platform_device_put(ahci_pdev);
207+
return ret;
208+
}
209+
210+
return 0;
211+
}
212+
213+
static int imx_ahci_remove(struct platform_device *pdev)
214+
{
215+
struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev);
216+
struct platform_device *ahci_pdev = imxpriv->ahci_pdev;
217+
218+
platform_device_unregister(ahci_pdev);
219+
return 0;
220+
}
221+
222+
static struct platform_driver imx_ahci_driver = {
223+
.probe = imx_ahci_probe,
224+
.remove = imx_ahci_remove,
225+
.driver = {
226+
.name = "ahci-imx",
227+
.owner = THIS_MODULE,
228+
.of_match_table = imx_ahci_of_match,
229+
},
230+
};
231+
module_platform_driver(imx_ahci_driver);
232+
233+
MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver");
234+
MODULE_AUTHOR("Richard Zhu <[email protected]>");
235+
MODULE_LICENSE("GPL");
236+
MODULE_ALIAS("ahci:imx");

0 commit comments

Comments
 (0)