xref: /OK3568_Linux_fs/kernel/drivers/phy/cadence/cdns-dphy.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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