blob: 108a6a424e20539f9a7be3d53369838f62c5f698 [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
Mahesh Sivasubramanian1f92c072012-08-23 18:52:59 -0600475 if (!pkt_sz)
476 return -EAGAIN;
477
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600478 BUG_ON(pkt_sz > MAX_ERR_BUFFER_SIZE);
479
480 if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info))
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600481 return -EAGAIN;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600482
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600483 do {
484 int len;
485
486 len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz);
487 pkt_sz -= len;
488 bytes_read += len;
489
490 } while (pkt_sz > 0);
491
492 BUG_ON(pkt_sz < 0);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600493
494 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600495}
496
497static void msm_rpm_smd_work(struct work_struct *work)
498{
499 uint32_t msg_id;
500 int errno;
501 char buf[MAX_ERR_BUFFER_SIZE] = {0};
502 unsigned long flags;
503
504 while (smd_is_pkt_avail(msm_rpm_data.ch_info) && !irq_process) {
505 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600506 if (msm_rpm_read_smd_data(buf)) {
507 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read,
508 flags);
509 break;
510 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600511 msg_id = msm_rpm_get_msg_id_from_ack(buf);
512 errno = msm_rpm_get_error_from_ack(buf);
513 msm_rpm_process_ack(msg_id, errno);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600514 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600515 }
516}
517
David Collinsc26c6522012-07-03 16:04:37 -0700518#define DEBUG_PRINT_BUFFER_SIZE 512
519
520static void msm_rpm_log_request(struct msm_rpm_request *cdata)
521{
522 char buf[DEBUG_PRINT_BUFFER_SIZE];
523 size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
524 char name[5];
525 u32 value;
526 int i, j, prev_valid;
527 int valid_count = 0;
528 int pos = 0;
529
530 name[4] = 0;
531
532 for (i = 0; i < cdata->write_idx; i++)
533 if (cdata->kvp[i].valid)
534 valid_count++;
535
536 pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO);
537 if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID)
538 pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ",
539 cdata->msg_hdr.msg_id);
540 pos += scnprintf(buf + pos, buflen - pos, "s=%s",
541 (cdata->msg_hdr.set == MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp"));
542
543 if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY)
544 && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) {
545 /* Both pretty and raw formatting */
546 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
547 pos += scnprintf(buf + pos, buflen - pos,
548 ", rsc_type=0x%08X (%s), rsc_id=%u; ",
549 cdata->msg_hdr.resource_type, name,
550 cdata->msg_hdr.resource_id);
551
552 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
553 if (!cdata->kvp[i].valid)
554 continue;
555
556 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
557 pos += scnprintf(buf + pos, buflen - pos,
558 "[key=0x%08X (%s), value=%s",
559 cdata->kvp[i].key, name,
560 (cdata->kvp[i].nbytes ? "0x" : "null"));
561
562 for (j = 0; j < cdata->kvp[i].nbytes; j++)
563 pos += scnprintf(buf + pos, buflen - pos,
564 "%02X ",
565 cdata->kvp[i].value[j]);
566
567 if (cdata->kvp[i].nbytes)
568 pos += scnprintf(buf + pos, buflen - pos, "(");
569
570 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
571 value = 0;
572 memcpy(&value, &cdata->kvp[i].value[j],
573 min(sizeof(uint32_t),
574 cdata->kvp[i].nbytes - j));
575 pos += scnprintf(buf + pos, buflen - pos, "%u",
576 value);
577 if (j + 4 < cdata->kvp[i].nbytes)
578 pos += scnprintf(buf + pos,
579 buflen - pos, " ");
580 }
581 if (cdata->kvp[i].nbytes)
582 pos += scnprintf(buf + pos, buflen - pos, ")");
583 pos += scnprintf(buf + pos, buflen - pos, "]");
584 if (prev_valid + 1 < valid_count)
585 pos += scnprintf(buf + pos, buflen - pos, ", ");
586 prev_valid++;
587 }
588 } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) {
589 /* Pretty formatting only */
590 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
591 pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name,
592 cdata->msg_hdr.resource_id);
593
594 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
595 if (!cdata->kvp[i].valid)
596 continue;
597
598 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
599 pos += scnprintf(buf + pos, buflen - pos, "%s=%s",
600 name, (cdata->kvp[i].nbytes ? "" : "null"));
601
602 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
603 value = 0;
604 memcpy(&value, &cdata->kvp[i].value[j],
605 min(sizeof(uint32_t),
606 cdata->kvp[i].nbytes - j));
607 pos += scnprintf(buf + pos, buflen - pos, "%u",
608 value);
609
610 if (j + 4 < cdata->kvp[i].nbytes)
611 pos += scnprintf(buf + pos,
612 buflen - pos, " ");
613 }
614 if (prev_valid + 1 < valid_count)
615 pos += scnprintf(buf + pos, buflen - pos, ", ");
616 prev_valid++;
617 }
618 } else {
619 /* Raw formatting only */
620 pos += scnprintf(buf + pos, buflen - pos,
621 ", rsc_type=0x%08X, rsc_id=%u; ",
622 cdata->msg_hdr.resource_type,
623 cdata->msg_hdr.resource_id);
624
625 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
626 if (!cdata->kvp[i].valid)
627 continue;
628
629 pos += scnprintf(buf + pos, buflen - pos,
630 "[key=0x%08X, value=%s",
631 cdata->kvp[i].key,
632 (cdata->kvp[i].nbytes ? "0x" : "null"));
633 for (j = 0; j < cdata->kvp[i].nbytes; j++) {
634 pos += scnprintf(buf + pos, buflen - pos,
635 "%02X",
636 cdata->kvp[i].value[j]);
637 if (j + 1 < cdata->kvp[i].nbytes)
638 pos += scnprintf(buf + pos,
639 buflen - pos, " ");
640 }
641 pos += scnprintf(buf + pos, buflen - pos, "]");
642 if (prev_valid + 1 < valid_count)
643 pos += scnprintf(buf + pos, buflen - pos, ", ");
644 prev_valid++;
645 }
646 }
647
648 pos += scnprintf(buf + pos, buflen - pos, "\n");
649 printk(buf);
650}
651
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600652static int msm_rpm_send_data(struct msm_rpm_request *cdata,
653 int msg_type, bool noirq)
654{
655 uint8_t *tmpbuff;
656 int i, ret, msg_size;
657 unsigned long flags;
658
659 int req_hdr_sz, msg_hdr_sz;
660
661 if (!cdata->msg_hdr.data_len)
662 return 0;
663 req_hdr_sz = sizeof(cdata->req_hdr);
664 msg_hdr_sz = sizeof(cdata->msg_hdr);
665
666 cdata->req_hdr.service_type = msm_rpm_request_service[msg_type];
667
668 cdata->msg_hdr.msg_id = msm_rpm_get_next_msg_id();
669
670 cdata->req_hdr.request_len = cdata->msg_hdr.data_len + msg_hdr_sz;
671 msg_size = cdata->req_hdr.request_len + req_hdr_sz;
672
673 /* populate data_len */
674 if (msg_size > cdata->numbytes) {
675 kfree(cdata->buf);
676 cdata->numbytes = msg_size;
677 cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq));
678 }
679
680 if (!cdata->buf)
681 return 0;
682
683 tmpbuff = cdata->buf;
684
685 memcpy(tmpbuff, &cdata->req_hdr, req_hdr_sz + msg_hdr_sz);
686
687 tmpbuff += req_hdr_sz + msg_hdr_sz;
688
689 for (i = 0; (i < cdata->write_idx); i++) {
690 /* Sanity check */
691 BUG_ON((tmpbuff - cdata->buf) > cdata->numbytes);
692
693 if (!cdata->kvp[i].valid)
694 continue;
695
696 memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t));
697 tmpbuff += sizeof(uint32_t);
698
699 memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t));
700 tmpbuff += sizeof(uint32_t);
701
702 memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
703 tmpbuff += cdata->kvp[i].nbytes;
704 }
705
David Collinsc26c6522012-07-03 16:04:37 -0700706 if (msm_rpm_debug_mask
707 & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW))
708 msm_rpm_log_request(cdata);
709
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600710 if (standalone) {
711 for (i = 0; (i < cdata->write_idx); i++)
712 cdata->kvp[i].valid = false;
713
714 cdata->msg_hdr.data_len = 0;
715 ret = cdata->msg_hdr.msg_id;
716 return ret;
717 }
718
719 msm_rpm_add_wait_list(cdata->msg_hdr.msg_id);
720
721 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
722
723 ret = smd_write_avail(msm_rpm_data.ch_info);
724
725 if (ret < 0) {
726 pr_warn("%s(): SMD not initialized\n", __func__);
727 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
728 return 0;
729 }
730
731 while ((ret < msg_size)) {
732 if (!noirq) {
733 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write,
734 flags);
735 cpu_relax();
736 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
737 } else
738 udelay(5);
739 ret = smd_write_avail(msm_rpm_data.ch_info);
740 }
741
742 ret = smd_write(msm_rpm_data.ch_info, &cdata->buf[0], msg_size);
743 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
744
745 if (ret == msg_size) {
746 for (i = 0; (i < cdata->write_idx); i++)
747 cdata->kvp[i].valid = false;
748 cdata->msg_hdr.data_len = 0;
749 ret = cdata->msg_hdr.msg_id;
750 } else if (ret < msg_size) {
751 struct msm_rpm_wait_data *rc;
752 ret = 0;
753 pr_info("Failed to write data msg_size:%d ret:%d\n",
754 msg_size, ret);
755 rc = msm_rpm_get_entry_from_msg_id(cdata->msg_hdr.msg_id);
756 if (rc)
757 msm_rpm_free_list_entry(rc);
758 }
759 return ret;
760}
761
762int msm_rpm_send_request(struct msm_rpm_request *handle)
763{
764 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
765}
766EXPORT_SYMBOL(msm_rpm_send_request);
767
768int msm_rpm_send_request_noirq(struct msm_rpm_request *handle)
769{
770 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true);
771}
772EXPORT_SYMBOL(msm_rpm_send_request_noirq);
773
774int msm_rpm_wait_for_ack(uint32_t msg_id)
775{
776 struct msm_rpm_wait_data *elem;
777 int rc = 0;
778
779 if (!msg_id)
780 return -EINVAL;
781
782 if (standalone)
783 return 0;
784
785 elem = msm_rpm_get_entry_from_msg_id(msg_id);
786 if (!elem)
787 return 0;
788
789 rc = wait_for_completion_timeout(&elem->ack, msecs_to_jiffies(1));
790 if (!rc) {
791 pr_warn("%s(): Timed out after 1 ms\n", __func__);
792 rc = -ETIMEDOUT;
793 } else {
794 rc = elem->errno;
795 msm_rpm_free_list_entry(elem);
796 }
797 return rc;
798}
799EXPORT_SYMBOL(msm_rpm_wait_for_ack);
800
801int msm_rpm_wait_for_ack_noirq(uint32_t msg_id)
802{
803 struct msm_rpm_wait_data *elem;
804 unsigned long flags;
805 int rc = 0;
806 uint32_t id = 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600807
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 Sivasubramanian88bdd4a2012-09-12 09:37:28 -0600831 while (id != msg_id) {
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600832 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);
Mahesh Sivasubramanian88bdd4a2012-09-12 09:37:28 -0600840 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600841 }
842
Mahesh Sivasubramanian88bdd4a2012-09-12 09:37:28 -0600843 rc = elem->errno;
844 msm_rpm_free_list_entry(elem);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600845wait_ack_cleanup:
846 irq_process = false;
847 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
848 return rc;
849}
850EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq);
851
852int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type,
853 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
854{
855 int i, rc;
856 struct msm_rpm_request *req =
857 msm_rpm_create_request(set, rsc_type, rsc_id, nelems);
858 if (!req)
859 return -ENOMEM;
860
861 for (i = 0; i < nelems; i++) {
862 rc = msm_rpm_add_kvp_data(req, kvp[i].key,
863 kvp[i].data, kvp[i].length);
864 if (rc)
865 goto bail;
866 }
867
868 rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req));
869bail:
870 msm_rpm_free_request(req);
871 return rc;
872}
873EXPORT_SYMBOL(msm_rpm_send_message);
874
875int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type,
876 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
877{
878 int i, rc;
879 struct msm_rpm_request *req =
880 msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems);
881 if (!req)
882 return -ENOMEM;
883
884 for (i = 0; i < nelems; i++) {
885 rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key,
886 kvp[i].data, kvp[i].length);
887 if (rc)
888 goto bail;
889 }
890
891 rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req));
892bail:
893 msm_rpm_free_request(req);
894 return rc;
895}
896EXPORT_SYMBOL(msm_rpm_send_message_noirq);
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600897
898/**
899 * During power collapse, the rpm driver disables the SMD interrupts to make
900 * sure that the interrupt doesn't wakes us from sleep.
901 */
902int msm_rpm_enter_sleep(void)
903{
904 return smd_mask_receive_interrupt(msm_rpm_data.ch_info, true);
905}
906EXPORT_SYMBOL(msm_rpm_enter_sleep);
907
908/**
909 * When the system resumes from power collapse, the SMD interrupt disabled by
910 * enter function has to reenabled to continue processing SMD message.
911 */
912void msm_rpm_exit_sleep(void)
913{
914 smd_mask_receive_interrupt(msm_rpm_data.ch_info, false);
915}
916EXPORT_SYMBOL(msm_rpm_exit_sleep);
917
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600918static bool msm_rpm_set_standalone(void)
919{
Abhimanyu Kapur90ced6e2012-06-26 17:41:25 -0700920 if (machine_is_msm8974()) {
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600921 pr_warn("%s(): Running in standalone mode, requests "
922 "will not be sent to RPM\n", __func__);
923 standalone = true;
924 }
925 return standalone;
926}
927
928static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
929{
930 char *key = NULL;
931 int ret;
932
933 key = "rpm-channel-name";
934 ret = of_property_read_string(pdev->dev.of_node, key,
935 &msm_rpm_data.ch_name);
936 if (ret)
937 goto fail;
938
939 key = "rpm-channel-type";
940 ret = of_property_read_u32(pdev->dev.of_node, key,
941 &msm_rpm_data.ch_type);
942 if (ret)
943 goto fail;
944
945 init_completion(&msm_rpm_data.smd_open);
946 spin_lock_init(&msm_rpm_data.smd_lock_write);
947 spin_lock_init(&msm_rpm_data.smd_lock_read);
948 INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work);
949
950 if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type,
951 &msm_rpm_data.ch_info, &msm_rpm_data,
952 msm_rpm_notify)) {
953 pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
954 msm_rpm_data.ch_type);
955
956 msm_rpm_set_standalone();
957 BUG_ON(!standalone);
958 complete(&msm_rpm_data.smd_open);
959 }
960
961 ret = wait_for_completion_timeout(&msm_rpm_data.smd_open,
962 msecs_to_jiffies(5));
963
964 BUG_ON(!ret);
965
966 smd_disable_read_intr(msm_rpm_data.ch_info);
967
968 if (!standalone) {
969 msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
970 if (!msm_rpm_smd_wq)
971 return -EINVAL;
972 }
973
974 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
975 return 0;
976fail:
977 pr_err("%s(): Failed to read node: %s, key=%s\n", __func__,
978 pdev->dev.of_node->full_name, key);
979 return -EINVAL;
980}
981
982static struct of_device_id msm_rpm_match_table[] = {
983 {.compatible = "qcom,rpm-smd"},
984 {},
985};
986
987static struct platform_driver msm_rpm_device_driver = {
988 .probe = msm_rpm_dev_probe,
989 .driver = {
990 .name = "rpm-smd",
991 .owner = THIS_MODULE,
992 .of_match_table = msm_rpm_match_table,
993 },
994};
995
996int __init msm_rpm_driver_init(void)
997{
998 static bool registered;
999
1000 if (registered)
1001 return 0;
1002 registered = true;
1003
1004 return platform_driver_register(&msm_rpm_device_driver);
1005}
1006EXPORT_SYMBOL(msm_rpm_driver_init);
1007late_initcall(msm_rpm_driver_init);