blob: ac3b19d89ddd49ca68df12c1ac1adc0428f14f44 [file] [log] [blame]
Laxminath Kasamd1f36192017-08-03 18:54:01 +05301/*
Laxminath Kasam63f71cc2018-03-05 15:56:29 +05302 * Copyright (c) 2012-2015, 2017-2018 The Linux Foundation. All rights reserved.
Laxminath Kasamd1f36192017-08-03 18:54:01 +05303 *
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
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/fs.h>
18#include <linux/cdev.h>
19#include <linux/uaccess.h>
20#include <linux/device.h>
21#include <linux/io.h>
22#include <linux/platform_device.h>
23#include <linux/avtimer.h>
24#include <linux/slab.h>
25#include <linux/of.h>
26#include <linux/wait.h>
27#include <linux/sched.h>
Laxminath Kasam63f71cc2018-03-05 15:56:29 +053028#if IS_ENABLED(CONFIG_AVTIMER_LEGACY)
29#include <media/msmb_isp.h>
30#endif
Laxminath Kasamd1f36192017-08-03 18:54:01 +053031#include <ipc/apr.h>
32#include <dsp/q6core.h>
33
34#define DEVICE_NAME "avtimer"
35#define TIMEOUT_MS 1000
36#define CORE_CLIENT 1
37#define TEMP_PORT ((CORE_CLIENT << 8) | 0x0001)
38#define SSR_WAKETIME 1000
39#define Q6_READY_RETRY 250
40#define Q6_READY_MAX_RETRIES 40
41
42#define AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST 0x00012914
43#define AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST 0x00012915
44#define AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST 0x00012916
45#define AVTIMER_REG_CNT 2
46
47struct adsp_avt_timer {
48 struct apr_hdr hdr;
49 union {
50 char client_name[8];
51 u32 avtimer_handle;
52 };
53} __packed;
54
55static int major;
56
57struct avtimer_t {
58 struct apr_svc *core_handle_q;
59 struct cdev myc;
60 struct class *avtimer_class;
61 struct mutex avtimer_lock;
62 int avtimer_open_cnt;
63 struct delayed_work ssr_dwork;
64 wait_queue_head_t adsp_resp_wait;
65 int enable_timer_resp_received;
66 int timer_handle;
67 void __iomem *p_avtimer_msw;
68 void __iomem *p_avtimer_lsw;
69 uint32_t clk_div;
70 uint32_t clk_mult;
71 atomic_t adsp_ready;
72 int num_retries;
73};
74
75static struct avtimer_t avtimer;
Laxminath Kasam63f71cc2018-03-05 15:56:29 +053076static void avcs_set_isp_fptr(bool enable);
Laxminath Kasamd1f36192017-08-03 18:54:01 +053077
78static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
79{
80 uint32_t *payload1;
81
82 if (!data) {
83 pr_err("%s: Invalid params\n", __func__);
84 return -EINVAL;
85 }
86 pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%X\n",
87 __func__, data->payload_size, data->opcode);
88
89 switch (data->opcode) {
90
91 case APR_BASIC_RSP_RESULT:{
92
93 if (!data->payload_size) {
94 pr_err("%s: APR_BASIC_RSP_RESULT No Payload ",
95 __func__);
96 return 0;
97 }
98
99 payload1 = data->payload;
Ajit Pandey73468d92019-08-26 16:06:51 +0530100
101 if (data->payload_size < 2 * sizeof(uint32_t)) {
102 pr_err("%s: payload has invalid size %d\n",
103 __func__, data->payload_size);
104 return -EINVAL;
105 }
106
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530107 switch (payload1[0]) {
108 case AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST:
109 pr_debug("%s: Cmd = TIMER RELEASE status[0x%x]\n",
110 __func__, payload1[1]);
111 break;
112 default:
113 pr_err("Invalid cmd rsp[0x%x][0x%x]\n",
114 payload1[0], payload1[1]);
115 break;
116 }
117 break;
118 }
119
120 case RESET_EVENTS:{
121 pr_debug("%s: Reset event received in AV timer\n", __func__);
122 apr_reset(avtimer.core_handle_q);
123 avtimer.core_handle_q = NULL;
124 avtimer.avtimer_open_cnt = 0;
125 atomic_set(&avtimer.adsp_ready, 0);
126 schedule_delayed_work(&avtimer.ssr_dwork,
127 msecs_to_jiffies(SSR_WAKETIME));
128 break;
129 }
130
131 case AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST:
Ajit Pandey73468d92019-08-26 16:06:51 +0530132 if (data->payload_size < sizeof(uint32_t)) {
133 pr_err("%s: payload has invalid size %d\n",
134 __func__, data->payload_size);
135 return -EINVAL;
136 }
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530137 payload1 = data->payload;
138 pr_debug("%s: RSP_REMOTE_AVTIMER_VOTE_REQUEST handle %x\n",
139 __func__, payload1[0]);
140 avtimer.timer_handle = payload1[0];
141 avtimer.enable_timer_resp_received = 1;
142 wake_up(&avtimer.adsp_resp_wait);
143 break;
144 default:
145 pr_err("%s: Message adspcore svc: %d\n",
146 __func__, data->opcode);
147 break;
148 }
149
150 return 0;
151}
152
153int avcs_core_open(void)
154{
155 if (!avtimer.core_handle_q)
156 avtimer.core_handle_q = apr_register("ADSP", "CORE",
157 aprv2_core_fn_q, TEMP_PORT, NULL);
158 pr_debug("%s: Open_q %p\n", __func__, avtimer.core_handle_q);
159 if (!avtimer.core_handle_q) {
160 pr_err("%s: Unable to register CORE\n", __func__);
161 return -EINVAL;
162 }
163 return 0;
164}
165EXPORT_SYMBOL(avcs_core_open);
166
167static int avcs_core_disable_avtimer(int timerhandle)
168{
169 int rc = -EINVAL;
170 struct adsp_avt_timer payload;
171
172 if (!timerhandle) {
173 pr_err("%s: Invalid timer handle\n", __func__);
174 return -EINVAL;
175 }
176 memset(&payload, 0, sizeof(payload));
177 rc = avcs_core_open();
178 if (!rc && avtimer.core_handle_q) {
179 payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
180 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
181 payload.hdr.pkt_size =
182 sizeof(struct adsp_avt_timer);
183 payload.hdr.src_svc = avtimer.core_handle_q->id;
184 payload.hdr.src_domain = APR_DOMAIN_APPS;
185 payload.hdr.dest_domain = APR_DOMAIN_ADSP;
186 payload.hdr.dest_svc = APR_SVC_ADSP_CORE;
187 payload.hdr.src_port = TEMP_PORT;
188 payload.hdr.dest_port = TEMP_PORT;
189 payload.hdr.token = CORE_CLIENT;
190 payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST;
191 payload.avtimer_handle = timerhandle;
192 pr_debug("%s: disable avtimer opcode %x handle %x\n",
193 __func__, payload.hdr.opcode, payload.avtimer_handle);
194 rc = apr_send_pkt(avtimer.core_handle_q,
195 (uint32_t *)&payload);
196 if (rc < 0)
197 pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n",
198 __func__, payload.hdr.opcode, rc);
199 else
200 rc = 0;
201 }
202 return rc;
203}
204
205static int avcs_core_enable_avtimer(char *client_name)
206{
207 int rc = -EINVAL, ret = -EINVAL;
208 struct adsp_avt_timer payload;
209
210 if (!client_name) {
211 pr_err("%s: Invalid params\n", __func__);
212 return -EINVAL;
213 }
214 memset(&payload, 0, sizeof(payload));
215 rc = avcs_core_open();
216 if (!rc && avtimer.core_handle_q) {
217 avtimer.enable_timer_resp_received = 0;
218 payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
219 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
220 payload.hdr.pkt_size =
221 sizeof(struct adsp_avt_timer);
222 payload.hdr.src_svc = avtimer.core_handle_q->id;
223 payload.hdr.src_domain = APR_DOMAIN_APPS;
224 payload.hdr.dest_domain = APR_DOMAIN_ADSP;
225 payload.hdr.dest_svc = APR_SVC_ADSP_CORE;
226 payload.hdr.src_port = TEMP_PORT;
227 payload.hdr.dest_port = TEMP_PORT;
228 payload.hdr.token = CORE_CLIENT;
229 payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST;
230 strlcpy(payload.client_name, client_name,
231 sizeof(payload.client_name));
232 pr_debug("%s: enable avtimer opcode %x client name %s\n",
233 __func__, payload.hdr.opcode, payload.client_name);
234 rc = apr_send_pkt(avtimer.core_handle_q,
235 (uint32_t *)&payload);
236 if (rc < 0) {
237 pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n",
238 __func__, payload.hdr.opcode, rc);
239 goto bail;
240 } else
241 rc = 0;
242 ret = wait_event_timeout(avtimer.adsp_resp_wait,
243 (avtimer.enable_timer_resp_received == 1),
244 msecs_to_jiffies(TIMEOUT_MS));
245 if (!ret) {
246 pr_err("%s: wait_event timeout for Enable timer\n",
247 __func__);
248 rc = -ETIMEDOUT;
249 }
250 if (rc)
251 avtimer.timer_handle = 0;
252 }
253bail:
254 return rc;
255}
256
257int avcs_core_disable_power_collapse(int enable)
258{
259 int rc = 0;
260
261 mutex_lock(&avtimer.avtimer_lock);
262 if (enable) {
263 if (avtimer.avtimer_open_cnt) {
264 avtimer.avtimer_open_cnt++;
265 pr_debug("%s: opened avtimer open count=%d\n",
266 __func__, avtimer.avtimer_open_cnt);
267 rc = 0;
268 goto done;
269 }
270 rc = avcs_core_enable_avtimer("timer");
271 if (!rc) {
272 avtimer.avtimer_open_cnt++;
273 atomic_set(&avtimer.adsp_ready, 1);
274 }
275 } else {
276 if (avtimer.avtimer_open_cnt > 0) {
277 avtimer.avtimer_open_cnt--;
278 if (!avtimer.avtimer_open_cnt) {
279 rc = avcs_core_disable_avtimer(
280 avtimer.timer_handle);
281 avtimer.timer_handle = 0;
282 }
283 }
284 }
285done:
286 mutex_unlock(&avtimer.avtimer_lock);
287 return rc;
288}
289EXPORT_SYMBOL(avcs_core_disable_power_collapse);
290
291static void reset_work(struct work_struct *work)
292{
293 if (q6core_is_adsp_ready()) {
294 avcs_core_disable_power_collapse(1);
295 avtimer.num_retries = Q6_READY_MAX_RETRIES;
296 return;
297 }
298 pr_debug("%s:Q6 not ready-retry after sometime\n", __func__);
299 if (--avtimer.num_retries > 0) {
300 schedule_delayed_work(&avtimer.ssr_dwork,
301 msecs_to_jiffies(Q6_READY_RETRY));
302 } else {
303 pr_err("%s: Q6 failed responding after multiple retries\n",
304 __func__);
305 avtimer.num_retries = Q6_READY_MAX_RETRIES;
306 }
307}
308
309int avcs_core_query_timer(uint64_t *avtimer_tick)
310{
311 uint32_t avtimer_msw = 0, avtimer_lsw = 0;
312 uint64_t avtimer_tick_temp;
313
314 if (!atomic_read(&avtimer.adsp_ready)) {
315 pr_debug("%s:In SSR, return\n", __func__);
316 return -ENETRESET;
317 }
318 avtimer_lsw = ioread32(avtimer.p_avtimer_lsw);
319 avtimer_msw = ioread32(avtimer.p_avtimer_msw);
320
321 avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32)
322 | avtimer_lsw;
323 *avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult,
324 avtimer.clk_div);
325 pr_debug_ratelimited("%s:Avtimer: msw: %u, lsw: %u, tick: %llu\n",
326 __func__,
327 avtimer_msw, avtimer_lsw, *avtimer_tick);
328 return 0;
329}
330EXPORT_SYMBOL(avcs_core_query_timer);
331
Laxminath Kasam63f71cc2018-03-05 15:56:29 +0530332#if IS_ENABLED(CONFIG_AVTIMER_LEGACY)
333static void avcs_set_isp_fptr(bool enable)
334{
335 struct avtimer_fptr_t av_fptr;
336
337 if (enable) {
338 av_fptr.fptr_avtimer_open = avcs_core_open;
339 av_fptr.fptr_avtimer_enable = avcs_core_disable_power_collapse;
340 av_fptr.fptr_avtimer_get_time = avcs_core_query_timer;
341 msm_isp_set_avtimer_fptr(av_fptr);
342 } else {
343 av_fptr.fptr_avtimer_open = NULL;
344 av_fptr.fptr_avtimer_enable = NULL;
345 av_fptr.fptr_avtimer_get_time = NULL;
346 msm_isp_set_avtimer_fptr(av_fptr);
347 }
348}
349#else
350static void avcs_set_isp_fptr(bool enable)
351{
352}
353#endif
354
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530355static int avtimer_open(struct inode *inode, struct file *file)
356{
357 return avcs_core_disable_power_collapse(1);
358}
359
360static int avtimer_release(struct inode *inode, struct file *file)
361{
362 return avcs_core_disable_power_collapse(0);
363}
364
365/*
366 * ioctl call provides GET_AVTIMER
367 */
368static long avtimer_ioctl(struct file *file, unsigned int ioctl_num,
369 unsigned long ioctl_param)
370{
371 switch (ioctl_num) {
372 case IOCTL_GET_AVTIMER_TICK:
373 {
374 uint64_t avtimer_tick = 0;
375 int rc;
376
377 rc = avcs_core_query_timer(&avtimer_tick);
378
379 if (rc) {
380 pr_err("%s: Error: Invalid AV Timer tick, rc = %d\n",
381 __func__, rc);
382 return rc;
383 }
384
385 pr_debug_ratelimited("%s: AV Timer tick: time %llx\n",
386 __func__, avtimer_tick);
387 if (copy_to_user((void *) ioctl_param, &avtimer_tick,
388 sizeof(avtimer_tick))) {
389 pr_err("%s: copy_to_user failed\n", __func__);
390 return -EFAULT;
391 }
392 }
393 break;
394
395 default:
396 pr_err("%s: invalid cmd\n", __func__);
397 return -EINVAL;
398 }
399 return 0;
400}
401
402static const struct file_operations avtimer_fops = {
403 .unlocked_ioctl = avtimer_ioctl,
404 .compat_ioctl = avtimer_ioctl,
405 .open = avtimer_open,
406 .release = avtimer_release
407};
408
409static int dev_avtimer_probe(struct platform_device *pdev)
410{
411 int result = 0;
412 dev_t dev = MKDEV(major, 0);
413 struct device *device_handle;
414 struct resource *reg_lsb = NULL, *reg_msb = NULL;
415 uint32_t clk_div_val;
416 uint32_t clk_mult_val;
417
418 if (!pdev) {
419 pr_err("%s: Invalid params\n", __func__);
420 return -EINVAL;
421 }
422 reg_lsb = platform_get_resource_byname(pdev,
423 IORESOURCE_MEM, "avtimer_lsb_addr");
424 if (!reg_lsb) {
425 dev_err(&pdev->dev, "%s: Looking up %s property",
426 "avtimer_lsb_addr", __func__);
427 return -EINVAL;
428 }
429 reg_msb = platform_get_resource_byname(pdev,
430 IORESOURCE_MEM, "avtimer_msb_addr");
431 if (!reg_msb) {
432 dev_err(&pdev->dev, "%s: Looking up %s property",
433 "avtimer_msb_addr", __func__);
434 return -EINVAL;
435 }
436 INIT_DELAYED_WORK(&avtimer.ssr_dwork, reset_work);
437
438 avtimer.p_avtimer_lsw = devm_ioremap_nocache(&pdev->dev,
439 reg_lsb->start, resource_size(reg_lsb));
440 if (!avtimer.p_avtimer_lsw) {
441 dev_err(&pdev->dev, "%s: ioremap failed for lsb avtimer register",
442 __func__);
443 return -ENOMEM;
444 }
445
446 avtimer.p_avtimer_msw = devm_ioremap_nocache(&pdev->dev,
447 reg_msb->start, resource_size(reg_msb));
448 if (!avtimer.p_avtimer_msw) {
449 dev_err(&pdev->dev, "%s: ioremap failed for msb avtimer register",
450 __func__);
451 goto unmap;
452 }
453 avtimer.num_retries = Q6_READY_MAX_RETRIES;
454 /* get the device number */
455 if (major)
456 result = register_chrdev_region(dev, 1, DEVICE_NAME);
457 else {
458 result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
459 major = MAJOR(dev);
460 }
461
462 if (result < 0) {
463 dev_err(&pdev->dev, "%s: Registering avtimer device failed\n",
464 __func__);
465 goto unmap;
466 }
467
468 avtimer.avtimer_class = class_create(THIS_MODULE, "avtimer");
469 if (IS_ERR(avtimer.avtimer_class)) {
470 result = PTR_ERR(avtimer.avtimer_class);
471 dev_err(&pdev->dev, "%s: Error creating avtimer class: %d\n",
472 __func__, result);
473 goto unregister_chrdev_region;
474 }
475
476 cdev_init(&avtimer.myc, &avtimer_fops);
477 result = cdev_add(&avtimer.myc, dev, 1);
478
479 if (result < 0) {
480 dev_err(&pdev->dev, "%s: Registering file operations failed\n",
481 __func__);
482 goto class_destroy;
483 }
484
485 device_handle = device_create(avtimer.avtimer_class,
486 NULL, avtimer.myc.dev, NULL, "avtimer");
487 if (IS_ERR(device_handle)) {
488 result = PTR_ERR(device_handle);
489 pr_err("%s: device_create failed: %d\n", __func__, result);
490 goto class_destroy;
491 }
492 init_waitqueue_head(&avtimer.adsp_resp_wait);
493 mutex_init(&avtimer.avtimer_lock);
494 avtimer.avtimer_open_cnt = 0;
495
496 pr_debug("%s: Device create done for avtimer major=%d\n",
497 __func__, major);
498
499 if (of_property_read_u32(pdev->dev.of_node,
500 "qcom,clk-div", &clk_div_val))
501 avtimer.clk_div = 1;
502 else
503 avtimer.clk_div = clk_div_val;
504
505 if (of_property_read_u32(pdev->dev.of_node,
506 "qcom,clk-mult", &clk_mult_val))
507 avtimer.clk_mult = 1;
508 else
509 avtimer.clk_mult = clk_mult_val;
510
Laxminath Kasam63f71cc2018-03-05 15:56:29 +0530511 avcs_set_isp_fptr(true);
512
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530513 pr_debug("%s: avtimer.clk_div = %d, avtimer.clk_mult = %d\n",
514 __func__, avtimer.clk_div, avtimer.clk_mult);
515 return 0;
516
517class_destroy:
518 class_destroy(avtimer.avtimer_class);
519unregister_chrdev_region:
520 unregister_chrdev_region(MKDEV(major, 0), 1);
521unmap:
522 if (avtimer.p_avtimer_lsw)
523 devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw);
524 if (avtimer.p_avtimer_msw)
525 devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw);
526 avtimer.p_avtimer_lsw = NULL;
527 avtimer.p_avtimer_msw = NULL;
528 return result;
529
530}
531
532static int dev_avtimer_remove(struct platform_device *pdev)
533{
534 pr_debug("%s: dev_avtimer_remove\n", __func__);
535
536 if (avtimer.p_avtimer_lsw)
537 devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw);
538 if (avtimer.p_avtimer_msw)
539 devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw);
540 device_destroy(avtimer.avtimer_class, avtimer.myc.dev);
541 cdev_del(&avtimer.myc);
542 class_destroy(avtimer.avtimer_class);
543 unregister_chrdev_region(MKDEV(major, 0), 1);
Laxminath Kasam63f71cc2018-03-05 15:56:29 +0530544 avcs_set_isp_fptr(false);
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530545
546 return 0;
547}
548
549static const struct of_device_id avtimer_machine_of_match[] = {
550 { .compatible = "qcom,avtimer", },
551 {},
552};
553static struct platform_driver dev_avtimer_driver = {
554 .probe = dev_avtimer_probe,
555 .remove = dev_avtimer_remove,
556 .driver = {
557 .name = "dev_avtimer",
558 .of_match_table = avtimer_machine_of_match,
559 },
560};
561
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530562int __init avtimer_init(void)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530563{
564 s32 rc;
565
566 rc = platform_driver_register(&dev_avtimer_driver);
567 if (rc < 0) {
568 pr_err("%s: platform_driver_register failed\n", __func__);
569 goto error_platform_driver;
570 }
571 pr_debug("%s: dev_avtimer_init : done\n", __func__);
572
573 return 0;
574error_platform_driver:
575
576 pr_err("%s: encounterd error\n", __func__);
577 return rc;
578}
579
Asish Bhattacharya5faacb32017-12-04 17:23:15 +0530580void avtimer_exit(void)
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530581{
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530582 platform_driver_unregister(&dev_avtimer_driver);
583}
584
Laxminath Kasamd1f36192017-08-03 18:54:01 +0530585MODULE_DESCRIPTION("avtimer driver");
586MODULE_LICENSE("GPL v2");