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