summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaehoon Chung <jh80.chung@samsung.com>2017-02-13 03:26:11 -0500
committerBjorn Helgaas <bhelgaas@google.com>2017-02-21 08:48:42 -0500
commitcf0adb8e281b69801fb8faef18c14443d9d41d3c (patch)
treee0c0bd08f443455122a3931693d9bc9cbac51a32
parentad8ec41afa98615a4154eee0121bcf8276695327 (diff)
phy: phy-exynos-pcie: Add support for Exynos PCIe PHY
Add support for Generic PHY framework about Exynos SoCs. Current Exynos PCIe driver doesn't use the PHY framework, which makes it difficult to upstream the other Exynos variants because of different PHY registers. Move the codes relevant to PHY from Exnyos PCIe driver to PHY Exynos PCIe driver. [bhelgaas: depend on "OF && (ARCH_EXYNOS || COMPILE_TEST)", update copyright year, both per Vivek] Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com> Acked-by: Krzysztof Kozlowski <krzk@kernel.org> Reviewed-by: Jingoo Han <jingoohan1@gmail.com> Reviewed-by: Pankaj Dubey <pankaj.dubey@samsung.com> Reviewed-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/phy/Kconfig8
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-exynos-pcie.c285
3 files changed, 294 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e8eb7f225a88..bbad035c60cc 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -331,6 +331,14 @@ config PHY_EXYNOS5_USBDRD
331 This driver provides PHY interface for USB 3.0 DRD controller 331 This driver provides PHY interface for USB 3.0 DRD controller
332 present on Exynos5 SoC series. 332 present on Exynos5 SoC series.
333 333
334config PHY_EXYNOS_PCIE
335 bool "Exynos PCIe PHY driver"
336 depends on OF && (ARCH_EXYNOS || COMPILE_TEST)
337 select GENERIC_PHY
338 help
339 Enable PCIe PHY support for Exynos SoC series.
340 This driver provides PHY interface for Exynos PCIe controller.
341
334config PHY_PISTACHIO_USB 342config PHY_PISTACHIO_USB
335 tristate "IMG Pistachio USB2.0 PHY driver" 343 tristate "IMG Pistachio USB2.0 PHY driver"
336 depends on MACH_PISTACHIO 344 depends on MACH_PISTACHIO
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 65eb2f436a41..081aeb4efd13 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -37,6 +37,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
37phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o 37phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
38phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o 38phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
39obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o 39obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
40obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o
40obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o 41obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
41obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o 42obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
42obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o 43obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
diff --git a/drivers/phy/phy-exynos-pcie.c b/drivers/phy/phy-exynos-pcie.c
new file mode 100644
index 000000000000..4f60b83641d5
--- /dev/null
+++ b/drivers/phy/phy-exynos-pcie.c
@@ -0,0 +1,285 @@
1/*
2 * Samsung EXYNOS SoC series PCIe PHY driver
3 *
4 * Phy provider for PCIe controller on Exynos SoC series
5 *
6 * Copyright (C) 2017 Samsung Electronics Co., Ltd.
7 * Jaehoon Chung <jh80.chung@samsung.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/delay.h>
15#include <linux/io.h>
16#include <linux/iopoll.h>
17#include <linux/mfd/syscon.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/of_platform.h>
22#include <linux/platform_device.h>
23#include <linux/phy/phy.h>
24#include <linux/regmap.h>
25
26/* PCIe Purple registers */
27#define PCIE_PHY_GLOBAL_RESET 0x000
28#define PCIE_PHY_COMMON_RESET 0x004
29#define PCIE_PHY_CMN_REG 0x008
30#define PCIE_PHY_MAC_RESET 0x00c
31#define PCIE_PHY_PLL_LOCKED 0x010
32#define PCIE_PHY_TRSVREG_RESET 0x020
33#define PCIE_PHY_TRSV_RESET 0x024
34
35/* PCIe PHY registers */
36#define PCIE_PHY_IMPEDANCE 0x004
37#define PCIE_PHY_PLL_DIV_0 0x008
38#define PCIE_PHY_PLL_BIAS 0x00c
39#define PCIE_PHY_DCC_FEEDBACK 0x014
40#define PCIE_PHY_PLL_DIV_1 0x05c
41#define PCIE_PHY_COMMON_POWER 0x064
42#define PCIE_PHY_COMMON_PD_CMN BIT(3)
43#define PCIE_PHY_TRSV0_EMP_LVL 0x084
44#define PCIE_PHY_TRSV0_DRV_LVL 0x088
45#define PCIE_PHY_TRSV0_RXCDR 0x0ac
46#define PCIE_PHY_TRSV0_POWER 0x0c4
47#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
48#define PCIE_PHY_TRSV0_LVCC 0x0dc
49#define PCIE_PHY_TRSV1_EMP_LVL 0x144
50#define PCIE_PHY_TRSV1_RXCDR 0x16c
51#define PCIE_PHY_TRSV1_POWER 0x184
52#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
53#define PCIE_PHY_TRSV1_LVCC 0x19c
54#define PCIE_PHY_TRSV2_EMP_LVL 0x204
55#define PCIE_PHY_TRSV2_RXCDR 0x22c
56#define PCIE_PHY_TRSV2_POWER 0x244
57#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
58#define PCIE_PHY_TRSV2_LVCC 0x25c
59#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
60#define PCIE_PHY_TRSV3_RXCDR 0x2ec
61#define PCIE_PHY_TRSV3_POWER 0x304
62#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
63#define PCIE_PHY_TRSV3_LVCC 0x31c
64
65struct exynos_pcie_phy_data {
66 const struct phy_ops *ops;
67};
68
69/* For Exynos pcie phy */
70struct exynos_pcie_phy {
71 const struct exynos_pcie_phy_data *drv_data;
72 void __iomem *phy_base;
73 void __iomem *blk_base; /* For exynos5440 */
74};
75
76static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset)
77{
78 writel(val, base + offset);
79}
80
81static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset)
82{
83 return readl(base + offset);
84}
85
86/* For Exynos5440 specific functions */
87static int exynos5440_pcie_phy_init(struct phy *phy)
88{
89 struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
90
91 /* DCC feedback control off */
92 exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
93
94 /* set TX/RX impedance */
95 exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
96
97 /* set 50Mhz PHY clock */
98 exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
99 exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
100
101 /* set TX Differential output for lane 0 */
102 exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
103
104 /* set TX Pre-emphasis Level Control for lane 0 to minimum */
105 exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
106
107 /* set RX clock and data recovery bandwidth */
108 exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
109 exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
110 exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
111 exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
112 exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
113
114 /* change TX Pre-emphasis Level Control for lanes */
115 exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
116 exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
117 exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
118 exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
119
120 /* set LVCC */
121 exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
122 exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
123 exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
124 exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
125
126 /* pulse for common reset */
127 exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET);
128 udelay(500);
129 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
130
131 return 0;
132}
133
134static int exynos5440_pcie_phy_power_on(struct phy *phy)
135{
136 struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
137 u32 val;
138
139 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET);
140 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG);
141 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET);
142 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET);
143
144 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
145 val &= ~PCIE_PHY_COMMON_PD_CMN;
146 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
147
148 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
149 val &= ~PCIE_PHY_TRSV0_PD_TSV;
150 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
151
152 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
153 val &= ~PCIE_PHY_TRSV1_PD_TSV;
154 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
155
156 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
157 val &= ~PCIE_PHY_TRSV2_PD_TSV;
158 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
159
160 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
161 val &= ~PCIE_PHY_TRSV3_PD_TSV;
162 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
163
164 return 0;
165}
166
167static int exynos5440_pcie_phy_power_off(struct phy *phy)
168{
169 struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
170 u32 val;
171
172 if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val,
173 (val != 0), 1, 500)) {
174 dev_err(&phy->dev, "PLL Locked: 0x%x\n", val);
175 return -ETIMEDOUT;
176 }
177
178 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
179 val |= PCIE_PHY_COMMON_PD_CMN;
180 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
181
182 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
183 val |= PCIE_PHY_TRSV0_PD_TSV;
184 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
185
186 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
187 val |= PCIE_PHY_TRSV1_PD_TSV;
188 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
189
190 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
191 val |= PCIE_PHY_TRSV2_PD_TSV;
192 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
193
194 val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
195 val |= PCIE_PHY_TRSV3_PD_TSV;
196 exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
197
198 return 0;
199}
200
201static int exynos5440_pcie_phy_reset(struct phy *phy)
202{
203 struct exynos_pcie_phy *ep = phy_get_drvdata(phy);
204
205 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET);
206 exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET);
207 exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET);
208
209 return 0;
210}
211
212static const struct phy_ops exynos5440_phy_ops = {
213 .init = exynos5440_pcie_phy_init,
214 .power_on = exynos5440_pcie_phy_power_on,
215 .power_off = exynos5440_pcie_phy_power_off,
216 .reset = exynos5440_pcie_phy_reset,
217 .owner = THIS_MODULE,
218};
219
220static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = {
221 .ops = &exynos5440_phy_ops,
222};
223
224static const struct of_device_id exynos_pcie_phy_match[] = {
225 {
226 .compatible = "samsung,exynos5440-pcie-phy",
227 .data = &exynos5440_pcie_phy_data,
228 },
229 {},
230};
231MODULE_DEVICE_TABLE(of, exynos_pcie_phy_match);
232
233static int exynos_pcie_phy_probe(struct platform_device *pdev)
234{
235 struct device *dev = &pdev->dev;
236 struct exynos_pcie_phy *exynos_phy;
237 struct phy *generic_phy;
238 struct phy_provider *phy_provider;
239 struct resource *res;
240 const struct exynos_pcie_phy_data *drv_data;
241
242 drv_data = of_device_get_match_data(dev);
243 if (!drv_data)
244 return -ENODEV;
245
246 exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL);
247 if (!exynos_phy)
248 return -ENOMEM;
249
250 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251 exynos_phy->phy_base = devm_ioremap_resource(dev, res);
252 if (IS_ERR(exynos_phy->phy_base))
253 return PTR_ERR(exynos_phy->phy_base);
254
255 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
256 exynos_phy->blk_base = devm_ioremap_resource(dev, res);
257 if (IS_ERR(exynos_phy->phy_base))
258 return PTR_ERR(exynos_phy->phy_base);
259
260 exynos_phy->drv_data = drv_data;
261
262 generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops);
263 if (IS_ERR(generic_phy)) {
264 dev_err(dev, "failed to create PHY\n");
265 return PTR_ERR(generic_phy);
266 }
267
268 phy_set_drvdata(generic_phy, exynos_phy);
269 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
270
271 return PTR_ERR_OR_ZERO(phy_provider);
272}
273
274static struct platform_driver exynos_pcie_phy_driver = {
275 .probe = exynos_pcie_phy_probe,
276 .driver = {
277 .of_match_table = exynos_pcie_phy_match,
278 .name = "exynos_pcie_phy",
279 }
280};
281module_platform_driver(exynos_pcie_phy_driver);
282
283MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC PCIe PHY driver");
284MODULE_AUTHOR("Jaehoon Chung <jh80.chung@samsung.com>");
285MODULE_LICENSE("GPL v2");