1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2016 Allwinnertech Co., Ltd.
4 * Copyright (C) 2017-2018 Bootlin
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
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>
17 #include <linux/phy/phy.h>
18 #include <linux/phy/phy-mipi-dphy.h>
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)
24 #define SUN6I_DPHY_TX_CTL_REG 0x04
25 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28)
27 #define SUN6I_DPHY_TX_TIME0_REG 0x10
28 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24)
29 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16)
30 #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff)
32 #define SUN6I_DPHY_TX_TIME1_REG 0x14
33 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24)
34 #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16)
35 #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8)
36 #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff)
38 #define SUN6I_DPHY_TX_TIME2_REG 0x18
39 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff)
41 #define SUN6I_DPHY_TX_TIME3_REG 0x1c
43 #define SUN6I_DPHY_TX_TIME4_REG 0x20
44 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8)
45 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff)
47 #define SUN6I_DPHY_ANA0_REG 0x4c
48 #define SUN6I_DPHY_ANA0_REG_PWS BIT(31)
49 #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28)
50 #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24)
51 #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12)
52 #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8)
54 #define SUN6I_DPHY_ANA1_REG 0x50
55 #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31)
56 #define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28)
57 #define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24)
59 #define SUN6I_DPHY_ANA2_REG 0x54
60 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24)
61 #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24)
62 #define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4)
63 #define SUN6I_DPHY_ANA2_REG_ENIB BIT(1)
65 #define SUN6I_DPHY_ANA3_REG 0x58
66 #define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28)
67 #define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28)
68 #define SUN6I_DPHY_ANA3_EN_VTTC BIT(27)
69 #define SUN6I_DPHY_ANA3_EN_DIV BIT(26)
70 #define SUN6I_DPHY_ANA3_EN_LDOC BIT(25)
71 #define SUN6I_DPHY_ANA3_EN_LDOD BIT(24)
72 #define SUN6I_DPHY_ANA3_EN_LDOR BIT(18)
74 #define SUN6I_DPHY_ANA4_REG 0x5c
75 #define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24)
76 #define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20)
77 #define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12)
78 #define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10)
79 #define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8)
80 #define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6)
81 #define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4)
82 #define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2)
83 #define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3)
85 #define SUN6I_DPHY_DBG5_REG 0xf4
91 struct reset_control
*reset
;
94 struct phy_configure_opts_mipi_dphy config
;
97 static int sun6i_dphy_init(struct phy
*phy
)
99 struct sun6i_dphy
*dphy
= phy_get_drvdata(phy
);
101 reset_control_deassert(dphy
->reset
);
102 clk_prepare_enable(dphy
->mod_clk
);
103 clk_set_rate_exclusive(dphy
->mod_clk
, 150000000);
108 static int sun6i_dphy_configure(struct phy
*phy
, union phy_configure_opts
*opts
)
110 struct sun6i_dphy
*dphy
= phy_get_drvdata(phy
);
113 ret
= phy_mipi_dphy_config_validate(&opts
->mipi_dphy
);
117 memcpy(&dphy
->config
, opts
, sizeof(dphy
->config
));
122 static int sun6i_dphy_power_on(struct phy
*phy
)
124 struct sun6i_dphy
*dphy
= phy_get_drvdata(phy
);
125 u8 lanes_mask
= GENMASK(dphy
->config
.lanes
- 1, 0);
127 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_CTL_REG
,
128 SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT
);
130 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME0_REG
,
131 SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
132 SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
133 SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
135 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME1_REG
,
136 SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
137 SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
138 SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
139 SUN6I_DPHY_TX_TIME1_CLK_POST(10));
141 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME2_REG
,
142 SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
144 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME3_REG
, 0);
146 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME4_REG
,
147 SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
148 SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
150 regmap_write(dphy
->regs
, SUN6I_DPHY_GCTL_REG
,
151 SUN6I_DPHY_GCTL_LANE_NUM(dphy
->config
.lanes
) |
154 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA0_REG
,
155 SUN6I_DPHY_ANA0_REG_PWS
|
156 SUN6I_DPHY_ANA0_REG_DMPC
|
157 SUN6I_DPHY_ANA0_REG_SLV(7) |
158 SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask
) |
159 SUN6I_DPHY_ANA0_REG_DEN(lanes_mask
));
161 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
162 SUN6I_DPHY_ANA1_REG_CSMPS(1) |
163 SUN6I_DPHY_ANA1_REG_SVTT(7));
165 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA4_REG
,
166 SUN6I_DPHY_ANA4_REG_CKDV(1) |
167 SUN6I_DPHY_ANA4_REG_TMSC(1) |
168 SUN6I_DPHY_ANA4_REG_TMSD(1) |
169 SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
170 SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
171 SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
172 SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
173 SUN6I_DPHY_ANA4_REG_DMPLVC
|
174 SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask
));
176 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
177 SUN6I_DPHY_ANA2_REG_ENIB
);
180 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
181 SUN6I_DPHY_ANA3_EN_LDOR
|
182 SUN6I_DPHY_ANA3_EN_LDOC
|
183 SUN6I_DPHY_ANA3_EN_LDOD
);
186 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
187 SUN6I_DPHY_ANA3_EN_VTTC
|
188 SUN6I_DPHY_ANA3_EN_VTTD_MASK
,
189 SUN6I_DPHY_ANA3_EN_VTTC
|
190 SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask
));
193 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
194 SUN6I_DPHY_ANA3_EN_DIV
,
195 SUN6I_DPHY_ANA3_EN_DIV
);
198 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
199 SUN6I_DPHY_ANA2_EN_CK_CPU
,
200 SUN6I_DPHY_ANA2_EN_CK_CPU
);
203 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
204 SUN6I_DPHY_ANA1_REG_VTTMODE
,
205 SUN6I_DPHY_ANA1_REG_VTTMODE
);
207 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
208 SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK
,
209 SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask
));
214 static int sun6i_dphy_power_off(struct phy
*phy
)
216 struct sun6i_dphy
*dphy
= phy_get_drvdata(phy
);
218 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
219 SUN6I_DPHY_ANA1_REG_VTTMODE
, 0);
224 static int sun6i_dphy_exit(struct phy
*phy
)
226 struct sun6i_dphy
*dphy
= phy_get_drvdata(phy
);
228 clk_rate_exclusive_put(dphy
->mod_clk
);
229 clk_disable_unprepare(dphy
->mod_clk
);
230 reset_control_assert(dphy
->reset
);
236 static const struct phy_ops sun6i_dphy_ops
= {
237 .configure
= sun6i_dphy_configure
,
238 .power_on
= sun6i_dphy_power_on
,
239 .power_off
= sun6i_dphy_power_off
,
240 .init
= sun6i_dphy_init
,
241 .exit
= sun6i_dphy_exit
,
244 static const struct regmap_config sun6i_dphy_regmap_config
= {
248 .max_register
= SUN6I_DPHY_DBG5_REG
,
252 static int sun6i_dphy_probe(struct platform_device
*pdev
)
254 struct phy_provider
*phy_provider
;
255 struct sun6i_dphy
*dphy
;
258 dphy
= devm_kzalloc(&pdev
->dev
, sizeof(*dphy
), GFP_KERNEL
);
262 regs
= devm_platform_ioremap_resource(pdev
, 0);
264 dev_err(&pdev
->dev
, "Couldn't map the DPHY encoder registers\n");
265 return PTR_ERR(regs
);
268 dphy
->regs
= devm_regmap_init_mmio_clk(&pdev
->dev
, "bus",
269 regs
, &sun6i_dphy_regmap_config
);
270 if (IS_ERR(dphy
->regs
)) {
271 dev_err(&pdev
->dev
, "Couldn't create the DPHY encoder regmap\n");
272 return PTR_ERR(dphy
->regs
);
275 dphy
->reset
= devm_reset_control_get_shared(&pdev
->dev
, NULL
);
276 if (IS_ERR(dphy
->reset
)) {
277 dev_err(&pdev
->dev
, "Couldn't get our reset line\n");
278 return PTR_ERR(dphy
->reset
);
281 dphy
->mod_clk
= devm_clk_get(&pdev
->dev
, "mod");
282 if (IS_ERR(dphy
->mod_clk
)) {
283 dev_err(&pdev
->dev
, "Couldn't get the DPHY mod clock\n");
284 return PTR_ERR(dphy
->mod_clk
);
287 dphy
->phy
= devm_phy_create(&pdev
->dev
, NULL
, &sun6i_dphy_ops
);
288 if (IS_ERR(dphy
->phy
)) {
289 dev_err(&pdev
->dev
, "failed to create PHY\n");
290 return PTR_ERR(dphy
->phy
);
293 phy_set_drvdata(dphy
->phy
, dphy
);
294 phy_provider
= devm_of_phy_provider_register(&pdev
->dev
, of_phy_simple_xlate
);
296 return PTR_ERR_OR_ZERO(phy_provider
);
299 static const struct of_device_id sun6i_dphy_of_table
[] = {
300 { .compatible
= "allwinner,sun6i-a31-mipi-dphy" },
303 MODULE_DEVICE_TABLE(of
, sun6i_dphy_of_table
);
305 static struct platform_driver sun6i_dphy_platform_driver
= {
306 .probe
= sun6i_dphy_probe
,
308 .name
= "sun6i-mipi-dphy",
309 .of_match_table
= sun6i_dphy_of_table
,
312 module_platform_driver(sun6i_dphy_platform_driver
);
314 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
315 MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
316 MODULE_LICENSE("GPL");