xref: /openbmc/linux/drivers/clk/imx/clk-scu.c (revision a61cea83)
1fe37b482SAisheng Dong // SPDX-License-Identifier: GPL-2.0+
2fe37b482SAisheng Dong /*
35964012cSDong Aisheng  * Copyright 2018-2021 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>
95964012cSDong Aisheng #include <linux/bsearch.h>
10fe37b482SAisheng Dong #include <linux/clk-provider.h>
11fe37b482SAisheng Dong #include <linux/err.h>
1277d8f306SDong Aisheng #include <linux/of_platform.h>
1377d8f306SDong Aisheng #include <linux/platform_device.h>
1477d8f306SDong Aisheng #include <linux/pm_domain.h>
1578edeb08SDong Aisheng #include <linux/pm_runtime.h>
16fe37b482SAisheng Dong #include <linux/slab.h>
17fe37b482SAisheng Dong 
18fe37b482SAisheng Dong #include "clk-scu.h"
19fe37b482SAisheng Dong 
203b9ea606SAnson Huang #define IMX_SIP_CPUFREQ			0xC2000001
213b9ea606SAnson Huang #define IMX_SIP_SET_CPUFREQ		0x00
223b9ea606SAnson Huang 
23fe37b482SAisheng Dong static struct imx_sc_ipc *ccm_ipc_handle;
24550b562aSZou Wei static struct device_node *pd_np;
25220175cdSDong Aisheng static struct platform_driver imx_clk_scu_driver;
265964012cSDong Aisheng static const struct imx_clk_scu_rsrc_table *rsrc_table;
2777d8f306SDong Aisheng 
2877d8f306SDong Aisheng struct imx_scu_clk_node {
2977d8f306SDong Aisheng 	const char *name;
3077d8f306SDong Aisheng 	u32 rsrc;
3177d8f306SDong Aisheng 	u8 clk_type;
3277d8f306SDong Aisheng 	const char * const *parents;
3377d8f306SDong Aisheng 	int num_parents;
3477d8f306SDong Aisheng 
3577d8f306SDong Aisheng 	struct clk_hw *hw;
3677d8f306SDong Aisheng 	struct list_head node;
3777d8f306SDong Aisheng };
3877d8f306SDong Aisheng 
3977d8f306SDong Aisheng struct list_head imx_scu_clks[IMX_SC_R_LAST];
40fe37b482SAisheng Dong 
41fe37b482SAisheng Dong /*
42fe37b482SAisheng Dong  * struct clk_scu - Description of one SCU clock
43fe37b482SAisheng Dong  * @hw: the common clk_hw
44fe37b482SAisheng Dong  * @rsrc_id: resource ID of this SCU clock
45fe37b482SAisheng Dong  * @clk_type: type of this clock resource
46fe37b482SAisheng Dong  */
47fe37b482SAisheng Dong struct clk_scu {
48fe37b482SAisheng Dong 	struct clk_hw hw;
49fe37b482SAisheng Dong 	u16 rsrc_id;
50fe37b482SAisheng Dong 	u8 clk_type;
51d0409631SDong Aisheng 
52d0409631SDong Aisheng 	/* for state save&restore */
53d0409631SDong Aisheng 	bool is_enabled;
54d0409631SDong Aisheng 	u32 rate;
55fe37b482SAisheng Dong };
56fe37b482SAisheng Dong 
57fe37b482SAisheng Dong /*
585392c5deSDong Aisheng  * struct clk_gpr_scu - Description of one SCU GPR clock
595392c5deSDong Aisheng  * @hw: the common clk_hw
605392c5deSDong Aisheng  * @rsrc_id: resource ID of this SCU clock
615392c5deSDong Aisheng  * @gpr_id: GPR ID index to control the divider
625392c5deSDong Aisheng  */
635392c5deSDong Aisheng struct clk_gpr_scu {
645392c5deSDong Aisheng 	struct clk_hw hw;
655392c5deSDong Aisheng 	u16 rsrc_id;
665392c5deSDong Aisheng 	u8 gpr_id;
675392c5deSDong Aisheng 	u8 flags;
685392c5deSDong Aisheng 	bool gate_invert;
695392c5deSDong Aisheng };
705392c5deSDong Aisheng 
715392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw)
725392c5deSDong Aisheng 
735392c5deSDong Aisheng /*
74fe37b482SAisheng Dong  * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
75fe37b482SAisheng Dong  * @hdr: SCU protocol header
76fe37b482SAisheng Dong  * @rate: rate to set
77fe37b482SAisheng Dong  * @resource: clock resource to set rate
78fe37b482SAisheng Dong  * @clk: clk type of this resource
79fe37b482SAisheng Dong  *
80fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate set
81fe37b482SAisheng Dong  */
82fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate {
83fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
84fe37b482SAisheng Dong 	__le32 rate;
85fe37b482SAisheng Dong 	__le16 resource;
86fe37b482SAisheng Dong 	u8 clk;
87a0ae04a2SLeonard Crestez } __packed __aligned(4);
88fe37b482SAisheng Dong 
89fe37b482SAisheng Dong struct req_get_clock_rate {
90fe37b482SAisheng Dong 	__le16 resource;
91fe37b482SAisheng Dong 	u8 clk;
92a0ae04a2SLeonard Crestez } __packed __aligned(4);
93fe37b482SAisheng Dong 
94fe37b482SAisheng Dong struct resp_get_clock_rate {
95fe37b482SAisheng Dong 	__le32 rate;
96fe37b482SAisheng Dong };
97fe37b482SAisheng Dong 
98fe37b482SAisheng Dong /*
99fe37b482SAisheng Dong  * struct imx_sc_msg_get_clock_rate - clock get rate protocol
100fe37b482SAisheng Dong  * @hdr: SCU protocol header
101fe37b482SAisheng Dong  * @req: get rate request protocol
102fe37b482SAisheng Dong  * @resp: get rate response protocol
103fe37b482SAisheng Dong  *
104fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate get
105fe37b482SAisheng Dong  */
106fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate {
107fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
108fe37b482SAisheng Dong 	union {
109fe37b482SAisheng Dong 		struct req_get_clock_rate req;
110fe37b482SAisheng Dong 		struct resp_get_clock_rate resp;
111fe37b482SAisheng Dong 	} data;
112fe37b482SAisheng Dong };
113fe37b482SAisheng Dong 
114fe37b482SAisheng Dong /*
115666aed2dSAisheng Dong  * struct imx_sc_msg_get_clock_parent - clock get parent protocol
116666aed2dSAisheng Dong  * @hdr: SCU protocol header
117666aed2dSAisheng Dong  * @req: get parent request protocol
118666aed2dSAisheng Dong  * @resp: get parent response protocol
119666aed2dSAisheng Dong  *
120666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock get parent
121666aed2dSAisheng Dong  */
122666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent {
123666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
124666aed2dSAisheng Dong 	union {
125666aed2dSAisheng Dong 		struct req_get_clock_parent {
126666aed2dSAisheng Dong 			__le16 resource;
127666aed2dSAisheng Dong 			u8 clk;
1288400ab88SLeonard Crestez 		} __packed __aligned(4) req;
129666aed2dSAisheng Dong 		struct resp_get_clock_parent {
130666aed2dSAisheng Dong 			u8 parent;
131666aed2dSAisheng Dong 		} resp;
132666aed2dSAisheng Dong 	} data;
133666aed2dSAisheng Dong };
134666aed2dSAisheng Dong 
135666aed2dSAisheng Dong /*
136666aed2dSAisheng Dong  * struct imx_sc_msg_set_clock_parent - clock set parent protocol
137666aed2dSAisheng Dong  * @hdr: SCU protocol header
138666aed2dSAisheng Dong  * @req: set parent request protocol
139666aed2dSAisheng Dong  *
140666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock set parent
141666aed2dSAisheng Dong  */
142666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent {
143666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
144666aed2dSAisheng Dong 	__le16 resource;
145666aed2dSAisheng Dong 	u8 clk;
146666aed2dSAisheng Dong 	u8 parent;
147666aed2dSAisheng Dong } __packed;
148666aed2dSAisheng Dong 
149666aed2dSAisheng Dong /*
150fe37b482SAisheng Dong  * struct imx_sc_msg_req_clock_enable - clock gate protocol
151fe37b482SAisheng Dong  * @hdr: SCU protocol header
152fe37b482SAisheng Dong  * @resource: clock resource to gate
153fe37b482SAisheng Dong  * @clk: clk type of this resource
154fe37b482SAisheng Dong  * @enable: whether gate off the clock
155fe37b482SAisheng Dong  * @autog: HW auto gate enable
156fe37b482SAisheng Dong  *
157fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock gate
158fe37b482SAisheng Dong  */
159fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable {
160fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
161fe37b482SAisheng Dong 	__le16 resource;
162fe37b482SAisheng Dong 	u8 clk;
163fe37b482SAisheng Dong 	u8 enable;
164fe37b482SAisheng Dong 	u8 autog;
165a0ae04a2SLeonard Crestez } __packed __aligned(4);
166fe37b482SAisheng Dong 
167fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
168fe37b482SAisheng Dong {
169fe37b482SAisheng Dong 	return container_of(hw, struct clk_scu, hw);
170fe37b482SAisheng Dong }
171fe37b482SAisheng Dong 
1725964012cSDong Aisheng static inline int imx_scu_clk_search_cmp(const void *rsrc, const void *rsrc_p)
1735964012cSDong Aisheng {
1745964012cSDong Aisheng 	return *(u32 *)rsrc - *(u32 *)rsrc_p;
1755964012cSDong Aisheng }
1765964012cSDong Aisheng 
1775964012cSDong Aisheng static bool imx_scu_clk_is_valid(u32 rsrc_id)
1785964012cSDong Aisheng {
1795964012cSDong Aisheng 	void *p;
1805964012cSDong Aisheng 
1815964012cSDong Aisheng 	if (!rsrc_table)
1825964012cSDong Aisheng 		return true;
1835964012cSDong Aisheng 
1845964012cSDong Aisheng 	p = bsearch(&rsrc_id, rsrc_table->rsrc, rsrc_table->num,
1855964012cSDong Aisheng 		    sizeof(rsrc_table->rsrc[0]), imx_scu_clk_search_cmp);
1865964012cSDong Aisheng 
1875964012cSDong Aisheng 	return p != NULL;
1885964012cSDong Aisheng }
1895964012cSDong Aisheng 
1905964012cSDong Aisheng int imx_clk_scu_init(struct device_node *np,
1915964012cSDong Aisheng 		     const struct imx_clk_scu_rsrc_table *data)
192fe37b482SAisheng Dong {
19377d8f306SDong Aisheng 	u32 clk_cells;
19477d8f306SDong Aisheng 	int ret, i;
19577d8f306SDong Aisheng 
19677d8f306SDong Aisheng 	ret = imx_scu_get_handle(&ccm_ipc_handle);
19777d8f306SDong Aisheng 	if (ret)
19877d8f306SDong Aisheng 		return ret;
19977d8f306SDong Aisheng 
20077d8f306SDong Aisheng 	of_property_read_u32(np, "#clock-cells", &clk_cells);
20177d8f306SDong Aisheng 
20277d8f306SDong Aisheng 	if (clk_cells == 2) {
20377d8f306SDong Aisheng 		for (i = 0; i < IMX_SC_R_LAST; i++)
20477d8f306SDong Aisheng 			INIT_LIST_HEAD(&imx_scu_clks[i]);
20543d24796SDong Aisheng 
20643d24796SDong Aisheng 		/* pd_np will be used to attach power domains later */
20777d8f306SDong Aisheng 		pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd");
20843d24796SDong Aisheng 		if (!pd_np)
20943d24796SDong Aisheng 			return -EINVAL;
2105964012cSDong Aisheng 
2115964012cSDong Aisheng 		rsrc_table = data;
21277d8f306SDong Aisheng 	}
21377d8f306SDong Aisheng 
214220175cdSDong Aisheng 	return platform_driver_register(&imx_clk_scu_driver);
215fe37b482SAisheng Dong }
216fe37b482SAisheng Dong 
217fe37b482SAisheng Dong /*
218fe37b482SAisheng Dong  * clk_scu_recalc_rate - Get clock rate for a SCU clock
219fe37b482SAisheng Dong  * @hw: clock to get rate for
220fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
221fe37b482SAisheng Dong  *
222fe37b482SAisheng Dong  * Gets the current clock rate of a SCU clock. Returns the current
223fe37b482SAisheng Dong  * clock rate, or zero in failure.
224fe37b482SAisheng Dong  */
225fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
226fe37b482SAisheng Dong 					 unsigned long parent_rate)
227fe37b482SAisheng Dong {
228fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
229fe37b482SAisheng Dong 	struct imx_sc_msg_get_clock_rate msg;
230fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
231fe37b482SAisheng Dong 	int ret;
232fe37b482SAisheng Dong 
233fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
234fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
235fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
236fe37b482SAisheng Dong 	hdr->size = 2;
237fe37b482SAisheng Dong 
238fe37b482SAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
239fe37b482SAisheng Dong 	msg.data.req.clk = clk->clk_type;
240fe37b482SAisheng Dong 
241fe37b482SAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
242fe37b482SAisheng Dong 	if (ret) {
243fe37b482SAisheng Dong 		pr_err("%s: failed to get clock rate %d\n",
244fe37b482SAisheng Dong 		       clk_hw_get_name(hw), ret);
245fe37b482SAisheng Dong 		return 0;
246fe37b482SAisheng Dong 	}
247fe37b482SAisheng Dong 
248fe37b482SAisheng Dong 	return le32_to_cpu(msg.data.resp.rate);
249fe37b482SAisheng Dong }
250fe37b482SAisheng Dong 
251fe37b482SAisheng Dong /*
252fe37b482SAisheng Dong  * clk_scu_round_rate - Round clock rate for a SCU clock
253fe37b482SAisheng Dong  * @hw: clock to round rate for
254fe37b482SAisheng Dong  * @rate: rate to round
255fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
256fe37b482SAisheng Dong  *
257fe37b482SAisheng Dong  * Returns the current clock rate, or zero in failure.
258fe37b482SAisheng Dong  */
259fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
260fe37b482SAisheng Dong 			       unsigned long *parent_rate)
261fe37b482SAisheng Dong {
262fe37b482SAisheng Dong 	/*
263fe37b482SAisheng Dong 	 * Assume we support all the requested rate and let the SCU firmware
264fe37b482SAisheng Dong 	 * to handle the left work
265fe37b482SAisheng Dong 	 */
266fe37b482SAisheng Dong 	return rate;
267fe37b482SAisheng Dong }
268fe37b482SAisheng Dong 
2693b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
2703b9ea606SAnson Huang 				    unsigned long parent_rate)
2713b9ea606SAnson Huang {
2723b9ea606SAnson Huang 	struct clk_scu *clk = to_clk_scu(hw);
2733b9ea606SAnson Huang 	struct arm_smccc_res res;
2743b9ea606SAnson Huang 	unsigned long cluster_id;
2753b9ea606SAnson Huang 
2763b9ea606SAnson Huang 	if (clk->rsrc_id == IMX_SC_R_A35)
2773b9ea606SAnson Huang 		cluster_id = 0;
2783b9ea606SAnson Huang 	else
2793b9ea606SAnson Huang 		return -EINVAL;
2803b9ea606SAnson Huang 
2813b9ea606SAnson Huang 	/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
2823b9ea606SAnson Huang 	arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
2833b9ea606SAnson Huang 		      cluster_id, rate, 0, 0, 0, 0, &res);
2843b9ea606SAnson Huang 
2853b9ea606SAnson Huang 	return 0;
2863b9ea606SAnson Huang }
2873b9ea606SAnson Huang 
288fe37b482SAisheng Dong /*
289fe37b482SAisheng Dong  * clk_scu_set_rate - Set rate for a SCU clock
290fe37b482SAisheng Dong  * @hw: clock to change rate for
291fe37b482SAisheng Dong  * @rate: target rate for the clock
292fe37b482SAisheng Dong  * @parent_rate: rate of the clock parent, not used for SCU clocks
293fe37b482SAisheng Dong  *
294fe37b482SAisheng Dong  * Sets a clock frequency for a SCU clock. Returns the SCU
295fe37b482SAisheng Dong  * protocol status.
296fe37b482SAisheng Dong  */
297fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
298fe37b482SAisheng Dong 			    unsigned long parent_rate)
299fe37b482SAisheng Dong {
300fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
301fe37b482SAisheng Dong 	struct imx_sc_msg_req_set_clock_rate msg;
302fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
303fe37b482SAisheng Dong 
304fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
305fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
306fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
307fe37b482SAisheng Dong 	hdr->size = 3;
308fe37b482SAisheng Dong 
309fe37b482SAisheng Dong 	msg.rate = cpu_to_le32(rate);
310fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
311fe37b482SAisheng Dong 	msg.clk = clk->clk_type;
312fe37b482SAisheng Dong 
313fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
314fe37b482SAisheng Dong }
315fe37b482SAisheng Dong 
316666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw)
317666aed2dSAisheng Dong {
318666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
319666aed2dSAisheng Dong 	struct imx_sc_msg_get_clock_parent msg;
320666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
321666aed2dSAisheng Dong 	int ret;
322666aed2dSAisheng Dong 
323666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
324666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
325666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
326666aed2dSAisheng Dong 	hdr->size = 2;
327666aed2dSAisheng Dong 
328666aed2dSAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
329666aed2dSAisheng Dong 	msg.data.req.clk = clk->clk_type;
330666aed2dSAisheng Dong 
331666aed2dSAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
332666aed2dSAisheng Dong 	if (ret) {
333666aed2dSAisheng Dong 		pr_err("%s: failed to get clock parent %d\n",
334666aed2dSAisheng Dong 		       clk_hw_get_name(hw), ret);
335666aed2dSAisheng Dong 		return 0;
336666aed2dSAisheng Dong 	}
337666aed2dSAisheng Dong 
338666aed2dSAisheng Dong 	return msg.data.resp.parent;
339666aed2dSAisheng Dong }
340666aed2dSAisheng Dong 
341666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
342666aed2dSAisheng Dong {
343666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
344666aed2dSAisheng Dong 	struct imx_sc_msg_set_clock_parent msg;
345666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
346666aed2dSAisheng Dong 
347666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
348666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
349666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
350666aed2dSAisheng Dong 	hdr->size = 2;
351666aed2dSAisheng Dong 
352666aed2dSAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
353666aed2dSAisheng Dong 	msg.clk = clk->clk_type;
354666aed2dSAisheng Dong 	msg.parent = index;
355666aed2dSAisheng Dong 
356666aed2dSAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
357666aed2dSAisheng Dong }
358666aed2dSAisheng Dong 
359fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
360fe37b482SAisheng Dong 			      u8 clk, bool enable, bool autog)
361fe37b482SAisheng Dong {
362fe37b482SAisheng Dong 	struct imx_sc_msg_req_clock_enable msg;
363fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
364fe37b482SAisheng Dong 
365fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
366fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
367fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
368fe37b482SAisheng Dong 	hdr->size = 3;
369fe37b482SAisheng Dong 
370fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(resource);
371fe37b482SAisheng Dong 	msg.clk = clk;
372fe37b482SAisheng Dong 	msg.enable = enable;
373fe37b482SAisheng Dong 	msg.autog = autog;
374fe37b482SAisheng Dong 
375fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
376fe37b482SAisheng Dong }
377fe37b482SAisheng Dong 
378fe37b482SAisheng Dong /*
379fe37b482SAisheng Dong  * clk_scu_prepare - Enable a SCU clock
380fe37b482SAisheng Dong  * @hw: clock to enable
381fe37b482SAisheng Dong  *
382fe37b482SAisheng Dong  * Enable the clock at the DSC slice level
383fe37b482SAisheng Dong  */
384fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw)
385fe37b482SAisheng Dong {
386fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
387fe37b482SAisheng Dong 
388fe37b482SAisheng Dong 	return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
389fe37b482SAisheng Dong 				  clk->clk_type, true, false);
390fe37b482SAisheng Dong }
391fe37b482SAisheng Dong 
392fe37b482SAisheng Dong /*
393fe37b482SAisheng Dong  * clk_scu_unprepare - Disable a SCU clock
394fe37b482SAisheng Dong  * @hw: clock to enable
395fe37b482SAisheng Dong  *
396fe37b482SAisheng Dong  * Disable the clock at the DSC slice level
397fe37b482SAisheng Dong  */
398fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw)
399fe37b482SAisheng Dong {
400fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
401fe37b482SAisheng Dong 	int ret;
402fe37b482SAisheng Dong 
403fe37b482SAisheng Dong 	ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
404fe37b482SAisheng Dong 				 clk->clk_type, false, false);
405fe37b482SAisheng Dong 	if (ret)
406fe37b482SAisheng Dong 		pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
407fe37b482SAisheng Dong 			ret);
408fe37b482SAisheng Dong }
409fe37b482SAisheng Dong 
410fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = {
411fe37b482SAisheng Dong 	.recalc_rate = clk_scu_recalc_rate,
412fe37b482SAisheng Dong 	.round_rate = clk_scu_round_rate,
413fe37b482SAisheng Dong 	.set_rate = clk_scu_set_rate,
414666aed2dSAisheng Dong 	.get_parent = clk_scu_get_parent,
415666aed2dSAisheng Dong 	.set_parent = clk_scu_set_parent,
416fe37b482SAisheng Dong 	.prepare = clk_scu_prepare,
417fe37b482SAisheng Dong 	.unprepare = clk_scu_unprepare,
418fe37b482SAisheng Dong };
419fe37b482SAisheng Dong 
4203b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = {
4213b9ea606SAnson Huang 	.recalc_rate = clk_scu_recalc_rate,
4223b9ea606SAnson Huang 	.round_rate = clk_scu_round_rate,
4233b9ea606SAnson Huang 	.set_rate = clk_scu_atf_set_cpu_rate,
4243b9ea606SAnson Huang 	.prepare = clk_scu_prepare,
4253b9ea606SAnson Huang 	.unprepare = clk_scu_unprepare,
4263b9ea606SAnson Huang };
4273b9ea606SAnson Huang 
428b192d040SGuoniu.zhou static const struct clk_ops clk_scu_pi_ops = {
429b192d040SGuoniu.zhou 	.recalc_rate = clk_scu_recalc_rate,
430b192d040SGuoniu.zhou 	.round_rate  = clk_scu_round_rate,
431b192d040SGuoniu.zhou 	.set_rate    = clk_scu_set_rate,
432b192d040SGuoniu.zhou };
433b192d040SGuoniu.zhou 
4342f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
4352f1a2c1dSDong Aisheng 			     const char * const *parents, int num_parents,
4362f1a2c1dSDong Aisheng 			     u32 rsrc_id, u8 clk_type)
437fe37b482SAisheng Dong {
438fe37b482SAisheng Dong 	struct clk_init_data init;
439fe37b482SAisheng Dong 	struct clk_scu *clk;
440fe37b482SAisheng Dong 	struct clk_hw *hw;
441fe37b482SAisheng Dong 	int ret;
442fe37b482SAisheng Dong 
443fe37b482SAisheng Dong 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
444fe37b482SAisheng Dong 	if (!clk)
445fe37b482SAisheng Dong 		return ERR_PTR(-ENOMEM);
446fe37b482SAisheng Dong 
447fe37b482SAisheng Dong 	clk->rsrc_id = rsrc_id;
448fe37b482SAisheng Dong 	clk->clk_type = clk_type;
449fe37b482SAisheng Dong 
450fe37b482SAisheng Dong 	init.name = name;
451fe37b482SAisheng Dong 	init.ops = &clk_scu_ops;
4523b9ea606SAnson Huang 	if (rsrc_id == IMX_SC_R_A35)
4533b9ea606SAnson Huang 		init.ops = &clk_scu_cpu_ops;
454b192d040SGuoniu.zhou 	else if (rsrc_id == IMX_SC_R_PI_0_PLL)
455b192d040SGuoniu.zhou 		init.ops = &clk_scu_pi_ops;
4563b9ea606SAnson Huang 	else
4573b9ea606SAnson Huang 		init.ops = &clk_scu_ops;
458666aed2dSAisheng Dong 	init.parent_names = parents;
459666aed2dSAisheng Dong 	init.num_parents = num_parents;
460666aed2dSAisheng Dong 
461fe37b482SAisheng Dong 	/*
462fe37b482SAisheng Dong 	 * Note on MX8, the clocks are tightly coupled with power domain
463fe37b482SAisheng Dong 	 * that once the power domain is off, the clock status may be
464fe37b482SAisheng Dong 	 * lost. So we make it NOCACHE to let user to retrieve the real
465fe37b482SAisheng Dong 	 * clock status from HW instead of using the possible invalid
466fe37b482SAisheng Dong 	 * cached rate.
467fe37b482SAisheng Dong 	 */
468fe37b482SAisheng Dong 	init.flags = CLK_GET_RATE_NOCACHE;
469fe37b482SAisheng Dong 	clk->hw.init = &init;
470fe37b482SAisheng Dong 
471fe37b482SAisheng Dong 	hw = &clk->hw;
4722f1a2c1dSDong Aisheng 	ret = clk_hw_register(dev, hw);
473fe37b482SAisheng Dong 	if (ret) {
474fe37b482SAisheng Dong 		kfree(clk);
475fe37b482SAisheng Dong 		hw = ERR_PTR(ret);
476054ef44eSJian Dong 		return hw;
477fe37b482SAisheng Dong 	}
478fe37b482SAisheng Dong 
479d0409631SDong Aisheng 	if (dev)
480d0409631SDong Aisheng 		dev_set_drvdata(dev, clk);
481d0409631SDong Aisheng 
482fe37b482SAisheng Dong 	return hw;
483fe37b482SAisheng Dong }
48477d8f306SDong Aisheng 
48577d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
48677d8f306SDong Aisheng 				      void *data)
48777d8f306SDong Aisheng {
48877d8f306SDong Aisheng 	unsigned int rsrc = clkspec->args[0];
48977d8f306SDong Aisheng 	unsigned int idx = clkspec->args[1];
49077d8f306SDong Aisheng 	struct list_head *scu_clks = data;
49177d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
49277d8f306SDong Aisheng 
49377d8f306SDong Aisheng 	list_for_each_entry(clk, &scu_clks[rsrc], node) {
49477d8f306SDong Aisheng 		if (clk->clk_type == idx)
49577d8f306SDong Aisheng 			return clk->hw;
49677d8f306SDong Aisheng 	}
49777d8f306SDong Aisheng 
49877d8f306SDong Aisheng 	return ERR_PTR(-ENODEV);
49977d8f306SDong Aisheng }
50077d8f306SDong Aisheng 
50177d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev)
50277d8f306SDong Aisheng {
50377d8f306SDong Aisheng 	struct device *dev = &pdev->dev;
50477d8f306SDong Aisheng 	struct imx_scu_clk_node *clk = dev_get_platdata(dev);
50577d8f306SDong Aisheng 	struct clk_hw *hw;
50678edeb08SDong Aisheng 	int ret;
50777d8f306SDong Aisheng 
50878edeb08SDong Aisheng 	pm_runtime_set_suspended(dev);
50978edeb08SDong Aisheng 	pm_runtime_set_autosuspend_delay(dev, 50);
51078edeb08SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
51178edeb08SDong Aisheng 	pm_runtime_enable(dev);
51278edeb08SDong Aisheng 
51378edeb08SDong Aisheng 	ret = pm_runtime_get_sync(dev);
51478edeb08SDong Aisheng 	if (ret) {
5156618b5aaSDong Aisheng 		pm_genpd_remove_device(dev);
51678edeb08SDong Aisheng 		pm_runtime_disable(dev);
51778edeb08SDong Aisheng 		return ret;
51878edeb08SDong Aisheng 	}
51978edeb08SDong Aisheng 
52078edeb08SDong Aisheng 	hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents,
52177d8f306SDong Aisheng 			   clk->rsrc, clk->clk_type);
52278edeb08SDong Aisheng 	if (IS_ERR(hw)) {
52378edeb08SDong Aisheng 		pm_runtime_disable(dev);
52477d8f306SDong Aisheng 		return PTR_ERR(hw);
52578edeb08SDong Aisheng 	}
52677d8f306SDong Aisheng 
52777d8f306SDong Aisheng 	clk->hw = hw;
52877d8f306SDong Aisheng 	list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]);
52977d8f306SDong Aisheng 
53078edeb08SDong Aisheng 	pm_runtime_mark_last_busy(&pdev->dev);
53178edeb08SDong Aisheng 	pm_runtime_put_autosuspend(&pdev->dev);
53278edeb08SDong Aisheng 
53377d8f306SDong Aisheng 	dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc,
53477d8f306SDong Aisheng 		clk->clk_type);
53577d8f306SDong Aisheng 
53677d8f306SDong Aisheng 	return 0;
53777d8f306SDong Aisheng }
53877d8f306SDong Aisheng 
539d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
540d0409631SDong Aisheng {
541d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
542aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
543aecf425fSDong Aisheng 
544aecf425fSDong Aisheng 	if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) ||
545aecf425fSDong Aisheng 	    (rsrc_id == IMX_SC_R_A72))
546aecf425fSDong Aisheng 		return 0;
547d0409631SDong Aisheng 
548d0409631SDong Aisheng 	clk->rate = clk_hw_get_rate(&clk->hw);
549d0409631SDong Aisheng 	clk->is_enabled = clk_hw_is_enabled(&clk->hw);
550d0409631SDong Aisheng 
551d0409631SDong Aisheng 	if (clk->rate)
552d0409631SDong Aisheng 		dev_dbg(dev, "save rate %d\n", clk->rate);
553d0409631SDong Aisheng 
554d0409631SDong Aisheng 	if (clk->is_enabled)
555d0409631SDong Aisheng 		dev_dbg(dev, "save enabled state\n");
556d0409631SDong Aisheng 
557d0409631SDong Aisheng 	return 0;
558d0409631SDong Aisheng }
559d0409631SDong Aisheng 
560d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev)
561d0409631SDong Aisheng {
562d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
563aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
564d0409631SDong Aisheng 	int ret = 0;
565d0409631SDong Aisheng 
566aecf425fSDong Aisheng 	if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) ||
567aecf425fSDong Aisheng 	    (rsrc_id == IMX_SC_R_A72))
568aecf425fSDong Aisheng 		return 0;
569aecf425fSDong Aisheng 
570d0409631SDong Aisheng 	if (clk->rate) {
571d0409631SDong Aisheng 		ret = clk_scu_set_rate(&clk->hw, clk->rate, 0);
572d0409631SDong Aisheng 		dev_dbg(dev, "restore rate %d %s\n", clk->rate,
573d0409631SDong Aisheng 			!ret ? "success" : "failed");
574d0409631SDong Aisheng 	}
575d0409631SDong Aisheng 
576*a61cea83SDong Aisheng 	if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) {
577d0409631SDong Aisheng 		ret = clk_scu_prepare(&clk->hw);
578d0409631SDong Aisheng 		dev_dbg(dev, "restore enabled state %s\n",
579d0409631SDong Aisheng 			!ret ? "success" : "failed");
580d0409631SDong Aisheng 	}
581d0409631SDong Aisheng 
582d0409631SDong Aisheng 	return ret;
583d0409631SDong Aisheng }
584d0409631SDong Aisheng 
585d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = {
586d0409631SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend,
587d0409631SDong Aisheng 				      imx_clk_scu_resume)
588d0409631SDong Aisheng };
589d0409631SDong Aisheng 
59077d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = {
59177d8f306SDong Aisheng 	.driver = {
59277d8f306SDong Aisheng 		.name = "imx-scu-clk",
59377d8f306SDong Aisheng 		.suppress_bind_attrs = true,
594d0409631SDong Aisheng 		.pm = &imx_clk_scu_pm_ops,
59577d8f306SDong Aisheng 	},
59677d8f306SDong Aisheng 	.probe = imx_clk_scu_probe,
59777d8f306SDong Aisheng };
59877d8f306SDong Aisheng 
59977d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
60077d8f306SDong Aisheng {
60177d8f306SDong Aisheng 	struct of_phandle_args genpdspec = {
60277d8f306SDong Aisheng 		.np = pd_np,
60377d8f306SDong Aisheng 		.args_count = 1,
60477d8f306SDong Aisheng 		.args[0] = rsrc_id,
60577d8f306SDong Aisheng 	};
60677d8f306SDong Aisheng 
6070d5f1f47SDong Aisheng 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 ||
6080d5f1f47SDong Aisheng 	    rsrc_id == IMX_SC_R_A72)
6090d5f1f47SDong Aisheng 		return 0;
6100d5f1f47SDong Aisheng 
61177d8f306SDong Aisheng 	return of_genpd_add_device(&genpdspec, dev);
61277d8f306SDong Aisheng }
61377d8f306SDong Aisheng 
61477d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
61577d8f306SDong Aisheng 				     const char * const *parents,
61677d8f306SDong Aisheng 				     int num_parents, u32 rsrc_id, u8 clk_type)
61777d8f306SDong Aisheng {
61877d8f306SDong Aisheng 	struct imx_scu_clk_node clk = {
61977d8f306SDong Aisheng 		.name = name,
62077d8f306SDong Aisheng 		.rsrc = rsrc_id,
62177d8f306SDong Aisheng 		.clk_type = clk_type,
62277d8f306SDong Aisheng 		.parents = parents,
62377d8f306SDong Aisheng 		.num_parents = num_parents,
62477d8f306SDong Aisheng 	};
62577d8f306SDong Aisheng 	struct platform_device *pdev;
62677d8f306SDong Aisheng 	int ret;
62777d8f306SDong Aisheng 
6285964012cSDong Aisheng 	if (!imx_scu_clk_is_valid(rsrc_id))
6295964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
6305964012cSDong Aisheng 
63177d8f306SDong Aisheng 	pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
63277d8f306SDong Aisheng 	if (!pdev) {
63377d8f306SDong Aisheng 		pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
63477d8f306SDong Aisheng 		       name, rsrc_id, clk_type);
63577d8f306SDong Aisheng 		return ERR_PTR(-ENOMEM);
63677d8f306SDong Aisheng 	}
63777d8f306SDong Aisheng 
63877d8f306SDong Aisheng 	ret = platform_device_add_data(pdev, &clk, sizeof(clk));
63977d8f306SDong Aisheng 	if (ret) {
64077d8f306SDong Aisheng 		platform_device_put(pdev);
64177d8f306SDong Aisheng 		return ERR_PTR(ret);
64277d8f306SDong Aisheng 	}
64377d8f306SDong Aisheng 
64477d8f306SDong Aisheng 	pdev->driver_override = "imx-scu-clk";
64577d8f306SDong Aisheng 
64677d8f306SDong Aisheng 	ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
64777d8f306SDong Aisheng 	if (ret)
64877d8f306SDong Aisheng 		pr_warn("%s: failed to attached the power domain %d\n",
64977d8f306SDong Aisheng 			name, ret);
65077d8f306SDong Aisheng 
65177d8f306SDong Aisheng 	platform_device_add(pdev);
65277d8f306SDong Aisheng 
65377d8f306SDong Aisheng 	/* For API backwards compatiblilty, simply return NULL for success */
65477d8f306SDong Aisheng 	return NULL;
65577d8f306SDong Aisheng }
65677d8f306SDong Aisheng 
65777d8f306SDong Aisheng void imx_clk_scu_unregister(void)
65877d8f306SDong Aisheng {
65977d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
66077d8f306SDong Aisheng 	int i;
66177d8f306SDong Aisheng 
66277d8f306SDong Aisheng 	for (i = 0; i < IMX_SC_R_LAST; i++) {
66377d8f306SDong Aisheng 		list_for_each_entry(clk, &imx_scu_clks[i], node) {
66477d8f306SDong Aisheng 			clk_hw_unregister(clk->hw);
66577d8f306SDong Aisheng 			kfree(clk);
66677d8f306SDong Aisheng 		}
66777d8f306SDong Aisheng 	}
66877d8f306SDong Aisheng }
6695392c5deSDong Aisheng 
6705392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw,
6715392c5deSDong Aisheng 						 unsigned long parent_rate)
6725392c5deSDong Aisheng {
6735392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
6745392c5deSDong Aisheng 	unsigned long rate = 0;
6755392c5deSDong Aisheng 	u32 val;
6765392c5deSDong Aisheng 	int err;
6775392c5deSDong Aisheng 
6785392c5deSDong Aisheng 	err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
6795392c5deSDong Aisheng 				      clk->gpr_id, &val);
6805392c5deSDong Aisheng 
6815392c5deSDong Aisheng 	rate  = val ? parent_rate / 2 : parent_rate;
6825392c5deSDong Aisheng 
6835392c5deSDong Aisheng 	return err ? 0 : rate;
6845392c5deSDong Aisheng }
6855392c5deSDong Aisheng 
6865392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate,
6875392c5deSDong Aisheng 				   unsigned long *prate)
6885392c5deSDong Aisheng {
6895392c5deSDong Aisheng 	if (rate < *prate)
6905392c5deSDong Aisheng 		rate = *prate / 2;
6915392c5deSDong Aisheng 	else
6925392c5deSDong Aisheng 		rate = *prate;
6935392c5deSDong Aisheng 
6945392c5deSDong Aisheng 	return rate;
6955392c5deSDong Aisheng }
6965392c5deSDong Aisheng 
6975392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
6985392c5deSDong Aisheng 				    unsigned long parent_rate)
6995392c5deSDong Aisheng {
7005392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7015392c5deSDong Aisheng 	uint32_t val;
7025392c5deSDong Aisheng 	int err;
7035392c5deSDong Aisheng 
7045392c5deSDong Aisheng 	val = (rate < parent_rate) ? 1 : 0;
7055392c5deSDong Aisheng 	err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7065392c5deSDong Aisheng 				      clk->gpr_id, val);
7075392c5deSDong Aisheng 
7085392c5deSDong Aisheng 	return err ? -EINVAL : 0;
7095392c5deSDong Aisheng }
7105392c5deSDong Aisheng 
7115392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = {
7125392c5deSDong Aisheng 	.recalc_rate = clk_gpr_div_scu_recalc_rate,
7135392c5deSDong Aisheng 	.round_rate = clk_gpr_div_scu_round_rate,
7145392c5deSDong Aisheng 	.set_rate = clk_gpr_div_scu_set_rate,
7155392c5deSDong Aisheng };
7165392c5deSDong Aisheng 
7175392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw)
7185392c5deSDong Aisheng {
7195392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7205392c5deSDong Aisheng 	u32 val = 0;
7215392c5deSDong Aisheng 
7225392c5deSDong Aisheng 	imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7235392c5deSDong Aisheng 				clk->gpr_id, &val);
7245392c5deSDong Aisheng 
7255392c5deSDong Aisheng 	return (u8)val;
7265392c5deSDong Aisheng }
7275392c5deSDong Aisheng 
7285392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
7295392c5deSDong Aisheng {
7305392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7315392c5deSDong Aisheng 
7325392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7335392c5deSDong Aisheng 				       clk->gpr_id, index);
7345392c5deSDong Aisheng }
7355392c5deSDong Aisheng 
7365392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = {
7375392c5deSDong Aisheng 	.get_parent = clk_gpr_mux_scu_get_parent,
7385392c5deSDong Aisheng 	.set_parent = clk_gpr_mux_scu_set_parent,
7395392c5deSDong Aisheng };
7405392c5deSDong Aisheng 
7415392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw)
7425392c5deSDong Aisheng {
7435392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7445392c5deSDong Aisheng 
7455392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7465392c5deSDong Aisheng 				       clk->gpr_id, !clk->gate_invert);
7475392c5deSDong Aisheng }
7485392c5deSDong Aisheng 
7495392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw)
7505392c5deSDong Aisheng {
7515392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7525392c5deSDong Aisheng 	int ret;
7535392c5deSDong Aisheng 
7545392c5deSDong Aisheng 	ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7555392c5deSDong Aisheng 				      clk->gpr_id, clk->gate_invert);
7565392c5deSDong Aisheng 	if (ret)
7575392c5deSDong Aisheng 		pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
7585392c5deSDong Aisheng 		       ret);
7595392c5deSDong Aisheng }
7605392c5deSDong Aisheng 
7615392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw)
7625392c5deSDong Aisheng {
7635392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7645392c5deSDong Aisheng 	int ret;
7655392c5deSDong Aisheng 	u32 val;
7665392c5deSDong Aisheng 
7675392c5deSDong Aisheng 	ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7685392c5deSDong Aisheng 				      clk->gpr_id, &val);
7695392c5deSDong Aisheng 	if (ret)
7705392c5deSDong Aisheng 		return ret;
7715392c5deSDong Aisheng 
7725392c5deSDong Aisheng 	return clk->gate_invert ? !val : val;
7735392c5deSDong Aisheng }
7745392c5deSDong Aisheng 
7755392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = {
7765392c5deSDong Aisheng 	.prepare = clk_gpr_gate_scu_prepare,
7775392c5deSDong Aisheng 	.unprepare = clk_gpr_gate_scu_unprepare,
7785392c5deSDong Aisheng 	.is_prepared = clk_gpr_gate_scu_is_prepared,
7795392c5deSDong Aisheng };
7805392c5deSDong Aisheng 
7815392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name,
7825392c5deSDong Aisheng 				 int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags,
7835392c5deSDong Aisheng 				 bool invert)
7845392c5deSDong Aisheng {
7855392c5deSDong Aisheng 	struct imx_scu_clk_node *clk_node;
7865392c5deSDong Aisheng 	struct clk_gpr_scu *clk;
7875392c5deSDong Aisheng 	struct clk_hw *hw;
7885392c5deSDong Aisheng 	struct clk_init_data init;
7895392c5deSDong Aisheng 	int ret;
7905392c5deSDong Aisheng 
7915392c5deSDong Aisheng 	if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST)
7925392c5deSDong Aisheng 		return ERR_PTR(-EINVAL);
7935392c5deSDong Aisheng 
7945392c5deSDong Aisheng 	clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL);
7955392c5deSDong Aisheng 	if (!clk_node)
7965392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
7975392c5deSDong Aisheng 
7985964012cSDong Aisheng 	if (!imx_scu_clk_is_valid(rsrc_id))
7995964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
8005964012cSDong Aisheng 
8015392c5deSDong Aisheng 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
8025392c5deSDong Aisheng 	if (!clk) {
8035392c5deSDong Aisheng 		kfree(clk_node);
8045392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
8055392c5deSDong Aisheng 	}
8065392c5deSDong Aisheng 
8075392c5deSDong Aisheng 	clk->rsrc_id = rsrc_id;
8085392c5deSDong Aisheng 	clk->gpr_id = gpr_id;
8095392c5deSDong Aisheng 	clk->flags = flags;
8105392c5deSDong Aisheng 	clk->gate_invert = invert;
8115392c5deSDong Aisheng 
8125392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_GATE)
8135392c5deSDong Aisheng 		init.ops = &clk_gpr_gate_scu_ops;
8145392c5deSDong Aisheng 
8155392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_DIV)
8165392c5deSDong Aisheng 		init.ops = &clk_gpr_div_scu_ops;
8175392c5deSDong Aisheng 
8185392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_MUX)
8195392c5deSDong Aisheng 		init.ops = &clk_gpr_mux_scu_ops;
8205392c5deSDong Aisheng 
8215392c5deSDong Aisheng 	init.flags = 0;
8225392c5deSDong Aisheng 	init.name = name;
8235392c5deSDong Aisheng 	init.parent_names = parent_name;
8245392c5deSDong Aisheng 	init.num_parents = num_parents;
8255392c5deSDong Aisheng 
8265392c5deSDong Aisheng 	clk->hw.init = &init;
8275392c5deSDong Aisheng 
8285392c5deSDong Aisheng 	hw = &clk->hw;
8295392c5deSDong Aisheng 	ret = clk_hw_register(NULL, hw);
8305392c5deSDong Aisheng 	if (ret) {
8315392c5deSDong Aisheng 		kfree(clk);
8325392c5deSDong Aisheng 		kfree(clk_node);
8335392c5deSDong Aisheng 		hw = ERR_PTR(ret);
8345392c5deSDong Aisheng 	} else {
8355392c5deSDong Aisheng 		clk_node->hw = hw;
8365392c5deSDong Aisheng 		clk_node->clk_type = gpr_id;
8375392c5deSDong Aisheng 		list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]);
8385392c5deSDong Aisheng 	}
8395392c5deSDong Aisheng 
8405392c5deSDong Aisheng 	return hw;
8415392c5deSDong Aisheng }
842