blob: 7ccf7edfe11cb3e7d26204d27daafcb50baad8e8 [file] [log] [blame]
Aisheng Dongfe37b482018-12-13 15:42:54 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2018 NXP
4 * Dong Aisheng <aisheng.dong@nxp.com>
5 */
6
7#include <linux/clk-provider.h>
8#include <linux/err.h>
9#include <linux/slab.h>
10
11#include "clk-scu.h"
12
13static struct imx_sc_ipc *ccm_ipc_handle;
14
15/*
16 * struct clk_scu - Description of one SCU clock
17 * @hw: the common clk_hw
18 * @rsrc_id: resource ID of this SCU clock
19 * @clk_type: type of this clock resource
20 */
21struct clk_scu {
22 struct clk_hw hw;
23 u16 rsrc_id;
24 u8 clk_type;
25};
26
27/*
28 * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol
29 * @hdr: SCU protocol header
30 * @rate: rate to set
31 * @resource: clock resource to set rate
32 * @clk: clk type of this resource
33 *
34 * This structure describes the SCU protocol of clock rate set
35 */
36struct imx_sc_msg_req_set_clock_rate {
37 struct imx_sc_rpc_msg hdr;
38 __le32 rate;
39 __le16 resource;
40 u8 clk;
41} __packed;
42
43struct req_get_clock_rate {
44 __le16 resource;
45 u8 clk;
46} __packed;
47
48struct resp_get_clock_rate {
49 __le32 rate;
50};
51
52/*
53 * struct imx_sc_msg_get_clock_rate - clock get rate protocol
54 * @hdr: SCU protocol header
55 * @req: get rate request protocol
56 * @resp: get rate response protocol
57 *
58 * This structure describes the SCU protocol of clock rate get
59 */
60struct imx_sc_msg_get_clock_rate {
61 struct imx_sc_rpc_msg hdr;
62 union {
63 struct req_get_clock_rate req;
64 struct resp_get_clock_rate resp;
65 } data;
66};
67
68/*
69 * struct imx_sc_msg_req_clock_enable - clock gate protocol
70 * @hdr: SCU protocol header
71 * @resource: clock resource to gate
72 * @clk: clk type of this resource
73 * @enable: whether gate off the clock
74 * @autog: HW auto gate enable
75 *
76 * This structure describes the SCU protocol of clock gate
77 */
78struct imx_sc_msg_req_clock_enable {
79 struct imx_sc_rpc_msg hdr;
80 __le16 resource;
81 u8 clk;
82 u8 enable;
83 u8 autog;
84} __packed;
85
86static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
87{
88 return container_of(hw, struct clk_scu, hw);
89}
90
91int imx_clk_scu_init(void)
92{
93 return imx_scu_get_handle(&ccm_ipc_handle);
94}
95
96/*
97 * clk_scu_recalc_rate - Get clock rate for a SCU clock
98 * @hw: clock to get rate for
99 * @parent_rate: parent rate provided by common clock framework, not used
100 *
101 * Gets the current clock rate of a SCU clock. Returns the current
102 * clock rate, or zero in failure.
103 */
104static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
105 unsigned long parent_rate)
106{
107 struct clk_scu *clk = to_clk_scu(hw);
108 struct imx_sc_msg_get_clock_rate msg;
109 struct imx_sc_rpc_msg *hdr = &msg.hdr;
110 int ret;
111
112 hdr->ver = IMX_SC_RPC_VERSION;
113 hdr->svc = IMX_SC_RPC_SVC_PM;
114 hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE;
115 hdr->size = 2;
116
117 msg.data.req.resource = cpu_to_le16(clk->rsrc_id);
118 msg.data.req.clk = clk->clk_type;
119
120 ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
121 if (ret) {
122 pr_err("%s: failed to get clock rate %d\n",
123 clk_hw_get_name(hw), ret);
124 return 0;
125 }
126
127 return le32_to_cpu(msg.data.resp.rate);
128}
129
130/*
131 * clk_scu_round_rate - Round clock rate for a SCU clock
132 * @hw: clock to round rate for
133 * @rate: rate to round
134 * @parent_rate: parent rate provided by common clock framework, not used
135 *
136 * Returns the current clock rate, or zero in failure.
137 */
138static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate,
139 unsigned long *parent_rate)
140{
141 /*
142 * Assume we support all the requested rate and let the SCU firmware
143 * to handle the left work
144 */
145 return rate;
146}
147
148/*
149 * clk_scu_set_rate - Set rate for a SCU clock
150 * @hw: clock to change rate for
151 * @rate: target rate for the clock
152 * @parent_rate: rate of the clock parent, not used for SCU clocks
153 *
154 * Sets a clock frequency for a SCU clock. Returns the SCU
155 * protocol status.
156 */
157static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate,
158 unsigned long parent_rate)
159{
160 struct clk_scu *clk = to_clk_scu(hw);
161 struct imx_sc_msg_req_set_clock_rate msg;
162 struct imx_sc_rpc_msg *hdr = &msg.hdr;
163
164 hdr->ver = IMX_SC_RPC_VERSION;
165 hdr->svc = IMX_SC_RPC_SVC_PM;
166 hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE;
167 hdr->size = 3;
168
169 msg.rate = cpu_to_le32(rate);
170 msg.resource = cpu_to_le16(clk->rsrc_id);
171 msg.clk = clk->clk_type;
172
173 return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
174}
175
176static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource,
177 u8 clk, bool enable, bool autog)
178{
179 struct imx_sc_msg_req_clock_enable msg;
180 struct imx_sc_rpc_msg *hdr = &msg.hdr;
181
182 hdr->ver = IMX_SC_RPC_VERSION;
183 hdr->svc = IMX_SC_RPC_SVC_PM;
184 hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE;
185 hdr->size = 3;
186
187 msg.resource = cpu_to_le16(resource);
188 msg.clk = clk;
189 msg.enable = enable;
190 msg.autog = autog;
191
192 return imx_scu_call_rpc(ccm_ipc_handle, &msg, true);
193}
194
195/*
196 * clk_scu_prepare - Enable a SCU clock
197 * @hw: clock to enable
198 *
199 * Enable the clock at the DSC slice level
200 */
201static int clk_scu_prepare(struct clk_hw *hw)
202{
203 struct clk_scu *clk = to_clk_scu(hw);
204
205 return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
206 clk->clk_type, true, false);
207}
208
209/*
210 * clk_scu_unprepare - Disable a SCU clock
211 * @hw: clock to enable
212 *
213 * Disable the clock at the DSC slice level
214 */
215static void clk_scu_unprepare(struct clk_hw *hw)
216{
217 struct clk_scu *clk = to_clk_scu(hw);
218 int ret;
219
220 ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id,
221 clk->clk_type, false, false);
222 if (ret)
223 pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw),
224 ret);
225}
226
227static const struct clk_ops clk_scu_ops = {
228 .recalc_rate = clk_scu_recalc_rate,
229 .round_rate = clk_scu_round_rate,
230 .set_rate = clk_scu_set_rate,
231 .prepare = clk_scu_prepare,
232 .unprepare = clk_scu_unprepare,
233};
234
235struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, u8 clk_type)
236{
237 struct clk_init_data init;
238 struct clk_scu *clk;
239 struct clk_hw *hw;
240 int ret;
241
242 clk = kzalloc(sizeof(*clk), GFP_KERNEL);
243 if (!clk)
244 return ERR_PTR(-ENOMEM);
245
246 clk->rsrc_id = rsrc_id;
247 clk->clk_type = clk_type;
248
249 init.name = name;
250 init.ops = &clk_scu_ops;
251 init.num_parents = 0;
252 /*
253 * Note on MX8, the clocks are tightly coupled with power domain
254 * that once the power domain is off, the clock status may be
255 * lost. So we make it NOCACHE to let user to retrieve the real
256 * clock status from HW instead of using the possible invalid
257 * cached rate.
258 */
259 init.flags = CLK_GET_RATE_NOCACHE;
260 clk->hw.init = &init;
261
262 hw = &clk->hw;
263 ret = clk_hw_register(NULL, hw);
264 if (ret) {
265 kfree(clk);
266 hw = ERR_PTR(ret);
267 }
268
269 return hw;
270}