Merge branches 'clk-of-refcount', 'clk-mmio-fixed-clock', 'clk-remove-clps', 'clk...
[sfrench/cifs-2.6.git] / drivers / clk / imx / clk-scu.c
1 // 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
13 static 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  */
21 struct 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  */
36 struct 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
43 struct req_get_clock_rate {
44         __le16 resource;
45         u8 clk;
46 } __packed;
47
48 struct 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  */
60 struct 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  */
78 struct 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
86 static inline struct clk_scu *to_clk_scu(struct clk_hw *hw)
87 {
88         return container_of(hw, struct clk_scu, hw);
89 }
90
91 int 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  */
104 static 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  */
138 static 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  */
157 static 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
176 static 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  */
201 static 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  */
215 static 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
227 static 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
235 struct 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 }