1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright: 2017-2018 Cadence Design Systems, Inc.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/bitops.h>
7*4882a593Smuzhiyun #include <linux/clk.h>
8*4882a593Smuzhiyun #include <linux/io.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/of_address.h>
11*4882a593Smuzhiyun #include <linux/of_device.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/reset.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/phy/phy.h>
16*4882a593Smuzhiyun #include <linux/phy/phy-mipi-dphy.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define REG_WAKEUP_TIME_NS 800
19*4882a593Smuzhiyun #define DPHY_PLL_RATE_HZ 108000000
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /* DPHY registers */
22*4882a593Smuzhiyun #define DPHY_PMA_CMN(reg) (reg)
23*4882a593Smuzhiyun #define DPHY_PMA_LCLK(reg) (0x100 + (reg))
24*4882a593Smuzhiyun #define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg))
25*4882a593Smuzhiyun #define DPHY_PMA_RCLK(reg) (0x600 + (reg))
26*4882a593Smuzhiyun #define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg))
27*4882a593Smuzhiyun #define DPHY_PCS(reg) (0xb00 + (reg))
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
30*4882a593Smuzhiyun #define DPHY_CMN_SSM_EN BIT(0)
31*4882a593Smuzhiyun #define DPHY_CMN_TX_MODE_EN BIT(9)
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40)
34*4882a593Smuzhiyun #define DPHY_CMN_PWM_DIV(x) ((x) << 20)
35*4882a593Smuzhiyun #define DPHY_CMN_PWM_LOW(x) ((x) << 10)
36*4882a593Smuzhiyun #define DPHY_CMN_PWM_HIGH(x) (x)
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c)
39*4882a593Smuzhiyun #define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22))
40*4882a593Smuzhiyun #define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21))
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50)
43*4882a593Smuzhiyun #define DPHY_CMN_IPDIV_FROM_REG BIT(0)
44*4882a593Smuzhiyun #define DPHY_CMN_IPDIV(x) ((x) << 1)
45*4882a593Smuzhiyun #define DPHY_CMN_OPDIV_FROM_REG BIT(6)
46*4882a593Smuzhiyun #define DPHY_CMN_OPDIV(x) ((x) << 7)
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun #define DPHY_PSM_CFG DPHY_PCS(0x4)
49*4882a593Smuzhiyun #define DPHY_PSM_CFG_FROM_REG BIT(0)
50*4882a593Smuzhiyun #define DPHY_PSM_CLK_DIV(x) ((x) << 1)
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #define DSI_HBP_FRAME_OVERHEAD 12
53*4882a593Smuzhiyun #define DSI_HSA_FRAME_OVERHEAD 14
54*4882a593Smuzhiyun #define DSI_HFP_FRAME_OVERHEAD 6
55*4882a593Smuzhiyun #define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
56*4882a593Smuzhiyun #define DSI_BLANKING_FRAME_OVERHEAD 6
57*4882a593Smuzhiyun #define DSI_NULL_FRAME_OVERHEAD 6
58*4882a593Smuzhiyun #define DSI_EOT_PKT_SIZE 4
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun struct cdns_dphy_cfg {
61*4882a593Smuzhiyun u8 pll_ipdiv;
62*4882a593Smuzhiyun u8 pll_opdiv;
63*4882a593Smuzhiyun u16 pll_fbdiv;
64*4882a593Smuzhiyun unsigned int nlanes;
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun enum cdns_dphy_clk_lane_cfg {
68*4882a593Smuzhiyun DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
69*4882a593Smuzhiyun DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
70*4882a593Smuzhiyun DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
71*4882a593Smuzhiyun DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun struct cdns_dphy;
75*4882a593Smuzhiyun struct cdns_dphy_ops {
76*4882a593Smuzhiyun int (*probe)(struct cdns_dphy *dphy);
77*4882a593Smuzhiyun void (*remove)(struct cdns_dphy *dphy);
78*4882a593Smuzhiyun void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
79*4882a593Smuzhiyun void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
80*4882a593Smuzhiyun enum cdns_dphy_clk_lane_cfg cfg);
81*4882a593Smuzhiyun void (*set_pll_cfg)(struct cdns_dphy *dphy,
82*4882a593Smuzhiyun const struct cdns_dphy_cfg *cfg);
83*4882a593Smuzhiyun unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun struct cdns_dphy {
87*4882a593Smuzhiyun struct cdns_dphy_cfg cfg;
88*4882a593Smuzhiyun void __iomem *regs;
89*4882a593Smuzhiyun struct clk *psm_clk;
90*4882a593Smuzhiyun struct clk *pll_ref_clk;
91*4882a593Smuzhiyun const struct cdns_dphy_ops *ops;
92*4882a593Smuzhiyun struct phy *phy;
93*4882a593Smuzhiyun };
94*4882a593Smuzhiyun
cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy * dphy,struct cdns_dphy_cfg * cfg,struct phy_configure_opts_mipi_dphy * opts,unsigned int * dsi_hfp_ext)95*4882a593Smuzhiyun static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
96*4882a593Smuzhiyun struct cdns_dphy_cfg *cfg,
97*4882a593Smuzhiyun struct phy_configure_opts_mipi_dphy *opts,
98*4882a593Smuzhiyun unsigned int *dsi_hfp_ext)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
101*4882a593Smuzhiyun u64 dlane_bps;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun memset(cfg, 0, sizeof(*cfg));
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
106*4882a593Smuzhiyun return -EINVAL;
107*4882a593Smuzhiyun else if (pll_ref_hz < 19200000)
108*4882a593Smuzhiyun cfg->pll_ipdiv = 1;
109*4882a593Smuzhiyun else if (pll_ref_hz < 38400000)
110*4882a593Smuzhiyun cfg->pll_ipdiv = 2;
111*4882a593Smuzhiyun else if (pll_ref_hz < 76800000)
112*4882a593Smuzhiyun cfg->pll_ipdiv = 4;
113*4882a593Smuzhiyun else
114*4882a593Smuzhiyun cfg->pll_ipdiv = 8;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun dlane_bps = opts->hs_clk_rate;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
119*4882a593Smuzhiyun return -EINVAL;
120*4882a593Smuzhiyun else if (dlane_bps >= 1250000000)
121*4882a593Smuzhiyun cfg->pll_opdiv = 1;
122*4882a593Smuzhiyun else if (dlane_bps >= 630000000)
123*4882a593Smuzhiyun cfg->pll_opdiv = 2;
124*4882a593Smuzhiyun else if (dlane_bps >= 320000000)
125*4882a593Smuzhiyun cfg->pll_opdiv = 4;
126*4882a593Smuzhiyun else if (dlane_bps >= 160000000)
127*4882a593Smuzhiyun cfg->pll_opdiv = 8;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
130*4882a593Smuzhiyun cfg->pll_ipdiv,
131*4882a593Smuzhiyun pll_ref_hz);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun return 0;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
cdns_dphy_setup_psm(struct cdns_dphy * dphy)136*4882a593Smuzhiyun static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
139*4882a593Smuzhiyun unsigned long psm_div;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun if (!psm_clk_hz || psm_clk_hz > 100000000)
142*4882a593Smuzhiyun return -EINVAL;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
145*4882a593Smuzhiyun if (dphy->ops->set_psm_div)
146*4882a593Smuzhiyun dphy->ops->set_psm_div(dphy, psm_div);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
cdns_dphy_set_clk_lane_cfg(struct cdns_dphy * dphy,enum cdns_dphy_clk_lane_cfg cfg)151*4882a593Smuzhiyun static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
152*4882a593Smuzhiyun enum cdns_dphy_clk_lane_cfg cfg)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun if (dphy->ops->set_clk_lane_cfg)
155*4882a593Smuzhiyun dphy->ops->set_clk_lane_cfg(dphy, cfg);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
cdns_dphy_set_pll_cfg(struct cdns_dphy * dphy,const struct cdns_dphy_cfg * cfg)158*4882a593Smuzhiyun static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
159*4882a593Smuzhiyun const struct cdns_dphy_cfg *cfg)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun if (dphy->ops->set_pll_cfg)
162*4882a593Smuzhiyun dphy->ops->set_pll_cfg(dphy, cfg);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
cdns_dphy_get_wakeup_time_ns(struct cdns_dphy * dphy)165*4882a593Smuzhiyun static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun return dphy->ops->get_wakeup_time_ns(dphy);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy * dphy)170*4882a593Smuzhiyun static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun /* Default wakeup time is 800 ns (in a simulated environment). */
173*4882a593Smuzhiyun return 800;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
cdns_dphy_ref_set_pll_cfg(struct cdns_dphy * dphy,const struct cdns_dphy_cfg * cfg)176*4882a593Smuzhiyun static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
177*4882a593Smuzhiyun const struct cdns_dphy_cfg *cfg)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun u32 fbdiv_low, fbdiv_high;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
182*4882a593Smuzhiyun fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
185*4882a593Smuzhiyun DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
186*4882a593Smuzhiyun DPHY_CMN_OPDIV(cfg->pll_opdiv),
187*4882a593Smuzhiyun dphy->regs + DPHY_CMN_OPIPDIV);
188*4882a593Smuzhiyun writel(DPHY_CMN_FBDIV_FROM_REG |
189*4882a593Smuzhiyun DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
190*4882a593Smuzhiyun dphy->regs + DPHY_CMN_FBDIV);
191*4882a593Smuzhiyun writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
192*4882a593Smuzhiyun DPHY_CMN_PWM_DIV(0x8),
193*4882a593Smuzhiyun dphy->regs + DPHY_CMN_PWM);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
cdns_dphy_ref_set_psm_div(struct cdns_dphy * dphy,u8 div)196*4882a593Smuzhiyun static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
199*4882a593Smuzhiyun dphy->regs + DPHY_PSM_CFG);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun /*
203*4882a593Smuzhiyun * This is the reference implementation of DPHY hooks. Specific integration of
204*4882a593Smuzhiyun * this IP may have to re-implement some of them depending on how they decided
205*4882a593Smuzhiyun * to wire things in the SoC.
206*4882a593Smuzhiyun */
207*4882a593Smuzhiyun static const struct cdns_dphy_ops ref_dphy_ops = {
208*4882a593Smuzhiyun .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
209*4882a593Smuzhiyun .set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
210*4882a593Smuzhiyun .set_psm_div = cdns_dphy_ref_set_psm_div,
211*4882a593Smuzhiyun };
212*4882a593Smuzhiyun
cdns_dphy_config_from_opts(struct phy * phy,struct phy_configure_opts_mipi_dphy * opts,struct cdns_dphy_cfg * cfg)213*4882a593Smuzhiyun static int cdns_dphy_config_from_opts(struct phy *phy,
214*4882a593Smuzhiyun struct phy_configure_opts_mipi_dphy *opts,
215*4882a593Smuzhiyun struct cdns_dphy_cfg *cfg)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun struct cdns_dphy *dphy = phy_get_drvdata(phy);
218*4882a593Smuzhiyun unsigned int dsi_hfp_ext = 0;
219*4882a593Smuzhiyun int ret;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun ret = phy_mipi_dphy_config_validate(opts);
222*4882a593Smuzhiyun if (ret)
223*4882a593Smuzhiyun return ret;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
226*4882a593Smuzhiyun opts, &dsi_hfp_ext);
227*4882a593Smuzhiyun if (ret)
228*4882a593Smuzhiyun return ret;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun return 0;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
cdns_dphy_validate(struct phy * phy,enum phy_mode mode,int submode,union phy_configure_opts * opts)235*4882a593Smuzhiyun static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
236*4882a593Smuzhiyun union phy_configure_opts *opts)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun struct cdns_dphy_cfg cfg = { 0 };
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun if (mode != PHY_MODE_MIPI_DPHY)
241*4882a593Smuzhiyun return -EINVAL;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
cdns_dphy_configure(struct phy * phy,union phy_configure_opts * opts)246*4882a593Smuzhiyun static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun struct cdns_dphy *dphy = phy_get_drvdata(phy);
249*4882a593Smuzhiyun struct cdns_dphy_cfg cfg = { 0 };
250*4882a593Smuzhiyun int ret;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
253*4882a593Smuzhiyun if (ret)
254*4882a593Smuzhiyun return ret;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun /*
257*4882a593Smuzhiyun * Configure the internal PSM clk divider so that the DPHY has a
258*4882a593Smuzhiyun * 1MHz clk (or something close).
259*4882a593Smuzhiyun */
260*4882a593Smuzhiyun ret = cdns_dphy_setup_psm(dphy);
261*4882a593Smuzhiyun if (ret)
262*4882a593Smuzhiyun return ret;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun /*
265*4882a593Smuzhiyun * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
266*4882a593Smuzhiyun * and 8 data lanes, each clk lane can be attache different set of
267*4882a593Smuzhiyun * data lanes. The 2 groups are named 'left' and 'right', so here we
268*4882a593Smuzhiyun * just say that we want the 'left' clk lane to drive the 'left' data
269*4882a593Smuzhiyun * lanes.
270*4882a593Smuzhiyun */
271*4882a593Smuzhiyun cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /*
274*4882a593Smuzhiyun * Configure the DPHY PLL that will be used to generate the TX byte
275*4882a593Smuzhiyun * clk.
276*4882a593Smuzhiyun */
277*4882a593Smuzhiyun cdns_dphy_set_pll_cfg(dphy, &cfg);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
cdns_dphy_power_on(struct phy * phy)282*4882a593Smuzhiyun static int cdns_dphy_power_on(struct phy *phy)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun struct cdns_dphy *dphy = phy_get_drvdata(phy);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun clk_prepare_enable(dphy->psm_clk);
287*4882a593Smuzhiyun clk_prepare_enable(dphy->pll_ref_clk);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun /* Start TX state machine. */
290*4882a593Smuzhiyun writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
291*4882a593Smuzhiyun dphy->regs + DPHY_CMN_SSM);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun return 0;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
cdns_dphy_power_off(struct phy * phy)296*4882a593Smuzhiyun static int cdns_dphy_power_off(struct phy *phy)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun struct cdns_dphy *dphy = phy_get_drvdata(phy);
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun clk_disable_unprepare(dphy->pll_ref_clk);
301*4882a593Smuzhiyun clk_disable_unprepare(dphy->psm_clk);
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun return 0;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static const struct phy_ops cdns_dphy_ops = {
307*4882a593Smuzhiyun .configure = cdns_dphy_configure,
308*4882a593Smuzhiyun .validate = cdns_dphy_validate,
309*4882a593Smuzhiyun .power_on = cdns_dphy_power_on,
310*4882a593Smuzhiyun .power_off = cdns_dphy_power_off,
311*4882a593Smuzhiyun };
312*4882a593Smuzhiyun
cdns_dphy_probe(struct platform_device * pdev)313*4882a593Smuzhiyun static int cdns_dphy_probe(struct platform_device *pdev)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun struct phy_provider *phy_provider;
316*4882a593Smuzhiyun struct cdns_dphy *dphy;
317*4882a593Smuzhiyun struct resource *res;
318*4882a593Smuzhiyun int ret;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
321*4882a593Smuzhiyun if (!dphy)
322*4882a593Smuzhiyun return -ENOMEM;
323*4882a593Smuzhiyun dev_set_drvdata(&pdev->dev, dphy);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun dphy->ops = of_device_get_match_data(&pdev->dev);
326*4882a593Smuzhiyun if (!dphy->ops)
327*4882a593Smuzhiyun return -EINVAL;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
330*4882a593Smuzhiyun dphy->regs = devm_ioremap_resource(&pdev->dev, res);
331*4882a593Smuzhiyun if (IS_ERR(dphy->regs))
332*4882a593Smuzhiyun return PTR_ERR(dphy->regs);
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
335*4882a593Smuzhiyun if (IS_ERR(dphy->psm_clk))
336*4882a593Smuzhiyun return PTR_ERR(dphy->psm_clk);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
339*4882a593Smuzhiyun if (IS_ERR(dphy->pll_ref_clk))
340*4882a593Smuzhiyun return PTR_ERR(dphy->pll_ref_clk);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun if (dphy->ops->probe) {
343*4882a593Smuzhiyun ret = dphy->ops->probe(dphy);
344*4882a593Smuzhiyun if (ret)
345*4882a593Smuzhiyun return ret;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
349*4882a593Smuzhiyun if (IS_ERR(dphy->phy)) {
350*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to create PHY\n");
351*4882a593Smuzhiyun if (dphy->ops->remove)
352*4882a593Smuzhiyun dphy->ops->remove(dphy);
353*4882a593Smuzhiyun return PTR_ERR(dphy->phy);
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun phy_set_drvdata(dphy->phy, dphy);
357*4882a593Smuzhiyun phy_provider = devm_of_phy_provider_register(&pdev->dev,
358*4882a593Smuzhiyun of_phy_simple_xlate);
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun return PTR_ERR_OR_ZERO(phy_provider);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun
cdns_dphy_remove(struct platform_device * pdev)363*4882a593Smuzhiyun static int cdns_dphy_remove(struct platform_device *pdev)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun if (dphy->ops->remove)
368*4882a593Smuzhiyun dphy->ops->remove(dphy);
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun static const struct of_device_id cdns_dphy_of_match[] = {
374*4882a593Smuzhiyun { .compatible = "cdns,dphy", .data = &ref_dphy_ops },
375*4882a593Smuzhiyun { /* sentinel */ },
376*4882a593Smuzhiyun };
377*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun static struct platform_driver cdns_dphy_platform_driver = {
380*4882a593Smuzhiyun .probe = cdns_dphy_probe,
381*4882a593Smuzhiyun .remove = cdns_dphy_remove,
382*4882a593Smuzhiyun .driver = {
383*4882a593Smuzhiyun .name = "cdns-mipi-dphy",
384*4882a593Smuzhiyun .of_match_table = cdns_dphy_of_match,
385*4882a593Smuzhiyun },
386*4882a593Smuzhiyun };
387*4882a593Smuzhiyun module_platform_driver(cdns_dphy_platform_driver);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
390*4882a593Smuzhiyun MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
391*4882a593Smuzhiyun MODULE_LICENSE("GPL");
392