blob: e2bfe422f174b7c132993c89349ff99cbb2ce47e [file] [log] [blame]
Taniya Das76b8e472017-03-24 15:25:41 +05301/*
2 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
Deepak Katragadda9abd7942017-06-13 14:20:09 -070014#define pr_fmt(fmt) "clk: %s: " fmt, __func__
Taniya Das76b8e472017-03-24 15:25:41 +053015
16#include <linux/clk-provider.h>
17#include <linux/clk.h>
18#include <linux/err.h>
19#include <linux/kernel.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/mailbox_client.h>
23#include <dt-bindings/clock/qcom,aop-qmp.h>
24
25#define MAX_LEN 96
26#define MBOX_TOUT_MS 1000
27
28struct qmp_pkt {
29 u32 size;
30 void *data;
31};
32
Deepak Katragadda90954d72017-07-27 14:22:24 -070033#define DEFINE_CLK_AOP_QMP(_name, _class, _res, _estate, _dstate, _flags) \
Taniya Das76b8e472017-03-24 15:25:41 +053034 static struct clk_aop_qmp _name = { \
35 .msg.class = #_class, \
36 .msg.res = #_res, \
37 .enable_state = _estate, \
38 .disable_state = _dstate, \
39 .hw.init = &(struct clk_init_data){ \
40 .ops = &aop_qmp_clk_ops, \
41 .name = #_name, \
42 .num_parents = 0, \
Deepak Katragadda90954d72017-07-27 14:22:24 -070043 .flags = _flags, \
Taniya Das76b8e472017-03-24 15:25:41 +053044 }, \
45 }
46
47#define to_aop_qmp_clk(hw) container_of(hw, struct clk_aop_qmp, hw)
48
49/*
50 * struct qmp_mbox_msg - mailbox data to QMP
51 * @class: identifies the class.
52 * @res: identifies the resource in the class
53 * @level: identifies the level for the resource.
54 */
55struct qmp_mbox_msg {
56 char class[MAX_LEN];
57 char res[MAX_LEN];
58 int level;
59};
60
61/*
62 * struct clk_aop_qmp - AOP clock
63 * @dev: The device that corresponds to this clock.
64 * @hw: The clock hardware for this clock.
65 * @cl: The client mailbox for this clock.
66 * @mbox: The mbox controller for this clock.
67 * @level: The clock level for this clock.
68 * @enable_state: The clock state when this clock is prepared.
69 * @disable_state: The clock state when this clock is unprepared.
70 * @msg: QMP data associated with this clock.
71 * @enabled: Status of the clock enable.
72 */
73struct clk_aop_qmp {
74 struct device *dev;
75 struct clk_hw hw;
76 struct mbox_client cl;
77 struct mbox_chan *mbox;
78 int level;
79 int enable_state;
80 int disable_state;
81 struct qmp_mbox_msg msg;
82 bool enabled;
83};
84
85static DEFINE_MUTEX(clk_aop_lock);
86
87static unsigned long clk_aop_qmp_recalc_rate(struct clk_hw *hw,
88 unsigned long parent_rate)
89{
90 struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
91
92 return clk->level;
93}
94
95static long clk_aop_qmp_round_rate(struct clk_hw *hw, unsigned long rate,
96 unsigned long *parent_rate)
97{
98 return rate;
99}
100
101static int clk_aop_qmp_set_rate(struct clk_hw *hw, unsigned long rate,
102 unsigned long parent_rate)
103{
104 char mbox_msg[MAX_LEN];
105 struct qmp_pkt pkt;
106 struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
107 int ret = 0;
108
109 mutex_lock(&clk_aop_lock);
110
111 snprintf(mbox_msg, MAX_LEN, "{class: %s, res: %s, val: %ld}",
112 clk->msg.class, clk->msg.res, rate);
113 pkt.size = MAX_LEN;
114 pkt.data = mbox_msg;
115
116 ret = mbox_send_message(clk->mbox, &pkt);
117 if (ret < 0) {
118 pr_err("Failed to send set rate request of %lu for %s, ret %d\n",
119 rate, clk_hw_get_name(hw), ret);
120 goto err;
121 } else
122 /* Success: update the return value */
123 ret = 0;
124
125 /* update the current clock level once the mailbox message is sent */
126 clk->level = rate;
127err:
128 mutex_unlock(&clk_aop_lock);
129
130 return ret;
131}
132
133static int clk_aop_qmp_prepare(struct clk_hw *hw)
134{
135 char mbox_msg[MAX_LEN];
136 unsigned long rate;
137 int ret = 0;
138 struct qmp_pkt pkt;
139 struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
140
141 mutex_lock(&clk_aop_lock);
Deepak Katragadda68e900c2017-09-14 11:21:18 -0700142 /*
143 * Return early if the clock has been enabled already. This
144 * is to avoid issues with sending duplicate enable requests.
145 */
146 if (clk->enabled)
147 goto err;
Taniya Das76b8e472017-03-24 15:25:41 +0530148
149 if (clk->level)
150 rate = clk->level;
151 else
152 rate = clk->enable_state;
153
154 snprintf(mbox_msg, MAX_LEN, "{class: %s, res: %s, val: %ld}",
155 clk->msg.class, clk->msg.res, rate);
156 pkt.size = MAX_LEN;
157 pkt.data = mbox_msg;
158
159 ret = mbox_send_message(clk->mbox, &pkt);
160 if (ret < 0) {
161 pr_err("Failed to send clk prepare request for %s, ret %d\n",
162 clk_hw_get_name(hw), ret);
163 goto err;
164 } else
165 /* Success: update the return value */
166 ret = 0;
167
168 /* update the current clock level once the mailbox message is sent */
169 clk->level = rate;
170
171 clk->enabled = true;
172err:
173 mutex_unlock(&clk_aop_lock);
174
175 return ret;
176}
177
178static void clk_aop_qmp_unprepare(struct clk_hw *hw)
179{
180 char mbox_msg[MAX_LEN];
181 unsigned long rate;
182 int ret = 0;
183 struct qmp_pkt pkt;
184 struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
185
186 mutex_lock(&clk_aop_lock);
187
Deepak Katragadda68e900c2017-09-14 11:21:18 -0700188 if (!clk->enabled)
189 goto err;
190
Taniya Das76b8e472017-03-24 15:25:41 +0530191 rate = clk->disable_state;
192
193 snprintf(mbox_msg, MAX_LEN, "{class: %s, res: %s, val: %ld}",
194 clk->msg.class, clk->msg.res, rate);
195 pkt.size = MAX_LEN;
196 pkt.data = mbox_msg;
197
198 ret = mbox_send_message(clk->mbox, &pkt);
199 if (ret < 0) {
200 pr_err("Failed to send clk unprepare request for %s, ret %d\n",
201 clk_hw_get_name(hw), ret);
202 goto err;
203 }
204
205 clk->enabled = false;
206err:
207 mutex_unlock(&clk_aop_lock);
208}
209
210static int clk_aop_qmp_is_enabled(struct clk_hw *hw)
211{
212 struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
213
214 return clk->enabled;
215}
216
217static const struct clk_ops aop_qmp_clk_ops = {
218 .prepare = clk_aop_qmp_prepare,
219 .unprepare = clk_aop_qmp_unprepare,
220 .recalc_rate = clk_aop_qmp_recalc_rate,
221 .set_rate = clk_aop_qmp_set_rate,
222 .round_rate = clk_aop_qmp_round_rate,
223 .is_enabled = clk_aop_qmp_is_enabled,
224};
225
Deepak Katragadda90954d72017-07-27 14:22:24 -0700226DEFINE_CLK_AOP_QMP(qdss_qmp_clk, clock, qdss, QDSS_CLK_LEVEL_DYNAMIC,
227 QDSS_CLK_LEVEL_OFF, CLK_ENABLE_HAND_OFF);
228DEFINE_CLK_AOP_QMP(qdss_ao_qmp_clk, clock, qdss_ao, QDSS_CLK_LEVEL_DYNAMIC,
229 QDSS_CLK_LEVEL_OFF, 0);
Taniya Das76b8e472017-03-24 15:25:41 +0530230
231static struct clk_hw *aop_qmp_clk_hws[] = {
232 [QDSS_CLK] = &qdss_qmp_clk.hw,
Deepak Katragadda90954d72017-07-27 14:22:24 -0700233 [QDSS_AO_CLK] = &qdss_ao_qmp_clk.hw,
Taniya Das76b8e472017-03-24 15:25:41 +0530234};
235
Deepak Katragadda90954d72017-07-27 14:22:24 -0700236/*
237 * Due to HW limitations on v1, the qdss_ao clock was not supported by the clock
238 * driver on AOP.
239 */
240static void aop_qmp_fixup_v1(void)
241{
242 aop_qmp_clk_hws[QDSS_AO_CLK] = NULL;
243}
244
Taniya Das76b8e472017-03-24 15:25:41 +0530245static int qmp_update_client(struct clk_hw *hw, struct device *dev,
246 struct mbox_chan *mbox)
247{
248 struct clk_aop_qmp *clk_aop = to_aop_qmp_clk(hw);
249
250 /* Use mailbox client with blocking mode */
251 clk_aop->cl.dev = dev;
252 clk_aop->cl.tx_block = true;
253 clk_aop->cl.tx_tout = MBOX_TOUT_MS;
254 clk_aop->cl.knows_txdone = false;
255
256 if (mbox) {
257 clk_aop->mbox = mbox;
258 return 0;
259 }
260
261 /* Allocate mailbox channel */
262 mbox = clk_aop->mbox = mbox_request_channel(&clk_aop->cl, 0);
263 if (IS_ERR(clk_aop->mbox) && PTR_ERR(clk_aop->mbox) != -EPROBE_DEFER) {
264 dev_err(dev, "Failed to get mailbox channel %pK %ld\n",
265 mbox, PTR_ERR(mbox));
266 return PTR_ERR(clk_aop->mbox);
267 }
268
269 return 0;
270}
271
272static int aop_qmp_clk_probe(struct platform_device *pdev)
273{
Deepak Katragadda90954d72017-07-27 14:22:24 -0700274 struct clk *clk = NULL;
Taniya Das76b8e472017-03-24 15:25:41 +0530275 struct device_node *np = pdev->dev.of_node;
276 struct mbox_chan *mbox = NULL;
277 int num_clks = ARRAY_SIZE(aop_qmp_clk_hws);
278 int ret = 0, i = 0;
279
280 /*
281 * Allocate mbox channel for the first clock client. The same channel
282 * would be used for the rest of the clock clients.
283 */
284 ret = qmp_update_client(aop_qmp_clk_hws[i], &pdev->dev, mbox);
285 if (ret < 0)
286 return ret;
287
Deepak Katragadda90954d72017-07-27 14:22:24 -0700288 if (of_device_is_compatible(pdev->dev.of_node, "qcom,aop-qmp-clk-v1"))
289 aop_qmp_fixup_v1();
290
Taniya Das76b8e472017-03-24 15:25:41 +0530291 for (i = 1; i < num_clks; i++) {
Deepak Katragadda90954d72017-07-27 14:22:24 -0700292 if (!aop_qmp_clk_hws[i])
293 continue;
Taniya Das76b8e472017-03-24 15:25:41 +0530294 ret = qmp_update_client(aop_qmp_clk_hws[i], &pdev->dev, mbox);
295 if (ret < 0) {
296 dev_err(&pdev->dev, "Failed to update QMP client %d\n",
297 ret);
298 goto fail;
299 }
300 }
301
Deepak Katragadda90954d72017-07-27 14:22:24 -0700302 /*
303 * Proxy vote on the QDSS clock. This is needed to avoid issues with
304 * excessive requests on the QMP layer during the QDSS driver probe.
305 */
306 ret = clk_aop_qmp_prepare(&qdss_qmp_clk.hw);
307 if (ret < 0)
308 goto fail;
Taniya Das76b8e472017-03-24 15:25:41 +0530309
310 for (i = 0; i < num_clks; i++) {
Deepak Katragadda90954d72017-07-27 14:22:24 -0700311 if (!aop_qmp_clk_hws[i])
312 continue;
Taniya Das76b8e472017-03-24 15:25:41 +0530313 clk = devm_clk_register(&pdev->dev, aop_qmp_clk_hws[i]);
314 if (IS_ERR(clk)) {
315 ret = PTR_ERR(clk);
316 goto fail;
317 }
318 }
319
320 ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
321 if (ret) {
322 dev_err(&pdev->dev, "Failed to register clock provider\n");
323 goto fail;
324 }
325
326 dev_info(&pdev->dev, "Registered clocks with AOP\n");
327
328 return ret;
329fail:
330 mbox_free_channel(mbox);
331
332 return ret;
333}
334
335static const struct of_device_id aop_qmp_clk_of_match[] = {
Deepak Katragadda90954d72017-07-27 14:22:24 -0700336 { .compatible = "qcom,aop-qmp-clk-v1" },
337 { .compatible = "qcom,aop-qmp-clk-v2" },
Taniya Das76b8e472017-03-24 15:25:41 +0530338 {}
339};
340
341static struct platform_driver aop_qmp_clk_driver = {
342 .driver = {
343 .name = "qmp-aop-clk",
344 .of_match_table = aop_qmp_clk_of_match,
345 },
346 .probe = aop_qmp_clk_probe,
347};
348
349static int __init aop_qmp_clk_init(void)
350{
351 return platform_driver_register(&aop_qmp_clk_driver);
352}
353subsys_initcall(aop_qmp_clk_init);