blob: b112c90a53f7a25e235eec86543e9e5deaf9aa9d [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)
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -060065#define INV_RSC "resource does not exist"
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060066#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
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600175 if (!handle) {
176 pr_err("%s(): Invalid handle\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600177 return -EINVAL;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600178 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600179
180 data_size = ALIGN(size, SZ_4);
181 msg_size = data_size + sizeof(struct rpm_request_header);
182
183 for (i = 0; i < handle->write_idx; i++) {
184 if (handle->kvp[i].key != key)
185 continue;
186 if (handle->kvp[i].nbytes != data_size) {
187 kfree(handle->kvp[i].value);
188 handle->kvp[i].value = NULL;
189 } else {
190 if (!memcmp(handle->kvp[i].value, data, data_size))
191 return 0;
192 }
193 break;
194 }
195
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600196 if (i >= handle->num_elements) {
197 pr_err("%s(): Number of resources exceeds max allocated\n",
198 __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600199 return -ENOMEM;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600200 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600201
202 if (i == handle->write_idx)
203 handle->write_idx++;
204
205 if (!handle->kvp[i].value) {
206 handle->kvp[i].value = kzalloc(data_size, GFP_FLAG(noirq));
207
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600208 if (!handle->kvp[i].value) {
209 pr_err("%s(): Failed malloc\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600210 return -ENOMEM;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600211 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600212 } else {
213 /* We enter the else case, if a key already exists but the
214 * data doesn't match. In which case, we should zero the data
215 * out.
216 */
217 memset(handle->kvp[i].value, 0, data_size);
218 }
219
220 if (!handle->kvp[i].valid)
221 handle->msg_hdr.data_len += msg_size;
222 else
223 handle->msg_hdr.data_len += (data_size - handle->kvp[i].nbytes);
224
225 handle->kvp[i].nbytes = data_size;
226 handle->kvp[i].key = key;
227 memcpy(handle->kvp[i].value, data, size);
228 handle->kvp[i].valid = true;
229
230 if (handle->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
231 msm_rpm_notify_sleep_chain(&handle->msg_hdr, &handle->kvp[i]);
232
233 return 0;
234
235}
236
237static struct msm_rpm_request *msm_rpm_create_request_common(
238 enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id,
239 int num_elements, bool noirq)
240{
241 struct msm_rpm_request *cdata;
242
243 cdata = kzalloc(sizeof(struct msm_rpm_request),
244 GFP_FLAG(noirq));
245
246 if (!cdata) {
247 printk(KERN_INFO"%s():Cannot allocate memory for client data\n",
248 __func__);
249 goto cdata_alloc_fail;
250 }
251
252 cdata->msg_hdr.set = set;
253 cdata->msg_hdr.resource_type = rsc_type;
254 cdata->msg_hdr.resource_id = rsc_id;
255 cdata->msg_hdr.data_len = 0;
256
257 cdata->num_elements = num_elements;
258 cdata->write_idx = 0;
259
260 cdata->kvp = kzalloc(sizeof(struct msm_rpm_kvp_data) * num_elements,
261 GFP_FLAG(noirq));
262
263 if (!cdata->kvp) {
264 pr_warn("%s(): Cannot allocate memory for key value data\n",
265 __func__);
266 goto kvp_alloc_fail;
267 }
268
269 cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_FLAG(noirq));
270
271 if (!cdata->buf)
272 goto buf_alloc_fail;
273
274 cdata->numbytes = DEFAULT_BUFFER_SIZE;
275 return cdata;
276
277buf_alloc_fail:
278 kfree(cdata->kvp);
279kvp_alloc_fail:
280 kfree(cdata);
281cdata_alloc_fail:
282 return NULL;
283
284}
285
286void msm_rpm_free_request(struct msm_rpm_request *handle)
287{
288 int i;
289
290 if (!handle)
291 return;
292 for (i = 0; i < handle->write_idx; i++)
293 kfree(handle->kvp[i].value);
294 kfree(handle->kvp);
295 kfree(handle);
296}
297EXPORT_SYMBOL(msm_rpm_free_request);
298
299struct msm_rpm_request *msm_rpm_create_request(
300 enum msm_rpm_set set, uint32_t rsc_type,
301 uint32_t rsc_id, int num_elements)
302{
303 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
304 num_elements, false);
305}
306EXPORT_SYMBOL(msm_rpm_create_request);
307
308struct msm_rpm_request *msm_rpm_create_request_noirq(
309 enum msm_rpm_set set, uint32_t rsc_type,
310 uint32_t rsc_id, int num_elements)
311{
312 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
313 num_elements, true);
314}
315EXPORT_SYMBOL(msm_rpm_create_request_noirq);
316
317int msm_rpm_add_kvp_data(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, false);
321
322}
323EXPORT_SYMBOL(msm_rpm_add_kvp_data);
324
325int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle,
326 uint32_t key, const uint8_t *data, int size)
327{
328 return msm_rpm_add_kvp_data_common(handle, key, data, size, true);
329}
330EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq);
331
332/* Runs in interrupt context */
333static void msm_rpm_notify(void *data, unsigned event)
334{
335 struct msm_rpm_driver_data *pdata = (struct msm_rpm_driver_data *)data;
336 BUG_ON(!pdata);
337
338 if (!(pdata->ch_info))
339 return;
340
341 switch (event) {
342 case SMD_EVENT_DATA:
343 queue_work(msm_rpm_smd_wq, &pdata->work);
344 break;
345 case SMD_EVENT_OPEN:
346 complete(&pdata->smd_open);
347 break;
348 case SMD_EVENT_CLOSE:
349 case SMD_EVENT_STATUS:
350 case SMD_EVENT_REOPEN_READY:
351 break;
352 default:
353 pr_info("Unknown SMD event\n");
354
355 }
356}
357
358static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
359{
360 struct list_head *ptr;
361 struct msm_rpm_wait_data *elem;
362 unsigned long flags;
363
364 spin_lock_irqsave(&msm_rpm_list_lock, flags);
365
366 list_for_each(ptr, &msm_rpm_wait_list) {
367 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
368 if (elem && (elem->msg_id == msg_id))
369 break;
370 elem = NULL;
371 }
372 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
373 return elem;
374}
375
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600376static uint32_t msm_rpm_get_next_msg_id(void)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600377{
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600378 uint32_t id;
379
380 /*
381 * A message id of 0 is used by the driver to indicate a error
382 * condition. The RPM driver uses a id of 1 to indicate unsent data
383 * when the data sent over hasn't been modified. This isn't a error
384 * scenario and wait for ack returns a success when the message id is 1.
385 */
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600386
387 do {
388 id = atomic_inc_return(&msm_rpm_msg_id);
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600389 } while ((id == 0) || (id == 1) || msm_rpm_get_entry_from_msg_id(id));
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600390
391 return id;
392}
393
394static int msm_rpm_add_wait_list(uint32_t msg_id)
395{
396 unsigned long flags;
397 struct msm_rpm_wait_data *data =
398 kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC);
399
400 if (!data)
401 return -ENOMEM;
402
403 init_completion(&data->ack);
404 data->ack_recd = false;
405 data->msg_id = msg_id;
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600406 data->errno = INIT_ERROR;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600407 spin_lock_irqsave(&msm_rpm_list_lock, flags);
408 list_add(&data->list, &msm_rpm_wait_list);
409 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
410
411 return 0;
412}
413
414static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem)
415{
416 unsigned long flags;
417
418 spin_lock_irqsave(&msm_rpm_list_lock, flags);
419 list_del(&elem->list);
420 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
421 kfree(elem);
422}
423
424static void msm_rpm_process_ack(uint32_t msg_id, int errno)
425{
426 struct list_head *ptr;
427 struct msm_rpm_wait_data *elem;
428 unsigned long flags;
429
430 spin_lock_irqsave(&msm_rpm_list_lock, flags);
431
432 list_for_each(ptr, &msm_rpm_wait_list) {
433 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
434 if (elem && (elem->msg_id == msg_id)) {
435 elem->errno = errno;
436 elem->ack_recd = true;
437 complete(&elem->ack);
438 break;
439 }
440 elem = NULL;
441 }
442 WARN_ON(!elem);
443
444 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
445}
446
447struct msm_rpm_kvp_packet {
448 uint32_t id;
449 uint32_t len;
450 uint32_t val;
451};
452
453static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf)
454{
455 return ((struct msm_rpm_ack_msg *)buf)->id_ack;
456}
457
458static inline int msm_rpm_get_error_from_ack(uint8_t *buf)
459{
460 uint8_t *tmp;
461 uint32_t req_len = ((struct msm_rpm_ack_msg *)buf)->req_len;
462
463 int rc = -ENODEV;
464
465 req_len -= sizeof(struct msm_rpm_ack_msg);
466 req_len += 2 * sizeof(uint32_t);
467 if (!req_len)
468 return 0;
469
470 tmp = buf + sizeof(struct msm_rpm_ack_msg);
471
472 BUG_ON(memcmp(tmp, ERR, sizeof(uint32_t)));
473
474 tmp += 2 * sizeof(uint32_t);
475
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600476 if (!(memcmp(tmp, INV_RSC, min(req_len, sizeof(INV_RSC))-1))) {
477 pr_err("%s(): RPM NACK Unsupported resource\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600478 rc = -EINVAL;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600479 } else {
480 pr_err("%s(): RPM NACK Invalid header\n", __func__);
481 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600482
483 return rc;
484}
485
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600486static int msm_rpm_read_smd_data(char *buf)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600487{
488 int pkt_sz;
489 int bytes_read = 0;
490
491 pkt_sz = smd_cur_packet_size(msm_rpm_data.ch_info);
492
Mahesh Sivasubramanian137cd702012-08-23 18:52:59 -0600493 if (!pkt_sz)
494 return -EAGAIN;
495
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600496 BUG_ON(pkt_sz > MAX_ERR_BUFFER_SIZE);
497
498 if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info))
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600499 return -EAGAIN;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600500
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600501 do {
502 int len;
503
504 len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz);
505 pkt_sz -= len;
506 bytes_read += len;
507
508 } while (pkt_sz > 0);
509
510 BUG_ON(pkt_sz < 0);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600511
512 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600513}
514
515static void msm_rpm_smd_work(struct work_struct *work)
516{
517 uint32_t msg_id;
518 int errno;
519 char buf[MAX_ERR_BUFFER_SIZE] = {0};
520 unsigned long flags;
521
522 while (smd_is_pkt_avail(msm_rpm_data.ch_info) && !irq_process) {
523 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600524 if (msm_rpm_read_smd_data(buf)) {
525 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read,
526 flags);
527 break;
528 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600529 msg_id = msm_rpm_get_msg_id_from_ack(buf);
530 errno = msm_rpm_get_error_from_ack(buf);
531 msm_rpm_process_ack(msg_id, errno);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600532 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600533 }
534}
535
David Collinsc26c6522012-07-03 16:04:37 -0700536#define DEBUG_PRINT_BUFFER_SIZE 512
537
538static void msm_rpm_log_request(struct msm_rpm_request *cdata)
539{
540 char buf[DEBUG_PRINT_BUFFER_SIZE];
541 size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
542 char name[5];
543 u32 value;
544 int i, j, prev_valid;
545 int valid_count = 0;
546 int pos = 0;
547
548 name[4] = 0;
549
550 for (i = 0; i < cdata->write_idx; i++)
551 if (cdata->kvp[i].valid)
552 valid_count++;
553
554 pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO);
555 if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID)
556 pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ",
557 cdata->msg_hdr.msg_id);
558 pos += scnprintf(buf + pos, buflen - pos, "s=%s",
559 (cdata->msg_hdr.set == MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp"));
560
561 if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY)
562 && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) {
563 /* Both pretty and raw formatting */
564 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
565 pos += scnprintf(buf + pos, buflen - pos,
566 ", rsc_type=0x%08X (%s), rsc_id=%u; ",
567 cdata->msg_hdr.resource_type, name,
568 cdata->msg_hdr.resource_id);
569
570 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
571 if (!cdata->kvp[i].valid)
572 continue;
573
574 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
575 pos += scnprintf(buf + pos, buflen - pos,
576 "[key=0x%08X (%s), value=%s",
577 cdata->kvp[i].key, name,
578 (cdata->kvp[i].nbytes ? "0x" : "null"));
579
580 for (j = 0; j < cdata->kvp[i].nbytes; j++)
581 pos += scnprintf(buf + pos, buflen - pos,
582 "%02X ",
583 cdata->kvp[i].value[j]);
584
585 if (cdata->kvp[i].nbytes)
586 pos += scnprintf(buf + pos, buflen - pos, "(");
587
588 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
589 value = 0;
590 memcpy(&value, &cdata->kvp[i].value[j],
591 min(sizeof(uint32_t),
592 cdata->kvp[i].nbytes - j));
593 pos += scnprintf(buf + pos, buflen - pos, "%u",
594 value);
595 if (j + 4 < cdata->kvp[i].nbytes)
596 pos += scnprintf(buf + pos,
597 buflen - pos, " ");
598 }
599 if (cdata->kvp[i].nbytes)
600 pos += scnprintf(buf + pos, buflen - pos, ")");
601 pos += scnprintf(buf + pos, buflen - pos, "]");
602 if (prev_valid + 1 < valid_count)
603 pos += scnprintf(buf + pos, buflen - pos, ", ");
604 prev_valid++;
605 }
606 } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) {
607 /* Pretty formatting only */
608 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
609 pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name,
610 cdata->msg_hdr.resource_id);
611
612 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
613 if (!cdata->kvp[i].valid)
614 continue;
615
616 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
617 pos += scnprintf(buf + pos, buflen - pos, "%s=%s",
618 name, (cdata->kvp[i].nbytes ? "" : "null"));
619
620 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
621 value = 0;
622 memcpy(&value, &cdata->kvp[i].value[j],
623 min(sizeof(uint32_t),
624 cdata->kvp[i].nbytes - j));
625 pos += scnprintf(buf + pos, buflen - pos, "%u",
626 value);
627
628 if (j + 4 < cdata->kvp[i].nbytes)
629 pos += scnprintf(buf + pos,
630 buflen - pos, " ");
631 }
632 if (prev_valid + 1 < valid_count)
633 pos += scnprintf(buf + pos, buflen - pos, ", ");
634 prev_valid++;
635 }
636 } else {
637 /* Raw formatting only */
638 pos += scnprintf(buf + pos, buflen - pos,
639 ", rsc_type=0x%08X, rsc_id=%u; ",
640 cdata->msg_hdr.resource_type,
641 cdata->msg_hdr.resource_id);
642
643 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
644 if (!cdata->kvp[i].valid)
645 continue;
646
647 pos += scnprintf(buf + pos, buflen - pos,
648 "[key=0x%08X, value=%s",
649 cdata->kvp[i].key,
650 (cdata->kvp[i].nbytes ? "0x" : "null"));
651 for (j = 0; j < cdata->kvp[i].nbytes; j++) {
652 pos += scnprintf(buf + pos, buflen - pos,
653 "%02X",
654 cdata->kvp[i].value[j]);
655 if (j + 1 < cdata->kvp[i].nbytes)
656 pos += scnprintf(buf + pos,
657 buflen - pos, " ");
658 }
659 pos += scnprintf(buf + pos, buflen - pos, "]");
660 if (prev_valid + 1 < valid_count)
661 pos += scnprintf(buf + pos, buflen - pos, ", ");
662 prev_valid++;
663 }
664 }
665
666 pos += scnprintf(buf + pos, buflen - pos, "\n");
667 printk(buf);
668}
669
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600670static int msm_rpm_send_data(struct msm_rpm_request *cdata,
671 int msg_type, bool noirq)
672{
673 uint8_t *tmpbuff;
674 int i, ret, msg_size;
675 unsigned long flags;
676
677 int req_hdr_sz, msg_hdr_sz;
678
679 if (!cdata->msg_hdr.data_len)
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600680 return 1;
681
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600682 req_hdr_sz = sizeof(cdata->req_hdr);
683 msg_hdr_sz = sizeof(cdata->msg_hdr);
684
685 cdata->req_hdr.service_type = msm_rpm_request_service[msg_type];
686
687 cdata->msg_hdr.msg_id = msm_rpm_get_next_msg_id();
688
689 cdata->req_hdr.request_len = cdata->msg_hdr.data_len + msg_hdr_sz;
690 msg_size = cdata->req_hdr.request_len + req_hdr_sz;
691
692 /* populate data_len */
693 if (msg_size > cdata->numbytes) {
694 kfree(cdata->buf);
695 cdata->numbytes = msg_size;
696 cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq));
697 }
698
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600699 if (!cdata->buf) {
700 pr_err("%s(): Failed malloc\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600701 return 0;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600702 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600703
704 tmpbuff = cdata->buf;
705
706 memcpy(tmpbuff, &cdata->req_hdr, req_hdr_sz + msg_hdr_sz);
707
708 tmpbuff += req_hdr_sz + msg_hdr_sz;
709
710 for (i = 0; (i < cdata->write_idx); i++) {
711 /* Sanity check */
712 BUG_ON((tmpbuff - cdata->buf) > cdata->numbytes);
713
714 if (!cdata->kvp[i].valid)
715 continue;
716
717 memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t));
718 tmpbuff += sizeof(uint32_t);
719
720 memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t));
721 tmpbuff += sizeof(uint32_t);
722
723 memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
724 tmpbuff += cdata->kvp[i].nbytes;
725 }
726
David Collinsc26c6522012-07-03 16:04:37 -0700727 if (msm_rpm_debug_mask
728 & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW))
729 msm_rpm_log_request(cdata);
730
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600731 if (standalone) {
732 for (i = 0; (i < cdata->write_idx); i++)
733 cdata->kvp[i].valid = false;
734
735 cdata->msg_hdr.data_len = 0;
736 ret = cdata->msg_hdr.msg_id;
737 return ret;
738 }
739
740 msm_rpm_add_wait_list(cdata->msg_hdr.msg_id);
741
742 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
743
744 ret = smd_write_avail(msm_rpm_data.ch_info);
745
746 if (ret < 0) {
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600747 pr_err("%s(): SMD not initialized\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600748 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
749 return 0;
750 }
751
752 while ((ret < msg_size)) {
753 if (!noirq) {
754 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write,
755 flags);
756 cpu_relax();
757 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
758 } else
759 udelay(5);
760 ret = smd_write_avail(msm_rpm_data.ch_info);
761 }
762
763 ret = smd_write(msm_rpm_data.ch_info, &cdata->buf[0], msg_size);
764 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
765
766 if (ret == msg_size) {
767 for (i = 0; (i < cdata->write_idx); i++)
768 cdata->kvp[i].valid = false;
769 cdata->msg_hdr.data_len = 0;
770 ret = cdata->msg_hdr.msg_id;
771 } else if (ret < msg_size) {
772 struct msm_rpm_wait_data *rc;
773 ret = 0;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600774 pr_err("Failed to write data msg_size:%d ret:%d\n",
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600775 msg_size, ret);
776 rc = msm_rpm_get_entry_from_msg_id(cdata->msg_hdr.msg_id);
777 if (rc)
778 msm_rpm_free_list_entry(rc);
779 }
780 return ret;
781}
782
783int msm_rpm_send_request(struct msm_rpm_request *handle)
784{
785 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
786}
787EXPORT_SYMBOL(msm_rpm_send_request);
788
789int msm_rpm_send_request_noirq(struct msm_rpm_request *handle)
790{
791 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true);
792}
793EXPORT_SYMBOL(msm_rpm_send_request_noirq);
794
795int msm_rpm_wait_for_ack(uint32_t msg_id)
796{
797 struct msm_rpm_wait_data *elem;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600798
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600799 if (!msg_id) {
800 pr_err("%s(): Invalid msg id\n", __func__);
801 return -ENOMEM;
802 }
803
804 if (msg_id == 1)
805 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600806
807 if (standalone)
808 return 0;
809
810 elem = msm_rpm_get_entry_from_msg_id(msg_id);
811 if (!elem)
812 return 0;
813
Michael Bohan5eb00812012-08-20 11:56:33 -0700814 wait_for_completion(&elem->ack);
815 msm_rpm_free_list_entry(elem);
816 return elem->errno;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600817}
818EXPORT_SYMBOL(msm_rpm_wait_for_ack);
819
820int msm_rpm_wait_for_ack_noirq(uint32_t msg_id)
821{
822 struct msm_rpm_wait_data *elem;
823 unsigned long flags;
824 int rc = 0;
825 uint32_t id = 0;
826 int count = 0;
827
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600828 if (!msg_id) {
829 pr_err("%s(): Invalid msg id\n", __func__);
830 return -ENOMEM;
831 }
832
833 if (msg_id == 1)
834 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600835
836 if (standalone)
837 return 0;
838
839 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
840 irq_process = true;
841
842 elem = msm_rpm_get_entry_from_msg_id(msg_id);
843
844 if (!elem)
845 /* Should this be a bug
846 * Is it ok for another thread to read the msg?
847 */
848 goto wait_ack_cleanup;
849
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600850 if (elem->errno != INIT_ERROR) {
851 rc = elem->errno;
852 msm_rpm_free_list_entry(elem);
853 goto wait_ack_cleanup;
854 }
855
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600856 while ((id != msg_id) && (count++ < 10)) {
857 if (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
858 int errno;
859 char buf[MAX_ERR_BUFFER_SIZE] = {};
860
861 msm_rpm_read_smd_data(buf);
862 id = msm_rpm_get_msg_id_from_ack(buf);
863 errno = msm_rpm_get_error_from_ack(buf);
864 msm_rpm_process_ack(id, errno);
865 } else
866 udelay(100);
867 }
868
869 if (count == 10) {
870 rc = -ETIMEDOUT;
871 pr_warn("%s(): Timed out after 1ms\n", __func__);
872 } else {
873 rc = elem->errno;
874 msm_rpm_free_list_entry(elem);
875 }
876wait_ack_cleanup:
877 irq_process = false;
878 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
879 return rc;
880}
881EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq);
882
883int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type,
884 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
885{
886 int i, rc;
887 struct msm_rpm_request *req =
888 msm_rpm_create_request(set, rsc_type, rsc_id, nelems);
889 if (!req)
890 return -ENOMEM;
891
892 for (i = 0; i < nelems; i++) {
893 rc = msm_rpm_add_kvp_data(req, kvp[i].key,
894 kvp[i].data, kvp[i].length);
895 if (rc)
896 goto bail;
897 }
898
899 rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req));
900bail:
901 msm_rpm_free_request(req);
902 return rc;
903}
904EXPORT_SYMBOL(msm_rpm_send_message);
905
906int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type,
907 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
908{
909 int i, rc;
910 struct msm_rpm_request *req =
911 msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems);
912 if (!req)
913 return -ENOMEM;
914
915 for (i = 0; i < nelems; i++) {
916 rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key,
917 kvp[i].data, kvp[i].length);
918 if (rc)
919 goto bail;
920 }
921
922 rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req));
923bail:
924 msm_rpm_free_request(req);
925 return rc;
926}
927EXPORT_SYMBOL(msm_rpm_send_message_noirq);
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600928
929/**
930 * During power collapse, the rpm driver disables the SMD interrupts to make
931 * sure that the interrupt doesn't wakes us from sleep.
932 */
933int msm_rpm_enter_sleep(void)
934{
935 return smd_mask_receive_interrupt(msm_rpm_data.ch_info, true);
936}
937EXPORT_SYMBOL(msm_rpm_enter_sleep);
938
939/**
940 * When the system resumes from power collapse, the SMD interrupt disabled by
941 * enter function has to reenabled to continue processing SMD message.
942 */
943void msm_rpm_exit_sleep(void)
944{
945 smd_mask_receive_interrupt(msm_rpm_data.ch_info, false);
946}
947EXPORT_SYMBOL(msm_rpm_exit_sleep);
948
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600949static bool msm_rpm_set_standalone(void)
950{
Abhimanyu Kapur90ced6e2012-06-26 17:41:25 -0700951 if (machine_is_msm8974()) {
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600952 pr_warn("%s(): Running in standalone mode, requests "
953 "will not be sent to RPM\n", __func__);
954 standalone = true;
955 }
956 return standalone;
957}
958
959static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
960{
961 char *key = NULL;
962 int ret;
963
964 key = "rpm-channel-name";
965 ret = of_property_read_string(pdev->dev.of_node, key,
966 &msm_rpm_data.ch_name);
967 if (ret)
968 goto fail;
969
970 key = "rpm-channel-type";
971 ret = of_property_read_u32(pdev->dev.of_node, key,
972 &msm_rpm_data.ch_type);
973 if (ret)
974 goto fail;
975
976 init_completion(&msm_rpm_data.smd_open);
977 spin_lock_init(&msm_rpm_data.smd_lock_write);
978 spin_lock_init(&msm_rpm_data.smd_lock_read);
979 INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work);
980
981 if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type,
982 &msm_rpm_data.ch_info, &msm_rpm_data,
983 msm_rpm_notify)) {
984 pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
985 msm_rpm_data.ch_type);
986
987 msm_rpm_set_standalone();
988 BUG_ON(!standalone);
989 complete(&msm_rpm_data.smd_open);
990 }
991
Michael Bohan5eb00812012-08-20 11:56:33 -0700992 wait_for_completion(&msm_rpm_data.smd_open);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600993
994 smd_disable_read_intr(msm_rpm_data.ch_info);
995
996 if (!standalone) {
997 msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
998 if (!msm_rpm_smd_wq)
999 return -EINVAL;
1000 }
1001
1002 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
1003 return 0;
1004fail:
1005 pr_err("%s(): Failed to read node: %s, key=%s\n", __func__,
1006 pdev->dev.of_node->full_name, key);
1007 return -EINVAL;
1008}
1009
1010static struct of_device_id msm_rpm_match_table[] = {
1011 {.compatible = "qcom,rpm-smd"},
1012 {},
1013};
1014
1015static struct platform_driver msm_rpm_device_driver = {
1016 .probe = msm_rpm_dev_probe,
1017 .driver = {
1018 .name = "rpm-smd",
1019 .owner = THIS_MODULE,
1020 .of_match_table = msm_rpm_match_table,
1021 },
1022};
1023
1024int __init msm_rpm_driver_init(void)
1025{
1026 static bool registered;
1027
1028 if (registered)
1029 return 0;
1030 registered = true;
1031
1032 return platform_driver_register(&msm_rpm_device_driver);
1033}
1034EXPORT_SYMBOL(msm_rpm_driver_init);
1035late_initcall(msm_rpm_driver_init);