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