xref: /openbmc/linux/drivers/clk/imx/clk-scu.c (revision a96cbb14)
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>
12*a96cbb14SRob Herring #include <linux/of.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 
to_clk_scu(struct clk_hw * hw)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 
imx_scu_clk_search_cmp(const void * rsrc,const void * rsrc_p)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 
imx_scu_clk_is_valid(u32 rsrc_id)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 
imx_clk_scu_init(struct device_node * np,const struct imx_clk_scu_rsrc_table * data)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  */
clk_scu_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)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 /*
25415d3f365SMaxime Ripard  * clk_scu_determine_rate - Returns the closest rate for a SCU clock
25515d3f365SMaxime Ripard  * @hw: clock to round rate for
25615d3f365SMaxime Ripard  * @req: clock rate request
25715d3f365SMaxime Ripard  *
25815d3f365SMaxime Ripard  * Returns 0 on success, a negative error on failure
25915d3f365SMaxime Ripard  */
clk_scu_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)26015d3f365SMaxime Ripard static int clk_scu_determine_rate(struct clk_hw *hw,
26115d3f365SMaxime Ripard 				  struct clk_rate_request *req)
26215d3f365SMaxime Ripard {
26315d3f365SMaxime Ripard 	/*
26415d3f365SMaxime Ripard 	 * Assume we support all the requested rate and let the SCU firmware
26515d3f365SMaxime Ripard 	 * to handle the left work
26615d3f365SMaxime Ripard 	 */
26715d3f365SMaxime Ripard 	return 0;
26815d3f365SMaxime Ripard }
26915d3f365SMaxime Ripard 
27015d3f365SMaxime Ripard /*
271fe37b482SAisheng Dong  * clk_scu_round_rate - Round clock rate for a SCU clock
272fe37b482SAisheng Dong  * @hw: clock to round rate for
273fe37b482SAisheng Dong  * @rate: rate to round
274fe37b482SAisheng Dong  * @parent_rate: parent rate provided by common clock framework, not used
275fe37b482SAisheng Dong  *
276fe37b482SAisheng Dong  * Returns the current clock rate, or zero in failure.
277fe37b482SAisheng Dong  */
clk_scu_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)278fe37b482SAisheng Dong static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
279fe37b482SAisheng Dong 			       unsigned long *parent_rate)
280fe37b482SAisheng Dong {
281fe37b482SAisheng Dong 	/*
282fe37b482SAisheng Dong 	 * Assume we support all the requested rate and let the SCU firmware
283fe37b482SAisheng Dong 	 * to handle the left work
284fe37b482SAisheng Dong 	 */
285fe37b482SAisheng Dong 	return rate;
286fe37b482SAisheng Dong }
287fe37b482SAisheng Dong 
clk_scu_atf_set_cpu_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)2883b9ea606SAnson Huang static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
2893b9ea606SAnson Huang 				    unsigned long parent_rate)
2903b9ea606SAnson Huang {
2913b9ea606SAnson Huang 	struct clk_scu *clk = to_clk_scu(hw);
2923b9ea606SAnson Huang 	struct arm_smccc_res res;
2933b9ea606SAnson Huang 	unsigned long cluster_id;
2943b9ea606SAnson Huang 
295cd8bd2f3SAnson Huang 	if (clk->rsrc_id == IMX_SC_R_A35 || clk->rsrc_id == IMX_SC_R_A53)
2963b9ea606SAnson Huang 		cluster_id = 0;
297a43f6e8aSAnson Huang 	else if (clk->rsrc_id == IMX_SC_R_A72)
298a43f6e8aSAnson Huang 		cluster_id = 1;
2993b9ea606SAnson Huang 	else
3003b9ea606SAnson Huang 		return -EINVAL;
3013b9ea606SAnson Huang 
3023b9ea606SAnson Huang 	/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
3033b9ea606SAnson Huang 	arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
3043b9ea606SAnson Huang 		      cluster_id, rate, 0, 0, 0, 0, &res);
3053b9ea606SAnson Huang 
3063b9ea606SAnson Huang 	return 0;
3073b9ea606SAnson Huang }
3083b9ea606SAnson Huang 
309fe37b482SAisheng Dong /*
310fe37b482SAisheng Dong  * clk_scu_set_rate - Set rate for a SCU clock
311fe37b482SAisheng Dong  * @hw: clock to change rate for
312fe37b482SAisheng Dong  * @rate: target rate for the clock
313fe37b482SAisheng Dong  * @parent_rate: rate of the clock parent, not used for SCU clocks
314fe37b482SAisheng Dong  *
315fe37b482SAisheng Dong  * Sets a clock frequency for a SCU clock. Returns the SCU
316fe37b482SAisheng Dong  * protocol status.
317fe37b482SAisheng Dong  */
clk_scu_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)318fe37b482SAisheng Dong static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
319fe37b482SAisheng Dong 			    unsigned long parent_rate)
320fe37b482SAisheng Dong {
321fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
322fe37b482SAisheng Dong 	struct imx_sc_msg_req_set_clock_rate msg;
323fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
324fe37b482SAisheng Dong 
325fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
326fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
327fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
328fe37b482SAisheng Dong 	hdr->size = 3;
329fe37b482SAisheng Dong 
330fe37b482SAisheng Dong 	msg.rate = cpu_to_le32(rate);
331fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
332fe37b482SAisheng Dong 	msg.clk = clk->clk_type;
333fe37b482SAisheng Dong 
334fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
335fe37b482SAisheng Dong }
336fe37b482SAisheng Dong 
clk_scu_get_parent(struct clk_hw * hw)337666aed2dSAisheng Dong static u8 clk_scu_get_parent(struct clk_hw *hw)
338666aed2dSAisheng Dong {
339666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
340666aed2dSAisheng Dong 	struct imx_sc_msg_get_clock_parent msg;
341666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
342666aed2dSAisheng Dong 	int ret;
343666aed2dSAisheng Dong 
344666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
345666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
346666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
347666aed2dSAisheng Dong 	hdr->size = 2;
348666aed2dSAisheng Dong 
349666aed2dSAisheng Dong 	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
350666aed2dSAisheng Dong 	msg.data.req.clk = clk->clk_type;
351666aed2dSAisheng Dong 
352666aed2dSAisheng Dong 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
353666aed2dSAisheng Dong 	if (ret) {
354666aed2dSAisheng Dong 		pr_err("%s: failed to get clock parent %d\n",
355666aed2dSAisheng Dong 		       clk_hw_get_name(hw), ret);
356666aed2dSAisheng Dong 		return 0;
357666aed2dSAisheng Dong 	}
358666aed2dSAisheng Dong 
359a82327ccSDong Aisheng 	clk->parent_index = msg.data.resp.parent;
360a82327ccSDong Aisheng 
361666aed2dSAisheng Dong 	return msg.data.resp.parent;
362666aed2dSAisheng Dong }
363666aed2dSAisheng Dong 
clk_scu_set_parent(struct clk_hw * hw,u8 index)364666aed2dSAisheng Dong static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
365666aed2dSAisheng Dong {
366666aed2dSAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
367666aed2dSAisheng Dong 	struct imx_sc_msg_set_clock_parent msg;
368666aed2dSAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
369a82327ccSDong Aisheng 	int ret;
370666aed2dSAisheng Dong 
371666aed2dSAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
372666aed2dSAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
373666aed2dSAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
374666aed2dSAisheng Dong 	hdr->size = 2;
375666aed2dSAisheng Dong 
376666aed2dSAisheng Dong 	msg.resource = cpu_to_le16(clk->rsrc_id);
377666aed2dSAisheng Dong 	msg.clk = clk->clk_type;
378666aed2dSAisheng Dong 	msg.parent = index;
379666aed2dSAisheng Dong 
380a82327ccSDong Aisheng 	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
381a82327ccSDong Aisheng 	if (ret) {
382a82327ccSDong Aisheng 		pr_err("%s: failed to set clock parent %d\n",
383a82327ccSDong Aisheng 		       clk_hw_get_name(hw), ret);
384a82327ccSDong Aisheng 		return ret;
385a82327ccSDong Aisheng 	}
386a82327ccSDong Aisheng 
387a82327ccSDong Aisheng 	clk->parent_index = index;
388a82327ccSDong Aisheng 
389a82327ccSDong Aisheng 	return 0;
390666aed2dSAisheng Dong }
391666aed2dSAisheng Dong 
sc_pm_clock_enable(struct imx_sc_ipc * ipc,u16 resource,u8 clk,bool enable,bool autog)392fe37b482SAisheng Dong static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
393fe37b482SAisheng Dong 			      u8 clk, bool enable, bool autog)
394fe37b482SAisheng Dong {
395fe37b482SAisheng Dong 	struct imx_sc_msg_req_clock_enable msg;
396fe37b482SAisheng Dong 	struct imx_sc_rpc_msg *hdr = &msg.hdr;
397fe37b482SAisheng Dong 
398fe37b482SAisheng Dong 	hdr->ver = IMX_SC_RPC_VERSION;
399fe37b482SAisheng Dong 	hdr->svc = IMX_SC_RPC_SVC_PM;
400fe37b482SAisheng Dong 	hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
401fe37b482SAisheng Dong 	hdr->size = 3;
402fe37b482SAisheng Dong 
403fe37b482SAisheng Dong 	msg.resource = cpu_to_le16(resource);
404fe37b482SAisheng Dong 	msg.clk = clk;
405fe37b482SAisheng Dong 	msg.enable = enable;
406fe37b482SAisheng Dong 	msg.autog = autog;
407fe37b482SAisheng Dong 
408fe37b482SAisheng Dong 	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
409fe37b482SAisheng Dong }
410fe37b482SAisheng Dong 
411fe37b482SAisheng Dong /*
412fe37b482SAisheng Dong  * clk_scu_prepare - Enable a SCU clock
413fe37b482SAisheng Dong  * @hw: clock to enable
414fe37b482SAisheng Dong  *
415fe37b482SAisheng Dong  * Enable the clock at the DSC slice level
416fe37b482SAisheng Dong  */
clk_scu_prepare(struct clk_hw * hw)417fe37b482SAisheng Dong static int clk_scu_prepare(struct clk_hw *hw)
418fe37b482SAisheng Dong {
419fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
420fe37b482SAisheng Dong 
421fe37b482SAisheng Dong 	return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
422fe37b482SAisheng Dong 				  clk->clk_type, true, false);
423fe37b482SAisheng Dong }
424fe37b482SAisheng Dong 
425fe37b482SAisheng Dong /*
426fe37b482SAisheng Dong  * clk_scu_unprepare - Disable a SCU clock
427fe37b482SAisheng Dong  * @hw: clock to enable
428fe37b482SAisheng Dong  *
429fe37b482SAisheng Dong  * Disable the clock at the DSC slice level
430fe37b482SAisheng Dong  */
clk_scu_unprepare(struct clk_hw * hw)431fe37b482SAisheng Dong static void clk_scu_unprepare(struct clk_hw *hw)
432fe37b482SAisheng Dong {
433fe37b482SAisheng Dong 	struct clk_scu *clk = to_clk_scu(hw);
434fe37b482SAisheng Dong 	int ret;
435fe37b482SAisheng Dong 
436fe37b482SAisheng Dong 	ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
437fe37b482SAisheng Dong 				 clk->clk_type, false, false);
438fe37b482SAisheng Dong 	if (ret)
439fe37b482SAisheng Dong 		pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
440fe37b482SAisheng Dong 			ret);
441fe37b482SAisheng Dong }
442fe37b482SAisheng Dong 
443fe37b482SAisheng Dong static const struct clk_ops clk_scu_ops = {
444fe37b482SAisheng Dong 	.recalc_rate = clk_scu_recalc_rate,
44515d3f365SMaxime Ripard 	.determine_rate = clk_scu_determine_rate,
446fe37b482SAisheng Dong 	.set_rate = clk_scu_set_rate,
447666aed2dSAisheng Dong 	.get_parent = clk_scu_get_parent,
448666aed2dSAisheng Dong 	.set_parent = clk_scu_set_parent,
449fe37b482SAisheng Dong 	.prepare = clk_scu_prepare,
450fe37b482SAisheng Dong 	.unprepare = clk_scu_unprepare,
451fe37b482SAisheng Dong };
452fe37b482SAisheng Dong 
4533b9ea606SAnson Huang static const struct clk_ops clk_scu_cpu_ops = {
4543b9ea606SAnson Huang 	.recalc_rate = clk_scu_recalc_rate,
4553b9ea606SAnson Huang 	.round_rate = clk_scu_round_rate,
4563b9ea606SAnson Huang 	.set_rate = clk_scu_atf_set_cpu_rate,
4573b9ea606SAnson Huang 	.prepare = clk_scu_prepare,
4583b9ea606SAnson Huang 	.unprepare = clk_scu_unprepare,
4593b9ea606SAnson Huang };
4603b9ea606SAnson Huang 
461b192d040SGuoniu.zhou static const struct clk_ops clk_scu_pi_ops = {
462b192d040SGuoniu.zhou 	.recalc_rate = clk_scu_recalc_rate,
463b192d040SGuoniu.zhou 	.round_rate  = clk_scu_round_rate,
464b192d040SGuoniu.zhou 	.set_rate    = clk_scu_set_rate,
465b192d040SGuoniu.zhou };
466b192d040SGuoniu.zhou 
__imx_clk_scu(struct device * dev,const char * name,const char * const * parents,int num_parents,u32 rsrc_id,u8 clk_type)4672f1a2c1dSDong Aisheng struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
4682f1a2c1dSDong Aisheng 			     const char * const *parents, int num_parents,
4692f1a2c1dSDong Aisheng 			     u32 rsrc_id, u8 clk_type)
470fe37b482SAisheng Dong {
471fe37b482SAisheng Dong 	struct clk_init_data init;
472fe37b482SAisheng Dong 	struct clk_scu *clk;
473fe37b482SAisheng Dong 	struct clk_hw *hw;
474fe37b482SAisheng Dong 	int ret;
475fe37b482SAisheng Dong 
476fe37b482SAisheng Dong 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
477fe37b482SAisheng Dong 	if (!clk)
478fe37b482SAisheng Dong 		return ERR_PTR(-ENOMEM);
479fe37b482SAisheng Dong 
480fe37b482SAisheng Dong 	clk->rsrc_id = rsrc_id;
481fe37b482SAisheng Dong 	clk->clk_type = clk_type;
482fe37b482SAisheng Dong 
483fe37b482SAisheng Dong 	init.name = name;
484fe37b482SAisheng Dong 	init.ops = &clk_scu_ops;
485a43f6e8aSAnson Huang 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72)
4863b9ea606SAnson Huang 		init.ops = &clk_scu_cpu_ops;
487b192d040SGuoniu.zhou 	else if (rsrc_id == IMX_SC_R_PI_0_PLL)
488b192d040SGuoniu.zhou 		init.ops = &clk_scu_pi_ops;
4893b9ea606SAnson Huang 	else
4903b9ea606SAnson Huang 		init.ops = &clk_scu_ops;
491666aed2dSAisheng Dong 	init.parent_names = parents;
492666aed2dSAisheng Dong 	init.num_parents = num_parents;
493666aed2dSAisheng Dong 
494fe37b482SAisheng Dong 	/*
495fe37b482SAisheng Dong 	 * Note on MX8, the clocks are tightly coupled with power domain
496fe37b482SAisheng Dong 	 * that once the power domain is off, the clock status may be
497fe37b482SAisheng Dong 	 * lost. So we make it NOCACHE to let user to retrieve the real
498fe37b482SAisheng Dong 	 * clock status from HW instead of using the possible invalid
499fe37b482SAisheng Dong 	 * cached rate.
500fe37b482SAisheng Dong 	 */
501fe37b482SAisheng Dong 	init.flags = CLK_GET_RATE_NOCACHE;
502fe37b482SAisheng Dong 	clk->hw.init = &init;
503fe37b482SAisheng Dong 
504fe37b482SAisheng Dong 	hw = &clk->hw;
5052f1a2c1dSDong Aisheng 	ret = clk_hw_register(dev, hw);
506fe37b482SAisheng Dong 	if (ret) {
507fe37b482SAisheng Dong 		kfree(clk);
508fe37b482SAisheng Dong 		hw = ERR_PTR(ret);
509054ef44eSJian Dong 		return hw;
510fe37b482SAisheng Dong 	}
511fe37b482SAisheng Dong 
512d0409631SDong Aisheng 	if (dev)
513d0409631SDong Aisheng 		dev_set_drvdata(dev, clk);
514d0409631SDong Aisheng 
515fe37b482SAisheng Dong 	return hw;
516fe37b482SAisheng Dong }
51777d8f306SDong Aisheng 
imx_scu_of_clk_src_get(struct of_phandle_args * clkspec,void * data)51877d8f306SDong Aisheng struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,
51977d8f306SDong Aisheng 				      void *data)
52077d8f306SDong Aisheng {
52177d8f306SDong Aisheng 	unsigned int rsrc = clkspec->args[0];
52277d8f306SDong Aisheng 	unsigned int idx = clkspec->args[1];
52377d8f306SDong Aisheng 	struct list_head *scu_clks = data;
52477d8f306SDong Aisheng 	struct imx_scu_clk_node *clk;
52577d8f306SDong Aisheng 
52677d8f306SDong Aisheng 	list_for_each_entry(clk, &scu_clks[rsrc], node) {
52777d8f306SDong Aisheng 		if (clk->clk_type == idx)
52877d8f306SDong Aisheng 			return clk->hw;
52977d8f306SDong Aisheng 	}
53077d8f306SDong Aisheng 
53177d8f306SDong Aisheng 	return ERR_PTR(-ENODEV);
53277d8f306SDong Aisheng }
53377d8f306SDong Aisheng 
imx_clk_scu_probe(struct platform_device * pdev)53477d8f306SDong Aisheng static int imx_clk_scu_probe(struct platform_device *pdev)
53577d8f306SDong Aisheng {
53677d8f306SDong Aisheng 	struct device *dev = &pdev->dev;
53777d8f306SDong Aisheng 	struct imx_scu_clk_node *clk = dev_get_platdata(dev);
53877d8f306SDong Aisheng 	struct clk_hw *hw;
53978edeb08SDong Aisheng 	int ret;
54077d8f306SDong Aisheng 
54118a50f82SNitin Garg 	if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) ||
54218a50f82SNitin Garg 	    (clk->rsrc == IMX_SC_R_A72))) {
54378edeb08SDong Aisheng 		pm_runtime_set_suspended(dev);
54478edeb08SDong Aisheng 		pm_runtime_set_autosuspend_delay(dev, 50);
54578edeb08SDong Aisheng 		pm_runtime_use_autosuspend(&pdev->dev);
54678edeb08SDong Aisheng 		pm_runtime_enable(dev);
54778edeb08SDong Aisheng 
548bfd594b3SMiaoqian Lin 		ret = pm_runtime_resume_and_get(dev);
54978edeb08SDong Aisheng 		if (ret) {
5506618b5aaSDong Aisheng 			pm_genpd_remove_device(dev);
55178edeb08SDong Aisheng 			pm_runtime_disable(dev);
55278edeb08SDong Aisheng 			return ret;
55378edeb08SDong Aisheng 		}
55418a50f82SNitin Garg 	}
55578edeb08SDong Aisheng 
55678edeb08SDong Aisheng 	hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents,
55777d8f306SDong Aisheng 			   clk->rsrc, clk->clk_type);
55878edeb08SDong Aisheng 	if (IS_ERR(hw)) {
55978edeb08SDong Aisheng 		pm_runtime_disable(dev);
56077d8f306SDong Aisheng 		return PTR_ERR(hw);
56178edeb08SDong Aisheng 	}
56277d8f306SDong Aisheng 
56377d8f306SDong Aisheng 	clk->hw = hw;
56477d8f306SDong Aisheng 	list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]);
56577d8f306SDong Aisheng 
56618a50f82SNitin Garg 	if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) ||
56718a50f82SNitin Garg 	    (clk->rsrc == IMX_SC_R_A72))) {
56878edeb08SDong Aisheng 		pm_runtime_mark_last_busy(&pdev->dev);
56978edeb08SDong Aisheng 		pm_runtime_put_autosuspend(&pdev->dev);
57018a50f82SNitin Garg 	}
57178edeb08SDong Aisheng 
57277d8f306SDong Aisheng 	dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc,
57377d8f306SDong Aisheng 		clk->clk_type);
57477d8f306SDong Aisheng 
57577d8f306SDong Aisheng 	return 0;
57677d8f306SDong Aisheng }
57777d8f306SDong Aisheng 
imx_clk_scu_suspend(struct device * dev)578d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_suspend(struct device *dev)
579d0409631SDong Aisheng {
580d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
581aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
582aecf425fSDong Aisheng 
583aecf425fSDong Aisheng 	if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) ||
584aecf425fSDong Aisheng 	    (rsrc_id == IMX_SC_R_A72))
585aecf425fSDong Aisheng 		return 0;
586d0409631SDong Aisheng 
587a82327ccSDong Aisheng 	clk->parent = clk_hw_get_parent(&clk->hw);
588a82327ccSDong Aisheng 
5897487986cSAnson Huang 	/* DC SS needs to handle bypass clock using non-cached clock rate */
5907487986cSAnson Huang 	if (clk->rsrc_id == IMX_SC_R_DC_0_VIDEO0 ||
5917487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_0_VIDEO1 ||
5927487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_1_VIDEO0 ||
5937487986cSAnson Huang 		clk->rsrc_id == IMX_SC_R_DC_1_VIDEO1)
5947487986cSAnson Huang 		clk->rate = clk_scu_recalc_rate(&clk->hw, 0);
5957487986cSAnson Huang 	else
596d0409631SDong Aisheng 		clk->rate = clk_hw_get_rate(&clk->hw);
597d0409631SDong Aisheng 	clk->is_enabled = clk_hw_is_enabled(&clk->hw);
598d0409631SDong Aisheng 
599a82327ccSDong Aisheng 	if (clk->parent)
600a82327ccSDong Aisheng 		dev_dbg(dev, "save parent %s idx %u\n", clk_hw_get_name(clk->parent),
601a82327ccSDong Aisheng 			clk->parent_index);
602a82327ccSDong Aisheng 
603d0409631SDong Aisheng 	if (clk->rate)
604d0409631SDong Aisheng 		dev_dbg(dev, "save rate %d\n", clk->rate);
605d0409631SDong Aisheng 
606d0409631SDong Aisheng 	if (clk->is_enabled)
607d0409631SDong Aisheng 		dev_dbg(dev, "save enabled state\n");
608d0409631SDong Aisheng 
609d0409631SDong Aisheng 	return 0;
610d0409631SDong Aisheng }
611d0409631SDong Aisheng 
imx_clk_scu_resume(struct device * dev)612d0409631SDong Aisheng static int __maybe_unused imx_clk_scu_resume(struct device *dev)
613d0409631SDong Aisheng {
614d0409631SDong Aisheng 	struct clk_scu *clk = dev_get_drvdata(dev);
615aecf425fSDong Aisheng 	u32 rsrc_id = clk->rsrc_id;
616d0409631SDong Aisheng 	int ret = 0;
617d0409631SDong Aisheng 
618aecf425fSDong Aisheng 	if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) ||
619aecf425fSDong Aisheng 	    (rsrc_id == IMX_SC_R_A72))
620aecf425fSDong Aisheng 		return 0;
621aecf425fSDong Aisheng 
622a82327ccSDong Aisheng 	if (clk->parent) {
623a82327ccSDong Aisheng 		ret = clk_scu_set_parent(&clk->hw, clk->parent_index);
624a82327ccSDong Aisheng 		dev_dbg(dev, "restore parent %s idx %u %s\n",
625a82327ccSDong Aisheng 			clk_hw_get_name(clk->parent),
626a82327ccSDong Aisheng 			clk->parent_index, !ret ? "success" : "failed");
627a82327ccSDong Aisheng 	}
628a82327ccSDong Aisheng 
629d0409631SDong Aisheng 	if (clk->rate) {
630d0409631SDong Aisheng 		ret = clk_scu_set_rate(&clk->hw, clk->rate, 0);
631d0409631SDong Aisheng 		dev_dbg(dev, "restore rate %d %s\n", clk->rate,
632d0409631SDong Aisheng 			!ret ? "success" : "failed");
633d0409631SDong Aisheng 	}
634d0409631SDong Aisheng 
635a61cea83SDong Aisheng 	if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) {
636d0409631SDong Aisheng 		ret = clk_scu_prepare(&clk->hw);
637d0409631SDong Aisheng 		dev_dbg(dev, "restore enabled state %s\n",
638d0409631SDong Aisheng 			!ret ? "success" : "failed");
639d0409631SDong Aisheng 	}
640d0409631SDong Aisheng 
641d0409631SDong Aisheng 	return ret;
642d0409631SDong Aisheng }
643d0409631SDong Aisheng 
644d0409631SDong Aisheng static const struct dev_pm_ops imx_clk_scu_pm_ops = {
645d0409631SDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend,
646d0409631SDong Aisheng 				      imx_clk_scu_resume)
647d0409631SDong Aisheng };
648d0409631SDong Aisheng 
64977d8f306SDong Aisheng static struct platform_driver imx_clk_scu_driver = {
65077d8f306SDong Aisheng 	.driver = {
65177d8f306SDong Aisheng 		.name = "imx-scu-clk",
65277d8f306SDong Aisheng 		.suppress_bind_attrs = true,
653d0409631SDong Aisheng 		.pm = &imx_clk_scu_pm_ops,
65477d8f306SDong Aisheng 	},
65577d8f306SDong Aisheng 	.probe = imx_clk_scu_probe,
65677d8f306SDong Aisheng };
65777d8f306SDong Aisheng 
imx_clk_scu_attach_pd(struct device * dev,u32 rsrc_id)65877d8f306SDong Aisheng static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id)
65977d8f306SDong Aisheng {
66077d8f306SDong Aisheng 	struct of_phandle_args genpdspec = {
66177d8f306SDong Aisheng 		.np = pd_np,
66277d8f306SDong Aisheng 		.args_count = 1,
66377d8f306SDong Aisheng 		.args[0] = rsrc_id,
66477d8f306SDong Aisheng 	};
66577d8f306SDong Aisheng 
6660d5f1f47SDong Aisheng 	if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 ||
6670d5f1f47SDong Aisheng 	    rsrc_id == IMX_SC_R_A72)
6680d5f1f47SDong Aisheng 		return 0;
6690d5f1f47SDong Aisheng 
67077d8f306SDong Aisheng 	return of_genpd_add_device(&genpdspec, dev);
67177d8f306SDong Aisheng }
67277d8f306SDong Aisheng 
imx_clk_scu_alloc_dev(const char * name,const char * const * parents,int num_parents,u32 rsrc_id,u8 clk_type)67377d8f306SDong Aisheng struct clk_hw *imx_clk_scu_alloc_dev(const char *name,
67477d8f306SDong Aisheng 				     const char * const *parents,
67577d8f306SDong Aisheng 				     int num_parents, u32 rsrc_id, u8 clk_type)
67677d8f306SDong Aisheng {
67777d8f306SDong Aisheng 	struct imx_scu_clk_node clk = {
67877d8f306SDong Aisheng 		.name = name,
67977d8f306SDong Aisheng 		.rsrc = rsrc_id,
68077d8f306SDong Aisheng 		.clk_type = clk_type,
68177d8f306SDong Aisheng 		.parents = parents,
68277d8f306SDong Aisheng 		.num_parents = num_parents,
68377d8f306SDong Aisheng 	};
68477d8f306SDong Aisheng 	struct platform_device *pdev;
68577d8f306SDong Aisheng 	int ret;
68677d8f306SDong Aisheng 
6875964012cSDong Aisheng 	if (!imx_scu_clk_is_valid(rsrc_id))
6885964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
6895964012cSDong Aisheng 
69077d8f306SDong Aisheng 	pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
69177d8f306SDong Aisheng 	if (!pdev) {
69277d8f306SDong Aisheng 		pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n",
69377d8f306SDong Aisheng 		       name, rsrc_id, clk_type);
69477d8f306SDong Aisheng 		return ERR_PTR(-ENOMEM);
69577d8f306SDong Aisheng 	}
69677d8f306SDong Aisheng 
69777d8f306SDong Aisheng 	ret = platform_device_add_data(pdev, &clk, sizeof(clk));
69877d8f306SDong Aisheng 	if (ret) {
69977d8f306SDong Aisheng 		platform_device_put(pdev);
70077d8f306SDong Aisheng 		return ERR_PTR(ret);
70177d8f306SDong Aisheng 	}
70277d8f306SDong Aisheng 
703fb4ac6f1SKrzysztof Kozlowski 	ret = driver_set_override(&pdev->dev, &pdev->driver_override,
704fb4ac6f1SKrzysztof Kozlowski 				  "imx-scu-clk", strlen("imx-scu-clk"));
705fb4ac6f1SKrzysztof Kozlowski 	if (ret) {
706fb4ac6f1SKrzysztof Kozlowski 		platform_device_put(pdev);
707fb4ac6f1SKrzysztof Kozlowski 		return ERR_PTR(ret);
708fb4ac6f1SKrzysztof Kozlowski 	}
70977d8f306SDong Aisheng 
71077d8f306SDong Aisheng 	ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id);
71177d8f306SDong Aisheng 	if (ret)
71277d8f306SDong Aisheng 		pr_warn("%s: failed to attached the power domain %d\n",
71377d8f306SDong Aisheng 			name, ret);
71477d8f306SDong Aisheng 
715855ae87aSLin Yujun 	ret = platform_device_add(pdev);
716855ae87aSLin Yujun 	if (ret) {
717855ae87aSLin Yujun 		platform_device_put(pdev);
718855ae87aSLin Yujun 		return ERR_PTR(ret);
719855ae87aSLin Yujun 	}
72077d8f306SDong Aisheng 
72177d8f306SDong Aisheng 	/* For API backwards compatiblilty, simply return NULL for success */
72277d8f306SDong Aisheng 	return NULL;
72377d8f306SDong Aisheng }
72477d8f306SDong Aisheng 
imx_clk_scu_unregister(void)72577d8f306SDong Aisheng void imx_clk_scu_unregister(void)
72677d8f306SDong Aisheng {
727632c60ecSDan Carpenter 	struct imx_scu_clk_node *clk, *n;
72877d8f306SDong Aisheng 	int i;
72977d8f306SDong Aisheng 
73077d8f306SDong Aisheng 	for (i = 0; i < IMX_SC_R_LAST; i++) {
731632c60ecSDan Carpenter 		list_for_each_entry_safe(clk, n, &imx_scu_clks[i], node) {
73277d8f306SDong Aisheng 			clk_hw_unregister(clk->hw);
73377d8f306SDong Aisheng 			kfree(clk);
73477d8f306SDong Aisheng 		}
73577d8f306SDong Aisheng 	}
73677d8f306SDong Aisheng }
7375392c5deSDong Aisheng 
clk_gpr_div_scu_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)7385392c5deSDong Aisheng static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw,
7395392c5deSDong Aisheng 						 unsigned long parent_rate)
7405392c5deSDong Aisheng {
7415392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7425392c5deSDong Aisheng 	unsigned long rate = 0;
7435392c5deSDong Aisheng 	u32 val;
7445392c5deSDong Aisheng 	int err;
7455392c5deSDong Aisheng 
7465392c5deSDong Aisheng 	err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7475392c5deSDong Aisheng 				      clk->gpr_id, &val);
7485392c5deSDong Aisheng 
7495392c5deSDong Aisheng 	rate  = val ? parent_rate / 2 : parent_rate;
7505392c5deSDong Aisheng 
7515392c5deSDong Aisheng 	return err ? 0 : rate;
7525392c5deSDong Aisheng }
7535392c5deSDong Aisheng 
clk_gpr_div_scu_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)7545392c5deSDong Aisheng static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate,
7555392c5deSDong Aisheng 				   unsigned long *prate)
7565392c5deSDong Aisheng {
7575392c5deSDong Aisheng 	if (rate < *prate)
7585392c5deSDong Aisheng 		rate = *prate / 2;
7595392c5deSDong Aisheng 	else
7605392c5deSDong Aisheng 		rate = *prate;
7615392c5deSDong Aisheng 
7625392c5deSDong Aisheng 	return rate;
7635392c5deSDong Aisheng }
7645392c5deSDong Aisheng 
clk_gpr_div_scu_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)7655392c5deSDong Aisheng static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate,
7665392c5deSDong Aisheng 				    unsigned long parent_rate)
7675392c5deSDong Aisheng {
7685392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7695392c5deSDong Aisheng 	uint32_t val;
7705392c5deSDong Aisheng 	int err;
7715392c5deSDong Aisheng 
7725392c5deSDong Aisheng 	val = (rate < parent_rate) ? 1 : 0;
7735392c5deSDong Aisheng 	err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
7745392c5deSDong Aisheng 				      clk->gpr_id, val);
7755392c5deSDong Aisheng 
7765392c5deSDong Aisheng 	return err ? -EINVAL : 0;
7775392c5deSDong Aisheng }
7785392c5deSDong Aisheng 
7795392c5deSDong Aisheng static const struct clk_ops clk_gpr_div_scu_ops = {
7805392c5deSDong Aisheng 	.recalc_rate = clk_gpr_div_scu_recalc_rate,
7815392c5deSDong Aisheng 	.round_rate = clk_gpr_div_scu_round_rate,
7825392c5deSDong Aisheng 	.set_rate = clk_gpr_div_scu_set_rate,
7835392c5deSDong Aisheng };
7845392c5deSDong Aisheng 
clk_gpr_mux_scu_get_parent(struct clk_hw * hw)7855392c5deSDong Aisheng static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw)
7865392c5deSDong Aisheng {
7875392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7885392c5deSDong Aisheng 	u32 val = 0;
7895392c5deSDong Aisheng 
7905392c5deSDong Aisheng 	imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
7915392c5deSDong Aisheng 				clk->gpr_id, &val);
7925392c5deSDong Aisheng 
7935392c5deSDong Aisheng 	return (u8)val;
7945392c5deSDong Aisheng }
7955392c5deSDong Aisheng 
clk_gpr_mux_scu_set_parent(struct clk_hw * hw,u8 index)7965392c5deSDong Aisheng static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
7975392c5deSDong Aisheng {
7985392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
7995392c5deSDong Aisheng 
8005392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
8015392c5deSDong Aisheng 				       clk->gpr_id, index);
8025392c5deSDong Aisheng }
8035392c5deSDong Aisheng 
8045392c5deSDong Aisheng static const struct clk_ops clk_gpr_mux_scu_ops = {
8051c2c20dbSMaxime Ripard 	.determine_rate = clk_hw_determine_rate_no_reparent,
8065392c5deSDong Aisheng 	.get_parent = clk_gpr_mux_scu_get_parent,
8075392c5deSDong Aisheng 	.set_parent = clk_gpr_mux_scu_set_parent,
8085392c5deSDong Aisheng };
8095392c5deSDong Aisheng 
clk_gpr_gate_scu_prepare(struct clk_hw * hw)8105392c5deSDong Aisheng static int clk_gpr_gate_scu_prepare(struct clk_hw *hw)
8115392c5deSDong Aisheng {
8125392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
8135392c5deSDong Aisheng 
8145392c5deSDong Aisheng 	return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
8155392c5deSDong Aisheng 				       clk->gpr_id, !clk->gate_invert);
8165392c5deSDong Aisheng }
8175392c5deSDong Aisheng 
clk_gpr_gate_scu_unprepare(struct clk_hw * hw)8185392c5deSDong Aisheng static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw)
8195392c5deSDong Aisheng {
8205392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
8215392c5deSDong Aisheng 	int ret;
8225392c5deSDong Aisheng 
8235392c5deSDong Aisheng 	ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id,
8245392c5deSDong Aisheng 				      clk->gpr_id, clk->gate_invert);
8255392c5deSDong Aisheng 	if (ret)
8265392c5deSDong Aisheng 		pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
8275392c5deSDong Aisheng 		       ret);
8285392c5deSDong Aisheng }
8295392c5deSDong Aisheng 
clk_gpr_gate_scu_is_prepared(struct clk_hw * hw)8305392c5deSDong Aisheng static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw)
8315392c5deSDong Aisheng {
8325392c5deSDong Aisheng 	struct clk_gpr_scu *clk = to_clk_gpr_scu(hw);
8335392c5deSDong Aisheng 	int ret;
8345392c5deSDong Aisheng 	u32 val;
8355392c5deSDong Aisheng 
8365392c5deSDong Aisheng 	ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id,
8375392c5deSDong Aisheng 				      clk->gpr_id, &val);
8385392c5deSDong Aisheng 	if (ret)
8395392c5deSDong Aisheng 		return ret;
8405392c5deSDong Aisheng 
8415392c5deSDong Aisheng 	return clk->gate_invert ? !val : val;
8425392c5deSDong Aisheng }
8435392c5deSDong Aisheng 
8445392c5deSDong Aisheng static const struct clk_ops clk_gpr_gate_scu_ops = {
8455392c5deSDong Aisheng 	.prepare = clk_gpr_gate_scu_prepare,
8465392c5deSDong Aisheng 	.unprepare = clk_gpr_gate_scu_unprepare,
8475392c5deSDong Aisheng 	.is_prepared = clk_gpr_gate_scu_is_prepared,
8485392c5deSDong Aisheng };
8495392c5deSDong Aisheng 
__imx_clk_gpr_scu(const char * name,const char * const * parent_name,int num_parents,u32 rsrc_id,u8 gpr_id,u8 flags,bool invert)8505392c5deSDong Aisheng struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name,
8515392c5deSDong Aisheng 				 int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags,
8525392c5deSDong Aisheng 				 bool invert)
8535392c5deSDong Aisheng {
8545392c5deSDong Aisheng 	struct imx_scu_clk_node *clk_node;
8555392c5deSDong Aisheng 	struct clk_gpr_scu *clk;
8565392c5deSDong Aisheng 	struct clk_hw *hw;
8575392c5deSDong Aisheng 	struct clk_init_data init;
8585392c5deSDong Aisheng 	int ret;
8595392c5deSDong Aisheng 
8605392c5deSDong Aisheng 	if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST)
8615392c5deSDong Aisheng 		return ERR_PTR(-EINVAL);
8625392c5deSDong Aisheng 
8635392c5deSDong Aisheng 	clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL);
8645392c5deSDong Aisheng 	if (!clk_node)
8655392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
8665392c5deSDong Aisheng 
8672759f38bSXiaoke Wang 	if (!imx_scu_clk_is_valid(rsrc_id)) {
8682759f38bSXiaoke Wang 		kfree(clk_node);
8695964012cSDong Aisheng 		return ERR_PTR(-EINVAL);
8702759f38bSXiaoke Wang 	}
8715964012cSDong Aisheng 
8725392c5deSDong Aisheng 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
8735392c5deSDong Aisheng 	if (!clk) {
8745392c5deSDong Aisheng 		kfree(clk_node);
8755392c5deSDong Aisheng 		return ERR_PTR(-ENOMEM);
8765392c5deSDong Aisheng 	}
8775392c5deSDong Aisheng 
8785392c5deSDong Aisheng 	clk->rsrc_id = rsrc_id;
8795392c5deSDong Aisheng 	clk->gpr_id = gpr_id;
8805392c5deSDong Aisheng 	clk->flags = flags;
8815392c5deSDong Aisheng 	clk->gate_invert = invert;
8825392c5deSDong Aisheng 
8835392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_GATE)
8845392c5deSDong Aisheng 		init.ops = &clk_gpr_gate_scu_ops;
8855392c5deSDong Aisheng 
8865392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_DIV)
8875392c5deSDong Aisheng 		init.ops = &clk_gpr_div_scu_ops;
8885392c5deSDong Aisheng 
8895392c5deSDong Aisheng 	if (flags & IMX_SCU_GPR_CLK_MUX)
8905392c5deSDong Aisheng 		init.ops = &clk_gpr_mux_scu_ops;
8915392c5deSDong Aisheng 
8925392c5deSDong Aisheng 	init.flags = 0;
8935392c5deSDong Aisheng 	init.name = name;
8945392c5deSDong Aisheng 	init.parent_names = parent_name;
8955392c5deSDong Aisheng 	init.num_parents = num_parents;
8965392c5deSDong Aisheng 
8975392c5deSDong Aisheng 	clk->hw.init = &init;
8985392c5deSDong Aisheng 
8995392c5deSDong Aisheng 	hw = &clk->hw;
9005392c5deSDong Aisheng 	ret = clk_hw_register(NULL, hw);
9015392c5deSDong Aisheng 	if (ret) {
9025392c5deSDong Aisheng 		kfree(clk);
9035392c5deSDong Aisheng 		kfree(clk_node);
9045392c5deSDong Aisheng 		hw = ERR_PTR(ret);
9055392c5deSDong Aisheng 	} else {
9065392c5deSDong Aisheng 		clk_node->hw = hw;
9075392c5deSDong Aisheng 		clk_node->clk_type = gpr_id;
9085392c5deSDong Aisheng 		list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]);
9095392c5deSDong Aisheng 	}
9105392c5deSDong Aisheng 
9115392c5deSDong Aisheng 	return hw;
9125392c5deSDong Aisheng }
913