862be0835ecbc17158ce8c8ba8afef6f99106d60
[15.05/openwrt.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