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