blob: 1225fec88b6673401746e51ac75731b9aaaae772 [file] [log] [blame]
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/types.h>
17#include <linux/bug.h>
18#include <linux/completion.h>
19#include <linux/delay.h>
20#include <linux/init.h>
21#include <linux/interrupt.h>
22#include <linux/io.h>
23#include <linux/irq.h>
24#include <linux/list.h>
25#include <linux/mutex.h>
26#include <linux/spinlock.h>
David Collinsc26c6522012-07-03 16:04:37 -070027#include <linux/string.h>
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060028#include <linux/device.h>
29#include <linux/notifier.h>
30#include <linux/slab.h>
31#include <linux/workqueue.h>
32#include <linux/platform_device.h>
33#include <linux/of.h>
34#include <linux/of_platform.h>
35#include <mach/socinfo.h>
36#include <mach/msm_smd.h>
37#include <mach/rpm-smd.h>
38#include "rpm-notifier.h"
39
David Collinsc26c6522012-07-03 16:04:37 -070040/* Debug Definitions */
41
42enum {
43 MSM_RPM_LOG_REQUEST_PRETTY = BIT(0),
44 MSM_RPM_LOG_REQUEST_RAW = BIT(1),
45 MSM_RPM_LOG_REQUEST_SHOW_MSG_ID = BIT(2),
46};
47
48static int msm_rpm_debug_mask;
49module_param_named(
50 debug_mask, msm_rpm_debug_mask, int, S_IRUGO | S_IWUSR
51);
52
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060053struct msm_rpm_driver_data {
54 const char *ch_name;
55 uint32_t ch_type;
56 smd_channel_t *ch_info;
57 struct work_struct work;
58 spinlock_t smd_lock_write;
59 spinlock_t smd_lock_read;
60 struct completion smd_open;
61};
62
63#define DEFAULT_BUFFER_SIZE 256
64#define GFP_FLAG(noirq) (noirq ? GFP_ATOMIC : GFP_KERNEL)
65#define INV_HDR "resource does not exist"
66#define ERR "err\0"
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -060067#define MAX_ERR_BUFFER_SIZE 128
68#define INIT_ERROR 1
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060069
Stephen Boydbc363fe2012-07-09 12:31:42 -070070static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060071static bool standalone;
72
73int msm_rpm_register_notifier(struct notifier_block *nb)
74{
75 return atomic_notifier_chain_register(&msm_rpm_sleep_notifier, nb);
76}
77
78int msm_rpm_unregister_notifier(struct notifier_block *nb)
79{
80 return atomic_notifier_chain_unregister(&msm_rpm_sleep_notifier, nb);
81}
82
83static struct workqueue_struct *msm_rpm_smd_wq;
84
85enum {
86 MSM_RPM_MSG_REQUEST_TYPE = 0,
87 MSM_RPM_MSG_TYPE_NR,
88};
89
90static const uint32_t msm_rpm_request_service[MSM_RPM_MSG_TYPE_NR] = {
91 0x716572, /* 'req\0' */
92};
93
94/*the order of fields matter and reflect the order expected by the RPM*/
95struct rpm_request_header {
96 uint32_t service_type;
97 uint32_t request_len;
98};
99
100struct rpm_message_header {
101 uint32_t msg_id;
102 enum msm_rpm_set set;
103 uint32_t resource_type;
104 uint32_t resource_id;
105 uint32_t data_len;
106};
107
108struct msm_rpm_kvp_data {
109 uint32_t key;
110 uint32_t nbytes; /* number of bytes */
111 uint8_t *value;
112 bool valid;
113};
114
115static atomic_t msm_rpm_msg_id = ATOMIC_INIT(0);
116
117static struct msm_rpm_driver_data msm_rpm_data;
118
119struct msm_rpm_request {
120 struct rpm_request_header req_hdr;
121 struct rpm_message_header msg_hdr;
122 struct msm_rpm_kvp_data *kvp;
123 uint32_t num_elements;
124 uint32_t write_idx;
125 uint8_t *buf;
126 uint32_t numbytes;
127};
128
129/*
130 * Data related to message acknowledgement
131 */
132
133LIST_HEAD(msm_rpm_wait_list);
134
135struct msm_rpm_wait_data {
136 struct list_head list;
137 uint32_t msg_id;
138 bool ack_recd;
139 int errno;
140 struct completion ack;
141};
142DEFINE_SPINLOCK(msm_rpm_list_lock);
143
144struct msm_rpm_ack_msg {
145 uint32_t req;
146 uint32_t req_len;
147 uint32_t rsc_id;
148 uint32_t msg_len;
149 uint32_t id_ack;
150};
151
152static int irq_process;
153
154LIST_HEAD(msm_rpm_ack_list);
155
156static void msm_rpm_notify_sleep_chain(struct rpm_message_header *hdr,
157 struct msm_rpm_kvp_data *kvp)
158{
159 struct msm_rpm_notifier_data notif;
160
161 notif.rsc_type = hdr->resource_type;
162 notif.rsc_id = hdr->resource_id;
163 notif.key = kvp->key;
164 notif.size = kvp->nbytes;
165 notif.value = kvp->value;
166 atomic_notifier_call_chain(&msm_rpm_sleep_notifier, 0, &notif);
167}
168
169static int msm_rpm_add_kvp_data_common(struct msm_rpm_request *handle,
170 uint32_t key, const uint8_t *data, int size, bool noirq)
171{
172 int i;
173 int data_size, msg_size;
174
175 if (!handle)
176 return -EINVAL;
177
178 data_size = ALIGN(size, SZ_4);
179 msg_size = data_size + sizeof(struct rpm_request_header);
180
181 for (i = 0; i < handle->write_idx; i++) {
182 if (handle->kvp[i].key != key)
183 continue;
184 if (handle->kvp[i].nbytes != data_size) {
185 kfree(handle->kvp[i].value);
186 handle->kvp[i].value = NULL;
187 } else {
188 if (!memcmp(handle->kvp[i].value, data, data_size))
189 return 0;
190 }
191 break;
192 }
193
194 if (i >= handle->num_elements)
195 return -ENOMEM;
196
197 if (i == handle->write_idx)
198 handle->write_idx++;
199
200 if (!handle->kvp[i].value) {
201 handle->kvp[i].value = kzalloc(data_size, GFP_FLAG(noirq));
202
203 if (!handle->kvp[i].value)
204 return -ENOMEM;
205 } else {
206 /* We enter the else case, if a key already exists but the
207 * data doesn't match. In which case, we should zero the data
208 * out.
209 */
210 memset(handle->kvp[i].value, 0, data_size);
211 }
212
213 if (!handle->kvp[i].valid)
214 handle->msg_hdr.data_len += msg_size;
215 else
216 handle->msg_hdr.data_len += (data_size - handle->kvp[i].nbytes);
217
218 handle->kvp[i].nbytes = data_size;
219 handle->kvp[i].key = key;
220 memcpy(handle->kvp[i].value, data, size);
221 handle->kvp[i].valid = true;
222
223 if (handle->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
224 msm_rpm_notify_sleep_chain(&handle->msg_hdr, &handle->kvp[i]);
225
226 return 0;
227
228}
229
230static struct msm_rpm_request *msm_rpm_create_request_common(
231 enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id,
232 int num_elements, bool noirq)
233{
234 struct msm_rpm_request *cdata;
235
236 cdata = kzalloc(sizeof(struct msm_rpm_request),
237 GFP_FLAG(noirq));
238
239 if (!cdata) {
240 printk(KERN_INFO"%s():Cannot allocate memory for client data\n",
241 __func__);
242 goto cdata_alloc_fail;
243 }
244
245 cdata->msg_hdr.set = set;
246 cdata->msg_hdr.resource_type = rsc_type;
247 cdata->msg_hdr.resource_id = rsc_id;
248 cdata->msg_hdr.data_len = 0;
249
250 cdata->num_elements = num_elements;
251 cdata->write_idx = 0;
252
253 cdata->kvp = kzalloc(sizeof(struct msm_rpm_kvp_data) * num_elements,
254 GFP_FLAG(noirq));
255
256 if (!cdata->kvp) {
257 pr_warn("%s(): Cannot allocate memory for key value data\n",
258 __func__);
259 goto kvp_alloc_fail;
260 }
261
262 cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_FLAG(noirq));
263
264 if (!cdata->buf)
265 goto buf_alloc_fail;
266
267 cdata->numbytes = DEFAULT_BUFFER_SIZE;
268 return cdata;
269
270buf_alloc_fail:
271 kfree(cdata->kvp);
272kvp_alloc_fail:
273 kfree(cdata);
274cdata_alloc_fail:
275 return NULL;
276
277}
278
279void msm_rpm_free_request(struct msm_rpm_request *handle)
280{
281 int i;
282
283 if (!handle)
284 return;
285 for (i = 0; i < handle->write_idx; i++)
286 kfree(handle->kvp[i].value);
287 kfree(handle->kvp);
288 kfree(handle);
289}
290EXPORT_SYMBOL(msm_rpm_free_request);
291
292struct msm_rpm_request *msm_rpm_create_request(
293 enum msm_rpm_set set, uint32_t rsc_type,
294 uint32_t rsc_id, int num_elements)
295{
296 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
297 num_elements, false);
298}
299EXPORT_SYMBOL(msm_rpm_create_request);
300
301struct msm_rpm_request *msm_rpm_create_request_noirq(
302 enum msm_rpm_set set, uint32_t rsc_type,
303 uint32_t rsc_id, int num_elements)
304{
305 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
306 num_elements, true);
307}
308EXPORT_SYMBOL(msm_rpm_create_request_noirq);
309
310int msm_rpm_add_kvp_data(struct msm_rpm_request *handle,
311 uint32_t key, const uint8_t *data, int size)
312{
313 return msm_rpm_add_kvp_data_common(handle, key, data, size, false);
314
315}
316EXPORT_SYMBOL(msm_rpm_add_kvp_data);
317
318int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle,
319 uint32_t key, const uint8_t *data, int size)
320{
321 return msm_rpm_add_kvp_data_common(handle, key, data, size, true);
322}
323EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq);
324
325/* Runs in interrupt context */
326static void msm_rpm_notify(void *data, unsigned event)
327{
328 struct msm_rpm_driver_data *pdata = (struct msm_rpm_driver_data *)data;
329 BUG_ON(!pdata);
330
331 if (!(pdata->ch_info))
332 return;
333
334 switch (event) {
335 case SMD_EVENT_DATA:
336 queue_work(msm_rpm_smd_wq, &pdata->work);
337 break;
338 case SMD_EVENT_OPEN:
339 complete(&pdata->smd_open);
340 break;
341 case SMD_EVENT_CLOSE:
342 case SMD_EVENT_STATUS:
343 case SMD_EVENT_REOPEN_READY:
344 break;
345 default:
346 pr_info("Unknown SMD event\n");
347
348 }
349}
350
351static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
352{
353 struct list_head *ptr;
354 struct msm_rpm_wait_data *elem;
355 unsigned long flags;
356
357 spin_lock_irqsave(&msm_rpm_list_lock, flags);
358
359 list_for_each(ptr, &msm_rpm_wait_list) {
360 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
361 if (elem && (elem->msg_id == msg_id))
362 break;
363 elem = NULL;
364 }
365 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
366 return elem;
367}
368
369static int msm_rpm_get_next_msg_id(void)
370{
371 int id;
372
373 do {
374 id = atomic_inc_return(&msm_rpm_msg_id);
375 } while ((id == 0) || msm_rpm_get_entry_from_msg_id(id));
376
377 return id;
378}
379
380static int msm_rpm_add_wait_list(uint32_t msg_id)
381{
382 unsigned long flags;
383 struct msm_rpm_wait_data *data =
384 kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC);
385
386 if (!data)
387 return -ENOMEM;
388
389 init_completion(&data->ack);
390 data->ack_recd = false;
391 data->msg_id = msg_id;
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600392 data->errno = INIT_ERROR;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600393 spin_lock_irqsave(&msm_rpm_list_lock, flags);
394 list_add(&data->list, &msm_rpm_wait_list);
395 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
396
397 return 0;
398}
399
400static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem)
401{
402 unsigned long flags;
403
404 spin_lock_irqsave(&msm_rpm_list_lock, flags);
405 list_del(&elem->list);
406 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
407 kfree(elem);
408}
409
410static void msm_rpm_process_ack(uint32_t msg_id, int errno)
411{
412 struct list_head *ptr;
413 struct msm_rpm_wait_data *elem;
414 unsigned long flags;
415
416 spin_lock_irqsave(&msm_rpm_list_lock, flags);
417
418 list_for_each(ptr, &msm_rpm_wait_list) {
419 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
420 if (elem && (elem->msg_id == msg_id)) {
421 elem->errno = errno;
422 elem->ack_recd = true;
423 complete(&elem->ack);
424 break;
425 }
426 elem = NULL;
427 }
428 WARN_ON(!elem);
429
430 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
431}
432
433struct msm_rpm_kvp_packet {
434 uint32_t id;
435 uint32_t len;
436 uint32_t val;
437};
438
439static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf)
440{
441 return ((struct msm_rpm_ack_msg *)buf)->id_ack;
442}
443
444static inline int msm_rpm_get_error_from_ack(uint8_t *buf)
445{
446 uint8_t *tmp;
447 uint32_t req_len = ((struct msm_rpm_ack_msg *)buf)->req_len;
448
449 int rc = -ENODEV;
450
451 req_len -= sizeof(struct msm_rpm_ack_msg);
452 req_len += 2 * sizeof(uint32_t);
453 if (!req_len)
454 return 0;
455
456 tmp = buf + sizeof(struct msm_rpm_ack_msg);
457
458 BUG_ON(memcmp(tmp, ERR, sizeof(uint32_t)));
459
460 tmp += 2 * sizeof(uint32_t);
461
462 if (!(memcmp(tmp, INV_HDR, min(req_len, sizeof(INV_HDR))-1)))
463 rc = -EINVAL;
464
465 return rc;
466}
467
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600468static int msm_rpm_read_smd_data(char *buf)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600469{
470 int pkt_sz;
471 int bytes_read = 0;
472
473 pkt_sz = smd_cur_packet_size(msm_rpm_data.ch_info);
474
475 BUG_ON(pkt_sz > MAX_ERR_BUFFER_SIZE);
476
477 if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info))
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600478 return -EAGAIN;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600479
480 BUG_ON(pkt_sz == 0);
481
482 do {
483 int len;
484
485 len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz);
486 pkt_sz -= len;
487 bytes_read += len;
488
489 } while (pkt_sz > 0);
490
491 BUG_ON(pkt_sz < 0);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600492
493 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600494}
495
496static void msm_rpm_smd_work(struct work_struct *work)
497{
498 uint32_t msg_id;
499 int errno;
500 char buf[MAX_ERR_BUFFER_SIZE] = {0};
501 unsigned long flags;
502
503 while (smd_is_pkt_avail(msm_rpm_data.ch_info) && !irq_process) {
504 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600505 if (msm_rpm_read_smd_data(buf)) {
506 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read,
507 flags);
508 break;
509 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600510 msg_id = msm_rpm_get_msg_id_from_ack(buf);
511 errno = msm_rpm_get_error_from_ack(buf);
512 msm_rpm_process_ack(msg_id, errno);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600513 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600514 }
515}
516
David Collinsc26c6522012-07-03 16:04:37 -0700517#define DEBUG_PRINT_BUFFER_SIZE 512
518
519static void msm_rpm_log_request(struct msm_rpm_request *cdata)
520{
521 char buf[DEBUG_PRINT_BUFFER_SIZE];
522 size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
523 char name[5];
524 u32 value;
525 int i, j, prev_valid;
526 int valid_count = 0;
527 int pos = 0;
528
529 name[4] = 0;
530
531 for (i = 0; i < cdata->write_idx; i++)
532 if (cdata->kvp[i].valid)
533 valid_count++;
534
535 pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO);
536 if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID)
537 pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ",
538 cdata->msg_hdr.msg_id);
539 pos += scnprintf(buf + pos, buflen - pos, "s=%s",
540 (cdata->msg_hdr.set == MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp"));
541
542 if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY)
543 && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) {
544 /* Both pretty and raw formatting */
545 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
546 pos += scnprintf(buf + pos, buflen - pos,
547 ", rsc_type=0x%08X (%s), rsc_id=%u; ",
548 cdata->msg_hdr.resource_type, name,
549 cdata->msg_hdr.resource_id);
550
551 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
552 if (!cdata->kvp[i].valid)
553 continue;
554
555 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
556 pos += scnprintf(buf + pos, buflen - pos,
557 "[key=0x%08X (%s), value=%s",
558 cdata->kvp[i].key, name,
559 (cdata->kvp[i].nbytes ? "0x" : "null"));
560
561 for (j = 0; j < cdata->kvp[i].nbytes; j++)
562 pos += scnprintf(buf + pos, buflen - pos,
563 "%02X ",
564 cdata->kvp[i].value[j]);
565
566 if (cdata->kvp[i].nbytes)
567 pos += scnprintf(buf + pos, buflen - pos, "(");
568
569 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
570 value = 0;
571 memcpy(&value, &cdata->kvp[i].value[j],
572 min(sizeof(uint32_t),
573 cdata->kvp[i].nbytes - j));
574 pos += scnprintf(buf + pos, buflen - pos, "%u",
575 value);
576 if (j + 4 < cdata->kvp[i].nbytes)
577 pos += scnprintf(buf + pos,
578 buflen - pos, " ");
579 }
580 if (cdata->kvp[i].nbytes)
581 pos += scnprintf(buf + pos, buflen - pos, ")");
582 pos += scnprintf(buf + pos, buflen - pos, "]");
583 if (prev_valid + 1 < valid_count)
584 pos += scnprintf(buf + pos, buflen - pos, ", ");
585 prev_valid++;
586 }
587 } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) {
588 /* Pretty formatting only */
589 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
590 pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name,
591 cdata->msg_hdr.resource_id);
592
593 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
594 if (!cdata->kvp[i].valid)
595 continue;
596
597 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
598 pos += scnprintf(buf + pos, buflen - pos, "%s=%s",
599 name, (cdata->kvp[i].nbytes ? "" : "null"));
600
601 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
602 value = 0;
603 memcpy(&value, &cdata->kvp[i].value[j],
604 min(sizeof(uint32_t),
605 cdata->kvp[i].nbytes - j));
606 pos += scnprintf(buf + pos, buflen - pos, "%u",
607 value);
608
609 if (j + 4 < cdata->kvp[i].nbytes)
610 pos += scnprintf(buf + pos,
611 buflen - pos, " ");
612 }
613 if (prev_valid + 1 < valid_count)
614 pos += scnprintf(buf + pos, buflen - pos, ", ");
615 prev_valid++;
616 }
617 } else {
618 /* Raw formatting only */
619 pos += scnprintf(buf + pos, buflen - pos,
620 ", rsc_type=0x%08X, rsc_id=%u; ",
621 cdata->msg_hdr.resource_type,
622 cdata->msg_hdr.resource_id);
623
624 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
625 if (!cdata->kvp[i].valid)
626 continue;
627
628 pos += scnprintf(buf + pos, buflen - pos,
629 "[key=0x%08X, value=%s",
630 cdata->kvp[i].key,
631 (cdata->kvp[i].nbytes ? "0x" : "null"));
632 for (j = 0; j < cdata->kvp[i].nbytes; j++) {
633 pos += scnprintf(buf + pos, buflen - pos,
634 "%02X",
635 cdata->kvp[i].value[j]);
636 if (j + 1 < cdata->kvp[i].nbytes)
637 pos += scnprintf(buf + pos,
638 buflen - pos, " ");
639 }
640 pos += scnprintf(buf + pos, buflen - pos, "]");
641 if (prev_valid + 1 < valid_count)
642 pos += scnprintf(buf + pos, buflen - pos, ", ");
643 prev_valid++;
644 }
645 }
646
647 pos += scnprintf(buf + pos, buflen - pos, "\n");
648 printk(buf);
649}
650
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600651static int msm_rpm_send_data(struct msm_rpm_request *cdata,
652 int msg_type, bool noirq)
653{
654 uint8_t *tmpbuff;
655 int i, ret, msg_size;
656 unsigned long flags;
657
658 int req_hdr_sz, msg_hdr_sz;
659
660 if (!cdata->msg_hdr.data_len)
661 return 0;
662 req_hdr_sz = sizeof(cdata->req_hdr);
663 msg_hdr_sz = sizeof(cdata->msg_hdr);
664
665 cdata->req_hdr.service_type = msm_rpm_request_service[msg_type];
666
667 cdata->msg_hdr.msg_id = msm_rpm_get_next_msg_id();
668
669 cdata->req_hdr.request_len = cdata->msg_hdr.data_len + msg_hdr_sz;
670 msg_size = cdata->req_hdr.request_len + req_hdr_sz;
671
672 /* populate data_len */
673 if (msg_size > cdata->numbytes) {
674 kfree(cdata->buf);
675 cdata->numbytes = msg_size;
676 cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq));
677 }
678
679 if (!cdata->buf)
680 return 0;
681
682 tmpbuff = cdata->buf;
683
684 memcpy(tmpbuff, &cdata->req_hdr, req_hdr_sz + msg_hdr_sz);
685
686 tmpbuff += req_hdr_sz + msg_hdr_sz;
687
688 for (i = 0; (i < cdata->write_idx); i++) {
689 /* Sanity check */
690 BUG_ON((tmpbuff - cdata->buf) > cdata->numbytes);
691
692 if (!cdata->kvp[i].valid)
693 continue;
694
695 memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t));
696 tmpbuff += sizeof(uint32_t);
697
698 memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t));
699 tmpbuff += sizeof(uint32_t);
700
701 memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
702 tmpbuff += cdata->kvp[i].nbytes;
703 }
704
David Collinsc26c6522012-07-03 16:04:37 -0700705 if (msm_rpm_debug_mask
706 & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW))
707 msm_rpm_log_request(cdata);
708
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600709 if (standalone) {
710 for (i = 0; (i < cdata->write_idx); i++)
711 cdata->kvp[i].valid = false;
712
713 cdata->msg_hdr.data_len = 0;
714 ret = cdata->msg_hdr.msg_id;
715 return ret;
716 }
717
718 msm_rpm_add_wait_list(cdata->msg_hdr.msg_id);
719
720 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
721
722 ret = smd_write_avail(msm_rpm_data.ch_info);
723
724 if (ret < 0) {
725 pr_warn("%s(): SMD not initialized\n", __func__);
726 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
727 return 0;
728 }
729
730 while ((ret < msg_size)) {
731 if (!noirq) {
732 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write,
733 flags);
734 cpu_relax();
735 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
736 } else
737 udelay(5);
738 ret = smd_write_avail(msm_rpm_data.ch_info);
739 }
740
741 ret = smd_write(msm_rpm_data.ch_info, &cdata->buf[0], msg_size);
742 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
743
744 if (ret == msg_size) {
745 for (i = 0; (i < cdata->write_idx); i++)
746 cdata->kvp[i].valid = false;
747 cdata->msg_hdr.data_len = 0;
748 ret = cdata->msg_hdr.msg_id;
749 } else if (ret < msg_size) {
750 struct msm_rpm_wait_data *rc;
751 ret = 0;
752 pr_info("Failed to write data msg_size:%d ret:%d\n",
753 msg_size, ret);
754 rc = msm_rpm_get_entry_from_msg_id(cdata->msg_hdr.msg_id);
755 if (rc)
756 msm_rpm_free_list_entry(rc);
757 }
758 return ret;
759}
760
761int msm_rpm_send_request(struct msm_rpm_request *handle)
762{
763 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
764}
765EXPORT_SYMBOL(msm_rpm_send_request);
766
767int msm_rpm_send_request_noirq(struct msm_rpm_request *handle)
768{
769 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true);
770}
771EXPORT_SYMBOL(msm_rpm_send_request_noirq);
772
773int msm_rpm_wait_for_ack(uint32_t msg_id)
774{
775 struct msm_rpm_wait_data *elem;
776 int rc = 0;
777
778 if (!msg_id)
779 return -EINVAL;
780
781 if (standalone)
782 return 0;
783
784 elem = msm_rpm_get_entry_from_msg_id(msg_id);
785 if (!elem)
786 return 0;
787
788 rc = wait_for_completion_timeout(&elem->ack, msecs_to_jiffies(1));
789 if (!rc) {
790 pr_warn("%s(): Timed out after 1 ms\n", __func__);
791 rc = -ETIMEDOUT;
792 } else {
793 rc = elem->errno;
794 msm_rpm_free_list_entry(elem);
795 }
796 return rc;
797}
798EXPORT_SYMBOL(msm_rpm_wait_for_ack);
799
800int msm_rpm_wait_for_ack_noirq(uint32_t msg_id)
801{
802 struct msm_rpm_wait_data *elem;
803 unsigned long flags;
804 int rc = 0;
805 uint32_t id = 0;
806 int count = 0;
807
808 if (!msg_id)
809 return -EINVAL;
810
811 if (standalone)
812 return 0;
813
814 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
815 irq_process = true;
816
817 elem = msm_rpm_get_entry_from_msg_id(msg_id);
818
819 if (!elem)
820 /* Should this be a bug
821 * Is it ok for another thread to read the msg?
822 */
823 goto wait_ack_cleanup;
824
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600825 if (elem->errno != INIT_ERROR) {
826 rc = elem->errno;
827 msm_rpm_free_list_entry(elem);
828 goto wait_ack_cleanup;
829 }
830
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600831 while ((id != msg_id) && (count++ < 10)) {
832 if (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
833 int errno;
834 char buf[MAX_ERR_BUFFER_SIZE] = {};
835
836 msm_rpm_read_smd_data(buf);
837 id = msm_rpm_get_msg_id_from_ack(buf);
838 errno = msm_rpm_get_error_from_ack(buf);
839 msm_rpm_process_ack(id, errno);
840 } else
841 udelay(100);
842 }
843
844 if (count == 10) {
845 rc = -ETIMEDOUT;
846 pr_warn("%s(): Timed out after 1ms\n", __func__);
847 } else {
848 rc = elem->errno;
849 msm_rpm_free_list_entry(elem);
850 }
851wait_ack_cleanup:
852 irq_process = false;
853 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
854 return rc;
855}
856EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq);
857
858int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type,
859 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
860{
861 int i, rc;
862 struct msm_rpm_request *req =
863 msm_rpm_create_request(set, rsc_type, rsc_id, nelems);
864 if (!req)
865 return -ENOMEM;
866
867 for (i = 0; i < nelems; i++) {
868 rc = msm_rpm_add_kvp_data(req, kvp[i].key,
869 kvp[i].data, kvp[i].length);
870 if (rc)
871 goto bail;
872 }
873
874 rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req));
875bail:
876 msm_rpm_free_request(req);
877 return rc;
878}
879EXPORT_SYMBOL(msm_rpm_send_message);
880
881int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type,
882 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
883{
884 int i, rc;
885 struct msm_rpm_request *req =
886 msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems);
887 if (!req)
888 return -ENOMEM;
889
890 for (i = 0; i < nelems; i++) {
891 rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key,
892 kvp[i].data, kvp[i].length);
893 if (rc)
894 goto bail;
895 }
896
897 rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req));
898bail:
899 msm_rpm_free_request(req);
900 return rc;
901}
902EXPORT_SYMBOL(msm_rpm_send_message_noirq);
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600903
904/**
905 * During power collapse, the rpm driver disables the SMD interrupts to make
906 * sure that the interrupt doesn't wakes us from sleep.
907 */
908int msm_rpm_enter_sleep(void)
909{
910 return smd_mask_receive_interrupt(msm_rpm_data.ch_info, true);
911}
912EXPORT_SYMBOL(msm_rpm_enter_sleep);
913
914/**
915 * When the system resumes from power collapse, the SMD interrupt disabled by
916 * enter function has to reenabled to continue processing SMD message.
917 */
918void msm_rpm_exit_sleep(void)
919{
920 smd_mask_receive_interrupt(msm_rpm_data.ch_info, false);
921}
922EXPORT_SYMBOL(msm_rpm_exit_sleep);
923
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600924static bool msm_rpm_set_standalone(void)
925{
Abhimanyu Kapur90ced6e2012-06-26 17:41:25 -0700926 if (machine_is_msm8974()) {
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600927 pr_warn("%s(): Running in standalone mode, requests "
928 "will not be sent to RPM\n", __func__);
929 standalone = true;
930 }
931 return standalone;
932}
933
934static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
935{
936 char *key = NULL;
937 int ret;
938
939 key = "rpm-channel-name";
940 ret = of_property_read_string(pdev->dev.of_node, key,
941 &msm_rpm_data.ch_name);
942 if (ret)
943 goto fail;
944
945 key = "rpm-channel-type";
946 ret = of_property_read_u32(pdev->dev.of_node, key,
947 &msm_rpm_data.ch_type);
948 if (ret)
949 goto fail;
950
951 init_completion(&msm_rpm_data.smd_open);
952 spin_lock_init(&msm_rpm_data.smd_lock_write);
953 spin_lock_init(&msm_rpm_data.smd_lock_read);
954 INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work);
955
956 if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type,
957 &msm_rpm_data.ch_info, &msm_rpm_data,
958 msm_rpm_notify)) {
959 pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
960 msm_rpm_data.ch_type);
961
962 msm_rpm_set_standalone();
963 BUG_ON(!standalone);
964 complete(&msm_rpm_data.smd_open);
965 }
966
967 ret = wait_for_completion_timeout(&msm_rpm_data.smd_open,
968 msecs_to_jiffies(5));
969
970 BUG_ON(!ret);
971
972 smd_disable_read_intr(msm_rpm_data.ch_info);
973
974 if (!standalone) {
975 msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
976 if (!msm_rpm_smd_wq)
977 return -EINVAL;
978 }
979
980 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
981 return 0;
982fail:
983 pr_err("%s(): Failed to read node: %s, key=%s\n", __func__,
984 pdev->dev.of_node->full_name, key);
985 return -EINVAL;
986}
987
988static struct of_device_id msm_rpm_match_table[] = {
989 {.compatible = "qcom,rpm-smd"},
990 {},
991};
992
993static struct platform_driver msm_rpm_device_driver = {
994 .probe = msm_rpm_dev_probe,
995 .driver = {
996 .name = "rpm-smd",
997 .owner = THIS_MODULE,
998 .of_match_table = msm_rpm_match_table,
999 },
1000};
1001
1002int __init msm_rpm_driver_init(void)
1003{
1004 static bool registered;
1005
1006 if (registered)
1007 return 0;
1008 registered = true;
1009
1010 return platform_driver_register(&msm_rpm_device_driver);
1011}
1012EXPORT_SYMBOL(msm_rpm_driver_init);
1013late_initcall(msm_rpm_driver_init);