diff options
author | Kumar Gala <galak@codeaurora.org> | 2014-07-16 12:10:08 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2014-07-22 03:16:12 -0400 |
commit | 4f6160d4089ec0e39e33a197138413bd0701ce21 (patch) | |
tree | baf00dae060d84a77d2248e80af21a572dc00c55 /drivers | |
parent | 175f02ebdfdf8431dbf607c04fe5caf667ba8e6c (diff) |
phy: qcom: Add driver for QCOM IPQ806x SATA PHY
Add a PHY driver for uses with AHCI based SATA controller driver on the
IPQ806x family of SoCs.
Signed-off-by: Kumar Gala <galak@codeaurora.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/phy/Kconfig | 7 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ipq806x-sata.c | 211 |
3 files changed, 219 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 1704fd4be99e..3e251aa64ffd 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -191,4 +191,11 @@ config PHY_QCOM_APQ8064_SATA | |||
191 | depends on OF | 191 | depends on OF |
192 | select GENERIC_PHY | 192 | select GENERIC_PHY |
193 | 193 | ||
194 | config PHY_QCOM_IPQ806X_SATA | ||
195 | tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" | ||
196 | depends on ARCH_QCOM | ||
197 | depends on HAS_IOMEM | ||
198 | depends on OF | ||
199 | select GENERIC_PHY | ||
200 | |||
194 | endmenu | 201 | endmenu |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 3c2ad5915be2..54ab9785932c 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -23,3 +23,4 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o | |||
23 | obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o | 23 | obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o |
24 | obj-$(CONFIG_PHY_XGENE) += phy-xgene.o | 24 | obj-$(CONFIG_PHY_XGENE) += phy-xgene.o |
25 | obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o | 25 | obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o |
26 | obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o | ||
diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c new file mode 100644 index 000000000000..909b5a87fc6a --- /dev/null +++ b/drivers/phy/phy-qcom-ipq806x-sata.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/io.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_address.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/phy/phy.h> | ||
25 | |||
26 | struct qcom_ipq806x_sata_phy { | ||
27 | void __iomem *mmio; | ||
28 | struct clk *cfg_clk; | ||
29 | struct device *dev; | ||
30 | }; | ||
31 | |||
32 | #define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) | ||
33 | |||
34 | #define SATA_PHY_P0_PARAM0 0x200 | ||
35 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) | ||
36 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) | ||
37 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) | ||
38 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) | ||
39 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) | ||
40 | #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) | ||
41 | |||
42 | #define SATA_PHY_P0_PARAM1 0x204 | ||
43 | #define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) | ||
44 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) | ||
45 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) | ||
46 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) | ||
47 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) | ||
48 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) | ||
49 | #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) | ||
50 | |||
51 | #define SATA_PHY_P0_PARAM2 0x208 | ||
52 | #define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) | ||
53 | #define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) | ||
54 | |||
55 | #define SATA_PHY_P0_PARAM3 0x20C | ||
56 | #define SATA_PHY_SSC_EN 0x8 | ||
57 | #define SATA_PHY_P0_PARAM4 0x210 | ||
58 | #define SATA_PHY_REF_SSP_EN 0x2 | ||
59 | #define SATA_PHY_RESET 0x1 | ||
60 | |||
61 | static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) | ||
62 | { | ||
63 | struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); | ||
64 | u32 reg; | ||
65 | |||
66 | /* Setting SSC_EN to 1 */ | ||
67 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); | ||
68 | reg = reg | SATA_PHY_SSC_EN; | ||
69 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); | ||
70 | |||
71 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & | ||
72 | ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | | ||
73 | SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | | ||
74 | SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); | ||
75 | reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); | ||
76 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); | ||
77 | |||
78 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & | ||
79 | ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | | ||
80 | SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | | ||
81 | SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); | ||
82 | reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | | ||
83 | SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | | ||
84 | SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); | ||
85 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); | ||
86 | |||
87 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & | ||
88 | ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; | ||
89 | reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); | ||
90 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); | ||
91 | |||
92 | /* Setting PHY_RESET to 1 */ | ||
93 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); | ||
94 | reg = reg | SATA_PHY_RESET; | ||
95 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); | ||
96 | |||
97 | /* Setting REF_SSP_EN to 1 */ | ||
98 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); | ||
99 | reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; | ||
100 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); | ||
101 | |||
102 | /* make sure all changes complete before we let the PHY out of reset */ | ||
103 | mb(); | ||
104 | |||
105 | /* sleep for max. 50us more to combine processor wakeups */ | ||
106 | usleep_range(20, 20 + 50); | ||
107 | |||
108 | /* Clearing PHY_RESET to 0 */ | ||
109 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); | ||
110 | reg = reg & ~SATA_PHY_RESET; | ||
111 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) | ||
117 | { | ||
118 | struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); | ||
119 | u32 reg; | ||
120 | |||
121 | /* Setting PHY_RESET to 1 */ | ||
122 | reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); | ||
123 | reg = reg | SATA_PHY_RESET; | ||
124 | writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static struct phy_ops qcom_ipq806x_sata_phy_ops = { | ||
130 | .init = qcom_ipq806x_sata_phy_init, | ||
131 | .exit = qcom_ipq806x_sata_phy_exit, | ||
132 | .owner = THIS_MODULE, | ||
133 | }; | ||
134 | |||
135 | static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) | ||
136 | { | ||
137 | struct qcom_ipq806x_sata_phy *phy; | ||
138 | struct device *dev = &pdev->dev; | ||
139 | struct resource *res; | ||
140 | struct phy_provider *phy_provider; | ||
141 | struct phy *generic_phy; | ||
142 | int ret; | ||
143 | |||
144 | phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); | ||
145 | if (!phy) | ||
146 | return -ENOMEM; | ||
147 | |||
148 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
149 | phy->mmio = devm_ioremap_resource(dev, res); | ||
150 | if (IS_ERR(phy->mmio)) | ||
151 | return PTR_ERR(phy->mmio); | ||
152 | |||
153 | generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops, | ||
154 | NULL); | ||
155 | if (IS_ERR(generic_phy)) { | ||
156 | dev_err(dev, "%s: failed to create phy\n", __func__); | ||
157 | return PTR_ERR(generic_phy); | ||
158 | } | ||
159 | |||
160 | phy->dev = dev; | ||
161 | phy_set_drvdata(generic_phy, phy); | ||
162 | platform_set_drvdata(pdev, phy); | ||
163 | |||
164 | phy->cfg_clk = devm_clk_get(dev, "cfg"); | ||
165 | if (IS_ERR(phy->cfg_clk)) { | ||
166 | dev_err(dev, "Failed to get sata cfg clock\n"); | ||
167 | return PTR_ERR(phy->cfg_clk); | ||
168 | } | ||
169 | |||
170 | ret = clk_prepare_enable(phy->cfg_clk); | ||
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
174 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
175 | if (IS_ERR(phy_provider)) { | ||
176 | clk_disable_unprepare(phy->cfg_clk); | ||
177 | dev_err(dev, "%s: failed to register phy\n", __func__); | ||
178 | return PTR_ERR(phy_provider); | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) | ||
185 | { | ||
186 | struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); | ||
187 | |||
188 | clk_disable_unprepare(phy->cfg_clk); | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { | ||
194 | { .compatible = "qcom,ipq806x-sata-phy" }, | ||
195 | { }, | ||
196 | }; | ||
197 | MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); | ||
198 | |||
199 | static struct platform_driver qcom_ipq806x_sata_phy_driver = { | ||
200 | .probe = qcom_ipq806x_sata_phy_probe, | ||
201 | .remove = qcom_ipq806x_sata_phy_remove, | ||
202 | .driver = { | ||
203 | .name = "qcom-ipq806x-sata-phy", | ||
204 | .owner = THIS_MODULE, | ||
205 | .of_match_table = qcom_ipq806x_sata_phy_of_match, | ||
206 | } | ||
207 | }; | ||
208 | module_platform_driver(qcom_ipq806x_sata_phy_driver); | ||
209 | |||
210 | MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); | ||
211 | MODULE_LICENSE("GPL v2"); | ||