diff options
-rw-r--r-- | drivers/phy/lantiq/Kconfig | 11 | ||||
-rw-r--r-- | drivers/phy/lantiq/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c | 494 |
3 files changed, 506 insertions, 0 deletions
diff --git a/drivers/phy/lantiq/Kconfig b/drivers/phy/lantiq/Kconfig index eb66c857ce25..c4df9709d53f 100644 --- a/drivers/phy/lantiq/Kconfig +++ b/drivers/phy/lantiq/Kconfig | |||
@@ -2,6 +2,17 @@ | |||
2 | # | 2 | # |
3 | # Phy drivers for Lantiq / Intel platforms | 3 | # Phy drivers for Lantiq / Intel platforms |
4 | # | 4 | # |
5 | config PHY_LANTIQ_VRX200_PCIE | ||
6 | tristate "Lantiq VRX200/ARX300 PCIe PHY" | ||
7 | depends on SOC_TYPE_XWAY || COMPILE_TEST | ||
8 | depends on OF && HAS_IOMEM | ||
9 | select GENERIC_PHY | ||
10 | select REGMAP_MMIO | ||
11 | help | ||
12 | Support for the PCIe PHY(s) on the Lantiq / Intel VRX200 and ARX300 | ||
13 | family SoCs. | ||
14 | If unsure, say N. | ||
15 | |||
5 | config PHY_LANTIQ_RCU_USB2 | 16 | config PHY_LANTIQ_RCU_USB2 |
6 | tristate "Lantiq XWAY SoC RCU based USB PHY" | 17 | tristate "Lantiq XWAY SoC RCU based USB PHY" |
7 | depends on OF && (SOC_TYPE_XWAY || COMPILE_TEST) | 18 | depends on OF && (SOC_TYPE_XWAY || COMPILE_TEST) |
diff --git a/drivers/phy/lantiq/Makefile b/drivers/phy/lantiq/Makefile index 540049039092..7c14eb24ab73 100644 --- a/drivers/phy/lantiq/Makefile +++ b/drivers/phy/lantiq/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
2 | obj-$(CONFIG_PHY_LANTIQ_RCU_USB2) += phy-lantiq-rcu-usb2.o | 2 | obj-$(CONFIG_PHY_LANTIQ_RCU_USB2) += phy-lantiq-rcu-usb2.o |
3 | obj-$(CONFIG_PHY_LANTIQ_VRX200_PCIE) += phy-lantiq-vrx200-pcie.o | ||
diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c new file mode 100644 index 000000000000..544d64a84cc0 --- /dev/null +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c | |||
@@ -0,0 +1,494 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * PCIe PHY driver for Lantiq VRX200 and ARX300 SoCs. | ||
4 | * | ||
5 | * Copyright (C) 2019 Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||
6 | * | ||
7 | * Based on the BSP (called "UGW") driver: | ||
8 | * Copyright (C) 2009-2015 Lei Chuanhua <chuanhua.lei@lantiq.com> | ||
9 | * Copyright (C) 2016 Intel Corporation | ||
10 | * | ||
11 | * TODO: PHY modes other than 36MHz (without "SSC") | ||
12 | */ | ||
13 | |||
14 | #include <linux/bitfield.h> | ||
15 | #include <linux/bits.h> | ||
16 | #include <linux/clk.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/mfd/syscon.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/phy/phy.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/property.h> | ||
24 | #include <linux/regmap.h> | ||
25 | #include <linux/reset.h> | ||
26 | |||
27 | #include <dt-bindings/phy/phy-lantiq-vrx200-pcie.h> | ||
28 | |||
29 | #define PCIE_PHY_PLL_CTRL1 0x44 | ||
30 | |||
31 | #define PCIE_PHY_PLL_CTRL2 0x46 | ||
32 | #define PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK GENMASK(7, 0) | ||
33 | #define PCIE_PHY_PLL_CTRL2_CONST_SDM_EN BIT(8) | ||
34 | #define PCIE_PHY_PLL_CTRL2_PLL_SDM_EN BIT(9) | ||
35 | |||
36 | #define PCIE_PHY_PLL_CTRL3 0x48 | ||
37 | #define PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_EN BIT(1) | ||
38 | #define PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_MASK GENMASK(6, 4) | ||
39 | |||
40 | #define PCIE_PHY_PLL_CTRL4 0x4a | ||
41 | #define PCIE_PHY_PLL_CTRL5 0x4c | ||
42 | #define PCIE_PHY_PLL_CTRL6 0x4e | ||
43 | #define PCIE_PHY_PLL_CTRL7 0x50 | ||
44 | #define PCIE_PHY_PLL_A_CTRL1 0x52 | ||
45 | |||
46 | #define PCIE_PHY_PLL_A_CTRL2 0x54 | ||
47 | #define PCIE_PHY_PLL_A_CTRL2_LF_MODE_EN BIT(14) | ||
48 | |||
49 | #define PCIE_PHY_PLL_A_CTRL3 0x56 | ||
50 | #define PCIE_PHY_PLL_A_CTRL3_MMD_MASK GENMASK(15, 13) | ||
51 | |||
52 | #define PCIE_PHY_PLL_STATUS 0x58 | ||
53 | |||
54 | #define PCIE_PHY_TX1_CTRL1 0x60 | ||
55 | #define PCIE_PHY_TX1_CTRL1_FORCE_EN BIT(3) | ||
56 | #define PCIE_PHY_TX1_CTRL1_LOAD_EN BIT(4) | ||
57 | |||
58 | #define PCIE_PHY_TX1_CTRL2 0x62 | ||
59 | #define PCIE_PHY_TX1_CTRL3 0x64 | ||
60 | #define PCIE_PHY_TX1_A_CTRL1 0x66 | ||
61 | #define PCIE_PHY_TX1_A_CTRL2 0x68 | ||
62 | #define PCIE_PHY_TX1_MOD1 0x6a | ||
63 | #define PCIE_PHY_TX1_MOD2 0x6c | ||
64 | #define PCIE_PHY_TX1_MOD3 0x6e | ||
65 | |||
66 | #define PCIE_PHY_TX2_CTRL1 0x70 | ||
67 | #define PCIE_PHY_TX2_CTRL1_LOAD_EN BIT(4) | ||
68 | |||
69 | #define PCIE_PHY_TX2_CTRL2 0x72 | ||
70 | #define PCIE_PHY_TX2_A_CTRL1 0x76 | ||
71 | #define PCIE_PHY_TX2_A_CTRL2 0x78 | ||
72 | #define PCIE_PHY_TX2_MOD1 0x7a | ||
73 | #define PCIE_PHY_TX2_MOD2 0x7c | ||
74 | #define PCIE_PHY_TX2_MOD3 0x7e | ||
75 | |||
76 | #define PCIE_PHY_RX1_CTRL1 0xa0 | ||
77 | #define PCIE_PHY_RX1_CTRL1_LOAD_EN BIT(1) | ||
78 | |||
79 | #define PCIE_PHY_RX1_CTRL2 0xa2 | ||
80 | #define PCIE_PHY_RX1_CDR 0xa4 | ||
81 | #define PCIE_PHY_RX1_EI 0xa6 | ||
82 | #define PCIE_PHY_RX1_A_CTRL 0xaa | ||
83 | |||
84 | struct ltq_vrx200_pcie_phy_priv { | ||
85 | struct phy *phy; | ||
86 | unsigned int mode; | ||
87 | struct device *dev; | ||
88 | struct regmap *phy_regmap; | ||
89 | struct regmap *rcu_regmap; | ||
90 | struct clk *pdi_clk; | ||
91 | struct clk *phy_clk; | ||
92 | struct reset_control *phy_reset; | ||
93 | struct reset_control *pcie_reset; | ||
94 | u32 rcu_ahb_endian_offset; | ||
95 | u32 rcu_ahb_endian_big_endian_mask; | ||
96 | }; | ||
97 | |||
98 | static void ltq_vrx200_pcie_phy_common_setup(struct phy *phy) | ||
99 | { | ||
100 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
101 | |||
102 | /* PLL Setting */ | ||
103 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL1, 0x120e); | ||
104 | |||
105 | /* increase the bias reference voltage */ | ||
106 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL2, 0x39d7); | ||
107 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL3, 0x0900); | ||
108 | |||
109 | /* Endcnt */ | ||
110 | regmap_write(priv->phy_regmap, PCIE_PHY_RX1_EI, 0x0004); | ||
111 | regmap_write(priv->phy_regmap, PCIE_PHY_RX1_A_CTRL, 0x6803); | ||
112 | |||
113 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_TX1_CTRL1, | ||
114 | PCIE_PHY_TX1_CTRL1_FORCE_EN, | ||
115 | PCIE_PHY_TX1_CTRL1_FORCE_EN); | ||
116 | |||
117 | /* predrv_ser_en */ | ||
118 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_A_CTRL2, 0x0706); | ||
119 | |||
120 | /* ctrl_lim */ | ||
121 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_CTRL3, 0x1fff); | ||
122 | |||
123 | /* ctrl */ | ||
124 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_A_CTRL1, 0x0810); | ||
125 | |||
126 | /* predrv_ser_en */ | ||
127 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_TX2_A_CTRL2, 0x7f00, | ||
128 | 0x4700); | ||
129 | |||
130 | /* RTERM */ | ||
131 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_CTRL2, 0x2e00); | ||
132 | |||
133 | /* Improved 100MHz clock output */ | ||
134 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_CTRL2, 0x3096); | ||
135 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_A_CTRL2, 0x4707); | ||
136 | |||
137 | /* Reduced CDR BW to avoid glitches */ | ||
138 | regmap_write(priv->phy_regmap, PCIE_PHY_RX1_CDR, 0x0235); | ||
139 | } | ||
140 | |||
141 | static void pcie_phy_36mhz_mode_setup(struct phy *phy) | ||
142 | { | ||
143 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
144 | |||
145 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL3, | ||
146 | PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_EN, 0x0000); | ||
147 | |||
148 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL3, | ||
149 | PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_MASK, 0x0000); | ||
150 | |||
151 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, | ||
152 | PCIE_PHY_PLL_CTRL2_PLL_SDM_EN, | ||
153 | PCIE_PHY_PLL_CTRL2_PLL_SDM_EN); | ||
154 | |||
155 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, | ||
156 | PCIE_PHY_PLL_CTRL2_CONST_SDM_EN, | ||
157 | PCIE_PHY_PLL_CTRL2_CONST_SDM_EN); | ||
158 | |||
159 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL3, | ||
160 | PCIE_PHY_PLL_A_CTRL3_MMD_MASK, | ||
161 | FIELD_PREP(PCIE_PHY_PLL_A_CTRL3_MMD_MASK, 0x1)); | ||
162 | |||
163 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL2, | ||
164 | PCIE_PHY_PLL_A_CTRL2_LF_MODE_EN, 0x0000); | ||
165 | |||
166 | /* const_sdm */ | ||
167 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL1, 0x38e4); | ||
168 | |||
169 | regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, | ||
170 | PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK, | ||
171 | FIELD_PREP(PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK, | ||
172 | 0xee)); | ||
173 | |||
174 | /* pllmod */ | ||
175 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL7, 0x0002); | ||
176 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL6, 0x3a04); | ||
177 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL5, 0xfae3); | ||
178 | regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL4, 0x1b72); | ||
179 | } | ||
180 | |||
181 | static int ltq_vrx200_pcie_phy_wait_for_pll(struct phy *phy) | ||
182 | { | ||
183 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
184 | unsigned int tmp; | ||
185 | int ret; | ||
186 | |||
187 | ret = regmap_read_poll_timeout(priv->phy_regmap, PCIE_PHY_PLL_STATUS, | ||
188 | tmp, ((tmp & 0x0070) == 0x0070), 10, | ||
189 | 10000); | ||
190 | if (ret) { | ||
191 | dev_err(priv->dev, "PLL Link timeout, PLL status = 0x%04x\n", | ||
192 | tmp); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void ltq_vrx200_pcie_phy_apply_workarounds(struct phy *phy) | ||
200 | { | ||
201 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
202 | static const struct reg_default slices[] = { | ||
203 | { | ||
204 | .reg = PCIE_PHY_TX1_CTRL1, | ||
205 | .def = PCIE_PHY_TX1_CTRL1_LOAD_EN, | ||
206 | }, | ||
207 | { | ||
208 | .reg = PCIE_PHY_TX2_CTRL1, | ||
209 | .def = PCIE_PHY_TX2_CTRL1_LOAD_EN, | ||
210 | }, | ||
211 | { | ||
212 | .reg = PCIE_PHY_RX1_CTRL1, | ||
213 | .def = PCIE_PHY_RX1_CTRL1_LOAD_EN, | ||
214 | } | ||
215 | }; | ||
216 | int i; | ||
217 | |||
218 | for (i = 0; i < ARRAY_SIZE(slices); i++) { | ||
219 | /* enable load_en */ | ||
220 | regmap_update_bits(priv->phy_regmap, slices[i].reg, | ||
221 | slices[i].def, slices[i].def); | ||
222 | |||
223 | udelay(1); | ||
224 | |||
225 | /* disable load_en */ | ||
226 | regmap_update_bits(priv->phy_regmap, slices[i].reg, | ||
227 | slices[i].def, 0x0); | ||
228 | } | ||
229 | |||
230 | for (i = 0; i < 5; i++) { | ||
231 | /* TX2 modulation */ | ||
232 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD1, 0x1ffe); | ||
233 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD2, 0xfffe); | ||
234 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD3, 0x0601); | ||
235 | usleep_range(1000, 2000); | ||
236 | regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD3, 0x0001); | ||
237 | |||
238 | /* TX1 modulation */ | ||
239 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD1, 0x1ffe); | ||
240 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD2, 0xfffe); | ||
241 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD3, 0x0601); | ||
242 | usleep_range(1000, 2000); | ||
243 | regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD3, 0x0001); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | static int ltq_vrx200_pcie_phy_init(struct phy *phy) | ||
248 | { | ||
249 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
250 | int ret; | ||
251 | |||
252 | if (of_device_is_big_endian(priv->dev->of_node)) | ||
253 | regmap_update_bits(priv->rcu_regmap, | ||
254 | priv->rcu_ahb_endian_offset, | ||
255 | priv->rcu_ahb_endian_big_endian_mask, | ||
256 | priv->rcu_ahb_endian_big_endian_mask); | ||
257 | else | ||
258 | regmap_update_bits(priv->rcu_regmap, | ||
259 | priv->rcu_ahb_endian_offset, | ||
260 | priv->rcu_ahb_endian_big_endian_mask, 0x0); | ||
261 | |||
262 | ret = reset_control_assert(priv->phy_reset); | ||
263 | if (ret) | ||
264 | goto err; | ||
265 | |||
266 | udelay(1); | ||
267 | |||
268 | ret = reset_control_deassert(priv->phy_reset); | ||
269 | if (ret) | ||
270 | goto err; | ||
271 | |||
272 | udelay(1); | ||
273 | |||
274 | ret = reset_control_deassert(priv->pcie_reset); | ||
275 | if (ret) | ||
276 | goto err_assert_phy_reset; | ||
277 | |||
278 | /* Make sure PHY PLL is stable */ | ||
279 | usleep_range(20, 40); | ||
280 | |||
281 | return 0; | ||
282 | |||
283 | err_assert_phy_reset: | ||
284 | reset_control_assert(priv->phy_reset); | ||
285 | err: | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | static int ltq_vrx200_pcie_phy_exit(struct phy *phy) | ||
290 | { | ||
291 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
292 | int ret; | ||
293 | |||
294 | ret = reset_control_assert(priv->pcie_reset); | ||
295 | if (ret) | ||
296 | return ret; | ||
297 | |||
298 | ret = reset_control_assert(priv->phy_reset); | ||
299 | if (ret) | ||
300 | return ret; | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int ltq_vrx200_pcie_phy_power_on(struct phy *phy) | ||
306 | { | ||
307 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
308 | int ret; | ||
309 | |||
310 | /* Enable PDI to access PCIe PHY register */ | ||
311 | ret = clk_prepare_enable(priv->pdi_clk); | ||
312 | if (ret) | ||
313 | goto err; | ||
314 | |||
315 | /* Configure PLL and PHY clock */ | ||
316 | ltq_vrx200_pcie_phy_common_setup(phy); | ||
317 | |||
318 | pcie_phy_36mhz_mode_setup(phy); | ||
319 | |||
320 | /* Enable the PCIe PHY and make PLL setting take effect */ | ||
321 | ret = clk_prepare_enable(priv->phy_clk); | ||
322 | if (ret) | ||
323 | goto err_disable_pdi_clk; | ||
324 | |||
325 | /* Check if we are in "startup ready" status */ | ||
326 | if (ltq_vrx200_pcie_phy_wait_for_pll(phy) != 0) | ||
327 | goto err_disable_phy_clk; | ||
328 | |||
329 | ltq_vrx200_pcie_phy_apply_workarounds(phy); | ||
330 | |||
331 | return 0; | ||
332 | |||
333 | err_disable_phy_clk: | ||
334 | clk_disable_unprepare(priv->phy_clk); | ||
335 | err_disable_pdi_clk: | ||
336 | clk_disable_unprepare(priv->pdi_clk); | ||
337 | err: | ||
338 | return ret; | ||
339 | } | ||
340 | |||
341 | static int ltq_vrx200_pcie_phy_power_off(struct phy *phy) | ||
342 | { | ||
343 | struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); | ||
344 | |||
345 | clk_disable_unprepare(priv->phy_clk); | ||
346 | clk_disable_unprepare(priv->pdi_clk); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static struct phy_ops ltq_vrx200_pcie_phy_ops = { | ||
352 | .init = ltq_vrx200_pcie_phy_init, | ||
353 | .exit = ltq_vrx200_pcie_phy_exit, | ||
354 | .power_on = ltq_vrx200_pcie_phy_power_on, | ||
355 | .power_off = ltq_vrx200_pcie_phy_power_off, | ||
356 | .owner = THIS_MODULE, | ||
357 | }; | ||
358 | |||
359 | static struct phy *ltq_vrx200_pcie_phy_xlate(struct device *dev, | ||
360 | struct of_phandle_args *args) | ||
361 | { | ||
362 | struct ltq_vrx200_pcie_phy_priv *priv = dev_get_drvdata(dev); | ||
363 | unsigned int mode; | ||
364 | |||
365 | if (args->args_count != 1) { | ||
366 | dev_err(dev, "invalid number of arguments\n"); | ||
367 | return ERR_PTR(-EINVAL); | ||
368 | } | ||
369 | |||
370 | mode = args->args[0]; | ||
371 | |||
372 | switch (mode) { | ||
373 | case LANTIQ_PCIE_PHY_MODE_36MHZ: | ||
374 | priv->mode = mode; | ||
375 | break; | ||
376 | |||
377 | case LANTIQ_PCIE_PHY_MODE_25MHZ: | ||
378 | case LANTIQ_PCIE_PHY_MODE_25MHZ_SSC: | ||
379 | case LANTIQ_PCIE_PHY_MODE_36MHZ_SSC: | ||
380 | case LANTIQ_PCIE_PHY_MODE_100MHZ: | ||
381 | case LANTIQ_PCIE_PHY_MODE_100MHZ_SSC: | ||
382 | dev_err(dev, "PHY mode not implemented yet: %u\n", mode); | ||
383 | return ERR_PTR(-EINVAL); | ||
384 | |||
385 | default: | ||
386 | dev_err(dev, "invalid PHY mode %u\n", mode); | ||
387 | return ERR_PTR(-EINVAL); | ||
388 | }; | ||
389 | |||
390 | return priv->phy; | ||
391 | } | ||
392 | |||
393 | static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev) | ||
394 | { | ||
395 | static const struct regmap_config regmap_config = { | ||
396 | .reg_bits = 8, | ||
397 | .val_bits = 16, | ||
398 | .reg_stride = 2, | ||
399 | .max_register = PCIE_PHY_RX1_A_CTRL, | ||
400 | }; | ||
401 | struct ltq_vrx200_pcie_phy_priv *priv; | ||
402 | struct device *dev = &pdev->dev; | ||
403 | struct phy_provider *provider; | ||
404 | struct resource *res; | ||
405 | void __iomem *base; | ||
406 | int ret; | ||
407 | |||
408 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
409 | if (!priv) | ||
410 | return -ENOMEM; | ||
411 | |||
412 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
413 | base = devm_ioremap_resource(dev, res); | ||
414 | if (IS_ERR(base)) | ||
415 | return PTR_ERR(base); | ||
416 | |||
417 | priv->phy_regmap = devm_regmap_init_mmio(dev, base, ®map_config); | ||
418 | if (IS_ERR(priv->phy_regmap)) | ||
419 | return PTR_ERR(priv->phy_regmap); | ||
420 | |||
421 | priv->rcu_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, | ||
422 | "lantiq,rcu"); | ||
423 | if (IS_ERR(priv->rcu_regmap)) | ||
424 | return PTR_ERR(priv->rcu_regmap); | ||
425 | |||
426 | ret = device_property_read_u32(dev, "lantiq,rcu-endian-offset", | ||
427 | &priv->rcu_ahb_endian_offset); | ||
428 | if (ret) { | ||
429 | dev_err(dev, | ||
430 | "failed to parse the 'lantiq,rcu-endian-offset' property\n"); | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | ret = device_property_read_u32(dev, "lantiq,rcu-big-endian-mask", | ||
435 | &priv->rcu_ahb_endian_big_endian_mask); | ||
436 | if (ret) { | ||
437 | dev_err(dev, | ||
438 | "failed to parse the 'lantiq,rcu-big-endian-mask' property\n"); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | priv->pdi_clk = devm_clk_get(dev, "pdi"); | ||
443 | if (IS_ERR(priv->pdi_clk)) | ||
444 | return PTR_ERR(priv->pdi_clk); | ||
445 | |||
446 | priv->phy_clk = devm_clk_get(dev, "phy"); | ||
447 | if (IS_ERR(priv->phy_clk)) | ||
448 | return PTR_ERR(priv->phy_clk); | ||
449 | |||
450 | priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); | ||
451 | if (IS_ERR(priv->phy_reset)) | ||
452 | return PTR_ERR(priv->phy_reset); | ||
453 | |||
454 | priv->pcie_reset = devm_reset_control_get_shared(dev, "pcie"); | ||
455 | if (IS_ERR(priv->pcie_reset)) | ||
456 | return PTR_ERR(priv->pcie_reset); | ||
457 | |||
458 | priv->dev = dev; | ||
459 | |||
460 | priv->phy = devm_phy_create(dev, dev->of_node, | ||
461 | <q_vrx200_pcie_phy_ops); | ||
462 | if (IS_ERR(priv->phy)) { | ||
463 | dev_err(dev, "failed to create PHY\n"); | ||
464 | return PTR_ERR(priv->phy); | ||
465 | } | ||
466 | |||
467 | phy_set_drvdata(priv->phy, priv); | ||
468 | dev_set_drvdata(dev, priv); | ||
469 | |||
470 | provider = devm_of_phy_provider_register(dev, | ||
471 | ltq_vrx200_pcie_phy_xlate); | ||
472 | |||
473 | return PTR_ERR_OR_ZERO(provider); | ||
474 | } | ||
475 | |||
476 | static const struct of_device_id ltq_vrx200_pcie_phy_of_match[] = { | ||
477 | { .compatible = "lantiq,vrx200-pcie-phy", }, | ||
478 | { .compatible = "lantiq,arx300-pcie-phy", }, | ||
479 | { /* sentinel */ }, | ||
480 | }; | ||
481 | MODULE_DEVICE_TABLE(of, ltq_vrx200_pcie_phy_of_match); | ||
482 | |||
483 | static struct platform_driver ltq_vrx200_pcie_phy_driver = { | ||
484 | .probe = ltq_vrx200_pcie_phy_probe, | ||
485 | .driver = { | ||
486 | .name = "ltq-vrx200-pcie-phy", | ||
487 | .of_match_table = ltq_vrx200_pcie_phy_of_match, | ||
488 | } | ||
489 | }; | ||
490 | module_platform_driver(ltq_vrx200_pcie_phy_driver); | ||
491 | |||
492 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); | ||
493 | MODULE_DESCRIPTION("Lantiq VRX200 and ARX300 PCIe PHY driver"); | ||
494 | MODULE_LICENSE("GPL v2"); | ||