1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright: 2017-2018 Cadence Design Systems, Inc.
6 #include <linux/bitops.h>
9 #include <linux/module.h>
10 #include <linux/of_address.h>
11 #include <linux/of_device.h>
12 #include <linux/platform_device.h>
13 #include <linux/reset.h>
15 #include <linux/phy/phy.h>
16 #include <linux/phy/phy-mipi-dphy.h>
18 #define REG_WAKEUP_TIME_NS 800
19 #define DPHY_PLL_RATE_HZ 108000000
22 #define DPHY_PMA_CMN(reg) (reg)
23 #define DPHY_PMA_LCLK(reg) (0x100 + (reg))
24 #define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg))
25 #define DPHY_PMA_RCLK(reg) (0x600 + (reg))
26 #define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg))
27 #define DPHY_PCS(reg) (0xb00 + (reg))
29 #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
30 #define DPHY_CMN_SSM_EN BIT(0)
31 #define DPHY_CMN_TX_MODE_EN BIT(9)
33 #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40)
34 #define DPHY_CMN_PWM_DIV(x) ((x) << 20)
35 #define DPHY_CMN_PWM_LOW(x) ((x) << 10)
36 #define DPHY_CMN_PWM_HIGH(x) (x)
38 #define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c)
39 #define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22))
40 #define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21))
42 #define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50)
43 #define DPHY_CMN_IPDIV_FROM_REG BIT(0)
44 #define DPHY_CMN_IPDIV(x) ((x) << 1)
45 #define DPHY_CMN_OPDIV_FROM_REG BIT(6)
46 #define DPHY_CMN_OPDIV(x) ((x) << 7)
48 #define DPHY_PSM_CFG DPHY_PCS(0x4)
49 #define DPHY_PSM_CFG_FROM_REG BIT(0)
50 #define DPHY_PSM_CLK_DIV(x) ((x) << 1)
52 #define DSI_HBP_FRAME_OVERHEAD 12
53 #define DSI_HSA_FRAME_OVERHEAD 14
54 #define DSI_HFP_FRAME_OVERHEAD 6
55 #define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
56 #define DSI_BLANKING_FRAME_OVERHEAD 6
57 #define DSI_NULL_FRAME_OVERHEAD 6
58 #define DSI_EOT_PKT_SIZE 4
60 struct cdns_dphy_cfg
{
67 enum cdns_dphy_clk_lane_cfg
{
68 DPHY_CLK_CFG_LEFT_DRIVES_ALL
= 0,
69 DPHY_CLK_CFG_LEFT_DRIVES_RIGHT
= 1,
70 DPHY_CLK_CFG_LEFT_DRIVES_LEFT
= 2,
71 DPHY_CLK_CFG_RIGHT_DRIVES_ALL
= 3,
75 struct cdns_dphy_ops
{
76 int (*probe
)(struct cdns_dphy
*dphy
);
77 void (*remove
)(struct cdns_dphy
*dphy
);
78 void (*set_psm_div
)(struct cdns_dphy
*dphy
, u8 div
);
79 void (*set_clk_lane_cfg
)(struct cdns_dphy
*dphy
,
80 enum cdns_dphy_clk_lane_cfg cfg
);
81 void (*set_pll_cfg
)(struct cdns_dphy
*dphy
,
82 const struct cdns_dphy_cfg
*cfg
);
83 unsigned long (*get_wakeup_time_ns
)(struct cdns_dphy
*dphy
);
87 struct cdns_dphy_cfg cfg
;
90 struct clk
*pll_ref_clk
;
91 const struct cdns_dphy_ops
*ops
;
95 static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy
*dphy
,
96 struct cdns_dphy_cfg
*cfg
,
97 struct phy_configure_opts_mipi_dphy
*opts
,
98 unsigned int *dsi_hfp_ext
)
100 unsigned long pll_ref_hz
= clk_get_rate(dphy
->pll_ref_clk
);
103 memset(cfg
, 0, sizeof(*cfg
));
105 if (pll_ref_hz
< 9600000 || pll_ref_hz
>= 150000000)
107 else if (pll_ref_hz
< 19200000)
109 else if (pll_ref_hz
< 38400000)
111 else if (pll_ref_hz
< 76800000)
116 dlane_bps
= opts
->hs_clk_rate
;
118 if (dlane_bps
> 2500000000UL || dlane_bps
< 160000000UL)
120 else if (dlane_bps
>= 1250000000)
122 else if (dlane_bps
>= 630000000)
124 else if (dlane_bps
>= 320000000)
126 else if (dlane_bps
>= 160000000)
129 cfg
->pll_fbdiv
= DIV_ROUND_UP_ULL(dlane_bps
* 2 * cfg
->pll_opdiv
*
136 static int cdns_dphy_setup_psm(struct cdns_dphy
*dphy
)
138 unsigned long psm_clk_hz
= clk_get_rate(dphy
->psm_clk
);
139 unsigned long psm_div
;
141 if (!psm_clk_hz
|| psm_clk_hz
> 100000000)
144 psm_div
= DIV_ROUND_CLOSEST(psm_clk_hz
, 1000000);
145 if (dphy
->ops
->set_psm_div
)
146 dphy
->ops
->set_psm_div(dphy
, psm_div
);
151 static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy
*dphy
,
152 enum cdns_dphy_clk_lane_cfg cfg
)
154 if (dphy
->ops
->set_clk_lane_cfg
)
155 dphy
->ops
->set_clk_lane_cfg(dphy
, cfg
);
158 static void cdns_dphy_set_pll_cfg(struct cdns_dphy
*dphy
,
159 const struct cdns_dphy_cfg
*cfg
)
161 if (dphy
->ops
->set_pll_cfg
)
162 dphy
->ops
->set_pll_cfg(dphy
, cfg
);
165 static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy
*dphy
)
167 return dphy
->ops
->get_wakeup_time_ns(dphy
);
170 static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy
*dphy
)
172 /* Default wakeup time is 800 ns (in a simulated environment). */
176 static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy
*dphy
,
177 const struct cdns_dphy_cfg
*cfg
)
179 u32 fbdiv_low
, fbdiv_high
;
181 fbdiv_low
= (cfg
->pll_fbdiv
/ 4) - 2;
182 fbdiv_high
= cfg
->pll_fbdiv
- fbdiv_low
- 2;
184 writel(DPHY_CMN_IPDIV_FROM_REG
| DPHY_CMN_OPDIV_FROM_REG
|
185 DPHY_CMN_IPDIV(cfg
->pll_ipdiv
) |
186 DPHY_CMN_OPDIV(cfg
->pll_opdiv
),
187 dphy
->regs
+ DPHY_CMN_OPIPDIV
);
188 writel(DPHY_CMN_FBDIV_FROM_REG
|
189 DPHY_CMN_FBDIV_VAL(fbdiv_low
, fbdiv_high
),
190 dphy
->regs
+ DPHY_CMN_FBDIV
);
191 writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
192 DPHY_CMN_PWM_DIV(0x8),
193 dphy
->regs
+ DPHY_CMN_PWM
);
196 static void cdns_dphy_ref_set_psm_div(struct cdns_dphy
*dphy
, u8 div
)
198 writel(DPHY_PSM_CFG_FROM_REG
| DPHY_PSM_CLK_DIV(div
),
199 dphy
->regs
+ DPHY_PSM_CFG
);
203 * This is the reference implementation of DPHY hooks. Specific integration of
204 * this IP may have to re-implement some of them depending on how they decided
205 * to wire things in the SoC.
207 static const struct cdns_dphy_ops ref_dphy_ops
= {
208 .get_wakeup_time_ns
= cdns_dphy_ref_get_wakeup_time_ns
,
209 .set_pll_cfg
= cdns_dphy_ref_set_pll_cfg
,
210 .set_psm_div
= cdns_dphy_ref_set_psm_div
,
213 static int cdns_dphy_config_from_opts(struct phy
*phy
,
214 struct phy_configure_opts_mipi_dphy
*opts
,
215 struct cdns_dphy_cfg
*cfg
)
217 struct cdns_dphy
*dphy
= phy_get_drvdata(phy
);
218 unsigned int dsi_hfp_ext
= 0;
221 ret
= phy_mipi_dphy_config_validate(opts
);
225 ret
= cdns_dsi_get_dphy_pll_cfg(dphy
, cfg
,
230 opts
->wakeup
= cdns_dphy_get_wakeup_time_ns(dphy
) / 1000;
235 static int cdns_dphy_validate(struct phy
*phy
, enum phy_mode mode
, int submode
,
236 union phy_configure_opts
*opts
)
238 struct cdns_dphy_cfg cfg
= { 0 };
240 if (mode
!= PHY_MODE_MIPI_DPHY
)
243 return cdns_dphy_config_from_opts(phy
, &opts
->mipi_dphy
, &cfg
);
246 static int cdns_dphy_configure(struct phy
*phy
, union phy_configure_opts
*opts
)
248 struct cdns_dphy
*dphy
= phy_get_drvdata(phy
);
249 struct cdns_dphy_cfg cfg
= { 0 };
252 ret
= cdns_dphy_config_from_opts(phy
, &opts
->mipi_dphy
, &cfg
);
257 * Configure the internal PSM clk divider so that the DPHY has a
258 * 1MHz clk (or something close).
260 ret
= cdns_dphy_setup_psm(dphy
);
265 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
266 * and 8 data lanes, each clk lane can be attache different set of
267 * data lanes. The 2 groups are named 'left' and 'right', so here we
268 * just say that we want the 'left' clk lane to drive the 'left' data
271 cdns_dphy_set_clk_lane_cfg(dphy
, DPHY_CLK_CFG_LEFT_DRIVES_LEFT
);
274 * Configure the DPHY PLL that will be used to generate the TX byte
277 cdns_dphy_set_pll_cfg(dphy
, &cfg
);
282 static int cdns_dphy_power_on(struct phy
*phy
)
284 struct cdns_dphy
*dphy
= phy_get_drvdata(phy
);
286 clk_prepare_enable(dphy
->psm_clk
);
287 clk_prepare_enable(dphy
->pll_ref_clk
);
289 /* Start TX state machine. */
290 writel(DPHY_CMN_SSM_EN
| DPHY_CMN_TX_MODE_EN
,
291 dphy
->regs
+ DPHY_CMN_SSM
);
296 static int cdns_dphy_power_off(struct phy
*phy
)
298 struct cdns_dphy
*dphy
= phy_get_drvdata(phy
);
300 clk_disable_unprepare(dphy
->pll_ref_clk
);
301 clk_disable_unprepare(dphy
->psm_clk
);
306 static const struct phy_ops cdns_dphy_ops
= {
307 .configure
= cdns_dphy_configure
,
308 .validate
= cdns_dphy_validate
,
309 .power_on
= cdns_dphy_power_on
,
310 .power_off
= cdns_dphy_power_off
,
313 static int cdns_dphy_probe(struct platform_device
*pdev
)
315 struct phy_provider
*phy_provider
;
316 struct cdns_dphy
*dphy
;
319 dphy
= devm_kzalloc(&pdev
->dev
, sizeof(*dphy
), GFP_KERNEL
);
322 dev_set_drvdata(&pdev
->dev
, dphy
);
324 dphy
->ops
= of_device_get_match_data(&pdev
->dev
);
328 dphy
->regs
= devm_platform_ioremap_resource(pdev
, 0);
329 if (IS_ERR(dphy
->regs
))
330 return PTR_ERR(dphy
->regs
);
332 dphy
->psm_clk
= devm_clk_get(&pdev
->dev
, "psm");
333 if (IS_ERR(dphy
->psm_clk
))
334 return PTR_ERR(dphy
->psm_clk
);
336 dphy
->pll_ref_clk
= devm_clk_get(&pdev
->dev
, "pll_ref");
337 if (IS_ERR(dphy
->pll_ref_clk
))
338 return PTR_ERR(dphy
->pll_ref_clk
);
340 if (dphy
->ops
->probe
) {
341 ret
= dphy
->ops
->probe(dphy
);
346 dphy
->phy
= devm_phy_create(&pdev
->dev
, NULL
, &cdns_dphy_ops
);
347 if (IS_ERR(dphy
->phy
)) {
348 dev_err(&pdev
->dev
, "failed to create PHY\n");
349 if (dphy
->ops
->remove
)
350 dphy
->ops
->remove(dphy
);
351 return PTR_ERR(dphy
->phy
);
354 phy_set_drvdata(dphy
->phy
, dphy
);
355 phy_provider
= devm_of_phy_provider_register(&pdev
->dev
,
356 of_phy_simple_xlate
);
358 return PTR_ERR_OR_ZERO(phy_provider
);
361 static int cdns_dphy_remove(struct platform_device
*pdev
)
363 struct cdns_dphy
*dphy
= dev_get_drvdata(&pdev
->dev
);
365 if (dphy
->ops
->remove
)
366 dphy
->ops
->remove(dphy
);
371 static const struct of_device_id cdns_dphy_of_match
[] = {
372 { .compatible
= "cdns,dphy", .data
= &ref_dphy_ops
},
375 MODULE_DEVICE_TABLE(of
, cdns_dphy_of_match
);
377 static struct platform_driver cdns_dphy_platform_driver
= {
378 .probe
= cdns_dphy_probe
,
379 .remove
= cdns_dphy_remove
,
381 .name
= "cdns-mipi-dphy",
382 .of_match_table
= cdns_dphy_of_match
,
385 module_platform_driver(cdns_dphy_platform_driver
);
387 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
388 MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
389 MODULE_LICENSE("GPL");