GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / phy / cadence / cdns-dphy.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright: 2017-2018 Cadence Design Systems, Inc.
4  */
5
6 #include <linux/bitfield.h>
7 #include <linux/bitops.h>
8 #include <linux/clk.h>
9 #include <linux/io.h>
10 #include <linux/iopoll.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 #include <linux/reset.h>
15
16 #include <linux/phy/phy.h>
17 #include <linux/phy/phy-mipi-dphy.h>
18
19 #define REG_WAKEUP_TIME_NS              800
20 #define DPHY_PLL_RATE_HZ                108000000
21 #define POLL_TIMEOUT_US                 1000
22
23 /* DPHY registers */
24 #define DPHY_PMA_CMN(reg)               (reg)
25 #define DPHY_PMA_LCLK(reg)              (0x100 + (reg))
26 #define DPHY_PMA_LDATA(lane, reg)       (0x200 + ((lane) * 0x100) + (reg))
27 #define DPHY_PMA_RCLK(reg)              (0x600 + (reg))
28 #define DPHY_PMA_RDATA(lane, reg)       (0x700 + ((lane) * 0x100) + (reg))
29 #define DPHY_PCS(reg)                   (0xb00 + (reg))
30
31 #define DPHY_CMN_SSM                    DPHY_PMA_CMN(0x20)
32 #define DPHY_CMN_SSM_EN                 BIT(0)
33 #define DPHY_CMN_TX_MODE_EN             BIT(9)
34
35 #define DPHY_CMN_PWM                    DPHY_PMA_CMN(0x40)
36 #define DPHY_CMN_PWM_DIV(x)             ((x) << 20)
37 #define DPHY_CMN_PWM_LOW(x)             ((x) << 10)
38 #define DPHY_CMN_PWM_HIGH(x)            (x)
39
40 #define DPHY_CMN_FBDIV                  DPHY_PMA_CMN(0x4c)
41 #define DPHY_CMN_FBDIV_VAL(low, high)   (((high) << 11) | ((low) << 22))
42 #define DPHY_CMN_FBDIV_FROM_REG         (BIT(10) | BIT(21))
43
44 #define DPHY_CMN_OPIPDIV                DPHY_PMA_CMN(0x50)
45 #define DPHY_CMN_IPDIV_FROM_REG         BIT(0)
46 #define DPHY_CMN_IPDIV(x)               ((x) << 1)
47 #define DPHY_CMN_OPDIV_FROM_REG         BIT(6)
48 #define DPHY_CMN_OPDIV(x)               ((x) << 7)
49
50 #define DPHY_BAND_CFG                   DPHY_PCS(0x0)
51 #define DPHY_BAND_CFG_LEFT_BAND         GENMASK(4, 0)
52 #define DPHY_BAND_CFG_RIGHT_BAND        GENMASK(9, 5)
53
54 #define DPHY_PSM_CFG                    DPHY_PCS(0x4)
55 #define DPHY_PSM_CFG_FROM_REG           BIT(0)
56 #define DPHY_PSM_CLK_DIV(x)             ((x) << 1)
57
58 #define DSI_HBP_FRAME_OVERHEAD          12
59 #define DSI_HSA_FRAME_OVERHEAD          14
60 #define DSI_HFP_FRAME_OVERHEAD          6
61 #define DSI_HSS_VSS_VSE_FRAME_OVERHEAD  4
62 #define DSI_BLANKING_FRAME_OVERHEAD     6
63 #define DSI_NULL_FRAME_OVERHEAD         6
64 #define DSI_EOT_PKT_SIZE                4
65
66 #define DPHY_TX_J721E_WIZ_PLL_CTRL      0xF04
67 #define DPHY_TX_J721E_WIZ_STATUS        0xF08
68 #define DPHY_TX_J721E_WIZ_RST_CTRL      0xF0C
69 #define DPHY_TX_J721E_WIZ_PSM_FREQ      0xF10
70
71 #define DPHY_TX_J721E_WIZ_IPDIV         GENMASK(4, 0)
72 #define DPHY_TX_J721E_WIZ_OPDIV         GENMASK(13, 8)
73 #define DPHY_TX_J721E_WIZ_FBDIV         GENMASK(25, 16)
74 #define DPHY_TX_J721E_WIZ_LANE_RSTB     BIT(31)
75 #define DPHY_TX_WIZ_PLL_LOCK            BIT(31)
76 #define DPHY_TX_WIZ_O_CMN_READY         BIT(31)
77
78 struct cdns_dphy_cfg {
79         u8 pll_ipdiv;
80         u8 pll_opdiv;
81         u16 pll_fbdiv;
82         unsigned int nlanes;
83 };
84
85 enum cdns_dphy_clk_lane_cfg {
86         DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
87         DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
88         DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
89         DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
90 };
91
92 struct cdns_dphy;
93 struct cdns_dphy_ops {
94         int (*probe)(struct cdns_dphy *dphy);
95         void (*remove)(struct cdns_dphy *dphy);
96         void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
97         void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
98                                  enum cdns_dphy_clk_lane_cfg cfg);
99         void (*set_pll_cfg)(struct cdns_dphy *dphy,
100                             const struct cdns_dphy_cfg *cfg);
101         unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
102 };
103
104 struct cdns_dphy {
105         struct cdns_dphy_cfg cfg;
106         void __iomem *regs;
107         struct clk *psm_clk;
108         struct clk *pll_ref_clk;
109         const struct cdns_dphy_ops *ops;
110         struct phy *phy;
111 };
112
113 /* Order of bands is important since the index is the band number. */
114 static const unsigned int tx_bands[] = {
115         80, 100, 120, 160, 200, 240, 320, 390, 450, 510, 560, 640, 690, 770,
116         870, 950, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500
117 };
118
119 static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
120                                      struct cdns_dphy_cfg *cfg,
121                                      struct phy_configure_opts_mipi_dphy *opts,
122                                      unsigned int *dsi_hfp_ext)
123 {
124         unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
125         u64 dlane_bps;
126
127         memset(cfg, 0, sizeof(*cfg));
128
129         if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
130                 return -EINVAL;
131         else if (pll_ref_hz < 19200000)
132                 cfg->pll_ipdiv = 1;
133         else if (pll_ref_hz < 38400000)
134                 cfg->pll_ipdiv = 2;
135         else if (pll_ref_hz < 76800000)
136                 cfg->pll_ipdiv = 4;
137         else
138                 cfg->pll_ipdiv = 8;
139
140         dlane_bps = opts->hs_clk_rate;
141
142         if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
143                 return -EINVAL;
144         else if (dlane_bps >= 1250000000)
145                 cfg->pll_opdiv = 1;
146         else if (dlane_bps >= 630000000)
147                 cfg->pll_opdiv = 2;
148         else if (dlane_bps >= 320000000)
149                 cfg->pll_opdiv = 4;
150         else if (dlane_bps >= 160000000)
151                 cfg->pll_opdiv = 8;
152
153         cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
154                                           cfg->pll_ipdiv,
155                                           pll_ref_hz);
156
157         return 0;
158 }
159
160 static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
161 {
162         unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
163         unsigned long psm_div;
164
165         if (!psm_clk_hz || psm_clk_hz > 100000000)
166                 return -EINVAL;
167
168         psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
169         if (dphy->ops->set_psm_div)
170                 dphy->ops->set_psm_div(dphy, psm_div);
171
172         return 0;
173 }
174
175 static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
176                                        enum cdns_dphy_clk_lane_cfg cfg)
177 {
178         if (dphy->ops->set_clk_lane_cfg)
179                 dphy->ops->set_clk_lane_cfg(dphy, cfg);
180 }
181
182 static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
183                                   const struct cdns_dphy_cfg *cfg)
184 {
185         if (dphy->ops->set_pll_cfg)
186                 dphy->ops->set_pll_cfg(dphy, cfg);
187 }
188
189 static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
190 {
191         return dphy->ops->get_wakeup_time_ns(dphy);
192 }
193
194 static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
195 {
196         /* Default wakeup time is 800 ns (in a simulated environment). */
197         return 800;
198 }
199
200 static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
201                                       const struct cdns_dphy_cfg *cfg)
202 {
203         u32 fbdiv_low, fbdiv_high;
204
205         fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
206         fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
207
208         writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
209                DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
210                DPHY_CMN_OPDIV(cfg->pll_opdiv),
211                dphy->regs + DPHY_CMN_OPIPDIV);
212         writel(DPHY_CMN_FBDIV_FROM_REG |
213                DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
214                dphy->regs + DPHY_CMN_FBDIV);
215         writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
216                DPHY_CMN_PWM_DIV(0x8),
217                dphy->regs + DPHY_CMN_PWM);
218 }
219
220 static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
221 {
222         writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
223                dphy->regs + DPHY_PSM_CFG);
224 }
225
226 static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy)
227 {
228         /* Minimum wakeup time as per MIPI D-PHY spec v1.2 */
229         return 1000000;
230 }
231
232 static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy,
233                                         const struct cdns_dphy_cfg *cfg)
234 {
235         u32 status;
236
237         /*
238          * set the PWM and PLL Byteclk divider settings to recommended values
239          * which is same as that of in ref ops
240          */
241         writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
242                DPHY_CMN_PWM_DIV(0x8),
243                dphy->regs + DPHY_CMN_PWM);
244
245         writel((FIELD_PREP(DPHY_TX_J721E_WIZ_IPDIV, cfg->pll_ipdiv) |
246                 FIELD_PREP(DPHY_TX_J721E_WIZ_OPDIV, cfg->pll_opdiv) |
247                 FIELD_PREP(DPHY_TX_J721E_WIZ_FBDIV, cfg->pll_fbdiv)),
248                 dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL);
249
250         writel(DPHY_TX_J721E_WIZ_LANE_RSTB,
251                dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL);
252
253         readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status,
254                            (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US);
255
256         readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status,
257                            (status & DPHY_TX_WIZ_O_CMN_READY), 0,
258                            POLL_TIMEOUT_US);
259 }
260
261 static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div)
262 {
263         writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ);
264 }
265
266 /*
267  * This is the reference implementation of DPHY hooks. Specific integration of
268  * this IP may have to re-implement some of them depending on how they decided
269  * to wire things in the SoC.
270  */
271 static const struct cdns_dphy_ops ref_dphy_ops = {
272         .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
273         .set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
274         .set_psm_div = cdns_dphy_ref_set_psm_div,
275 };
276
277 static const struct cdns_dphy_ops j721e_dphy_ops = {
278         .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns,
279         .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg,
280         .set_psm_div = cdns_dphy_j721e_set_psm_div,
281 };
282
283 static int cdns_dphy_config_from_opts(struct phy *phy,
284                                       struct phy_configure_opts_mipi_dphy *opts,
285                                       struct cdns_dphy_cfg *cfg)
286 {
287         struct cdns_dphy *dphy = phy_get_drvdata(phy);
288         unsigned int dsi_hfp_ext = 0;
289         int ret;
290
291         ret = phy_mipi_dphy_config_validate(opts);
292         if (ret)
293                 return ret;
294
295         ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
296                                         opts, &dsi_hfp_ext);
297         if (ret)
298                 return ret;
299
300         opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
301
302         return 0;
303 }
304
305 static int cdns_dphy_tx_get_band_ctrl(unsigned long hs_clk_rate)
306 {
307         unsigned int rate;
308         int i;
309
310         rate = hs_clk_rate / 1000000UL;
311
312         if (rate < tx_bands[0])
313                 return -EOPNOTSUPP;
314
315         for (i = 0; i < ARRAY_SIZE(tx_bands) - 1; i++) {
316                 if (rate >= tx_bands[i] && rate < tx_bands[i + 1])
317                         return i;
318         }
319
320         return -EOPNOTSUPP;
321 }
322
323 static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
324                               union phy_configure_opts *opts)
325 {
326         struct cdns_dphy_cfg cfg = { 0 };
327
328         if (mode != PHY_MODE_MIPI_DPHY)
329                 return -EINVAL;
330
331         return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
332 }
333
334 static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
335 {
336         struct cdns_dphy *dphy = phy_get_drvdata(phy);
337         struct cdns_dphy_cfg cfg = { 0 };
338         int ret, band_ctrl;
339         unsigned int reg;
340
341         ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
342         if (ret)
343                 return ret;
344
345         /*
346          * Configure the internal PSM clk divider so that the DPHY has a
347          * 1MHz clk (or something close).
348          */
349         ret = cdns_dphy_setup_psm(dphy);
350         if (ret)
351                 return ret;
352
353         /*
354          * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
355          * and 8 data lanes, each clk lane can be attache different set of
356          * data lanes. The 2 groups are named 'left' and 'right', so here we
357          * just say that we want the 'left' clk lane to drive the 'left' data
358          * lanes.
359          */
360         cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
361
362         /*
363          * Configure the DPHY PLL that will be used to generate the TX byte
364          * clk.
365          */
366         cdns_dphy_set_pll_cfg(dphy, &cfg);
367
368         band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate);
369         if (band_ctrl < 0)
370                 return band_ctrl;
371
372         reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) |
373               FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl);
374         writel(reg, dphy->regs + DPHY_BAND_CFG);
375
376         return 0;
377 }
378
379 static int cdns_dphy_power_on(struct phy *phy)
380 {
381         struct cdns_dphy *dphy = phy_get_drvdata(phy);
382
383         clk_prepare_enable(dphy->psm_clk);
384         clk_prepare_enable(dphy->pll_ref_clk);
385
386         /* Start TX state machine. */
387         writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
388                dphy->regs + DPHY_CMN_SSM);
389
390         return 0;
391 }
392
393 static int cdns_dphy_power_off(struct phy *phy)
394 {
395         struct cdns_dphy *dphy = phy_get_drvdata(phy);
396
397         clk_disable_unprepare(dphy->pll_ref_clk);
398         clk_disable_unprepare(dphy->psm_clk);
399
400         return 0;
401 }
402
403 static const struct phy_ops cdns_dphy_ops = {
404         .configure      = cdns_dphy_configure,
405         .validate       = cdns_dphy_validate,
406         .power_on       = cdns_dphy_power_on,
407         .power_off      = cdns_dphy_power_off,
408 };
409
410 static int cdns_dphy_probe(struct platform_device *pdev)
411 {
412         struct phy_provider *phy_provider;
413         struct cdns_dphy *dphy;
414         int ret;
415
416         dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
417         if (!dphy)
418                 return -ENOMEM;
419         dev_set_drvdata(&pdev->dev, dphy);
420
421         dphy->ops = of_device_get_match_data(&pdev->dev);
422         if (!dphy->ops)
423                 return -EINVAL;
424
425         dphy->regs = devm_platform_ioremap_resource(pdev, 0);
426         if (IS_ERR(dphy->regs))
427                 return PTR_ERR(dphy->regs);
428
429         dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
430         if (IS_ERR(dphy->psm_clk))
431                 return PTR_ERR(dphy->psm_clk);
432
433         dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
434         if (IS_ERR(dphy->pll_ref_clk))
435                 return PTR_ERR(dphy->pll_ref_clk);
436
437         if (dphy->ops->probe) {
438                 ret = dphy->ops->probe(dphy);
439                 if (ret)
440                         return ret;
441         }
442
443         dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
444         if (IS_ERR(dphy->phy)) {
445                 dev_err(&pdev->dev, "failed to create PHY\n");
446                 if (dphy->ops->remove)
447                         dphy->ops->remove(dphy);
448                 return PTR_ERR(dphy->phy);
449         }
450
451         phy_set_drvdata(dphy->phy, dphy);
452         phy_provider = devm_of_phy_provider_register(&pdev->dev,
453                                                      of_phy_simple_xlate);
454
455         return PTR_ERR_OR_ZERO(phy_provider);
456 }
457
458 static void cdns_dphy_remove(struct platform_device *pdev)
459 {
460         struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
461
462         if (dphy->ops->remove)
463                 dphy->ops->remove(dphy);
464 }
465
466 static const struct of_device_id cdns_dphy_of_match[] = {
467         { .compatible = "cdns,dphy", .data = &ref_dphy_ops },
468         { .compatible = "ti,j721e-dphy", .data = &j721e_dphy_ops },
469         { /* sentinel */ },
470 };
471 MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
472
473 static struct platform_driver cdns_dphy_platform_driver = {
474         .probe          = cdns_dphy_probe,
475         .remove_new     = cdns_dphy_remove,
476         .driver         = {
477                 .name           = "cdns-mipi-dphy",
478                 .of_match_table = cdns_dphy_of_match,
479         },
480 };
481 module_platform_driver(cdns_dphy_platform_driver);
482
483 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
484 MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
485 MODULE_LICENSE("GPL");