1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Samsung Exynos SoC series PCIe PHY driver |
4 | * |
5 | * Phy provider for PCIe controller on Exynos SoC series |
6 | * |
7 | * Copyright (C) 2017-2020 Samsung Electronics Co., Ltd. |
8 | * Jaehoon Chung <jh80.chung@samsung.com> |
9 | */ |
10 | |
11 | #include <linux/io.h> |
12 | #include <linux/mfd/syscon.h> |
13 | #include <linux/of_platform.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/phy/phy.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | #define PCIE_PHY_OFFSET(x) ((x) * 0x4) |
19 | |
20 | /* Sysreg FSYS register offsets and bits for Exynos5433 */ |
21 | #define PCIE_EXYNOS5433_PHY_MAC_RESET 0x0208 |
22 | #define PCIE_MAC_RESET_MASK 0xFF |
23 | #define PCIE_MAC_RESET BIT(4) |
24 | #define PCIE_EXYNOS5433_PHY_L1SUB_CM_CON 0x1010 |
25 | #define PCIE_REFCLK_GATING_EN BIT(0) |
26 | #define PCIE_EXYNOS5433_PHY_COMMON_RESET 0x1020 |
27 | #define PCIE_PHY_RESET BIT(0) |
28 | #define PCIE_EXYNOS5433_PHY_GLOBAL_RESET 0x1040 |
29 | #define PCIE_GLOBAL_RESET BIT(0) |
30 | #define PCIE_REFCLK BIT(1) |
31 | #define PCIE_REFCLK_MASK 0x16 |
32 | #define PCIE_APP_REQ_EXIT_L1_MODE BIT(5) |
33 | |
34 | /* PMU PCIE PHY isolation control */ |
35 | #define EXYNOS5433_PMU_PCIE_PHY_OFFSET 0x730 |
36 | |
37 | /* For Exynos pcie phy */ |
38 | struct exynos_pcie_phy { |
39 | void __iomem *base; |
40 | struct regmap *pmureg; |
41 | struct regmap *fsysreg; |
42 | }; |
43 | |
44 | static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) |
45 | { |
46 | writel(val, addr: base + offset); |
47 | } |
48 | |
49 | /* Exynos5433 specific functions */ |
50 | static int exynos5433_pcie_phy_init(struct phy *phy) |
51 | { |
52 | struct exynos_pcie_phy *ep = phy_get_drvdata(phy); |
53 | |
54 | regmap_update_bits(map: ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET, |
55 | BIT(0), val: 1); |
56 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, |
57 | PCIE_APP_REQ_EXIT_L1_MODE, val: 0); |
58 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON, |
59 | PCIE_REFCLK_GATING_EN, val: 0); |
60 | |
61 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET, |
62 | PCIE_PHY_RESET, val: 1); |
63 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET, |
64 | PCIE_MAC_RESET, val: 0); |
65 | |
66 | /* PHY refclk 24MHz */ |
67 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, |
68 | PCIE_REFCLK_MASK, PCIE_REFCLK); |
69 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET, |
70 | PCIE_GLOBAL_RESET, val: 0); |
71 | |
72 | |
73 | exynos_pcie_phy_writel(base: ep->base, val: 0x11, PCIE_PHY_OFFSET(0x3)); |
74 | |
75 | /* band gap reference on */ |
76 | exynos_pcie_phy_writel(base: ep->base, val: 0, PCIE_PHY_OFFSET(0x20)); |
77 | exynos_pcie_phy_writel(base: ep->base, val: 0, PCIE_PHY_OFFSET(0x4b)); |
78 | |
79 | /* jitter tuning */ |
80 | exynos_pcie_phy_writel(base: ep->base, val: 0x34, PCIE_PHY_OFFSET(0x4)); |
81 | exynos_pcie_phy_writel(base: ep->base, val: 0x02, PCIE_PHY_OFFSET(0x7)); |
82 | exynos_pcie_phy_writel(base: ep->base, val: 0x41, PCIE_PHY_OFFSET(0x21)); |
83 | exynos_pcie_phy_writel(base: ep->base, val: 0x7F, PCIE_PHY_OFFSET(0x14)); |
84 | exynos_pcie_phy_writel(base: ep->base, val: 0xC0, PCIE_PHY_OFFSET(0x15)); |
85 | exynos_pcie_phy_writel(base: ep->base, val: 0x61, PCIE_PHY_OFFSET(0x36)); |
86 | |
87 | /* D0 uninit.. */ |
88 | exynos_pcie_phy_writel(base: ep->base, val: 0x44, PCIE_PHY_OFFSET(0x3D)); |
89 | |
90 | /* 24MHz */ |
91 | exynos_pcie_phy_writel(base: ep->base, val: 0x94, PCIE_PHY_OFFSET(0x8)); |
92 | exynos_pcie_phy_writel(base: ep->base, val: 0xA7, PCIE_PHY_OFFSET(0x9)); |
93 | exynos_pcie_phy_writel(base: ep->base, val: 0x93, PCIE_PHY_OFFSET(0xA)); |
94 | exynos_pcie_phy_writel(base: ep->base, val: 0x6B, PCIE_PHY_OFFSET(0xC)); |
95 | exynos_pcie_phy_writel(base: ep->base, val: 0xA5, PCIE_PHY_OFFSET(0xF)); |
96 | exynos_pcie_phy_writel(base: ep->base, val: 0x34, PCIE_PHY_OFFSET(0x16)); |
97 | exynos_pcie_phy_writel(base: ep->base, val: 0xA3, PCIE_PHY_OFFSET(0x17)); |
98 | exynos_pcie_phy_writel(base: ep->base, val: 0xA7, PCIE_PHY_OFFSET(0x1A)); |
99 | exynos_pcie_phy_writel(base: ep->base, val: 0x71, PCIE_PHY_OFFSET(0x23)); |
100 | exynos_pcie_phy_writel(base: ep->base, val: 0x4C, PCIE_PHY_OFFSET(0x24)); |
101 | |
102 | exynos_pcie_phy_writel(base: ep->base, val: 0x0E, PCIE_PHY_OFFSET(0x26)); |
103 | exynos_pcie_phy_writel(base: ep->base, val: 0x14, PCIE_PHY_OFFSET(0x7)); |
104 | exynos_pcie_phy_writel(base: ep->base, val: 0x48, PCIE_PHY_OFFSET(0x43)); |
105 | exynos_pcie_phy_writel(base: ep->base, val: 0x44, PCIE_PHY_OFFSET(0x44)); |
106 | exynos_pcie_phy_writel(base: ep->base, val: 0x03, PCIE_PHY_OFFSET(0x45)); |
107 | exynos_pcie_phy_writel(base: ep->base, val: 0xA7, PCIE_PHY_OFFSET(0x48)); |
108 | exynos_pcie_phy_writel(base: ep->base, val: 0x13, PCIE_PHY_OFFSET(0x54)); |
109 | exynos_pcie_phy_writel(base: ep->base, val: 0x04, PCIE_PHY_OFFSET(0x31)); |
110 | exynos_pcie_phy_writel(base: ep->base, val: 0, PCIE_PHY_OFFSET(0x32)); |
111 | |
112 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET, |
113 | PCIE_PHY_RESET, val: 0); |
114 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET, |
115 | PCIE_MAC_RESET_MASK, PCIE_MAC_RESET); |
116 | return 0; |
117 | } |
118 | |
119 | static int exynos5433_pcie_phy_exit(struct phy *phy) |
120 | { |
121 | struct exynos_pcie_phy *ep = phy_get_drvdata(phy); |
122 | |
123 | regmap_update_bits(map: ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON, |
124 | PCIE_REFCLK_GATING_EN, PCIE_REFCLK_GATING_EN); |
125 | regmap_update_bits(map: ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET, |
126 | BIT(0), val: 0); |
127 | return 0; |
128 | } |
129 | |
130 | static const struct phy_ops exynos5433_phy_ops = { |
131 | .init = exynos5433_pcie_phy_init, |
132 | .exit = exynos5433_pcie_phy_exit, |
133 | .owner = THIS_MODULE, |
134 | }; |
135 | |
136 | static const struct of_device_id exynos_pcie_phy_match[] = { |
137 | { |
138 | .compatible = "samsung,exynos5433-pcie-phy" , |
139 | }, |
140 | {}, |
141 | }; |
142 | |
143 | static int exynos_pcie_phy_probe(struct platform_device *pdev) |
144 | { |
145 | struct device *dev = &pdev->dev; |
146 | struct exynos_pcie_phy *exynos_phy; |
147 | struct phy *generic_phy; |
148 | struct phy_provider *phy_provider; |
149 | |
150 | exynos_phy = devm_kzalloc(dev, size: sizeof(*exynos_phy), GFP_KERNEL); |
151 | if (!exynos_phy) |
152 | return -ENOMEM; |
153 | |
154 | exynos_phy->base = devm_platform_ioremap_resource(pdev, index: 0); |
155 | if (IS_ERR(ptr: exynos_phy->base)) |
156 | return PTR_ERR(ptr: exynos_phy->base); |
157 | |
158 | exynos_phy->pmureg = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
159 | property: "samsung,pmu-syscon" ); |
160 | if (IS_ERR(ptr: exynos_phy->pmureg)) { |
161 | dev_err(&pdev->dev, "PMU regmap lookup failed.\n" ); |
162 | return PTR_ERR(ptr: exynos_phy->pmureg); |
163 | } |
164 | |
165 | exynos_phy->fsysreg = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
166 | property: "samsung,fsys-sysreg" ); |
167 | if (IS_ERR(ptr: exynos_phy->fsysreg)) { |
168 | dev_err(&pdev->dev, "FSYS sysreg regmap lookup failed.\n" ); |
169 | return PTR_ERR(ptr: exynos_phy->fsysreg); |
170 | } |
171 | |
172 | generic_phy = devm_phy_create(dev, node: dev->of_node, ops: &exynos5433_phy_ops); |
173 | if (IS_ERR(ptr: generic_phy)) { |
174 | dev_err(dev, "failed to create PHY\n" ); |
175 | return PTR_ERR(ptr: generic_phy); |
176 | } |
177 | |
178 | phy_set_drvdata(phy: generic_phy, data: exynos_phy); |
179 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
180 | |
181 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
182 | } |
183 | |
184 | static struct platform_driver exynos_pcie_phy_driver = { |
185 | .probe = exynos_pcie_phy_probe, |
186 | .driver = { |
187 | .of_match_table = exynos_pcie_phy_match, |
188 | .name = "exynos_pcie_phy" , |
189 | .suppress_bind_attrs = true, |
190 | } |
191 | }; |
192 | builtin_platform_driver(exynos_pcie_phy_driver); |
193 | |