xref: /openbmc/linux/drivers/clk/imx/clk-scu.c (revision 5392c5de)
1fe37b482SAisheng Dong // SPDX-License-Identifier: GPL-2.0+
2fe37b482SAisheng Dong /*
3fe37b482SAisheng Dong  * Copyright 2018 NXP
4fe37b482SAisheng Dong  *   Dong Aisheng <aisheng.dong@nxp.com>
5fe37b482SAisheng Dong  */
6fe37b482SAisheng Dong 
73b9ea606SAnson Huang #include <dt-bindings/firmware/imx/rsrc.h>
83b9ea606SAnson Huang #include <linux/arm-smccc.h>
9fe37b482SAisheng Dong #include <linux/clk-provider.h>
10fe37b482SAisheng Dong #include <linux/err.h>
1177d8f306SDong Aisheng #include <linux/of_platform.h>
1277d8f306SDong Aisheng #include <linux/platform_device.h>
1377d8f306SDong Aisheng #include <linux/pm_domain.h>
1478edeb08SDong Aisheng #include <linux/pm_runtime.h>
15fe37b482SAisheng Dong #include <linux/slab.h>
16fe37b482SAisheng Dong 
17fe37b482SAisheng Dong #include "clk-scu.h"
18fe37b482SAisheng Dong 
193b9ea606SAnson Huang #define IMX_SIP_CPUFREQ			0xC2000001
203b9ea606SAnson Huang #define IMX_SIP_SET_CPUFREQ		0x00
213b9ea606SAnson Huang 
22fe37b482SAisheng Dong static struct imx_sc_ipc *ccm_ipc_handle;
23550b562aSZou Wei static struct device_node *pd_np;
24220175cdSDong Aisheng static struct platform_driver imx_clk_scu_driver;
2577d8f306SDong Aisheng 
2677d8f306SDong Aisheng struct imx_scu_clk_node {
2777d8f306SDong Aisheng 	const char *name;
2877d8f306SDong Aisheng 	u32 rsrc;
2977d8f306SDong Aisheng 	u8 clk_type;
3077d8f306SDong Aisheng 	const char * const *parents;
3177d8f306SDong Aisheng 	int num_parents;
3277d8f306SDong Aisheng 
3377d8f306SDong Aisheng 	struct clk_hw *hw;
3477d8f306SDong Aisheng 	struct list_head node;
3577d8f306SDong Aisheng };
3677d8f306SDong Aisheng 
3777d8f306SDong Aisheng struct list_head imx_scu_clks[IMX_SC_R_LAST];
38fe37b482SAisheng Dong 
39fe37b482SAisheng Dong /*
40fe37b482SAisheng Dong  * struct clk_scu - Description of one SCU clock
41fe37b482SAisheng Dong  * @hw: the common clk_hw
42fe37b482SAisheng Dong  * @rsrc_id: resource ID of this SCU clock
43fe37b482SAisheng Dong  * @clk_type: type of this clock resource
44fe37b482SAisheng Dong  */
45fe37b482SAisheng Dong struct clk_scu {
46fe37b482SAisheng Dong 	struct clk_hw hw;
47fe37b482SAisheng Dong 	u16 rsrc_id;
48fe37b482SAisheng Dong 	u8 clk_type;
49d0409631SDong Aisheng 
50d0409631SDong Aisheng 	/* for state save&restore */
51d0409631SDong Aisheng 	bool is_enabled;
52d0409631SDong Aisheng 	u32 rate;
53fe37b482SAisheng Dong };
54fe37b482SAisheng Dong 
55fe37b482SAisheng Dong /*
56*5392c5deSDong Aisheng  * struct clk_gpr_scu - Description of one SCU GPR clock
57*5392c5deSDong Aisheng  * @hw: the common clk_hw
58*5392c5deSDong Aisheng  * @rsrc_id: resource ID of this SCU clock
59*5392c5deSDong Aisheng  * @gpr_id: GPR ID index to control the divider
60*5392c5deSDong Aisheng  */
61*5392c5deSDong Aisheng struct clk_gpr_scu {
62*5392c5deSDong Aisheng 	struct clk_hw hw;
63*5392c5deSDong Aisheng 	u16 rsrc_id;
64*5392c5deSDong Aisheng 	u8 gpr_id;
65*5392c5deSDong Aisheng 	u8 flags;
66*5392c5deSDong Aisheng 	bool gate_invert;
67*5392c5deSDong Aisheng };
68*5392c5deSDong Aisheng 
69*5392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw)
70*5392c5deSDong Aisheng 
71*5392c5deSDong Aisheng /*
72fe37b482SAisheng Dong  * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
73fe37b482SAisheng Dong  * @hdr: SCU protocol header
74fe37b482SAisheng Dong  * @rate: rate to set
75fe37b482SAisheng Dong  * @resource: clock resource to set rate
76fe37b482SAisheng Dong  * @clk: clk type of this resource
77fe37b482SAisheng Dong  *
78fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate set
79fe37b482SAisheng Dong  */
80fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate {
81fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
82fe37b482SAisheng Dong 	__le32 rate;
83fe37b482SAisheng Dong 	__le16 resource;
84fe37b482SAisheng Dong 	u8 clk;
85a0ae04a2SLeonard Crestez } __packed __aligned(4);
86fe37b482SAisheng Dong 
87fe37b482SAisheng Dong struct req_get_clock_rate {
88fe37b482SAisheng Dong 	__le16 resource;
89fe37b482SAisheng Dong 	u8 clk;
90a0ae04a2SLeonard Crestez } __packed __aligned(4);
91fe37b482SAisheng Dong 
92fe37b482SAisheng Dong struct resp_get_clock_rate {
93fe37b482SAisheng Dong 	__le32 rate;
94fe37b482SAisheng Dong };
95fe37b482SAisheng Dong 
96fe37b482SAisheng Dong /*
97fe37b482SAisheng Dong  * struct imx_sc_msg_get_clock_rate - clock get rate protocol
98fe37b482SAisheng Dong  * @hdr: SCU protocol header
99fe37b482SAisheng Dong  * @req: get rate request protocol
100fe37b482SAisheng Dong  * @resp: get rate response protocol
101fe37b482SAisheng Dong  *
102fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate get
103fe37b482SAisheng Dong  */
104fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate {
105fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
106fe37b482SAisheng Dong 	union {
107fe37b482SAisheng Dong 		struct req_get_clock_rate req;
108fe37b482SAisheng Dong 		struct resp_get_clock_rate resp;
109fe37b482SAisheng Dong 	} data;
110fe37b482SAisheng Dong };
111fe37b482SAisheng Dong 
112fe37b482SAisheng Dong /*
113666aed2dSAisheng Dong  * struct imx_sc_msg_get_clock_parent - clock get parent protocol
114666aed2dSAisheng Dong  * @hdr: SCU protocol header
115666aed2dSAisheng Dong  * @req: get parent request protocol
116666aed2dSAisheng Dong  * @resp: get parent response protocol
117666aed2dSAisheng Dong  *
118666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock get parent
119666aed2dSAisheng Dong  */
120666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent {
121666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
122666aed2dSAisheng Dong 	union {
123666aed2dSAisheng Dong 		struct req_get_clock_parent {
124666aed2dSAisheng Dong 			__le16 resource;
125666aed2dSAisheng Dong 			u8 clk;
1268400ab88SLeonard Crestez 		} __packed __aligned(4) req;
127666aed2dSAisheng Dong 		struct resp_get_clock_parent {
128666aed2dSAisheng Dong 			u8 parent;
129666aed2dSAisheng Dong 		} resp;
130666aed2dSAisheng Dong 	} data;
131666aed2dSAisheng Dong };
132666aed2dSAisheng Dong 
133666aed2dSAisheng Dong /*
134666aed2dSAisheng Dong  * struct imx_sc_msg_set_clock_parent - clock set parent protocol
135666aed2dSAisheng Dong  * @hdr: SCU protocol header
136666aed2dSAisheng Dong  * @req: set parent request protocol
137666aed2dSAisheng Dong  *
138666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock set parent
139666aed2dSAisheng Dong  */
140666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent {
141666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
142666aed2dSAisheng Dong 	__le16 resource;
143666aed2dSAisheng Dong 	u8 clk;
144666aed2dSAisheng Dong 	u8 parent;
145666aed2dSAisheng Dong } __packed;
146666aed2dSAisheng Dong 
147666aed2dSAisheng Dong /*
148fe37b482SAisheng Dong  * struct imx_sc_msg_req_clock_enable - clock gate protocol
149fe37b482SAisheng Dong  * @hdr: SCU protocol header
150fe37b482SAisheng Dong  * @resource: clock resource to gate
151fe37b482SAisheng Dong  * @clk: clk type of this resource
152fe37b482SAisheng Dong  * @enable: whether gate off the clock
153fe37b482SAisheng Dong  * @autog: HW auto gate enable
154fe37b482SAisheng Dong  *
155fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock gate
156fe37b482SAisheng Dong  */
157fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable {
158fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
159fe37b482SAisheng Dong 	__le16 resource;
160fe37b482SAisheng Dong 	u8 clk;
161fe37b482SAisheng Dong 	u8 enable;
162fe37b482SAisheng Dong 	u8 autog;
163a0ae04a2SLeonard Crestez } __packed __aligned(4);
164fe37b482SAisheng Dong 
165fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
166fe37b482SAisheng Dong {
167fe37b482SAisheng Dong 	return container_of(hw, struct clk_scu, hw);
168fe37b482SAisheng Dong }
169fe37b482SAisheng Dong 
17077d8f306SDong Aisheng int imx_clk_scu_init(struct device_node *np)
171fe37b482SAisheng Dong {
17277d8f306SDong Aisheng 	u32 clk_cells;
17377d8f306SDong Aisheng 	int ret, i;
17477d8f306SDong Aisheng 
17577d8f306SDong Aisheng 	ret = imx_scu_get_handle(&ccm_ipc_handle);
17677d8f306SDong Aisheng 	if (ret)
17777d8f306SDong Aisheng 		return ret;
17877d8f306SDong Aisheng 
17977d8f306SDong Aisheng 	of_property_read_u32(np, "#clock-cells", &clk_cells);
18077d8f306SDong Aisheng 
18177d8f306SDong Aisheng 	if (clk_cells == 2) {
18277d8f306SDong Aisheng 		for (i = 0; i < IMX_SC_R_LAST; i++)
18377d8f306SDong Aisheng 			INIT_LIST_HEAD(&imx_scu_clks[i]);
18443d24796SDong Aisheng 
18543d24796SDong Aisheng 		/* pd_np will be used to attach power domains later */
18677d8f306SDong Aisheng 		pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd");
18743d24796SDong Aisheng 		if (!pd_np)
18843d24796SDong Aisheng 			return -EINVAL;
18977d8f306SDong Aisheng 	}
19077d8f306SDong Aisheng 
191220175cdSDong Aisheng 	return platform_driver_register(&imx_clk_scu_driver);
192fe37b482SAisheng Dong }
193fe37b482SAisheng Dong 
194fe37b482SAisheng Dong /*
195fe37b482SAisheng Dong  * clk_scu_recalc_rate - Get clock rate for a SCU clock
196fe37b482SAisheng Dong  * @hw: clock to get rate for
197fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
198fe37b482SAisheng Dong  *
199fe37b482SAisheng Dong  * Gets the current clock rate of a SCU clock. Returns the current
200fe37b482SAisheng Dong  * clock rate, or zero in failure.
201fe37b482SAisheng Dong  */
202fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
203fe37b482SAisheng Dong 					 unsigned long parent_rate)
204fe37b482SAisheng Dong {
205fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
206fe37b482SAisheng Dong 	struct imx_sc_msg_get_clock_rate msg;
207fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
208fe37b482SAisheng Dong 	int ret;
209fe37b482SAisheng Dong 
210fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
211fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
212fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
213fe37b482SAisheng Dong 	hdr->size = 2;
214fe37b482SAisheng Dong 
215fe37b482SAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
216fe37b482SAisheng Dong 	msg.data.req.clk = clk->clk_type;
217fe37b482SAisheng Dong 
218fe37b482SAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
219fe37b482SAisheng Dong 	if (ret) {
220fe37b482SAisheng Dong 		pr_err("%s: failed to get clock rate %d\n",
221fe37b482SAisheng Dong 		       clk_hw_get_name(hw), ret);
222fe37b482SAisheng Dong 		return 0;
223fe37b482SAisheng Dong 	}
224fe37b482SAisheng Dong 
225fe37b482SAisheng Dong 	return le32_to_cpu(msg.data.resp.rate);
226fe37b482SAisheng Dong }
227fe37b482SAisheng Dong 
228fe37b482SAisheng Dong /*
229fe37b482SAisheng Dong  * clk_scu_round_rate - Round clock rate for a SCU clock
230fe37b482SAisheng Dong  * @hw: clock to round rate for
231fe37b482SAisheng Dong  * @rate: rate to round
232fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
233fe37b482SAisheng Dong  *
234fe37b482SAisheng Dong  * Returns the current clock rate, or zero in failure.
235fe37b482SAisheng Dong  */
236fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
237fe37b482SAisheng Dong 			       unsigned long *parent_rate)
238fe37b482SAisheng Dong {
239fe37b482SAisheng Dong 	/*
240fe37b482SAisheng Dong 	 * Assume we support all the requested rate and let the SCU firmware
241fe37b482SAisheng Dong 	 * to handle the left work
242fe37b482SAisheng Dong 	 */
243fe37b482SAisheng Dong 	return rate;
244fe37b482SAisheng Dong }
245fe37b482SAisheng Dong 
2463b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
2473b9ea606SAnson Huang 				    unsigned long parent_rate)
2483b9ea606SAnson Huang {
2493b9ea606SAnson Huang 	struct clk_scu *clk = to_clk_scu(hw);
2503b9ea606SAnson Huang 	struct arm_smccc_res res;
2513b9ea606SAnson Huang 	unsigned long cluster_id;
2523b9ea606SAnson Huang 
2533b9ea606SAnson Huang 	if (clk->rsrc_id == IMX_SC_R_A35)
2543b9ea606SAnson Huang 		cluster_id = 0;
2553b9ea606SAnson Huang 	else
2563b9ea606SAnson Huang 		return -EINVAL;
2573b9ea606SAnson Huang 
2583b9ea606SAnson Huang 	/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
2593b9ea606SAnson Huang 	arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
2603b9ea606SAnson Huang 		      cluster_id, rate, 0, 0, 0, 0, &res);
2613b9ea606SAnson Huang 
2623b9ea606SAnson Huang 	return 0;
2633b9ea606SAnson Huang }
2643b9ea606SAnson Huang 
265fe37b482SAisheng Dong /*
266fe37b482SAisheng Dong  * clk_scu_set_rate - Set rate for a SCU clock
267fe37b482SAisheng Dong  * @hw: clock to change rate for
268fe37b482SAisheng Dong  * @rate: target rate for the clock
269fe37b482SAisheng Dong  * @parent_rate: rate of the clock parent, not used for SCU clocks
270fe37b482SAisheng Dong  *
271fe37b482SAisheng Dong  * Sets a clock frequency for a SCU clock. Returns the SCU
272fe37b482SAisheng Dong  * protocol status.
273fe37b482SAisheng Dong  */
274fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
275fe37b482SAisheng Dong 			    unsigned long parent_rate)
276fe37b482SAisheng Dong {
277fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
278fe37b482SAisheng Dong 	struct imx_sc_msg_req_set_clock_rate msg;
279fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
280fe37b482SAisheng Dong 
281fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
282fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
283fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
284fe37b482SAisheng Dong 	hdr->size = 3;
285fe37b482SAisheng Dong 
286fe37b482SAisheng Dong 	msg.rate = cpu_to_le32(rate);
287fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
288fe37b482SAisheng Dong 	msg.clk = clk->clk_type;
289fe37b482SAisheng Dong 
290fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
291fe37b482SAisheng Dong }
292fe37b482SAisheng Dong 
293666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw)
294666aed2dSAisheng Dong {
295666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
296666aed2dSAisheng Dong 	struct imx_sc_msg_get_clock_parent msg;
297666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
298666aed2dSAisheng Dong 	int ret;
299666aed2dSAisheng Dong 
300666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
301666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
302666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
303666aed2dSAisheng Dong 	hdr->size = 2;
304666aed2dSAisheng Dong 
305666aed2dSAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
306666aed2dSAisheng Dong 	msg.data.req.clk = clk->clk_type;
307666aed2dSAisheng Dong 
308666aed2dSAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
309666aed2dSAisheng Dong 	if (ret) {
310666aed2dSAisheng Dong 		pr_err("%s: failed to get clock parent %d\n",
311666aed2dSAisheng Dong 		       clk_hw_get_name(hw), ret);
312666aed2dSAisheng Dong 		return 0;
313666aed2dSAisheng Dong 	}
314666aed2dSAisheng Dong 
315666aed2dSAisheng Dong 	return msg.data.resp.parent;
316666aed2dSAisheng Dong }
317666aed2dSAisheng Dong 
318666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
319666aed2dSAisheng Dong {
320666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
321666aed2dSAisheng Dong 	struct imx_sc_msg_set_clock_parent msg;
322666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
323666aed2dSAisheng Dong 
324666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
325666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
326666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
327666aed2dSAisheng Dong 	hdr->size = 2;
328666aed2dSAisheng Dong 
329666aed2dSAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
330666aed2dSAisheng Dong 	msg.clk = clk->clk_type;
331666aed2dSAisheng Dong 	msg.parent = index;
332666aed2dSAisheng Dong 
333666aed2dSAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
334666aed2dSAisheng Dong }
335666aed2dSAisheng Dong 
336fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
337fe37b482SAisheng Dong 			      u8 clk, bool enable, bool autog)
338fe37b482SAisheng Dong {
339fe37b482SAisheng Dong 	struct imx_sc_msg_req_clock_enable msg;
340fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
341fe37b482SAisheng Dong 
342fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
343fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
344fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
345fe37b482SAisheng Dong 	hdr->size = 3;
346fe37b482SAisheng Dong 
347fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(resource);
348fe37b482SAisheng Dong 	msg.clk = clk;
349fe37b482SAisheng Dong 	msg.enable = enable;
350fe37b482SAisheng Dong 	msg.autog = autog;
351fe37b482SAisheng Dong 
352fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
353fe37b482SAisheng Dong }
354fe37b482SAisheng Dong 
355fe37b482SAisheng Dong /*
356fe37b482SAisheng Dong  * clk_scu_prepare - Enable a SCU clock
357fe37b482SAisheng Dong  * @hw: clock to enable
358fe37b482SAisheng Dong  *
359fe37b482SAisheng Dong  * Enable the clock at the DSC slice level
360fe37b482SAisheng Dong  */
361fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw)
362fe37b482SAisheng Dong {
363fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
364fe37b482SAisheng Dong 
365fe37b482SAisheng Dong 	return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
366fe37b482SAisheng Dong 				  clk->clk_type, true, false);
367fe37b482SAisheng Dong }
368fe37b482SAisheng Dong 
369fe37b482SAisheng Dong /*
370fe37b482SAisheng Dong  * clk_scu_unprepare - Disable a SCU clock
371fe37b482SAisheng Dong  * @hw: clock to enable
372fe37b482SAisheng Dong  *
373fe37b482SAisheng Dong  * Disable the clock at the DSC slice level
374fe37b482SAisheng Dong  */
375fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw)
376fe37b482SAisheng Dong {
377fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
378fe37b482SAisheng Dong 	int ret;
379fe37b482SAisheng Dong 
380fe37b482SAisheng Dong 	ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
381fe37b482SAisheng Dong 				 clk->clk_type, false, false);
382fe37b482SAisheng Dong 	if (ret)
383fe37b482SAisheng Dong 		pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
384fe37b482SAisheng Dong 			ret);
385fe37b482SAisheng Dong }
386fe37b482SAisheng Dong 
387fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = {
388fe37b482SAisheng Dong 	.recalc_rate = clk_scu_recalc_rate,
389fe37b482SAisheng Dong 	.round_rate = clk_scu_round_rate,
390fe37b482SAisheng Dong 	.set_rate = clk_scu_set_rate,
391666aed2dSAisheng Dong 	.get_parent = clk_scu_get_parent,
392666aed2dSAisheng Dong 	.set_parent = clk_scu_set_parent,
393fe37b482SAisheng Dong 	.prepare = clk_scu_prepare,
394fe37b482SAisheng Dong 	.unprepare = clk_scu_unprepare,
395fe37b482SAisheng Dong };
396fe37b482SAisheng Dong 
3973b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = {
3983b9ea606SAnson Huang 	.recalc_rate = clk_scu_recalc_rate,
3993b9ea606SAnson Huang 	.round_rate = clk_scu_round_rate,
4003b9ea606SAnson Huang 	.set_rate = clk_scu_atf_set_cpu_rate,
4013b9ea606SAnson Huang 	.prepare = clk_scu_prepare,
4023b9ea606SAnson Huang 	.unprepare = clk_scu_unprepare,
4033b9ea606SAnson Huang };
4043b9ea606SAnson Huang 
4052f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
4062f1a2c1dSDong Aisheng 			     const char * const *parents, int num_parents,
4072f1a2c1dSDong Aisheng 			     u32 rsrc_id, u8 clk_type)
408fe37b482SAisheng Dong {
409fe37b482SAisheng Dong 	struct clk_init_data init;
410fe37b482SAisheng Dong 	struct clk_scu *clk;
411fe37b482SAisheng Dong 	struct clk_hw *hw;
412fe37b482SAisheng Dong 	int ret;
413fe37b482SAisheng Dong 
414fe37b482SAisheng Dong 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
415fe37b482SAisheng Dong 	if (!clk)
416fe37b482SAisheng Dong 		return ERR_PTR(-ENOMEM);
417fe37b482SAisheng Dong 
418fe37b482SAisheng Dong 	clk->rsrc_id = rsrc_id;
419fe37b482SAisheng Dong 	clk->clk_type = clk_type;
420fe37b482SAisheng Dong 
421fe37b482SAisheng Dong 	init.name = name;
422fe37b482SAisheng Dong 	init.ops = &clk_scu_ops;
4233b9ea606SAnson Huang 	if (rsrc_id == IMX_SC_R_A35)
4243b9ea606SAnson Huang 		init.ops = &clk_scu_cpu_ops;
4253b9ea606SAnson Huang 	else
4263b9ea606SAnson Huang 		init.ops = &clk_scu_ops;
427666aed2dSAisheng Dong 	init.parent_names = parents;
428666aed2dSAisheng Dong 	init.num_parents = num_parents;
429666aed2dSAisheng Dong 
430fe37b482SAisheng Dong 	/*
431fe37b482SAisheng Dong 	 * Note on MX8, the clocks are tightly coupled with power domain
432fe37b482SAisheng Dong 	 * that once the power domain is off, the clock status may be
433fe37b482SAisheng Dong 	 * lost. So we make it NOCACHE to let user to retrieve the real
434fe37b482SAisheng Dong 	 * clock status from HW instead of using the possible invalid
435fe37b482SAisheng Dong 	 * cached rate.
436fe37b482SAisheng Dong 	 */
437fe37b482SAisheng Dong 	init.flags = CLK_GET_RATE_NOCACHE;
438fe37b482SAisheng Dong 	clk->hw.init = &init;
439fe37b482SAisheng Dong 
440fe37b482SAisheng Dong 	hw = &clk->hw;
4412f1a2c1dSDong Aisheng 	ret = clk_hw_register(dev, hw);
442fe37b482SAisheng Dong 	if (ret) {
443fe37b482SAisheng Dong 		kfree(clk);
444fe37b482SAisheng Dong 		hw = ERR_PTR(ret);
445054ef44eSJian Dong 		return hw;
446fe37b482SAisheng Dong 	}
447fe37b482SAisheng Dong 
448d0409631SDong Aisheng 	if (dev)
449d0409631SDong Aisheng 		dev_set_drvdata(dev, clk);
450d0409631SDong Aisheng 
451fe37b482SAisheng Dong 	return hw;
452fe37b482SAisheng Dong }
45377d8f306SDong Aisheng 
45477d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
45577d8f306SDong Aisheng 				      void *data)
45677d8f306SDong Aisheng {
45777d8f306SDong Aisheng 	unsigned int rsrc = clkspec->args[0];
45877d8f306SDong Aisheng 	unsigned int idx = clkspec->args[1];
45977d8f306SDong Aisheng 	struct list_head *scu_clks = data;
46077d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
46177d8f306SDong Aisheng 
46277d8f306SDong Aisheng 	list_for_each_entry(clk, &scu_clks[rsrc], node) {
46377d8f306SDong Aisheng 		if (clk->clk_type == idx)
46477d8f306SDong Aisheng 			return clk->hw;
46577d8f306SDong Aisheng 	}
46677d8f306SDong Aisheng 
46777d8f306SDong Aisheng 	return ERR_PTR(-ENODEV);
46877d8f306SDong Aisheng }
46977d8f306SDong Aisheng 
47077d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev)
47177d8f306SDong Aisheng {
47277d8f306SDong Aisheng 	struct device *dev = &pdev->dev;
47377d8f306SDong Aisheng 	struct imx_scu_clk_node *clk = dev_get_platdata(dev);
47477d8f306SDong Aisheng 	struct clk_hw *hw;
47578edeb08SDong Aisheng 	int ret;
47677d8f306SDong Aisheng 
47778edeb08SDong Aisheng 	pm_runtime_set_suspended(dev);
47878edeb08SDong Aisheng 	pm_runtime_set_autosuspend_delay(dev, 50);
47978edeb08SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
48078edeb08SDong Aisheng 	pm_runtime_enable(dev);
48178edeb08SDong Aisheng 
48278edeb08SDong Aisheng 	ret = pm_runtime_get_sync(dev);
48378edeb08SDong Aisheng 	if (ret) {
48478edeb08SDong Aisheng 		pm_runtime_disable(dev);
48578edeb08SDong Aisheng 		return ret;
48678edeb08SDong Aisheng 	}
48778edeb08SDong Aisheng 
48878edeb08SDong Aisheng 	hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents,
48977d8f306SDong Aisheng 			   clk->rsrc, clk->clk_type);
49078edeb08SDong Aisheng 	if (IS_ERR(hw)) {
49178edeb08SDong Aisheng 		pm_runtime_disable(dev);
49277d8f306SDong Aisheng 		return PTR_ERR(hw);
49378edeb08SDong Aisheng 	}
49477d8f306SDong Aisheng 
49577d8f306SDong Aisheng 	clk->hw = hw;
49677d8f306SDong Aisheng 	list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]);
49777d8f306SDong Aisheng 
49878edeb08SDong Aisheng 	pm_runtime_mark_last_busy(&pdev->dev);
49978edeb08SDong Aisheng 	pm_runtime_put_autosuspend(&pdev->dev);
50078edeb08SDong Aisheng 
50177d8f306SDong Aisheng 	dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc,
50277d8f306SDong Aisheng 		clk->clk_type);
50377d8f306SDong Aisheng 
50477d8f306SDong Aisheng 	return 0;
50577d8f306SDong Aisheng }
50677d8f306SDong Aisheng 
507d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
508d0409631SDong Aisheng {
509d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
510d0409631SDong Aisheng 
511d0409631SDong Aisheng 	clk->rate = clk_hw_get_rate(&clk->hw);
512d0409631SDong Aisheng 	clk->is_enabled = clk_hw_is_enabled(&clk->hw);
513d0409631SDong Aisheng 
514d0409631SDong Aisheng 	if (clk->rate)
515d0409631SDong Aisheng 		dev_dbg(dev, "save rate %d\n", clk->rate);
516d0409631SDong Aisheng 
517d0409631SDong Aisheng 	if (clk->is_enabled)
518d0409631SDong Aisheng 		dev_dbg(dev, "save enabled state\n");
519d0409631SDong Aisheng 
520d0409631SDong Aisheng 	return 0;
521d0409631SDong Aisheng }
522d0409631SDong Aisheng 
523d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev)
524d0409631SDong Aisheng {
525d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
526d0409631SDong Aisheng 	int ret = 0;
527d0409631SDong Aisheng 
528d0409631SDong Aisheng 	if (clk->rate) {
529d0409631SDong Aisheng 		ret = clk_scu_set_rate(&clk->hw, clk->rate, 0);
530d0409631SDong Aisheng 		dev_dbg(dev, "restore rate %d %s\n", clk->rate,
531d0409631SDong Aisheng 			!ret ? "success" : "failed");
532d0409631SDong Aisheng 	}
533d0409631SDong Aisheng 
534d0409631SDong Aisheng 	if (clk->is_enabled) {
535d0409631SDong Aisheng 		ret = clk_scu_prepare(&clk->hw);
536d0409631SDong Aisheng 		dev_dbg(dev, "restore enabled state %s\n",
537d0409631SDong Aisheng 			!ret ? "success" : "failed");
538d0409631SDong Aisheng 	}
539d0409631SDong Aisheng 
540d0409631SDong Aisheng 	return ret;
541d0409631SDong Aisheng }
542d0409631SDong Aisheng 
543d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = {
544d0409631SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend,
545d0409631SDong Aisheng 				      imx_clk_scu_resume)
546d0409631SDong Aisheng };
547d0409631SDong Aisheng 
54877d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = {
54977d8f306SDong Aisheng 	.driver = {
55077d8f306SDong Aisheng 		.name = "imx-scu-clk",
55177d8f306SDong Aisheng 		.suppress_bind_attrs = true,
552d0409631SDong Aisheng 		.pm = &imx_clk_scu_pm_ops,
55377d8f306SDong Aisheng 	},
55477d8f306SDong Aisheng 	.probe = imx_clk_scu_probe,
55577d8f306SDong Aisheng };
55677d8f306SDong Aisheng 
55777d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
55877d8f306SDong Aisheng {
55977d8f306SDong Aisheng 	struct of_phandle_args genpdspec = {
56077d8f306SDong Aisheng 		.np = pd_np,
56177d8f306SDong Aisheng 		.args_count = 1,
56277d8f306SDong Aisheng 		.args[0] = rsrc_id,
56377d8f306SDong Aisheng 	};
56477d8f306SDong Aisheng 
5650d5f1f47SDong Aisheng 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 ||
5660d5f1f47SDong Aisheng 	    rsrc_id == IMX_SC_R_A72)
5670d5f1f47SDong Aisheng 		return 0;
5680d5f1f47SDong Aisheng 
56977d8f306SDong Aisheng 	return of_genpd_add_device(&genpdspec, dev);
57077d8f306SDong Aisheng }
57177d8f306SDong Aisheng 
57277d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
57377d8f306SDong Aisheng 				     const char * const *parents,
57477d8f306SDong Aisheng 				     int num_parents, u32 rsrc_id, u8 clk_type)
57577d8f306SDong Aisheng {
57677d8f306SDong Aisheng 	struct imx_scu_clk_node clk = {
57777d8f306SDong Aisheng 		.name = name,
57877d8f306SDong Aisheng 		.rsrc = rsrc_id,
57977d8f306SDong Aisheng 		.clk_type = clk_type,
58077d8f306SDong Aisheng 		.parents = parents,
58177d8f306SDong Aisheng 		.num_parents = num_parents,
58277d8f306SDong Aisheng 	};
58377d8f306SDong Aisheng 	struct platform_device *pdev;
58477d8f306SDong Aisheng 	int ret;
58577d8f306SDong Aisheng 
58677d8f306SDong Aisheng 	pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
58777d8f306SDong Aisheng 	if (!pdev) {
58877d8f306SDong Aisheng 		pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
58977d8f306SDong Aisheng 		       name, rsrc_id, clk_type);
59077d8f306SDong Aisheng 		return ERR_PTR(-ENOMEM);
59177d8f306SDong Aisheng 	}
59277d8f306SDong Aisheng 
59377d8f306SDong Aisheng 	ret = platform_device_add_data(pdev, &clk, sizeof(clk));
59477d8f306SDong Aisheng 	if (ret) {
59577d8f306SDong Aisheng 		platform_device_put(pdev);
59677d8f306SDong Aisheng 		return ERR_PTR(ret);
59777d8f306SDong Aisheng 	}
59877d8f306SDong Aisheng 
59977d8f306SDong Aisheng 	pdev->driver_override = "imx-scu-clk";
60077d8f306SDong Aisheng 
60177d8f306SDong Aisheng 	ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
60277d8f306SDong Aisheng 	if (ret)
60377d8f306SDong Aisheng 		pr_warn("%s: failed to attached the power domain %d\n",
60477d8f306SDong Aisheng 			name, ret);
60577d8f306SDong Aisheng 
60677d8f306SDong Aisheng 	platform_device_add(pdev);
60777d8f306SDong Aisheng 
60877d8f306SDong Aisheng 	/* For API backwards compatiblilty, simply return NULL for success */
60977d8f306SDong Aisheng 	return NULL;
61077d8f306SDong Aisheng }
61177d8f306SDong Aisheng 
61277d8f306SDong Aisheng void imx_clk_scu_unregister(void)
61377d8f306SDong Aisheng {
61477d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
61577d8f306SDong Aisheng 	int i;
61677d8f306SDong Aisheng 
61777d8f306SDong Aisheng 	for (i = 0; i < IMX_SC_R_LAST; i++) {
61877d8f306SDong Aisheng 		list_for_each_entry(clk, &imx_scu_clks[i], node) {
61977d8f306SDong Aisheng 			clk_hw_unregister(clk->hw);
62077d8f306SDong Aisheng 			kfree(clk);
62177d8f306SDong Aisheng 		}
62277d8f306SDong Aisheng 	}
62377d8f306SDong Aisheng }
624*5392c5deSDong Aisheng 
625*5392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw,
626*5392c5deSDong Aisheng 						 unsigned long parent_rate)
627*5392c5deSDong Aisheng {
628*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
629*5392c5deSDong Aisheng 	unsigned long rate = 0;
630*5392c5deSDong Aisheng 	u32 val;
631*5392c5deSDong Aisheng 	int err;
632*5392c5deSDong Aisheng 
633*5392c5deSDong Aisheng 	err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
634*5392c5deSDong Aisheng 				      clk->gpr_id, &val);
635*5392c5deSDong Aisheng 
636*5392c5deSDong Aisheng 	rate  = val ? parent_rate / 2 : parent_rate;
637*5392c5deSDong Aisheng 
638*5392c5deSDong Aisheng 	return err ? 0 : rate;
639*5392c5deSDong Aisheng }
640*5392c5deSDong Aisheng 
641*5392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate,
642*5392c5deSDong Aisheng 				   unsigned long *prate)
643*5392c5deSDong Aisheng {
644*5392c5deSDong Aisheng 	if (rate < *prate)
645*5392c5deSDong Aisheng 		rate = *prate / 2;
646*5392c5deSDong Aisheng 	else
647*5392c5deSDong Aisheng 		rate = *prate;
648*5392c5deSDong Aisheng 
649*5392c5deSDong Aisheng 	return rate;
650*5392c5deSDong Aisheng }
651*5392c5deSDong Aisheng 
652*5392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
653*5392c5deSDong Aisheng 				    unsigned long parent_rate)
654*5392c5deSDong Aisheng {
655*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
656*5392c5deSDong Aisheng 	uint32_t val;
657*5392c5deSDong Aisheng 	int err;
658*5392c5deSDong Aisheng 
659*5392c5deSDong Aisheng 	val = (rate < parent_rate) ? 1 : 0;
660*5392c5deSDong Aisheng 	err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
661*5392c5deSDong Aisheng 				      clk->gpr_id, val);
662*5392c5deSDong Aisheng 
663*5392c5deSDong Aisheng 	return err ? -EINVAL : 0;
664*5392c5deSDong Aisheng }
665*5392c5deSDong Aisheng 
666*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = {
667*5392c5deSDong Aisheng 	.recalc_rate = clk_gpr_div_scu_recalc_rate,
668*5392c5deSDong Aisheng 	.round_rate = clk_gpr_div_scu_round_rate,
669*5392c5deSDong Aisheng 	.set_rate = clk_gpr_div_scu_set_rate,
670*5392c5deSDong Aisheng };
671*5392c5deSDong Aisheng 
672*5392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw)
673*5392c5deSDong Aisheng {
674*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
675*5392c5deSDong Aisheng 	u32 val = 0;
676*5392c5deSDong Aisheng 
677*5392c5deSDong Aisheng 	imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
678*5392c5deSDong Aisheng 				clk->gpr_id, &val);
679*5392c5deSDong Aisheng 
680*5392c5deSDong Aisheng 	return (u8)val;
681*5392c5deSDong Aisheng }
682*5392c5deSDong Aisheng 
683*5392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
684*5392c5deSDong Aisheng {
685*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
686*5392c5deSDong Aisheng 
687*5392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
688*5392c5deSDong Aisheng 				       clk->gpr_id, index);
689*5392c5deSDong Aisheng }
690*5392c5deSDong Aisheng 
691*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = {
692*5392c5deSDong Aisheng 	.get_parent = clk_gpr_mux_scu_get_parent,
693*5392c5deSDong Aisheng 	.set_parent = clk_gpr_mux_scu_set_parent,
694*5392c5deSDong Aisheng };
695*5392c5deSDong Aisheng 
696*5392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw)
697*5392c5deSDong Aisheng {
698*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
699*5392c5deSDong Aisheng 
700*5392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
701*5392c5deSDong Aisheng 				       clk->gpr_id, !clk->gate_invert);
702*5392c5deSDong Aisheng }
703*5392c5deSDong Aisheng 
704*5392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw)
705*5392c5deSDong Aisheng {
706*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
707*5392c5deSDong Aisheng 	int ret;
708*5392c5deSDong Aisheng 
709*5392c5deSDong Aisheng 	ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
710*5392c5deSDong Aisheng 				      clk->gpr_id, clk->gate_invert);
711*5392c5deSDong Aisheng 	if (ret)
712*5392c5deSDong Aisheng 		pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
713*5392c5deSDong Aisheng 		       ret);
714*5392c5deSDong Aisheng }
715*5392c5deSDong Aisheng 
716*5392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw)
717*5392c5deSDong Aisheng {
718*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
719*5392c5deSDong Aisheng 	int ret;
720*5392c5deSDong Aisheng 	u32 val;
721*5392c5deSDong Aisheng 
722*5392c5deSDong Aisheng 	ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
723*5392c5deSDong Aisheng 				      clk->gpr_id, &val);
724*5392c5deSDong Aisheng 	if (ret)
725*5392c5deSDong Aisheng 		return ret;
726*5392c5deSDong Aisheng 
727*5392c5deSDong Aisheng 	return clk->gate_invert ? !val : val;
728*5392c5deSDong Aisheng }
729*5392c5deSDong Aisheng 
730*5392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = {
731*5392c5deSDong Aisheng 	.prepare = clk_gpr_gate_scu_prepare,
732*5392c5deSDong Aisheng 	.unprepare = clk_gpr_gate_scu_unprepare,
733*5392c5deSDong Aisheng 	.is_prepared = clk_gpr_gate_scu_is_prepared,
734*5392c5deSDong Aisheng };
735*5392c5deSDong Aisheng 
736*5392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name,
737*5392c5deSDong Aisheng 				 int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags,
738*5392c5deSDong Aisheng 				 bool invert)
739*5392c5deSDong Aisheng {
740*5392c5deSDong Aisheng 	struct imx_scu_clk_node *clk_node;
741*5392c5deSDong Aisheng 	struct clk_gpr_scu *clk;
742*5392c5deSDong Aisheng 	struct clk_hw *hw;
743*5392c5deSDong Aisheng 	struct clk_init_data init;
744*5392c5deSDong Aisheng 	int ret;
745*5392c5deSDong Aisheng 
746*5392c5deSDong Aisheng 	if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST)
747*5392c5deSDong Aisheng 		return ERR_PTR(-EINVAL);
748*5392c5deSDong Aisheng 
749*5392c5deSDong Aisheng 	clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL);
750*5392c5deSDong Aisheng 	if (!clk_node)
751*5392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
752*5392c5deSDong Aisheng 
753*5392c5deSDong Aisheng 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
754*5392c5deSDong Aisheng 	if (!clk) {
755*5392c5deSDong Aisheng 		kfree(clk_node);
756*5392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
757*5392c5deSDong Aisheng 	}
758*5392c5deSDong Aisheng 
759*5392c5deSDong Aisheng 	clk->rsrc_id = rsrc_id;
760*5392c5deSDong Aisheng 	clk->gpr_id = gpr_id;
761*5392c5deSDong Aisheng 	clk->flags = flags;
762*5392c5deSDong Aisheng 	clk->gate_invert = invert;
763*5392c5deSDong Aisheng 
764*5392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_GATE)
765*5392c5deSDong Aisheng 		init.ops = &clk_gpr_gate_scu_ops;
766*5392c5deSDong Aisheng 
767*5392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_DIV)
768*5392c5deSDong Aisheng 		init.ops = &clk_gpr_div_scu_ops;
769*5392c5deSDong Aisheng 
770*5392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_MUX)
771*5392c5deSDong Aisheng 		init.ops = &clk_gpr_mux_scu_ops;
772*5392c5deSDong Aisheng 
773*5392c5deSDong Aisheng 	init.flags = 0;
774*5392c5deSDong Aisheng 	init.name = name;
775*5392c5deSDong Aisheng 	init.parent_names = parent_name;
776*5392c5deSDong Aisheng 	init.num_parents = num_parents;
777*5392c5deSDong Aisheng 
778*5392c5deSDong Aisheng 	clk->hw.init = &init;
779*5392c5deSDong Aisheng 
780*5392c5deSDong Aisheng 	hw = &clk->hw;
781*5392c5deSDong Aisheng 	ret = clk_hw_register(NULL, hw);
782*5392c5deSDong Aisheng 	if (ret) {
783*5392c5deSDong Aisheng 		kfree(clk);
784*5392c5deSDong Aisheng 		kfree(clk_node);
785*5392c5deSDong Aisheng 		hw = ERR_PTR(ret);
786*5392c5deSDong Aisheng 	} else {
787*5392c5deSDong Aisheng 		clk_node->hw = hw;
788*5392c5deSDong Aisheng 		clk_node->clk_type = gpr_id;
789*5392c5deSDong Aisheng 		list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]);
790*5392c5deSDong Aisheng 	}
791*5392c5deSDong Aisheng 
792*5392c5deSDong Aisheng 	return hw;
793*5392c5deSDong Aisheng }
794