xref: /openbmc/linux/drivers/clk/imx/clk-scu.c (revision bfd594b3)
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 */
53a82327ccSDong Aisheng 	struct clk_hw *parent;
54a82327ccSDong Aisheng 	u8 parent_index;
55d0409631SDong Aisheng 	bool is_enabled;
56d0409631SDong Aisheng 	u32 rate;
57fe37b482SAisheng Dong };
58fe37b482SAisheng Dong 
59fe37b482SAisheng Dong /*
605392c5deSDong Aisheng  * struct clk_gpr_scu - Description of one SCU GPR clock
615392c5deSDong Aisheng  * @hw: the common clk_hw
625392c5deSDong Aisheng  * @rsrc_id: resource ID of this SCU clock
635392c5deSDong Aisheng  * @gpr_id: GPR ID index to control the divider
645392c5deSDong Aisheng  */
655392c5deSDong Aisheng struct clk_gpr_scu {
665392c5deSDong Aisheng 	struct clk_hw hw;
675392c5deSDong Aisheng 	u16 rsrc_id;
685392c5deSDong Aisheng 	u8 gpr_id;
695392c5deSDong Aisheng 	u8 flags;
705392c5deSDong Aisheng 	bool gate_invert;
715392c5deSDong Aisheng };
725392c5deSDong Aisheng 
735392c5deSDong Aisheng #define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw)
745392c5deSDong Aisheng 
755392c5deSDong Aisheng /*
76fe37b482SAisheng Dong  * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
77fe37b482SAisheng Dong  * @hdr: SCU protocol header
78fe37b482SAisheng Dong  * @rate: rate to set
79fe37b482SAisheng Dong  * @resource: clock resource to set rate
80fe37b482SAisheng Dong  * @clk: clk type of this resource
81fe37b482SAisheng Dong  *
82fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate set
83fe37b482SAisheng Dong  */
84fe37b482SAisheng Dong struct imx_sc_msg_req_set_clock_rate {
85fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
86fe37b482SAisheng Dong 	__le32 rate;
87fe37b482SAisheng Dong 	__le16 resource;
88fe37b482SAisheng Dong 	u8 clk;
89a0ae04a2SLeonard Crestez } __packed __aligned(4);
90fe37b482SAisheng Dong 
91fe37b482SAisheng Dong struct req_get_clock_rate {
92fe37b482SAisheng Dong 	__le16 resource;
93fe37b482SAisheng Dong 	u8 clk;
94a0ae04a2SLeonard Crestez } __packed __aligned(4);
95fe37b482SAisheng Dong 
96fe37b482SAisheng Dong struct resp_get_clock_rate {
97fe37b482SAisheng Dong 	__le32 rate;
98fe37b482SAisheng Dong };
99fe37b482SAisheng Dong 
100fe37b482SAisheng Dong /*
101fe37b482SAisheng Dong  * struct imx_sc_msg_get_clock_rate - clock get rate protocol
102fe37b482SAisheng Dong  * @hdr: SCU protocol header
103fe37b482SAisheng Dong  * @req: get rate request protocol
104fe37b482SAisheng Dong  * @resp: get rate response protocol
105fe37b482SAisheng Dong  *
106fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock rate get
107fe37b482SAisheng Dong  */
108fe37b482SAisheng Dong struct imx_sc_msg_get_clock_rate {
109fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
110fe37b482SAisheng Dong 	union {
111fe37b482SAisheng Dong 		struct req_get_clock_rate req;
112fe37b482SAisheng Dong 		struct resp_get_clock_rate resp;
113fe37b482SAisheng Dong 	} data;
114fe37b482SAisheng Dong };
115fe37b482SAisheng Dong 
116fe37b482SAisheng Dong /*
117666aed2dSAisheng Dong  * struct imx_sc_msg_get_clock_parent - clock get parent protocol
118666aed2dSAisheng Dong  * @hdr: SCU protocol header
119666aed2dSAisheng Dong  * @req: get parent request protocol
120666aed2dSAisheng Dong  * @resp: get parent response protocol
121666aed2dSAisheng Dong  *
122666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock get parent
123666aed2dSAisheng Dong  */
124666aed2dSAisheng Dong struct imx_sc_msg_get_clock_parent {
125666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
126666aed2dSAisheng Dong 	union {
127666aed2dSAisheng Dong 		struct req_get_clock_parent {
128666aed2dSAisheng Dong 			__le16 resource;
129666aed2dSAisheng Dong 			u8 clk;
1308400ab88SLeonard Crestez 		} __packed __aligned(4) req;
131666aed2dSAisheng Dong 		struct resp_get_clock_parent {
132666aed2dSAisheng Dong 			u8 parent;
133666aed2dSAisheng Dong 		} resp;
134666aed2dSAisheng Dong 	} data;
135666aed2dSAisheng Dong };
136666aed2dSAisheng Dong 
137666aed2dSAisheng Dong /*
138666aed2dSAisheng Dong  * struct imx_sc_msg_set_clock_parent - clock set parent protocol
139666aed2dSAisheng Dong  * @hdr: SCU protocol header
140666aed2dSAisheng Dong  * @req: set parent request protocol
141666aed2dSAisheng Dong  *
142666aed2dSAisheng Dong  * This structure describes the SCU protocol of clock set parent
143666aed2dSAisheng Dong  */
144666aed2dSAisheng Dong struct imx_sc_msg_set_clock_parent {
145666aed2dSAisheng Dong 	struct imx_sc_rpc_msg hdr;
146666aed2dSAisheng Dong 	__le16 resource;
147666aed2dSAisheng Dong 	u8 clk;
148666aed2dSAisheng Dong 	u8 parent;
149666aed2dSAisheng Dong } __packed;
150666aed2dSAisheng Dong 
151666aed2dSAisheng Dong /*
152fe37b482SAisheng Dong  * struct imx_sc_msg_req_clock_enable - clock gate protocol
153fe37b482SAisheng Dong  * @hdr: SCU protocol header
154fe37b482SAisheng Dong  * @resource: clock resource to gate
155fe37b482SAisheng Dong  * @clk: clk type of this resource
156fe37b482SAisheng Dong  * @enable: whether gate off the clock
157fe37b482SAisheng Dong  * @autog: HW auto gate enable
158fe37b482SAisheng Dong  *
159fe37b482SAisheng Dong  * This structure describes the SCU protocol of clock gate
160fe37b482SAisheng Dong  */
161fe37b482SAisheng Dong struct imx_sc_msg_req_clock_enable {
162fe37b482SAisheng Dong 	struct imx_sc_rpc_msg hdr;
163fe37b482SAisheng Dong 	__le16 resource;
164fe37b482SAisheng Dong 	u8 clk;
165fe37b482SAisheng Dong 	u8 enable;
166fe37b482SAisheng Dong 	u8 autog;
167a0ae04a2SLeonard Crestez } __packed __aligned(4);
168fe37b482SAisheng Dong 
169fe37b482SAisheng Dong static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
170fe37b482SAisheng Dong {
171fe37b482SAisheng Dong 	return container_of(hw, struct clk_scu, hw);
172fe37b482SAisheng Dong }
173fe37b482SAisheng Dong 
1745964012cSDong Aisheng static inline int imx_scu_clk_search_cmp(const void *rsrc, const void *rsrc_p)
1755964012cSDong Aisheng {
1765964012cSDong Aisheng 	return *(u32 *)rsrc - *(u32 *)rsrc_p;
1775964012cSDong Aisheng }
1785964012cSDong Aisheng 
1795964012cSDong Aisheng static bool imx_scu_clk_is_valid(u32 rsrc_id)
1805964012cSDong Aisheng {
1815964012cSDong Aisheng 	void *p;
1825964012cSDong Aisheng 
1835964012cSDong Aisheng 	if (!rsrc_table)
1845964012cSDong Aisheng 		return true;
1855964012cSDong Aisheng 
1865964012cSDong Aisheng 	p = bsearch(&rsrc_id, rsrc_table->rsrc, rsrc_table->num,
1875964012cSDong Aisheng 		    sizeof(rsrc_table->rsrc[0]), imx_scu_clk_search_cmp);
1885964012cSDong Aisheng 
1895964012cSDong Aisheng 	return p != NULL;
1905964012cSDong Aisheng }
1915964012cSDong Aisheng 
1925964012cSDong Aisheng int imx_clk_scu_init(struct device_node *np,
1935964012cSDong Aisheng 		     const struct imx_clk_scu_rsrc_table *data)
194fe37b482SAisheng Dong {
19577d8f306SDong Aisheng 	u32 clk_cells;
19677d8f306SDong Aisheng 	int ret, i;
19777d8f306SDong Aisheng 
19877d8f306SDong Aisheng 	ret = imx_scu_get_handle(&ccm_ipc_handle);
19977d8f306SDong Aisheng 	if (ret)
20077d8f306SDong Aisheng 		return ret;
20177d8f306SDong Aisheng 
20277d8f306SDong Aisheng 	of_property_read_u32(np, "#clock-cells", &clk_cells);
20377d8f306SDong Aisheng 
20477d8f306SDong Aisheng 	if (clk_cells == 2) {
20577d8f306SDong Aisheng 		for (i = 0; i < IMX_SC_R_LAST; i++)
20677d8f306SDong Aisheng 			INIT_LIST_HEAD(&imx_scu_clks[i]);
20743d24796SDong Aisheng 
20843d24796SDong Aisheng 		/* pd_np will be used to attach power domains later */
20977d8f306SDong Aisheng 		pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd");
21043d24796SDong Aisheng 		if (!pd_np)
21143d24796SDong Aisheng 			return -EINVAL;
2125964012cSDong Aisheng 
2135964012cSDong Aisheng 		rsrc_table = data;
21477d8f306SDong Aisheng 	}
21577d8f306SDong Aisheng 
216220175cdSDong Aisheng 	return platform_driver_register(&imx_clk_scu_driver);
217fe37b482SAisheng Dong }
218fe37b482SAisheng Dong 
219fe37b482SAisheng Dong /*
220fe37b482SAisheng Dong  * clk_scu_recalc_rate - Get clock rate for a SCU clock
221fe37b482SAisheng Dong  * @hw: clock to get rate for
222fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
223fe37b482SAisheng Dong  *
224fe37b482SAisheng Dong  * Gets the current clock rate of a SCU clock. Returns the current
225fe37b482SAisheng Dong  * clock rate, or zero in failure.
226fe37b482SAisheng Dong  */
227fe37b482SAisheng Dong static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
228fe37b482SAisheng Dong 					 unsigned long parent_rate)
229fe37b482SAisheng Dong {
230fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
231fe37b482SAisheng Dong 	struct imx_sc_msg_get_clock_rate msg;
232fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
233fe37b482SAisheng Dong 	int ret;
234fe37b482SAisheng Dong 
235fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
236fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
237fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
238fe37b482SAisheng Dong 	hdr->size = 2;
239fe37b482SAisheng Dong 
240fe37b482SAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
241fe37b482SAisheng Dong 	msg.data.req.clk = clk->clk_type;
242fe37b482SAisheng Dong 
243fe37b482SAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
244fe37b482SAisheng Dong 	if (ret) {
245fe37b482SAisheng Dong 		pr_err("%s: failed to get clock rate %d\n",
246fe37b482SAisheng Dong 		       clk_hw_get_name(hw), ret);
247fe37b482SAisheng Dong 		return 0;
248fe37b482SAisheng Dong 	}
249fe37b482SAisheng Dong 
250fe37b482SAisheng Dong 	return le32_to_cpu(msg.data.resp.rate);
251fe37b482SAisheng Dong }
252fe37b482SAisheng Dong 
253fe37b482SAisheng Dong /*
254fe37b482SAisheng Dong  * clk_scu_round_rate - Round clock rate for a SCU clock
255fe37b482SAisheng Dong  * @hw: clock to round rate for
256fe37b482SAisheng Dong  * @rate: rate to round
257fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
258fe37b482SAisheng Dong  *
259fe37b482SAisheng Dong  * Returns the current clock rate, or zero in failure.
260fe37b482SAisheng Dong  */
261fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
262fe37b482SAisheng Dong 			       unsigned long *parent_rate)
263fe37b482SAisheng Dong {
264fe37b482SAisheng Dong 	/*
265fe37b482SAisheng Dong 	 * Assume we support all the requested rate and let the SCU firmware
266fe37b482SAisheng Dong 	 * to handle the left work
267fe37b482SAisheng Dong 	 */
268fe37b482SAisheng Dong 	return rate;
269fe37b482SAisheng Dong }
270fe37b482SAisheng Dong 
2713b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
2723b9ea606SAnson Huang 				    unsigned long parent_rate)
2733b9ea606SAnson Huang {
2743b9ea606SAnson Huang 	struct clk_scu *clk = to_clk_scu(hw);
2753b9ea606SAnson Huang 	struct arm_smccc_res res;
2763b9ea606SAnson Huang 	unsigned long cluster_id;
2773b9ea606SAnson Huang 
278cd8bd2f3SAnson Huang 	if (clk->rsrc_id == IMX_SC_R_A35 || clk->rsrc_id == IMX_SC_R_A53)
2793b9ea606SAnson Huang 		cluster_id = 0;
280a43f6e8aSAnson Huang 	else if (clk->rsrc_id == IMX_SC_R_A72)
281a43f6e8aSAnson Huang 		cluster_id = 1;
2823b9ea606SAnson Huang 	else
2833b9ea606SAnson Huang 		return -EINVAL;
2843b9ea606SAnson Huang 
2853b9ea606SAnson Huang 	/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
2863b9ea606SAnson Huang 	arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
2873b9ea606SAnson Huang 		      cluster_id, rate, 0, 0, 0, 0, &res);
2883b9ea606SAnson Huang 
2893b9ea606SAnson Huang 	return 0;
2903b9ea606SAnson Huang }
2913b9ea606SAnson Huang 
292fe37b482SAisheng Dong /*
293fe37b482SAisheng Dong  * clk_scu_set_rate - Set rate for a SCU clock
294fe37b482SAisheng Dong  * @hw: clock to change rate for
295fe37b482SAisheng Dong  * @rate: target rate for the clock
296fe37b482SAisheng Dong  * @parent_rate: rate of the clock parent, not used for SCU clocks
297fe37b482SAisheng Dong  *
298fe37b482SAisheng Dong  * Sets a clock frequency for a SCU clock. Returns the SCU
299fe37b482SAisheng Dong  * protocol status.
300fe37b482SAisheng Dong  */
301fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
302fe37b482SAisheng Dong 			    unsigned long parent_rate)
303fe37b482SAisheng Dong {
304fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
305fe37b482SAisheng Dong 	struct imx_sc_msg_req_set_clock_rate msg;
306fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
307fe37b482SAisheng Dong 
308fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
309fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
310fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
311fe37b482SAisheng Dong 	hdr->size = 3;
312fe37b482SAisheng Dong 
313fe37b482SAisheng Dong 	msg.rate = cpu_to_le32(rate);
314fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
315fe37b482SAisheng Dong 	msg.clk = clk->clk_type;
316fe37b482SAisheng Dong 
317fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
318fe37b482SAisheng Dong }
319fe37b482SAisheng Dong 
320666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw)
321666aed2dSAisheng Dong {
322666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
323666aed2dSAisheng Dong 	struct imx_sc_msg_get_clock_parent msg;
324666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
325666aed2dSAisheng Dong 	int ret;
326666aed2dSAisheng Dong 
327666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
328666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
329666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
330666aed2dSAisheng Dong 	hdr->size = 2;
331666aed2dSAisheng Dong 
332666aed2dSAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
333666aed2dSAisheng Dong 	msg.data.req.clk = clk->clk_type;
334666aed2dSAisheng Dong 
335666aed2dSAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
336666aed2dSAisheng Dong 	if (ret) {
337666aed2dSAisheng Dong 		pr_err("%s: failed to get clock parent %d\n",
338666aed2dSAisheng Dong 		       clk_hw_get_name(hw), ret);
339666aed2dSAisheng Dong 		return 0;
340666aed2dSAisheng Dong 	}
341666aed2dSAisheng Dong 
342a82327ccSDong Aisheng 	clk->parent_index = msg.data.resp.parent;
343a82327ccSDong Aisheng 
344666aed2dSAisheng Dong 	return msg.data.resp.parent;
345666aed2dSAisheng Dong }
346666aed2dSAisheng Dong 
347666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
348666aed2dSAisheng Dong {
349666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
350666aed2dSAisheng Dong 	struct imx_sc_msg_set_clock_parent msg;
351666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
352a82327ccSDong Aisheng 	int ret;
353666aed2dSAisheng Dong 
354666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
355666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
356666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
357666aed2dSAisheng Dong 	hdr->size = 2;
358666aed2dSAisheng Dong 
359666aed2dSAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
360666aed2dSAisheng Dong 	msg.clk = clk->clk_type;
361666aed2dSAisheng Dong 	msg.parent = index;
362666aed2dSAisheng Dong 
363a82327ccSDong Aisheng 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
364a82327ccSDong Aisheng 	if (ret) {
365a82327ccSDong Aisheng 		pr_err("%s: failed to set clock parent %d\n",
366a82327ccSDong Aisheng 		       clk_hw_get_name(hw), ret);
367a82327ccSDong Aisheng 		return ret;
368a82327ccSDong Aisheng 	}
369a82327ccSDong Aisheng 
370a82327ccSDong Aisheng 	clk->parent_index = index;
371a82327ccSDong Aisheng 
372a82327ccSDong Aisheng 	return 0;
373666aed2dSAisheng Dong }
374666aed2dSAisheng Dong 
375fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
376fe37b482SAisheng Dong 			      u8 clk, bool enable, bool autog)
377fe37b482SAisheng Dong {
378fe37b482SAisheng Dong 	struct imx_sc_msg_req_clock_enable msg;
379fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
380fe37b482SAisheng Dong 
381fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
382fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
383fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
384fe37b482SAisheng Dong 	hdr->size = 3;
385fe37b482SAisheng Dong 
386fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(resource);
387fe37b482SAisheng Dong 	msg.clk = clk;
388fe37b482SAisheng Dong 	msg.enable = enable;
389fe37b482SAisheng Dong 	msg.autog = autog;
390fe37b482SAisheng Dong 
391fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
392fe37b482SAisheng Dong }
393fe37b482SAisheng Dong 
394fe37b482SAisheng Dong /*
395fe37b482SAisheng Dong  * clk_scu_prepare - Enable a SCU clock
396fe37b482SAisheng Dong  * @hw: clock to enable
397fe37b482SAisheng Dong  *
398fe37b482SAisheng Dong  * Enable the clock at the DSC slice level
399fe37b482SAisheng Dong  */
400fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw)
401fe37b482SAisheng Dong {
402fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
403fe37b482SAisheng Dong 
404fe37b482SAisheng Dong 	return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
405fe37b482SAisheng Dong 				  clk->clk_type, true, false);
406fe37b482SAisheng Dong }
407fe37b482SAisheng Dong 
408fe37b482SAisheng Dong /*
409fe37b482SAisheng Dong  * clk_scu_unprepare - Disable a SCU clock
410fe37b482SAisheng Dong  * @hw: clock to enable
411fe37b482SAisheng Dong  *
412fe37b482SAisheng Dong  * Disable the clock at the DSC slice level
413fe37b482SAisheng Dong  */
414fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw)
415fe37b482SAisheng Dong {
416fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
417fe37b482SAisheng Dong 	int ret;
418fe37b482SAisheng Dong 
419fe37b482SAisheng Dong 	ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
420fe37b482SAisheng Dong 				 clk->clk_type, false, false);
421fe37b482SAisheng Dong 	if (ret)
422fe37b482SAisheng Dong 		pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
423fe37b482SAisheng Dong 			ret);
424fe37b482SAisheng Dong }
425fe37b482SAisheng Dong 
426fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = {
427fe37b482SAisheng Dong 	.recalc_rate = clk_scu_recalc_rate,
428fe37b482SAisheng Dong 	.round_rate = clk_scu_round_rate,
429fe37b482SAisheng Dong 	.set_rate = clk_scu_set_rate,
430666aed2dSAisheng Dong 	.get_parent = clk_scu_get_parent,
431666aed2dSAisheng Dong 	.set_parent = clk_scu_set_parent,
432fe37b482SAisheng Dong 	.prepare = clk_scu_prepare,
433fe37b482SAisheng Dong 	.unprepare = clk_scu_unprepare,
434fe37b482SAisheng Dong };
435fe37b482SAisheng Dong 
4363b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = {
4373b9ea606SAnson Huang 	.recalc_rate = clk_scu_recalc_rate,
4383b9ea606SAnson Huang 	.round_rate = clk_scu_round_rate,
4393b9ea606SAnson Huang 	.set_rate = clk_scu_atf_set_cpu_rate,
4403b9ea606SAnson Huang 	.prepare = clk_scu_prepare,
4413b9ea606SAnson Huang 	.unprepare = clk_scu_unprepare,
4423b9ea606SAnson Huang };
4433b9ea606SAnson Huang 
444b192d040SGuoniu.zhou static const struct clk_ops clk_scu_pi_ops = {
445b192d040SGuoniu.zhou 	.recalc_rate = clk_scu_recalc_rate,
446b192d040SGuoniu.zhou 	.round_rate  = clk_scu_round_rate,
447b192d040SGuoniu.zhou 	.set_rate    = clk_scu_set_rate,
448b192d040SGuoniu.zhou };
449b192d040SGuoniu.zhou 
4502f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
4512f1a2c1dSDong Aisheng 			     const char * const *parents, int num_parents,
4522f1a2c1dSDong Aisheng 			     u32 rsrc_id, u8 clk_type)
453fe37b482SAisheng Dong {
454fe37b482SAisheng Dong 	struct clk_init_data init;
455fe37b482SAisheng Dong 	struct clk_scu *clk;
456fe37b482SAisheng Dong 	struct clk_hw *hw;
457fe37b482SAisheng Dong 	int ret;
458fe37b482SAisheng Dong 
459fe37b482SAisheng Dong 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
460fe37b482SAisheng Dong 	if (!clk)
461fe37b482SAisheng Dong 		return ERR_PTR(-ENOMEM);
462fe37b482SAisheng Dong 
463fe37b482SAisheng Dong 	clk->rsrc_id = rsrc_id;
464fe37b482SAisheng Dong 	clk->clk_type = clk_type;
465fe37b482SAisheng Dong 
466fe37b482SAisheng Dong 	init.name = name;
467fe37b482SAisheng Dong 	init.ops = &clk_scu_ops;
468a43f6e8aSAnson Huang 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72)
4693b9ea606SAnson Huang 		init.ops = &clk_scu_cpu_ops;
470b192d040SGuoniu.zhou 	else if (rsrc_id == IMX_SC_R_PI_0_PLL)
471b192d040SGuoniu.zhou 		init.ops = &clk_scu_pi_ops;
4723b9ea606SAnson Huang 	else
4733b9ea606SAnson Huang 		init.ops = &clk_scu_ops;
474666aed2dSAisheng Dong 	init.parent_names = parents;
475666aed2dSAisheng Dong 	init.num_parents = num_parents;
476666aed2dSAisheng Dong 
477fe37b482SAisheng Dong 	/*
478fe37b482SAisheng Dong 	 * Note on MX8, the clocks are tightly coupled with power domain
479fe37b482SAisheng Dong 	 * that once the power domain is off, the clock status may be
480fe37b482SAisheng Dong 	 * lost. So we make it NOCACHE to let user to retrieve the real
481fe37b482SAisheng Dong 	 * clock status from HW instead of using the possible invalid
482fe37b482SAisheng Dong 	 * cached rate.
483fe37b482SAisheng Dong 	 */
484fe37b482SAisheng Dong 	init.flags = CLK_GET_RATE_NOCACHE;
485fe37b482SAisheng Dong 	clk->hw.init = &init;
486fe37b482SAisheng Dong 
487fe37b482SAisheng Dong 	hw = &clk->hw;
4882f1a2c1dSDong Aisheng 	ret = clk_hw_register(dev, hw);
489fe37b482SAisheng Dong 	if (ret) {
490fe37b482SAisheng Dong 		kfree(clk);
491fe37b482SAisheng Dong 		hw = ERR_PTR(ret);
492054ef44eSJian Dong 		return hw;
493fe37b482SAisheng Dong 	}
494fe37b482SAisheng Dong 
495d0409631SDong Aisheng 	if (dev)
496d0409631SDong Aisheng 		dev_set_drvdata(dev, clk);
497d0409631SDong Aisheng 
498fe37b482SAisheng Dong 	return hw;
499fe37b482SAisheng Dong }
50077d8f306SDong Aisheng 
50177d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
50277d8f306SDong Aisheng 				      void *data)
50377d8f306SDong Aisheng {
50477d8f306SDong Aisheng 	unsigned int rsrc = clkspec->args[0];
50577d8f306SDong Aisheng 	unsigned int idx = clkspec->args[1];
50677d8f306SDong Aisheng 	struct list_head *scu_clks = data;
50777d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
50877d8f306SDong Aisheng 
50977d8f306SDong Aisheng 	list_for_each_entry(clk, &scu_clks[rsrc], node) {
51077d8f306SDong Aisheng 		if (clk->clk_type == idx)
51177d8f306SDong Aisheng 			return clk->hw;
51277d8f306SDong Aisheng 	}
51377d8f306SDong Aisheng 
51477d8f306SDong Aisheng 	return ERR_PTR(-ENODEV);
51577d8f306SDong Aisheng }
51677d8f306SDong Aisheng 
51777d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev)
51877d8f306SDong Aisheng {
51977d8f306SDong Aisheng 	struct device *dev = &pdev->dev;
52077d8f306SDong Aisheng 	struct imx_scu_clk_node *clk = dev_get_platdata(dev);
52177d8f306SDong Aisheng 	struct clk_hw *hw;
52278edeb08SDong Aisheng 	int ret;
52377d8f306SDong Aisheng 
52418a50f82SNitin Garg 	if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) ||
52518a50f82SNitin Garg 	    (clk->rsrc == IMX_SC_R_A72))) {
52678edeb08SDong Aisheng 		pm_runtime_set_suspended(dev);
52778edeb08SDong Aisheng 		pm_runtime_set_autosuspend_delay(dev, 50);
52878edeb08SDong Aisheng 		pm_runtime_use_autosuspend(&pdev->dev);
52978edeb08SDong Aisheng 		pm_runtime_enable(dev);
53078edeb08SDong Aisheng 
531*bfd594b3SMiaoqian Lin 		ret = pm_runtime_resume_and_get(dev);
53278edeb08SDong Aisheng 		if (ret) {
5336618b5aaSDong Aisheng 			pm_genpd_remove_device(dev);
53478edeb08SDong Aisheng 			pm_runtime_disable(dev);
53578edeb08SDong Aisheng 			return ret;
53678edeb08SDong Aisheng 		}
53718a50f82SNitin Garg 	}
53878edeb08SDong Aisheng 
53978edeb08SDong Aisheng 	hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents,
54077d8f306SDong Aisheng 			   clk->rsrc, clk->clk_type);
54178edeb08SDong Aisheng 	if (IS_ERR(hw)) {
54278edeb08SDong Aisheng 		pm_runtime_disable(dev);
54377d8f306SDong Aisheng 		return PTR_ERR(hw);
54478edeb08SDong Aisheng 	}
54577d8f306SDong Aisheng 
54677d8f306SDong Aisheng 	clk->hw = hw;
54777d8f306SDong Aisheng 	list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]);
54877d8f306SDong Aisheng 
54918a50f82SNitin Garg 	if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) ||
55018a50f82SNitin Garg 	    (clk->rsrc == IMX_SC_R_A72))) {
55178edeb08SDong Aisheng 		pm_runtime_mark_last_busy(&pdev->dev);
55278edeb08SDong Aisheng 		pm_runtime_put_autosuspend(&pdev->dev);
55318a50f82SNitin Garg 	}
55478edeb08SDong Aisheng 
55577d8f306SDong Aisheng 	dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc,
55677d8f306SDong Aisheng 		clk->clk_type);
55777d8f306SDong Aisheng 
55877d8f306SDong Aisheng 	return 0;
55977d8f306SDong Aisheng }
56077d8f306SDong Aisheng 
561d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
562d0409631SDong Aisheng {
563d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
564aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
565aecf425fSDong 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;
569d0409631SDong Aisheng 
570a82327ccSDong Aisheng 	clk->parent = clk_hw_get_parent(&clk->hw);
571a82327ccSDong Aisheng 
5727487986cSAnson Huang 	/* DC SS needs to handle bypass clock using non-cached clock rate */
5737487986cSAnson Huang 	if (clk->rsrc_id == IMX_SC_R_DC_0_VIDEO0 ||
5747487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_0_VIDEO1 ||
5757487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_1_VIDEO0 ||
5767487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_1_VIDEO1)
5777487986cSAnson Huang 		clk->rate = clk_scu_recalc_rate(&clk->hw, 0);
5787487986cSAnson Huang 	else
579d0409631SDong Aisheng 		clk->rate = clk_hw_get_rate(&clk->hw);
580d0409631SDong Aisheng 	clk->is_enabled = clk_hw_is_enabled(&clk->hw);
581d0409631SDong Aisheng 
582a82327ccSDong Aisheng 	if (clk->parent)
583a82327ccSDong Aisheng 		dev_dbg(dev, "save parent %s idx %u\n", clk_hw_get_name(clk->parent),
584a82327ccSDong Aisheng 			clk->parent_index);
585a82327ccSDong Aisheng 
586d0409631SDong Aisheng 	if (clk->rate)
587d0409631SDong Aisheng 		dev_dbg(dev, "save rate %d\n", clk->rate);
588d0409631SDong Aisheng 
589d0409631SDong Aisheng 	if (clk->is_enabled)
590d0409631SDong Aisheng 		dev_dbg(dev, "save enabled state\n");
591d0409631SDong Aisheng 
592d0409631SDong Aisheng 	return 0;
593d0409631SDong Aisheng }
594d0409631SDong Aisheng 
595d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev)
596d0409631SDong Aisheng {
597d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
598aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
599d0409631SDong Aisheng 	int ret = 0;
600d0409631SDong Aisheng 
601aecf425fSDong Aisheng 	if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) ||
602aecf425fSDong Aisheng 	    (rsrc_id == IMX_SC_R_A72))
603aecf425fSDong Aisheng 		return 0;
604aecf425fSDong Aisheng 
605a82327ccSDong Aisheng 	if (clk->parent) {
606a82327ccSDong Aisheng 		ret = clk_scu_set_parent(&clk->hw, clk->parent_index);
607a82327ccSDong Aisheng 		dev_dbg(dev, "restore parent %s idx %u %s\n",
608a82327ccSDong Aisheng 			clk_hw_get_name(clk->parent),
609a82327ccSDong Aisheng 			clk->parent_index, !ret ? "success" : "failed");
610a82327ccSDong Aisheng 	}
611a82327ccSDong Aisheng 
612d0409631SDong Aisheng 	if (clk->rate) {
613d0409631SDong Aisheng 		ret = clk_scu_set_rate(&clk->hw, clk->rate, 0);
614d0409631SDong Aisheng 		dev_dbg(dev, "restore rate %d %s\n", clk->rate,
615d0409631SDong Aisheng 			!ret ? "success" : "failed");
616d0409631SDong Aisheng 	}
617d0409631SDong Aisheng 
618a61cea83SDong Aisheng 	if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) {
619d0409631SDong Aisheng 		ret = clk_scu_prepare(&clk->hw);
620d0409631SDong Aisheng 		dev_dbg(dev, "restore enabled state %s\n",
621d0409631SDong Aisheng 			!ret ? "success" : "failed");
622d0409631SDong Aisheng 	}
623d0409631SDong Aisheng 
624d0409631SDong Aisheng 	return ret;
625d0409631SDong Aisheng }
626d0409631SDong Aisheng 
627d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = {
628d0409631SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend,
629d0409631SDong Aisheng 				      imx_clk_scu_resume)
630d0409631SDong Aisheng };
631d0409631SDong Aisheng 
63277d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = {
63377d8f306SDong Aisheng 	.driver = {
63477d8f306SDong Aisheng 		.name = "imx-scu-clk",
63577d8f306SDong Aisheng 		.suppress_bind_attrs = true,
636d0409631SDong Aisheng 		.pm = &imx_clk_scu_pm_ops,
63777d8f306SDong Aisheng 	},
63877d8f306SDong Aisheng 	.probe = imx_clk_scu_probe,
63977d8f306SDong Aisheng };
64077d8f306SDong Aisheng 
64177d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
64277d8f306SDong Aisheng {
64377d8f306SDong Aisheng 	struct of_phandle_args genpdspec = {
64477d8f306SDong Aisheng 		.np = pd_np,
64577d8f306SDong Aisheng 		.args_count = 1,
64677d8f306SDong Aisheng 		.args[0] = rsrc_id,
64777d8f306SDong Aisheng 	};
64877d8f306SDong Aisheng 
6490d5f1f47SDong Aisheng 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 ||
6500d5f1f47SDong Aisheng 	    rsrc_id == IMX_SC_R_A72)
6510d5f1f47SDong Aisheng 		return 0;
6520d5f1f47SDong Aisheng 
65377d8f306SDong Aisheng 	return of_genpd_add_device(&genpdspec, dev);
65477d8f306SDong Aisheng }
65577d8f306SDong Aisheng 
65677d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
65777d8f306SDong Aisheng 				     const char * const *parents,
65877d8f306SDong Aisheng 				     int num_parents, u32 rsrc_id, u8 clk_type)
65977d8f306SDong Aisheng {
66077d8f306SDong Aisheng 	struct imx_scu_clk_node clk = {
66177d8f306SDong Aisheng 		.name = name,
66277d8f306SDong Aisheng 		.rsrc = rsrc_id,
66377d8f306SDong Aisheng 		.clk_type = clk_type,
66477d8f306SDong Aisheng 		.parents = parents,
66577d8f306SDong Aisheng 		.num_parents = num_parents,
66677d8f306SDong Aisheng 	};
66777d8f306SDong Aisheng 	struct platform_device *pdev;
66877d8f306SDong Aisheng 	int ret;
66977d8f306SDong Aisheng 
6705964012cSDong Aisheng 	if (!imx_scu_clk_is_valid(rsrc_id))
6715964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
6725964012cSDong Aisheng 
67377d8f306SDong Aisheng 	pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
67477d8f306SDong Aisheng 	if (!pdev) {
67577d8f306SDong Aisheng 		pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
67677d8f306SDong Aisheng 		       name, rsrc_id, clk_type);
67777d8f306SDong Aisheng 		return ERR_PTR(-ENOMEM);
67877d8f306SDong Aisheng 	}
67977d8f306SDong Aisheng 
68077d8f306SDong Aisheng 	ret = platform_device_add_data(pdev, &clk, sizeof(clk));
68177d8f306SDong Aisheng 	if (ret) {
68277d8f306SDong Aisheng 		platform_device_put(pdev);
68377d8f306SDong Aisheng 		return ERR_PTR(ret);
68477d8f306SDong Aisheng 	}
68577d8f306SDong Aisheng 
68677d8f306SDong Aisheng 	pdev->driver_override = "imx-scu-clk";
68777d8f306SDong Aisheng 
68877d8f306SDong Aisheng 	ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
68977d8f306SDong Aisheng 	if (ret)
69077d8f306SDong Aisheng 		pr_warn("%s: failed to attached the power domain %d\n",
69177d8f306SDong Aisheng 			name, ret);
69277d8f306SDong Aisheng 
69377d8f306SDong Aisheng 	platform_device_add(pdev);
69477d8f306SDong Aisheng 
69577d8f306SDong Aisheng 	/* For API backwards compatiblilty, simply return NULL for success */
69677d8f306SDong Aisheng 	return NULL;
69777d8f306SDong Aisheng }
69877d8f306SDong Aisheng 
69977d8f306SDong Aisheng void imx_clk_scu_unregister(void)
70077d8f306SDong Aisheng {
70177d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
70277d8f306SDong Aisheng 	int i;
70377d8f306SDong Aisheng 
70477d8f306SDong Aisheng 	for (i = 0; i < IMX_SC_R_LAST; i++) {
70577d8f306SDong Aisheng 		list_for_each_entry(clk, &imx_scu_clks[i], node) {
70677d8f306SDong Aisheng 			clk_hw_unregister(clk->hw);
70777d8f306SDong Aisheng 			kfree(clk);
70877d8f306SDong Aisheng 		}
70977d8f306SDong Aisheng 	}
71077d8f306SDong Aisheng }
7115392c5deSDong Aisheng 
7125392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw,
7135392c5deSDong Aisheng 						 unsigned long parent_rate)
7145392c5deSDong Aisheng {
7155392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7165392c5deSDong Aisheng 	unsigned long rate = 0;
7175392c5deSDong Aisheng 	u32 val;
7185392c5deSDong Aisheng 	int err;
7195392c5deSDong Aisheng 
7205392c5deSDong Aisheng 	err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7215392c5deSDong Aisheng 				      clk->gpr_id, &val);
7225392c5deSDong Aisheng 
7235392c5deSDong Aisheng 	rate  = val ? parent_rate / 2 : parent_rate;
7245392c5deSDong Aisheng 
7255392c5deSDong Aisheng 	return err ? 0 : rate;
7265392c5deSDong Aisheng }
7275392c5deSDong Aisheng 
7285392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate,
7295392c5deSDong Aisheng 				   unsigned long *prate)
7305392c5deSDong Aisheng {
7315392c5deSDong Aisheng 	if (rate < *prate)
7325392c5deSDong Aisheng 		rate = *prate / 2;
7335392c5deSDong Aisheng 	else
7345392c5deSDong Aisheng 		rate = *prate;
7355392c5deSDong Aisheng 
7365392c5deSDong Aisheng 	return rate;
7375392c5deSDong Aisheng }
7385392c5deSDong Aisheng 
7395392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
7405392c5deSDong Aisheng 				    unsigned long parent_rate)
7415392c5deSDong Aisheng {
7425392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7435392c5deSDong Aisheng 	uint32_t val;
7445392c5deSDong Aisheng 	int err;
7455392c5deSDong Aisheng 
7465392c5deSDong Aisheng 	val = (rate < parent_rate) ? 1 : 0;
7475392c5deSDong Aisheng 	err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7485392c5deSDong Aisheng 				      clk->gpr_id, val);
7495392c5deSDong Aisheng 
7505392c5deSDong Aisheng 	return err ? -EINVAL : 0;
7515392c5deSDong Aisheng }
7525392c5deSDong Aisheng 
7535392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = {
7545392c5deSDong Aisheng 	.recalc_rate = clk_gpr_div_scu_recalc_rate,
7555392c5deSDong Aisheng 	.round_rate = clk_gpr_div_scu_round_rate,
7565392c5deSDong Aisheng 	.set_rate = clk_gpr_div_scu_set_rate,
7575392c5deSDong Aisheng };
7585392c5deSDong Aisheng 
7595392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw)
7605392c5deSDong Aisheng {
7615392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7625392c5deSDong Aisheng 	u32 val = 0;
7635392c5deSDong Aisheng 
7645392c5deSDong Aisheng 	imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7655392c5deSDong Aisheng 				clk->gpr_id, &val);
7665392c5deSDong Aisheng 
7675392c5deSDong Aisheng 	return (u8)val;
7685392c5deSDong Aisheng }
7695392c5deSDong Aisheng 
7705392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
7715392c5deSDong Aisheng {
7725392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7735392c5deSDong Aisheng 
7745392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7755392c5deSDong Aisheng 				       clk->gpr_id, index);
7765392c5deSDong Aisheng }
7775392c5deSDong Aisheng 
7785392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = {
7795392c5deSDong Aisheng 	.get_parent = clk_gpr_mux_scu_get_parent,
7805392c5deSDong Aisheng 	.set_parent = clk_gpr_mux_scu_set_parent,
7815392c5deSDong Aisheng };
7825392c5deSDong Aisheng 
7835392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw)
7845392c5deSDong Aisheng {
7855392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7865392c5deSDong Aisheng 
7875392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7885392c5deSDong Aisheng 				       clk->gpr_id, !clk->gate_invert);
7895392c5deSDong Aisheng }
7905392c5deSDong Aisheng 
7915392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw)
7925392c5deSDong Aisheng {
7935392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7945392c5deSDong Aisheng 	int ret;
7955392c5deSDong Aisheng 
7965392c5deSDong Aisheng 	ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7975392c5deSDong Aisheng 				      clk->gpr_id, clk->gate_invert);
7985392c5deSDong Aisheng 	if (ret)
7995392c5deSDong Aisheng 		pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
8005392c5deSDong Aisheng 		       ret);
8015392c5deSDong Aisheng }
8025392c5deSDong Aisheng 
8035392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw)
8045392c5deSDong Aisheng {
8055392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
8065392c5deSDong Aisheng 	int ret;
8075392c5deSDong Aisheng 	u32 val;
8085392c5deSDong Aisheng 
8095392c5deSDong Aisheng 	ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
8105392c5deSDong Aisheng 				      clk->gpr_id, &val);
8115392c5deSDong Aisheng 	if (ret)
8125392c5deSDong Aisheng 		return ret;
8135392c5deSDong Aisheng 
8145392c5deSDong Aisheng 	return clk->gate_invert ? !val : val;
8155392c5deSDong Aisheng }
8165392c5deSDong Aisheng 
8175392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = {
8185392c5deSDong Aisheng 	.prepare = clk_gpr_gate_scu_prepare,
8195392c5deSDong Aisheng 	.unprepare = clk_gpr_gate_scu_unprepare,
8205392c5deSDong Aisheng 	.is_prepared = clk_gpr_gate_scu_is_prepared,
8215392c5deSDong Aisheng };
8225392c5deSDong Aisheng 
8235392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name,
8245392c5deSDong Aisheng 				 int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags,
8255392c5deSDong Aisheng 				 bool invert)
8265392c5deSDong Aisheng {
8275392c5deSDong Aisheng 	struct imx_scu_clk_node *clk_node;
8285392c5deSDong Aisheng 	struct clk_gpr_scu *clk;
8295392c5deSDong Aisheng 	struct clk_hw *hw;
8305392c5deSDong Aisheng 	struct clk_init_data init;
8315392c5deSDong Aisheng 	int ret;
8325392c5deSDong Aisheng 
8335392c5deSDong Aisheng 	if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST)
8345392c5deSDong Aisheng 		return ERR_PTR(-EINVAL);
8355392c5deSDong Aisheng 
8365392c5deSDong Aisheng 	clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL);
8375392c5deSDong Aisheng 	if (!clk_node)
8385392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
8395392c5deSDong Aisheng 
8402759f38bSXiaoke Wang 	if (!imx_scu_clk_is_valid(rsrc_id)) {
8412759f38bSXiaoke Wang 		kfree(clk_node);
8425964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
8432759f38bSXiaoke Wang 	}
8445964012cSDong Aisheng 
8455392c5deSDong Aisheng 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
8465392c5deSDong Aisheng 	if (!clk) {
8475392c5deSDong Aisheng 		kfree(clk_node);
8485392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
8495392c5deSDong Aisheng 	}
8505392c5deSDong Aisheng 
8515392c5deSDong Aisheng 	clk->rsrc_id = rsrc_id;
8525392c5deSDong Aisheng 	clk->gpr_id = gpr_id;
8535392c5deSDong Aisheng 	clk->flags = flags;
8545392c5deSDong Aisheng 	clk->gate_invert = invert;
8555392c5deSDong Aisheng 
8565392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_GATE)
8575392c5deSDong Aisheng 		init.ops = &clk_gpr_gate_scu_ops;
8585392c5deSDong Aisheng 
8595392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_DIV)
8605392c5deSDong Aisheng 		init.ops = &clk_gpr_div_scu_ops;
8615392c5deSDong Aisheng 
8625392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_MUX)
8635392c5deSDong Aisheng 		init.ops = &clk_gpr_mux_scu_ops;
8645392c5deSDong Aisheng 
8655392c5deSDong Aisheng 	init.flags = 0;
8665392c5deSDong Aisheng 	init.name = name;
8675392c5deSDong Aisheng 	init.parent_names = parent_name;
8685392c5deSDong Aisheng 	init.num_parents = num_parents;
8695392c5deSDong Aisheng 
8705392c5deSDong Aisheng 	clk->hw.init = &init;
8715392c5deSDong Aisheng 
8725392c5deSDong Aisheng 	hw = &clk->hw;
8735392c5deSDong Aisheng 	ret = clk_hw_register(NULL, hw);
8745392c5deSDong Aisheng 	if (ret) {
8755392c5deSDong Aisheng 		kfree(clk);
8765392c5deSDong Aisheng 		kfree(clk_node);
8775392c5deSDong Aisheng 		hw = ERR_PTR(ret);
8785392c5deSDong Aisheng 	} else {
8795392c5deSDong Aisheng 		clk_node->hw = hw;
8805392c5deSDong Aisheng 		clk_node->clk_type = gpr_id;
8815392c5deSDong Aisheng 		list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]);
8825392c5deSDong Aisheng 	}
8835392c5deSDong Aisheng 
8845392c5deSDong Aisheng 	return hw;
8855392c5deSDong Aisheng }
886