1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2016 Allwinnertech Co., Ltd.
4  * Copyright (C) 2017-2018 Bootlin
5  *
6  * Maxime Ripard <maxime.ripard@free-electrons.com>
7  */
8 
9 #include <linux/bitops.h>
10 #include <linux/clk.h>
11 #include <linux/module.h>
12 #include <linux/of_address.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/reset.h>
16 
17 #include <linux/phy/phy.h>
18 #include <linux/phy/phy-mipi-dphy.h>
19 
20 #define SUN6I_DPHY_GCTL_REG		0x00
21 #define SUN6I_DPHY_GCTL_LANE_NUM(n)		((((n) - 1) & 3) << 4)
22 #define SUN6I_DPHY_GCTL_EN			BIT(0)
23 
24 #define SUN6I_DPHY_TX_CTL_REG		0x04
25 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT	BIT(28)
26 
27 #define SUN6I_DPHY_RX_CTL_REG		0x08
28 #define SUN6I_DPHY_RX_CTL_EN_DBC	BIT(31)
29 #define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE	BIT(24)
30 #define SUN6I_DPHY_RX_CTL_RX_D3_FORCE	BIT(23)
31 #define SUN6I_DPHY_RX_CTL_RX_D2_FORCE	BIT(22)
32 #define SUN6I_DPHY_RX_CTL_RX_D1_FORCE	BIT(21)
33 #define SUN6I_DPHY_RX_CTL_RX_D0_FORCE	BIT(20)
34 
35 #define SUN6I_DPHY_TX_TIME0_REG		0x10
36 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)		(((n) & 0xff) << 24)
37 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)	(((n) & 0xff) << 16)
38 #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)	((n) & 0xff)
39 
40 #define SUN6I_DPHY_TX_TIME1_REG		0x14
41 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n)		(((n) & 0xff) << 24)
42 #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)		(((n) & 0xff) << 16)
43 #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)		(((n) & 0xff) << 8)
44 #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)	((n) & 0xff)
45 
46 #define SUN6I_DPHY_TX_TIME2_REG		0x18
47 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)	((n) & 0xff)
48 
49 #define SUN6I_DPHY_TX_TIME3_REG		0x1c
50 
51 #define SUN6I_DPHY_TX_TIME4_REG		0x20
52 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)	(((n) & 0xff) << 8)
53 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)	((n) & 0xff)
54 
55 #define SUN6I_DPHY_RX_TIME0_REG		0x30
56 #define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n)	(((n) & 0xff) << 24)
57 #define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n)	(((n) & 0xff) << 16)
58 #define SUN6I_DPHY_RX_TIME0_LP_RX(n)		(((n) & 0xff) << 8)
59 
60 #define SUN6I_DPHY_RX_TIME1_REG		0x34
61 #define SUN6I_DPHY_RX_TIME1_RX_DLY(n)		(((n) & 0xfff) << 20)
62 #define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n)	((n) & 0xfffff)
63 
64 #define SUN6I_DPHY_RX_TIME2_REG		0x38
65 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n)	(((n) & 0xff) << 8)
66 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n)	((n) & 0xff)
67 
68 #define SUN6I_DPHY_RX_TIME3_REG		0x40
69 #define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n)	(((n) & 0xffff) << 16)
70 
71 #define SUN6I_DPHY_ANA0_REG		0x4c
72 #define SUN6I_DPHY_ANA0_REG_PWS			BIT(31)
73 #define SUN6I_DPHY_ANA0_REG_DMPC		BIT(28)
74 #define SUN6I_DPHY_ANA0_REG_DMPD(n)		(((n) & 0xf) << 24)
75 #define SUN6I_DPHY_ANA0_REG_SLV(n)		(((n) & 7) << 12)
76 #define SUN6I_DPHY_ANA0_REG_DEN(n)		(((n) & 0xf) << 8)
77 #define SUN6I_DPHY_ANA0_REG_SFB(n)		(((n) & 3) << 2)
78 
79 #define SUN6I_DPHY_ANA1_REG		0x50
80 #define SUN6I_DPHY_ANA1_REG_VTTMODE		BIT(31)
81 #define SUN6I_DPHY_ANA1_REG_CSMPS(n)		(((n) & 3) << 28)
82 #define SUN6I_DPHY_ANA1_REG_SVTT(n)		(((n) & 0xf) << 24)
83 
84 #define SUN6I_DPHY_ANA2_REG		0x54
85 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)		(((n) & 0xf) << 24)
86 #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK		GENMASK(27, 24)
87 #define SUN6I_DPHY_ANA2_EN_CK_CPU		BIT(4)
88 #define SUN6I_DPHY_ANA2_REG_ENIB		BIT(1)
89 
90 #define SUN6I_DPHY_ANA3_REG		0x58
91 #define SUN6I_DPHY_ANA3_EN_VTTD(n)		(((n) & 0xf) << 28)
92 #define SUN6I_DPHY_ANA3_EN_VTTD_MASK		GENMASK(31, 28)
93 #define SUN6I_DPHY_ANA3_EN_VTTC			BIT(27)
94 #define SUN6I_DPHY_ANA3_EN_DIV			BIT(26)
95 #define SUN6I_DPHY_ANA3_EN_LDOC			BIT(25)
96 #define SUN6I_DPHY_ANA3_EN_LDOD			BIT(24)
97 #define SUN6I_DPHY_ANA3_EN_LDOR			BIT(18)
98 
99 #define SUN6I_DPHY_ANA4_REG		0x5c
100 #define SUN6I_DPHY_ANA4_REG_DMPLVC		BIT(24)
101 #define SUN6I_DPHY_ANA4_REG_DMPLVD(n)		(((n) & 0xf) << 20)
102 #define SUN6I_DPHY_ANA4_REG_CKDV(n)		(((n) & 0x1f) << 12)
103 #define SUN6I_DPHY_ANA4_REG_TMSC(n)		(((n) & 3) << 10)
104 #define SUN6I_DPHY_ANA4_REG_TMSD(n)		(((n) & 3) << 8)
105 #define SUN6I_DPHY_ANA4_REG_TXDNSC(n)		(((n) & 3) << 6)
106 #define SUN6I_DPHY_ANA4_REG_TXDNSD(n)		(((n) & 3) << 4)
107 #define SUN6I_DPHY_ANA4_REG_TXPUSC(n)		(((n) & 3) << 2)
108 #define SUN6I_DPHY_ANA4_REG_TXPUSD(n)		((n) & 3)
109 
110 #define SUN6I_DPHY_DBG5_REG		0xf4
111 
112 enum sun6i_dphy_direction {
113 	SUN6I_DPHY_DIRECTION_TX,
114 	SUN6I_DPHY_DIRECTION_RX,
115 };
116 
117 struct sun6i_dphy;
118 
119 struct sun6i_dphy_variant {
120 	void	(*tx_power_on)(struct sun6i_dphy *dphy);
121 	bool	rx_supported;
122 };
123 
124 struct sun6i_dphy {
125 	struct clk				*bus_clk;
126 	struct clk				*mod_clk;
127 	struct regmap				*regs;
128 	struct reset_control			*reset;
129 
130 	struct phy				*phy;
131 	struct phy_configure_opts_mipi_dphy	config;
132 
133 	const struct sun6i_dphy_variant		*variant;
134 	enum sun6i_dphy_direction		direction;
135 };
136 
137 static int sun6i_dphy_init(struct phy *phy)
138 {
139 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
140 
141 	reset_control_deassert(dphy->reset);
142 	clk_prepare_enable(dphy->mod_clk);
143 	clk_set_rate_exclusive(dphy->mod_clk, 150000000);
144 
145 	return 0;
146 }
147 
148 static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
149 {
150 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
151 	int ret;
152 
153 	ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
154 	if (ret)
155 		return ret;
156 
157 	memcpy(&dphy->config, opts, sizeof(dphy->config));
158 
159 	return 0;
160 }
161 
162 static void sun6i_a31_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy)
163 {
164 	u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
165 
166 	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
167 		     SUN6I_DPHY_ANA0_REG_PWS |
168 		     SUN6I_DPHY_ANA0_REG_DMPC |
169 		     SUN6I_DPHY_ANA0_REG_SLV(7) |
170 		     SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
171 		     SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
172 
173 	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
174 		     SUN6I_DPHY_ANA1_REG_CSMPS(1) |
175 		     SUN6I_DPHY_ANA1_REG_SVTT(7));
176 
177 	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
178 		     SUN6I_DPHY_ANA4_REG_CKDV(1) |
179 		     SUN6I_DPHY_ANA4_REG_TMSC(1) |
180 		     SUN6I_DPHY_ANA4_REG_TMSD(1) |
181 		     SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
182 		     SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
183 		     SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
184 		     SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
185 		     SUN6I_DPHY_ANA4_REG_DMPLVC |
186 		     SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
187 
188 	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
189 		     SUN6I_DPHY_ANA2_REG_ENIB);
190 	udelay(5);
191 
192 	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
193 		     SUN6I_DPHY_ANA3_EN_LDOR |
194 		     SUN6I_DPHY_ANA3_EN_LDOC |
195 		     SUN6I_DPHY_ANA3_EN_LDOD);
196 	udelay(1);
197 }
198 
199 static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
200 {
201 	u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
202 
203 	regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
204 		     SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
205 
206 	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
207 		     SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
208 		     SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
209 		     SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
210 
211 	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
212 		     SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
213 		     SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
214 		     SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
215 		     SUN6I_DPHY_TX_TIME1_CLK_POST(10));
216 
217 	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
218 		     SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
219 
220 	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
221 
222 	regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
223 		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
224 		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
225 
226 	dphy->variant->tx_power_on(dphy);
227 
228 	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
229 			   SUN6I_DPHY_ANA3_EN_VTTC |
230 			   SUN6I_DPHY_ANA3_EN_VTTD_MASK,
231 			   SUN6I_DPHY_ANA3_EN_VTTC |
232 			   SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
233 	udelay(1);
234 
235 	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
236 			   SUN6I_DPHY_ANA3_EN_DIV,
237 			   SUN6I_DPHY_ANA3_EN_DIV);
238 	udelay(1);
239 
240 	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
241 			   SUN6I_DPHY_ANA2_EN_CK_CPU,
242 			   SUN6I_DPHY_ANA2_EN_CK_CPU);
243 	udelay(1);
244 
245 	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
246 			   SUN6I_DPHY_ANA1_REG_VTTMODE,
247 			   SUN6I_DPHY_ANA1_REG_VTTMODE);
248 
249 	regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
250 			   SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
251 			   SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
252 
253 	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
254 		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
255 		     SUN6I_DPHY_GCTL_EN);
256 
257 	return 0;
258 }
259 
260 static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy)
261 {
262 	/* Physical clock rate is actually half of symbol rate with DDR. */
263 	unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
264 	unsigned long dphy_clk_rate;
265 	unsigned int rx_dly;
266 	unsigned int lprst_dly;
267 	u32 value;
268 
269 	dphy_clk_rate = clk_get_rate(dphy->mod_clk);
270 	if (!dphy_clk_rate)
271 		return -EINVAL;
272 
273 	/* Hardcoded timing parameters from the Allwinner BSP. */
274 	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG,
275 		     SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) |
276 		     SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) |
277 		     SUN6I_DPHY_RX_TIME0_LP_RX(255));
278 
279 	/*
280 	 * Formula from the Allwinner BSP, with hardcoded coefficients
281 	 * (probably internal divider/multiplier).
282 	 */
283 	rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8));
284 
285 	/*
286 	 * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP:
287 	 * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000
288 	 * but does not use it and hardcodes 255 instead.
289 	 */
290 	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG,
291 		     SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) |
292 		     SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255));
293 
294 	/* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */
295 	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG,
296 		     SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4));
297 
298 	/*
299 	 * Formula from the Allwinner BSP, with hardcoded coefficients
300 	 * (probably internal divider/multiplier).
301 	 */
302 	lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2));
303 
304 	regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG,
305 		     SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly));
306 
307 	/* Analog parameters are hardcoded in the Allwinner BSP. */
308 	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
309 		     SUN6I_DPHY_ANA0_REG_PWS |
310 		     SUN6I_DPHY_ANA0_REG_SLV(7) |
311 		     SUN6I_DPHY_ANA0_REG_SFB(2));
312 
313 	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
314 		     SUN6I_DPHY_ANA1_REG_SVTT(4));
315 
316 	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
317 		     SUN6I_DPHY_ANA4_REG_DMPLVC |
318 		     SUN6I_DPHY_ANA4_REG_DMPLVD(1));
319 
320 	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
321 		     SUN6I_DPHY_ANA2_REG_ENIB);
322 
323 	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
324 		     SUN6I_DPHY_ANA3_EN_LDOR |
325 		     SUN6I_DPHY_ANA3_EN_LDOC |
326 		     SUN6I_DPHY_ANA3_EN_LDOD);
327 
328 	/*
329 	 * Delay comes from the Allwinner BSP, likely for internal regulator
330 	 * ramp-up.
331 	 */
332 	udelay(3);
333 
334 	value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE;
335 
336 	/*
337 	 * Rx data lane force-enable bits are used as regular RX enable by the
338 	 * Allwinner BSP.
339 	 */
340 	if (dphy->config.lanes >= 1)
341 		value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE;
342 	if (dphy->config.lanes >= 2)
343 		value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE;
344 	if (dphy->config.lanes >= 3)
345 		value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE;
346 	if (dphy->config.lanes == 4)
347 		value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE;
348 
349 	regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value);
350 
351 	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
352 		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
353 		     SUN6I_DPHY_GCTL_EN);
354 
355 	return 0;
356 }
357 
358 static int sun6i_dphy_power_on(struct phy *phy)
359 {
360 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
361 
362 	switch (dphy->direction) {
363 	case SUN6I_DPHY_DIRECTION_TX:
364 		return sun6i_dphy_tx_power_on(dphy);
365 	case SUN6I_DPHY_DIRECTION_RX:
366 		return sun6i_dphy_rx_power_on(dphy);
367 	default:
368 		return -EINVAL;
369 	}
370 }
371 
372 static int sun6i_dphy_power_off(struct phy *phy)
373 {
374 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
375 
376 	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0);
377 
378 	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0);
379 	regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0);
380 	regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0);
381 	regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0);
382 	regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0);
383 
384 	return 0;
385 }
386 
387 static int sun6i_dphy_exit(struct phy *phy)
388 {
389 	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
390 
391 	clk_rate_exclusive_put(dphy->mod_clk);
392 	clk_disable_unprepare(dphy->mod_clk);
393 	reset_control_assert(dphy->reset);
394 
395 	return 0;
396 }
397 
398 
399 static const struct phy_ops sun6i_dphy_ops = {
400 	.configure	= sun6i_dphy_configure,
401 	.power_on	= sun6i_dphy_power_on,
402 	.power_off	= sun6i_dphy_power_off,
403 	.init		= sun6i_dphy_init,
404 	.exit		= sun6i_dphy_exit,
405 };
406 
407 static const struct regmap_config sun6i_dphy_regmap_config = {
408 	.reg_bits	= 32,
409 	.val_bits	= 32,
410 	.reg_stride	= 4,
411 	.max_register	= SUN6I_DPHY_DBG5_REG,
412 	.name		= "mipi-dphy",
413 };
414 
415 static int sun6i_dphy_probe(struct platform_device *pdev)
416 {
417 	struct phy_provider *phy_provider;
418 	struct sun6i_dphy *dphy;
419 	const char *direction;
420 	void __iomem *regs;
421 	int ret;
422 
423 	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
424 	if (!dphy)
425 		return -ENOMEM;
426 
427 	dphy->variant = device_get_match_data(&pdev->dev);
428 	if (!dphy->variant)
429 		return -EINVAL;
430 
431 	regs = devm_platform_ioremap_resource(pdev, 0);
432 	if (IS_ERR(regs)) {
433 		dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
434 		return PTR_ERR(regs);
435 	}
436 
437 	dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
438 					       regs, &sun6i_dphy_regmap_config);
439 	if (IS_ERR(dphy->regs)) {
440 		dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
441 		return PTR_ERR(dphy->regs);
442 	}
443 
444 	dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
445 	if (IS_ERR(dphy->reset)) {
446 		dev_err(&pdev->dev, "Couldn't get our reset line\n");
447 		return PTR_ERR(dphy->reset);
448 	}
449 
450 	dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
451 	if (IS_ERR(dphy->mod_clk)) {
452 		dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
453 		return PTR_ERR(dphy->mod_clk);
454 	}
455 
456 	dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
457 	if (IS_ERR(dphy->phy)) {
458 		dev_err(&pdev->dev, "failed to create PHY\n");
459 		return PTR_ERR(dphy->phy);
460 	}
461 
462 	dphy->direction = SUN6I_DPHY_DIRECTION_TX;
463 
464 	ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction",
465 				      &direction);
466 
467 	if (!ret && !strncmp(direction, "rx", 2)) {
468 		if (!dphy->variant->rx_supported) {
469 			dev_err(&pdev->dev, "RX not supported on this variant\n");
470 			return -EOPNOTSUPP;
471 		}
472 
473 		dphy->direction = SUN6I_DPHY_DIRECTION_RX;
474 	}
475 
476 	phy_set_drvdata(dphy->phy, dphy);
477 	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
478 
479 	return PTR_ERR_OR_ZERO(phy_provider);
480 }
481 
482 static const struct sun6i_dphy_variant sun6i_a31_mipi_dphy_variant = {
483 	.tx_power_on	= sun6i_a31_mipi_dphy_tx_power_on,
484 	.rx_supported	= true,
485 };
486 
487 static const struct of_device_id sun6i_dphy_of_table[] = {
488 	{
489 		.compatible	= "allwinner,sun6i-a31-mipi-dphy",
490 		.data		= &sun6i_a31_mipi_dphy_variant,
491 	},
492 	{ }
493 };
494 MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
495 
496 static struct platform_driver sun6i_dphy_platform_driver = {
497 	.probe		= sun6i_dphy_probe,
498 	.driver		= {
499 		.name		= "sun6i-mipi-dphy",
500 		.of_match_table	= sun6i_dphy_of_table,
501 	},
502 };
503 module_platform_driver(sun6i_dphy_platform_driver);
504 
505 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
506 MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
507 MODULE_LICENSE("GPL");
508