Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright 2018 NXP
  4 *   Dong Aisheng <aisheng.dong@nxp.com>
  5 */
  6
  7#include <dt-bindings/firmware/imx/rsrc.h>
  8#include <linux/arm-smccc.h>
  9#include <linux/clk-provider.h>
 10#include <linux/err.h>
 11#include <linux/slab.h>
 12
 13#include "clk-scu.h"
 14
 15#define IMX_SIP_CPUFREQ			0xC2000001
 16#define IMX_SIP_SET_CPUFREQ		0x00
 17
 18static struct imx_sc_ipc *ccm_ipc_handle;
 19
 20/*
 21 * struct clk_scu - Description of one SCU clock
 22 * @hw: the common clk_hw
 23 * @rsrc_id: resource ID of this SCU clock
 24 * @clk_type: type of this clock resource
 25 */
 26struct clk_scu {
 27	struct clk_hw hw;
 28	u16 rsrc_id;
 29	u8 clk_type;
 30};
 31
 32/*
 33 * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
 34 * @hdr: SCU protocol header
 35 * @rate: rate to set
 36 * @resource: clock resource to set rate
 37 * @clk: clk type of this resource
 38 *
 39 * This structure describes the SCU protocol of clock rate set
 40 */
 41struct imx_sc_msg_req_set_clock_rate {
 42	struct imx_sc_rpc_msg hdr;
 43	__le32 rate;
 44	__le16 resource;
 45	u8 clk;
 46} __packed;
 47
 48struct req_get_clock_rate {
 49	__le16 resource;
 50	u8 clk;
 51} __packed;
 52
 53struct resp_get_clock_rate {
 54	__le32 rate;
 55};
 56
 57/*
 58 * struct imx_sc_msg_get_clock_rate - clock get rate protocol
 59 * @hdr: SCU protocol header
 60 * @req: get rate request protocol
 61 * @resp: get rate response protocol
 62 *
 63 * This structure describes the SCU protocol of clock rate get
 64 */
 65struct imx_sc_msg_get_clock_rate {
 66	struct imx_sc_rpc_msg hdr;
 67	union {
 68		struct req_get_clock_rate req;
 69		struct resp_get_clock_rate resp;
 70	} data;
 71};
 72
 73/*
 74 * struct imx_sc_msg_get_clock_parent - clock get parent protocol
 75 * @hdr: SCU protocol header
 76 * @req: get parent request protocol
 77 * @resp: get parent response protocol
 78 *
 79 * This structure describes the SCU protocol of clock get parent
 80 */
 81struct imx_sc_msg_get_clock_parent {
 82	struct imx_sc_rpc_msg hdr;
 83	union {
 84		struct req_get_clock_parent {
 85			__le16 resource;
 86			u8 clk;
 87		} __packed req;
 88		struct resp_get_clock_parent {
 89			u8 parent;
 90		} resp;
 91	} data;
 92};
 93
 94/*
 95 * struct imx_sc_msg_set_clock_parent - clock set parent protocol
 96 * @hdr: SCU protocol header
 97 * @req: set parent request protocol
 98 *
 99 * This structure describes the SCU protocol of clock set parent
100 */
101struct imx_sc_msg_set_clock_parent {
102	struct imx_sc_rpc_msg hdr;
103	__le16 resource;
104	u8 clk;
105	u8 parent;
106} __packed;
107
108/*
109 * struct imx_sc_msg_req_clock_enable - clock gate protocol
110 * @hdr: SCU protocol header
111 * @resource: clock resource to gate
112 * @clk: clk type of this resource
113 * @enable: whether gate off the clock
114 * @autog: HW auto gate enable
115 *
116 * This structure describes the SCU protocol of clock gate
117 */
118struct imx_sc_msg_req_clock_enable {
119	struct imx_sc_rpc_msg hdr;
120	__le16 resource;
121	u8 clk;
122	u8 enable;
123	u8 autog;
124} __packed;
125
126static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
127{
128	return container_of(hw, struct clk_scu, hw);
129}
130
131int imx_clk_scu_init(void)
132{
133	return imx_scu_get_handle(&ccm_ipc_handle);
134}
135
136/*
137 * clk_scu_recalc_rate - Get clock rate for a SCU clock
138 * @hw: clock to get rate for
139 * @parent_rate: parent rate provided by common clock framework, not used
140 *
141 * Gets the current clock rate of a SCU clock. Returns the current
142 * clock rate, or zero in failure.
143 */
144static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
145					 unsigned long parent_rate)
146{
147	struct clk_scu *clk = to_clk_scu(hw);
148	struct imx_sc_msg_get_clock_rate msg;
149	struct imx_sc_rpc_msg *hdr = &msg.hdr;
150	int ret;
151
152	hdr->ver = IMX_SC_RPC_VERSION;
153	hdr->svc = IMX_SC_RPC_SVC_PM;
154	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
155	hdr->size = 2;
156
157	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
158	msg.data.req.clk = clk->clk_type;
159
160	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
161	if (ret) {
162		pr_err("%s: failed to get clock rate %d\n",
163		       clk_hw_get_name(hw), ret);
164		return 0;
165	}
166
167	return le32_to_cpu(msg.data.resp.rate);
168}
169
170/*
171 * clk_scu_round_rate - Round clock rate for a SCU clock
172 * @hw: clock to round rate for
173 * @rate: rate to round
174 * @parent_rate: parent rate provided by common clock framework, not used
175 *
176 * Returns the current clock rate, or zero in failure.
177 */
178static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
179			       unsigned long *parent_rate)
180{
181	/*
182	 * Assume we support all the requested rate and let the SCU firmware
183	 * to handle the left work
184	 */
185	return rate;
186}
187
188static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
189				    unsigned long parent_rate)
190{
191	struct clk_scu *clk = to_clk_scu(hw);
192	struct arm_smccc_res res;
193	unsigned long cluster_id;
194
195	if (clk->rsrc_id == IMX_SC_R_A35)
196		cluster_id = 0;
197	else
198		return -EINVAL;
199
200	/* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */
201	arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ,
202		      cluster_id, rate, 0, 0, 0, 0, &res);
203
204	return 0;
205}
206
207/*
208 * clk_scu_set_rate - Set rate for a SCU clock
209 * @hw: clock to change rate for
210 * @rate: target rate for the clock
211 * @parent_rate: rate of the clock parent, not used for SCU clocks
212 *
213 * Sets a clock frequency for a SCU clock. Returns the SCU
214 * protocol status.
215 */
216static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
217			    unsigned long parent_rate)
218{
219	struct clk_scu *clk = to_clk_scu(hw);
220	struct imx_sc_msg_req_set_clock_rate msg;
221	struct imx_sc_rpc_msg *hdr = &msg.hdr;
222
223	hdr->ver = IMX_SC_RPC_VERSION;
224	hdr->svc = IMX_SC_RPC_SVC_PM;
225	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
226	hdr->size = 3;
227
228	msg.rate = cpu_to_le32(rate);
229	msg.resource = cpu_to_le16(clk->rsrc_id);
230	msg.clk = clk->clk_type;
231
232	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
233}
234
235static u8 clk_scu_get_parent(struct clk_hw *hw)
236{
237	struct clk_scu *clk = to_clk_scu(hw);
238	struct imx_sc_msg_get_clock_parent msg;
239	struct imx_sc_rpc_msg *hdr = &msg.hdr;
240	int ret;
241
242	hdr->ver = IMX_SC_RPC_VERSION;
243	hdr->svc = IMX_SC_RPC_SVC_PM;
244	hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT;
245	hdr->size = 2;
246
247	msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
248	msg.data.req.clk = clk->clk_type;
249
250	ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
251	if (ret) {
252		pr_err("%s: failed to get clock parent %d\n",
253		       clk_hw_get_name(hw), ret);
254		return 0;
255	}
256
257	return msg.data.resp.parent;
258}
259
260static int clk_scu_set_parent(struct clk_hw *hw, u8 index)
261{
262	struct clk_scu *clk = to_clk_scu(hw);
263	struct imx_sc_msg_set_clock_parent msg;
264	struct imx_sc_rpc_msg *hdr = &msg.hdr;
265
266	hdr->ver = IMX_SC_RPC_VERSION;
267	hdr->svc = IMX_SC_RPC_SVC_PM;
268	hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT;
269	hdr->size = 2;
270
271	msg.resource = cpu_to_le16(clk->rsrc_id);
272	msg.clk = clk->clk_type;
273	msg.parent = index;
274
275	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
276}
277
278static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
279			      u8 clk, bool enable, bool autog)
280{
281	struct imx_sc_msg_req_clock_enable msg;
282	struct imx_sc_rpc_msg *hdr = &msg.hdr;
283
284	hdr->ver = IMX_SC_RPC_VERSION;
285	hdr->svc = IMX_SC_RPC_SVC_PM;
286	hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
287	hdr->size = 3;
288
289	msg.resource = cpu_to_le16(resource);
290	msg.clk = clk;
291	msg.enable = enable;
292	msg.autog = autog;
293
294	return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
295}
296
297/*
298 * clk_scu_prepare - Enable a SCU clock
299 * @hw: clock to enable
300 *
301 * Enable the clock at the DSC slice level
302 */
303static int clk_scu_prepare(struct clk_hw *hw)
304{
305	struct clk_scu *clk = to_clk_scu(hw);
306
307	return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
308				  clk->clk_type, true, false);
309}
310
311/*
312 * clk_scu_unprepare - Disable a SCU clock
313 * @hw: clock to enable
314 *
315 * Disable the clock at the DSC slice level
316 */
317static void clk_scu_unprepare(struct clk_hw *hw)
318{
319	struct clk_scu *clk = to_clk_scu(hw);
320	int ret;
321
322	ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
323				 clk->clk_type, false, false);
324	if (ret)
325		pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
326			ret);
327}
328
329static const struct clk_ops clk_scu_ops = {
330	.recalc_rate = clk_scu_recalc_rate,
331	.round_rate = clk_scu_round_rate,
332	.set_rate = clk_scu_set_rate,
333	.get_parent = clk_scu_get_parent,
334	.set_parent = clk_scu_set_parent,
335	.prepare = clk_scu_prepare,
336	.unprepare = clk_scu_unprepare,
337};
338
339static const struct clk_ops clk_scu_cpu_ops = {
340	.recalc_rate = clk_scu_recalc_rate,
341	.round_rate = clk_scu_round_rate,
342	.set_rate = clk_scu_atf_set_cpu_rate,
343	.prepare = clk_scu_prepare,
344	.unprepare = clk_scu_unprepare,
345};
346
347struct clk_hw *__imx_clk_scu(const char *name, const char * const *parents,
348			     int num_parents, u32 rsrc_id, u8 clk_type)
349{
350	struct clk_init_data init;
351	struct clk_scu *clk;
352	struct clk_hw *hw;
353	int ret;
354
355	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
356	if (!clk)
357		return ERR_PTR(-ENOMEM);
358
359	clk->rsrc_id = rsrc_id;
360	clk->clk_type = clk_type;
361
362	init.name = name;
363	init.ops = &clk_scu_ops;
364	if (rsrc_id == IMX_SC_R_A35)
365		init.ops = &clk_scu_cpu_ops;
366	else
367		init.ops = &clk_scu_ops;
368	init.parent_names = parents;
369	init.num_parents = num_parents;
370
371	/*
372	 * Note on MX8, the clocks are tightly coupled with power domain
373	 * that once the power domain is off, the clock status may be
374	 * lost. So we make it NOCACHE to let user to retrieve the real
375	 * clock status from HW instead of using the possible invalid
376	 * cached rate.
377	 */
378	init.flags = CLK_GET_RATE_NOCACHE;
379	clk->hw.init = &init;
380
381	hw = &clk->hw;
382	ret = clk_hw_register(NULL, hw);
383	if (ret) {
384		kfree(clk);
385		hw = ERR_PTR(ret);
386	}
387
388	return hw;
389}