ipq806x: Add support for IPQ806x chip family
[openwrt/staging/wigyori.git] / target / linux / ipq806x / patches / 0144-phy-qcom-Add-driver-for-QCOM-IPQ806x-SATA-PHY.patch
1 From 7554dfbbec255e4cd5dd4445841c28c48fd4a855 Mon Sep 17 00:00:00 2001
2 From: Kumar Gala <galak@codeaurora.org>
3 Date: Tue, 3 Jun 2014 11:59:09 -0500
4 Subject: [PATCH 144/182] phy: qcom: Add driver for QCOM IPQ806x SATA PHY
5
6 Add a PHY driver for uses with AHCI based SATA controller driver on the
7 IPQ806x family of SoCs.
8
9 Signed-off-by: Kumar Gala <galak@codeaurora.org>
10 ---
11 drivers/phy/Kconfig | 7 ++
12 drivers/phy/Makefile | 1 +
13 drivers/phy/phy-qcom-ipq806x-sata.c | 211 +++++++++++++++++++++++++++++++++++
14 3 files changed, 219 insertions(+)
15 create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c
16
17 diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
18 index c7a551c..3642a65 100644
19 --- a/drivers/phy/Kconfig
20 +++ b/drivers/phy/Kconfig
21 @@ -65,4 +65,11 @@ config BCM_KONA_USB2_PHY
22 help
23 Enable this to support the Broadcom Kona USB 2.0 PHY.
24
25 +config PHY_QCOM_IPQ806X_SATA
26 + tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
27 + depends on ARCH_QCOM
28 + depends on HAS_IOMEM
29 + depends on OF
30 + select GENERIC_PHY
31 +
32 endmenu
33 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
34 index b57c253..74f78cc 100644
35 --- a/drivers/phy/Makefile
36 +++ b/drivers/phy/Makefile
37 @@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
38 obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
39 obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
40 obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
41 +obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
42 diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c
43 new file mode 100644
44 index 0000000..e931aee
45 --- /dev/null
46 +++ b/drivers/phy/phy-qcom-ipq806x-sata.c
47 @@ -0,0 +1,211 @@
48 +/*
49 + * Copyright (c) 2014, The Linux Foundation. All rights reserved.
50 + *
51 + * This program is free software; you can redistribute it and/or modify
52 + * it under the terms of the GNU General Public License version 2 and
53 + * only version 2 as published by the Free Software Foundation.
54 + *
55 + * This program is distributed in the hope that it will be useful,
56 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
57 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58 + * GNU General Public License for more details.
59 + */
60 +
61 +#include <linux/io.h>
62 +#include <linux/kernel.h>
63 +#include <linux/module.h>
64 +#include <linux/of.h>
65 +#include <linux/of_address.h>
66 +#include <linux/time.h>
67 +#include <linux/delay.h>
68 +#include <linux/clk.h>
69 +#include <linux/slab.h>
70 +#include <linux/platform_device.h>
71 +#include <linux/phy/phy.h>
72 +
73 +struct qcom_ipq806x_sata_phy {
74 + void __iomem *mmio;
75 + struct clk *cfg_clk;
76 +};
77 +
78 +#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
79 +
80 +#define SATA_PHY_P0_PARAM0 0x200
81 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
82 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
83 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
84 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
85 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
86 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
87 +
88 +#define SATA_PHY_P0_PARAM1 0x204
89 +#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
90 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
91 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
92 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
93 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
94 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
95 +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
96 +
97 +#define SATA_PHY_P0_PARAM2 0x208
98 +#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
99 +#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
100 +
101 +#define SATA_PHY_P0_PARAM3 0x20C
102 +#define SATA_PHY_SSC_EN 0x8
103 +#define SATA_PHY_P0_PARAM4 0x210
104 +#define SATA_PHY_REF_SSP_EN 0x2
105 +#define SATA_PHY_RESET 0x1
106 +
107 +static inline void qcom_ipq806x_sata_delay_us(unsigned int delay)
108 +{
109 + /* sleep for max. 50us more to combine processor wakeups */
110 + usleep_range(delay, delay + 50);
111 +}
112 +
113 +static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
114 +{
115 + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
116 + u32 reg;
117 +
118 + /* Setting SSC_EN to 1 */
119 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
120 + reg = reg | SATA_PHY_SSC_EN;
121 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
122 +
123 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
124 + ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
125 + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
126 + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
127 + reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
128 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
129 +
130 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
131 + ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
132 + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
133 + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
134 + reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
135 + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
136 + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
137 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
138 +
139 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
140 + ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
141 + reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
142 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
143 +
144 + /* Setting PHY_RESET to 1 */
145 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
146 + reg = reg | SATA_PHY_RESET;
147 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
148 +
149 + /* Setting REF_SSP_EN to 1 */
150 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
151 + reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
152 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
153 + mb();
154 + qcom_ipq806x_sata_delay_us(20);
155 +
156 + /* Clearing PHY_RESET to 0 */
157 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
158 + reg = reg & ~SATA_PHY_RESET;
159 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
160 +
161 + return 0;
162 +}
163 +
164 +static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
165 +{
166 + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
167 + u32 reg;
168 +
169 + /* Setting PHY_RESET to 1 */
170 + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
171 + reg = reg | SATA_PHY_RESET;
172 + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
173 +
174 + return 0;
175 +}
176 +
177 +static struct phy_ops qcom_ipq806x_sata_phy_ops = {
178 + .init = qcom_ipq806x_sata_phy_init,
179 + .exit = qcom_ipq806x_sata_phy_exit,
180 + .owner = THIS_MODULE,
181 +};
182 +
183 +static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
184 +{
185 + struct qcom_ipq806x_sata_phy *phy;
186 + struct device *dev = &pdev->dev;
187 + struct resource *res;
188 + struct phy_provider *phy_provider;
189 + struct phy *generic_phy;
190 + int ret;
191 +
192 + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
193 + if (!phy) {
194 + dev_err(dev, "%s: failed to allocate phy\n", __func__);
195 + return -ENOMEM;
196 + }
197 +
198 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
199 + phy->mmio = devm_ioremap_resource(dev, res);
200 + if (IS_ERR(phy->mmio))
201 + return PTR_ERR(phy->mmio);
202 +
203 + generic_phy = devm_phy_create(dev, &qcom_ipq806x_sata_phy_ops, NULL);
204 + if (IS_ERR(generic_phy)) {
205 + dev_err(dev, "%s: failed to create phy\n", __func__);
206 + return PTR_ERR(generic_phy);
207 + }
208 +
209 + phy_set_drvdata(generic_phy, phy);
210 +
211 + phy->cfg_clk = devm_clk_get(dev, "cfg");
212 + if (IS_ERR(phy->cfg_clk)) {
213 + dev_err(dev, "Failed to get sata cfg clock\n");
214 + return PTR_ERR(phy->cfg_clk);
215 + }
216 +
217 + ret = clk_prepare_enable(phy->cfg_clk);
218 + if (ret)
219 + return ret;
220 +
221 + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
222 + if (IS_ERR(phy_provider)) {
223 + clk_disable_unprepare(phy->cfg_clk);
224 + dev_err(dev, "%s: failed to register phy\n", __func__);
225 + return PTR_ERR(phy_provider);
226 + }
227 +
228 + return 0;
229 +}
230 +
231 +static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
232 +{
233 + struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
234 +
235 + clk_disable_unprepare(phy->cfg_clk);
236 +
237 + return 0;
238 +}
239 +
240 +static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
241 + { .compatible = "qcom,ipq806x-sata-phy" },
242 + { },
243 +};
244 +MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
245 +
246 +static struct platform_driver qcom_ipq806x_sata_phy_driver = {
247 + .probe = qcom_ipq806x_sata_phy_probe,
248 + .remove = qcom_ipq806x_sata_phy_remove,
249 + .driver = {
250 + .name = "qcom-ipq806x-sata-phy",
251 + .owner = THIS_MODULE,
252 + .of_match_table = qcom_ipq806x_sata_phy_of_match,
253 + }
254 +};
255 +module_platform_driver(qcom_ipq806x_sata_phy_driver);
256 +
257 +MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
258 +MODULE_LICENSE("GPL v2");
259 --
260 1.7.10.4
261