blob: 4295fd405b7902d7322b2e7471df43f060de6212 [file] [log] [blame]
Praveen Chidambaram51bb7962013-01-09 13:42:33 -07001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06002 *
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"
Mahesh Sivasubramaniand69058b2012-10-01 16:49:26 -060039#define CREATE_TRACE_POINTS
40#include "trace_rpm_smd.h"
David Collinsc26c6522012-07-03 16:04:37 -070041/* Debug Definitions */
42
43enum {
44 MSM_RPM_LOG_REQUEST_PRETTY = BIT(0),
45 MSM_RPM_LOG_REQUEST_RAW = BIT(1),
46 MSM_RPM_LOG_REQUEST_SHOW_MSG_ID = BIT(2),
47};
48
49static int msm_rpm_debug_mask;
50module_param_named(
51 debug_mask, msm_rpm_debug_mask, int, S_IRUGO | S_IWUSR
52);
53
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060054struct msm_rpm_driver_data {
55 const char *ch_name;
56 uint32_t ch_type;
57 smd_channel_t *ch_info;
58 struct work_struct work;
59 spinlock_t smd_lock_write;
60 spinlock_t smd_lock_read;
61 struct completion smd_open;
62};
63
64#define DEFAULT_BUFFER_SIZE 256
65#define GFP_FLAG(noirq) (noirq ? GFP_ATOMIC : GFP_KERNEL)
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -060066#define INV_RSC "resource does not exist"
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060067#define ERR "err\0"
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -060068#define MAX_ERR_BUFFER_SIZE 128
69#define INIT_ERROR 1
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060070
Stephen Boydbc363fe2012-07-09 12:31:42 -070071static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -060072static bool standalone;
73
74int msm_rpm_register_notifier(struct notifier_block *nb)
75{
76 return atomic_notifier_chain_register(&msm_rpm_sleep_notifier, nb);
77}
78
79int msm_rpm_unregister_notifier(struct notifier_block *nb)
80{
81 return atomic_notifier_chain_unregister(&msm_rpm_sleep_notifier, nb);
82}
83
84static struct workqueue_struct *msm_rpm_smd_wq;
85
86enum {
87 MSM_RPM_MSG_REQUEST_TYPE = 0,
88 MSM_RPM_MSG_TYPE_NR,
89};
90
91static const uint32_t msm_rpm_request_service[MSM_RPM_MSG_TYPE_NR] = {
92 0x716572, /* 'req\0' */
93};
94
95/*the order of fields matter and reflect the order expected by the RPM*/
96struct rpm_request_header {
97 uint32_t service_type;
98 uint32_t request_len;
99};
100
101struct rpm_message_header {
102 uint32_t msg_id;
103 enum msm_rpm_set set;
104 uint32_t resource_type;
105 uint32_t resource_id;
106 uint32_t data_len;
107};
108
109struct msm_rpm_kvp_data {
110 uint32_t key;
111 uint32_t nbytes; /* number of bytes */
112 uint8_t *value;
113 bool valid;
114};
115
116static atomic_t msm_rpm_msg_id = ATOMIC_INIT(0);
117
118static struct msm_rpm_driver_data msm_rpm_data;
119
120struct msm_rpm_request {
121 struct rpm_request_header req_hdr;
122 struct rpm_message_header msg_hdr;
123 struct msm_rpm_kvp_data *kvp;
124 uint32_t num_elements;
125 uint32_t write_idx;
126 uint8_t *buf;
127 uint32_t numbytes;
128};
129
130/*
131 * Data related to message acknowledgement
132 */
133
134LIST_HEAD(msm_rpm_wait_list);
135
136struct msm_rpm_wait_data {
137 struct list_head list;
138 uint32_t msg_id;
139 bool ack_recd;
140 int errno;
141 struct completion ack;
142};
143DEFINE_SPINLOCK(msm_rpm_list_lock);
144
145struct msm_rpm_ack_msg {
146 uint32_t req;
147 uint32_t req_len;
148 uint32_t rsc_id;
149 uint32_t msg_len;
150 uint32_t id_ack;
151};
152
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600153LIST_HEAD(msm_rpm_ack_list);
154
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -0700155static DECLARE_COMPLETION(data_ready);
156
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600157static void msm_rpm_notify_sleep_chain(struct rpm_message_header *hdr,
158 struct msm_rpm_kvp_data *kvp)
159{
160 struct msm_rpm_notifier_data notif;
161
162 notif.rsc_type = hdr->resource_type;
163 notif.rsc_id = hdr->resource_id;
164 notif.key = kvp->key;
165 notif.size = kvp->nbytes;
166 notif.value = kvp->value;
167 atomic_notifier_call_chain(&msm_rpm_sleep_notifier, 0, &notif);
168}
169
170static int msm_rpm_add_kvp_data_common(struct msm_rpm_request *handle,
171 uint32_t key, const uint8_t *data, int size, bool noirq)
172{
173 int i;
174 int data_size, msg_size;
175
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600176 if (!handle) {
177 pr_err("%s(): Invalid handle\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600178 return -EINVAL;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600179 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600180
181 data_size = ALIGN(size, SZ_4);
182 msg_size = data_size + sizeof(struct rpm_request_header);
183
184 for (i = 0; i < handle->write_idx; i++) {
185 if (handle->kvp[i].key != key)
186 continue;
187 if (handle->kvp[i].nbytes != data_size) {
188 kfree(handle->kvp[i].value);
189 handle->kvp[i].value = NULL;
190 } else {
191 if (!memcmp(handle->kvp[i].value, data, data_size))
192 return 0;
193 }
194 break;
195 }
196
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600197 if (i >= handle->num_elements) {
198 pr_err("%s(): Number of resources exceeds max allocated\n",
199 __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600200 return -ENOMEM;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600201 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600202
203 if (i == handle->write_idx)
204 handle->write_idx++;
205
206 if (!handle->kvp[i].value) {
207 handle->kvp[i].value = kzalloc(data_size, GFP_FLAG(noirq));
208
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600209 if (!handle->kvp[i].value) {
210 pr_err("%s(): Failed malloc\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600211 return -ENOMEM;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600212 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600213 } else {
214 /* We enter the else case, if a key already exists but the
215 * data doesn't match. In which case, we should zero the data
216 * out.
217 */
218 memset(handle->kvp[i].value, 0, data_size);
219 }
220
221 if (!handle->kvp[i].valid)
222 handle->msg_hdr.data_len += msg_size;
223 else
224 handle->msg_hdr.data_len += (data_size - handle->kvp[i].nbytes);
225
226 handle->kvp[i].nbytes = data_size;
227 handle->kvp[i].key = key;
228 memcpy(handle->kvp[i].value, data, size);
229 handle->kvp[i].valid = true;
230
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600231 return 0;
232
233}
234
235static struct msm_rpm_request *msm_rpm_create_request_common(
236 enum msm_rpm_set set, uint32_t rsc_type, uint32_t rsc_id,
237 int num_elements, bool noirq)
238{
239 struct msm_rpm_request *cdata;
240
241 cdata = kzalloc(sizeof(struct msm_rpm_request),
242 GFP_FLAG(noirq));
243
244 if (!cdata) {
245 printk(KERN_INFO"%s():Cannot allocate memory for client data\n",
246 __func__);
247 goto cdata_alloc_fail;
248 }
249
250 cdata->msg_hdr.set = set;
251 cdata->msg_hdr.resource_type = rsc_type;
252 cdata->msg_hdr.resource_id = rsc_id;
253 cdata->msg_hdr.data_len = 0;
254
255 cdata->num_elements = num_elements;
256 cdata->write_idx = 0;
257
258 cdata->kvp = kzalloc(sizeof(struct msm_rpm_kvp_data) * num_elements,
259 GFP_FLAG(noirq));
260
261 if (!cdata->kvp) {
262 pr_warn("%s(): Cannot allocate memory for key value data\n",
263 __func__);
264 goto kvp_alloc_fail;
265 }
266
267 cdata->buf = kzalloc(DEFAULT_BUFFER_SIZE, GFP_FLAG(noirq));
268
269 if (!cdata->buf)
270 goto buf_alloc_fail;
271
272 cdata->numbytes = DEFAULT_BUFFER_SIZE;
273 return cdata;
274
275buf_alloc_fail:
276 kfree(cdata->kvp);
277kvp_alloc_fail:
278 kfree(cdata);
279cdata_alloc_fail:
280 return NULL;
281
282}
283
284void msm_rpm_free_request(struct msm_rpm_request *handle)
285{
286 int i;
287
288 if (!handle)
289 return;
Michael Bohan667aaf82012-09-20 14:32:55 -0700290 for (i = 0; i < handle->num_elements; i++)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600291 kfree(handle->kvp[i].value);
292 kfree(handle->kvp);
Michael Bohan667aaf82012-09-20 14:32:55 -0700293 kfree(handle->buf);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600294 kfree(handle);
295}
296EXPORT_SYMBOL(msm_rpm_free_request);
297
298struct msm_rpm_request *msm_rpm_create_request(
299 enum msm_rpm_set set, uint32_t rsc_type,
300 uint32_t rsc_id, int num_elements)
301{
302 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
303 num_elements, false);
304}
305EXPORT_SYMBOL(msm_rpm_create_request);
306
307struct msm_rpm_request *msm_rpm_create_request_noirq(
308 enum msm_rpm_set set, uint32_t rsc_type,
309 uint32_t rsc_id, int num_elements)
310{
311 return msm_rpm_create_request_common(set, rsc_type, rsc_id,
312 num_elements, true);
313}
314EXPORT_SYMBOL(msm_rpm_create_request_noirq);
315
316int msm_rpm_add_kvp_data(struct msm_rpm_request *handle,
317 uint32_t key, const uint8_t *data, int size)
318{
319 return msm_rpm_add_kvp_data_common(handle, key, data, size, false);
320
321}
322EXPORT_SYMBOL(msm_rpm_add_kvp_data);
323
324int msm_rpm_add_kvp_data_noirq(struct msm_rpm_request *handle,
325 uint32_t key, const uint8_t *data, int size)
326{
327 return msm_rpm_add_kvp_data_common(handle, key, data, size, true);
328}
329EXPORT_SYMBOL(msm_rpm_add_kvp_data_noirq);
330
331/* Runs in interrupt context */
332static void msm_rpm_notify(void *data, unsigned event)
333{
334 struct msm_rpm_driver_data *pdata = (struct msm_rpm_driver_data *)data;
335 BUG_ON(!pdata);
336
337 if (!(pdata->ch_info))
338 return;
339
340 switch (event) {
341 case SMD_EVENT_DATA:
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -0700342 complete(&data_ready);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600343 break;
344 case SMD_EVENT_OPEN:
345 complete(&pdata->smd_open);
346 break;
347 case SMD_EVENT_CLOSE:
348 case SMD_EVENT_STATUS:
349 case SMD_EVENT_REOPEN_READY:
350 break;
351 default:
352 pr_info("Unknown SMD event\n");
353
354 }
355}
356
Mahesh Sivasubramanian9063a292012-11-09 09:15:30 -0700357bool msm_rpm_waiting_for_ack(void)
358{
359 bool ret;
360 unsigned long flags;
361
362 spin_lock_irqsave(&msm_rpm_list_lock, flags);
363 ret = list_empty(&msm_rpm_wait_list);
364 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
365
366 return !ret;
367}
368
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600369static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
370{
371 struct list_head *ptr;
372 struct msm_rpm_wait_data *elem;
373 unsigned long flags;
374
375 spin_lock_irqsave(&msm_rpm_list_lock, flags);
376
377 list_for_each(ptr, &msm_rpm_wait_list) {
378 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
379 if (elem && (elem->msg_id == msg_id))
380 break;
381 elem = NULL;
382 }
383 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
384 return elem;
385}
386
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600387static uint32_t msm_rpm_get_next_msg_id(void)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600388{
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600389 uint32_t id;
390
391 /*
392 * A message id of 0 is used by the driver to indicate a error
393 * condition. The RPM driver uses a id of 1 to indicate unsent data
394 * when the data sent over hasn't been modified. This isn't a error
395 * scenario and wait for ack returns a success when the message id is 1.
396 */
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600397
398 do {
399 id = atomic_inc_return(&msm_rpm_msg_id);
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600400 } while ((id == 0) || (id == 1) || msm_rpm_get_entry_from_msg_id(id));
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600401
402 return id;
403}
404
405static int msm_rpm_add_wait_list(uint32_t msg_id)
406{
407 unsigned long flags;
408 struct msm_rpm_wait_data *data =
409 kzalloc(sizeof(struct msm_rpm_wait_data), GFP_ATOMIC);
410
411 if (!data)
412 return -ENOMEM;
413
414 init_completion(&data->ack);
415 data->ack_recd = false;
416 data->msg_id = msg_id;
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600417 data->errno = INIT_ERROR;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600418 spin_lock_irqsave(&msm_rpm_list_lock, flags);
419 list_add(&data->list, &msm_rpm_wait_list);
420 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
421
422 return 0;
423}
424
425static void msm_rpm_free_list_entry(struct msm_rpm_wait_data *elem)
426{
427 unsigned long flags;
428
429 spin_lock_irqsave(&msm_rpm_list_lock, flags);
430 list_del(&elem->list);
431 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
432 kfree(elem);
433}
434
435static void msm_rpm_process_ack(uint32_t msg_id, int errno)
436{
437 struct list_head *ptr;
438 struct msm_rpm_wait_data *elem;
439 unsigned long flags;
440
441 spin_lock_irqsave(&msm_rpm_list_lock, flags);
442
443 list_for_each(ptr, &msm_rpm_wait_list) {
444 elem = list_entry(ptr, struct msm_rpm_wait_data, list);
445 if (elem && (elem->msg_id == msg_id)) {
446 elem->errno = errno;
447 elem->ack_recd = true;
448 complete(&elem->ack);
449 break;
450 }
451 elem = NULL;
452 }
453 WARN_ON(!elem);
454
455 spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
456}
457
458struct msm_rpm_kvp_packet {
459 uint32_t id;
460 uint32_t len;
461 uint32_t val;
462};
463
464static inline uint32_t msm_rpm_get_msg_id_from_ack(uint8_t *buf)
465{
466 return ((struct msm_rpm_ack_msg *)buf)->id_ack;
467}
468
469static inline int msm_rpm_get_error_from_ack(uint8_t *buf)
470{
471 uint8_t *tmp;
472 uint32_t req_len = ((struct msm_rpm_ack_msg *)buf)->req_len;
473
474 int rc = -ENODEV;
475
476 req_len -= sizeof(struct msm_rpm_ack_msg);
477 req_len += 2 * sizeof(uint32_t);
478 if (!req_len)
479 return 0;
480
481 tmp = buf + sizeof(struct msm_rpm_ack_msg);
482
483 BUG_ON(memcmp(tmp, ERR, sizeof(uint32_t)));
484
485 tmp += 2 * sizeof(uint32_t);
486
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600487 if (!(memcmp(tmp, INV_RSC, min(req_len, sizeof(INV_RSC))-1))) {
488 pr_err("%s(): RPM NACK Unsupported resource\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600489 rc = -EINVAL;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600490 } else {
491 pr_err("%s(): RPM NACK Invalid header\n", __func__);
492 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600493
494 return rc;
495}
496
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600497static int msm_rpm_read_smd_data(char *buf)
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600498{
499 int pkt_sz;
500 int bytes_read = 0;
501
502 pkt_sz = smd_cur_packet_size(msm_rpm_data.ch_info);
503
Mahesh Sivasubramanian137cd702012-08-23 18:52:59 -0600504 if (!pkt_sz)
505 return -EAGAIN;
506
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600507 BUG_ON(pkt_sz > MAX_ERR_BUFFER_SIZE);
508
509 if (pkt_sz != smd_read_avail(msm_rpm_data.ch_info))
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600510 return -EAGAIN;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600511
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600512 do {
513 int len;
514
515 len = smd_read(msm_rpm_data.ch_info, buf + bytes_read, pkt_sz);
516 pkt_sz -= len;
517 bytes_read += len;
518
519 } while (pkt_sz > 0);
520
521 BUG_ON(pkt_sz < 0);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600522
523 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600524}
525
526static void msm_rpm_smd_work(struct work_struct *work)
527{
528 uint32_t msg_id;
529 int errno;
530 char buf[MAX_ERR_BUFFER_SIZE] = {0};
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600531
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -0700532 while (1) {
533 wait_for_completion(&data_ready);
534
535 spin_lock(&msm_rpm_data.smd_lock_read);
536 while (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
537 if (msm_rpm_read_smd_data(buf))
538 break;
539 msg_id = msm_rpm_get_msg_id_from_ack(buf);
540 errno = msm_rpm_get_error_from_ack(buf);
541 msm_rpm_process_ack(msg_id, errno);
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600542 }
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -0700543 spin_unlock(&msm_rpm_data.smd_lock_read);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600544 }
545}
546
David Collinsc26c6522012-07-03 16:04:37 -0700547#define DEBUG_PRINT_BUFFER_SIZE 512
548
549static void msm_rpm_log_request(struct msm_rpm_request *cdata)
550{
551 char buf[DEBUG_PRINT_BUFFER_SIZE];
552 size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
553 char name[5];
554 u32 value;
555 int i, j, prev_valid;
556 int valid_count = 0;
557 int pos = 0;
558
559 name[4] = 0;
560
561 for (i = 0; i < cdata->write_idx; i++)
562 if (cdata->kvp[i].valid)
563 valid_count++;
564
565 pos += scnprintf(buf + pos, buflen - pos, "%sRPM req: ", KERN_INFO);
566 if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_SHOW_MSG_ID)
567 pos += scnprintf(buf + pos, buflen - pos, "msg_id=%u, ",
568 cdata->msg_hdr.msg_id);
569 pos += scnprintf(buf + pos, buflen - pos, "s=%s",
570 (cdata->msg_hdr.set == MSM_RPM_CTX_ACTIVE_SET ? "act" : "slp"));
571
572 if ((msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY)
573 && (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_RAW)) {
574 /* Both pretty and raw formatting */
575 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
576 pos += scnprintf(buf + pos, buflen - pos,
577 ", rsc_type=0x%08X (%s), rsc_id=%u; ",
578 cdata->msg_hdr.resource_type, name,
579 cdata->msg_hdr.resource_id);
580
581 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
582 if (!cdata->kvp[i].valid)
583 continue;
584
585 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
586 pos += scnprintf(buf + pos, buflen - pos,
587 "[key=0x%08X (%s), value=%s",
588 cdata->kvp[i].key, name,
589 (cdata->kvp[i].nbytes ? "0x" : "null"));
590
591 for (j = 0; j < cdata->kvp[i].nbytes; j++)
592 pos += scnprintf(buf + pos, buflen - pos,
593 "%02X ",
594 cdata->kvp[i].value[j]);
595
596 if (cdata->kvp[i].nbytes)
597 pos += scnprintf(buf + pos, buflen - pos, "(");
598
599 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
600 value = 0;
601 memcpy(&value, &cdata->kvp[i].value[j],
602 min(sizeof(uint32_t),
603 cdata->kvp[i].nbytes - j));
604 pos += scnprintf(buf + pos, buflen - pos, "%u",
605 value);
606 if (j + 4 < cdata->kvp[i].nbytes)
607 pos += scnprintf(buf + pos,
608 buflen - pos, " ");
609 }
610 if (cdata->kvp[i].nbytes)
611 pos += scnprintf(buf + pos, buflen - pos, ")");
612 pos += scnprintf(buf + pos, buflen - pos, "]");
613 if (prev_valid + 1 < valid_count)
614 pos += scnprintf(buf + pos, buflen - pos, ", ");
615 prev_valid++;
616 }
617 } else if (msm_rpm_debug_mask & MSM_RPM_LOG_REQUEST_PRETTY) {
618 /* Pretty formatting only */
619 memcpy(name, &cdata->msg_hdr.resource_type, sizeof(uint32_t));
620 pos += scnprintf(buf + pos, buflen - pos, " %s %u; ", name,
621 cdata->msg_hdr.resource_id);
622
623 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
624 if (!cdata->kvp[i].valid)
625 continue;
626
627 memcpy(name, &cdata->kvp[i].key, sizeof(uint32_t));
628 pos += scnprintf(buf + pos, buflen - pos, "%s=%s",
629 name, (cdata->kvp[i].nbytes ? "" : "null"));
630
631 for (j = 0; j < cdata->kvp[i].nbytes; j += 4) {
632 value = 0;
633 memcpy(&value, &cdata->kvp[i].value[j],
634 min(sizeof(uint32_t),
635 cdata->kvp[i].nbytes - j));
636 pos += scnprintf(buf + pos, buflen - pos, "%u",
637 value);
638
639 if (j + 4 < cdata->kvp[i].nbytes)
640 pos += scnprintf(buf + pos,
641 buflen - pos, " ");
642 }
643 if (prev_valid + 1 < valid_count)
644 pos += scnprintf(buf + pos, buflen - pos, ", ");
645 prev_valid++;
646 }
647 } else {
648 /* Raw formatting only */
649 pos += scnprintf(buf + pos, buflen - pos,
650 ", rsc_type=0x%08X, rsc_id=%u; ",
651 cdata->msg_hdr.resource_type,
652 cdata->msg_hdr.resource_id);
653
654 for (i = 0, prev_valid = 0; i < cdata->write_idx; i++) {
655 if (!cdata->kvp[i].valid)
656 continue;
657
658 pos += scnprintf(buf + pos, buflen - pos,
659 "[key=0x%08X, value=%s",
660 cdata->kvp[i].key,
661 (cdata->kvp[i].nbytes ? "0x" : "null"));
662 for (j = 0; j < cdata->kvp[i].nbytes; j++) {
663 pos += scnprintf(buf + pos, buflen - pos,
664 "%02X",
665 cdata->kvp[i].value[j]);
666 if (j + 1 < cdata->kvp[i].nbytes)
667 pos += scnprintf(buf + pos,
668 buflen - pos, " ");
669 }
670 pos += scnprintf(buf + pos, buflen - pos, "]");
671 if (prev_valid + 1 < valid_count)
672 pos += scnprintf(buf + pos, buflen - pos, ", ");
673 prev_valid++;
674 }
675 }
676
677 pos += scnprintf(buf + pos, buflen - pos, "\n");
678 printk(buf);
679}
680
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600681static int msm_rpm_send_data(struct msm_rpm_request *cdata,
682 int msg_type, bool noirq)
683{
684 uint8_t *tmpbuff;
685 int i, ret, msg_size;
686 unsigned long flags;
687
688 int req_hdr_sz, msg_hdr_sz;
689
690 if (!cdata->msg_hdr.data_len)
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600691 return 1;
692
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600693 req_hdr_sz = sizeof(cdata->req_hdr);
694 msg_hdr_sz = sizeof(cdata->msg_hdr);
695
696 cdata->req_hdr.service_type = msm_rpm_request_service[msg_type];
697
698 cdata->msg_hdr.msg_id = msm_rpm_get_next_msg_id();
699
700 cdata->req_hdr.request_len = cdata->msg_hdr.data_len + msg_hdr_sz;
701 msg_size = cdata->req_hdr.request_len + req_hdr_sz;
702
703 /* populate data_len */
704 if (msg_size > cdata->numbytes) {
705 kfree(cdata->buf);
706 cdata->numbytes = msg_size;
707 cdata->buf = kzalloc(msg_size, GFP_FLAG(noirq));
708 }
709
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600710 if (!cdata->buf) {
711 pr_err("%s(): Failed malloc\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600712 return 0;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600713 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600714
715 tmpbuff = cdata->buf;
716
717 memcpy(tmpbuff, &cdata->req_hdr, req_hdr_sz + msg_hdr_sz);
718
719 tmpbuff += req_hdr_sz + msg_hdr_sz;
720
721 for (i = 0; (i < cdata->write_idx); i++) {
722 /* Sanity check */
723 BUG_ON((tmpbuff - cdata->buf) > cdata->numbytes);
724
725 if (!cdata->kvp[i].valid)
726 continue;
727
728 memcpy(tmpbuff, &cdata->kvp[i].key, sizeof(uint32_t));
729 tmpbuff += sizeof(uint32_t);
730
731 memcpy(tmpbuff, &cdata->kvp[i].nbytes, sizeof(uint32_t));
732 tmpbuff += sizeof(uint32_t);
733
734 memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
735 tmpbuff += cdata->kvp[i].nbytes;
Mahesh Sivasubramaniane733ddc2012-11-05 16:38:56 -0700736
737 if (cdata->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
738 msm_rpm_notify_sleep_chain(&cdata->msg_hdr,
739 &cdata->kvp[i]);
740
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600741 }
742
David Collinsc26c6522012-07-03 16:04:37 -0700743 if (msm_rpm_debug_mask
744 & (MSM_RPM_LOG_REQUEST_PRETTY | MSM_RPM_LOG_REQUEST_RAW))
745 msm_rpm_log_request(cdata);
746
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600747 if (standalone) {
748 for (i = 0; (i < cdata->write_idx); i++)
749 cdata->kvp[i].valid = false;
750
751 cdata->msg_hdr.data_len = 0;
752 ret = cdata->msg_hdr.msg_id;
753 return ret;
754 }
755
756 msm_rpm_add_wait_list(cdata->msg_hdr.msg_id);
757
758 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
759
760 ret = smd_write_avail(msm_rpm_data.ch_info);
761
762 if (ret < 0) {
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600763 pr_err("%s(): SMD not initialized\n", __func__);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600764 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
765 return 0;
766 }
767
768 while ((ret < msg_size)) {
769 if (!noirq) {
770 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write,
771 flags);
772 cpu_relax();
773 spin_lock_irqsave(&msm_rpm_data.smd_lock_write, flags);
774 } else
775 udelay(5);
776 ret = smd_write_avail(msm_rpm_data.ch_info);
777 }
778
779 ret = smd_write(msm_rpm_data.ch_info, &cdata->buf[0], msg_size);
780 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
781
782 if (ret == msg_size) {
Mahesh Sivasubramaniand69058b2012-10-01 16:49:26 -0600783 trace_rpm_send_message(noirq, cdata->msg_hdr.set,
784 cdata->msg_hdr.resource_type,
785 cdata->msg_hdr.resource_id,
786 cdata->msg_hdr.msg_id);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600787 for (i = 0; (i < cdata->write_idx); i++)
788 cdata->kvp[i].valid = false;
789 cdata->msg_hdr.data_len = 0;
790 ret = cdata->msg_hdr.msg_id;
791 } else if (ret < msg_size) {
792 struct msm_rpm_wait_data *rc;
793 ret = 0;
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600794 pr_err("Failed to write data msg_size:%d ret:%d\n",
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600795 msg_size, ret);
796 rc = msm_rpm_get_entry_from_msg_id(cdata->msg_hdr.msg_id);
797 if (rc)
798 msm_rpm_free_list_entry(rc);
799 }
800 return ret;
801}
802
803int msm_rpm_send_request(struct msm_rpm_request *handle)
804{
Mahesh Sivasubramanian4f534cb2012-09-28 14:17:05 -0600805 int ret;
806 static DEFINE_MUTEX(send_mtx);
807
808 mutex_lock(&send_mtx);
809 ret = msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, false);
810 mutex_unlock(&send_mtx);
811
812 return ret;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600813}
814EXPORT_SYMBOL(msm_rpm_send_request);
815
816int msm_rpm_send_request_noirq(struct msm_rpm_request *handle)
817{
818 return msm_rpm_send_data(handle, MSM_RPM_MSG_REQUEST_TYPE, true);
819}
820EXPORT_SYMBOL(msm_rpm_send_request_noirq);
821
822int msm_rpm_wait_for_ack(uint32_t msg_id)
823{
824 struct msm_rpm_wait_data *elem;
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700825 int rc = 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600826
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600827 if (!msg_id) {
828 pr_err("%s(): Invalid msg id\n", __func__);
829 return -ENOMEM;
830 }
831
832 if (msg_id == 1)
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700833 return rc;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600834
835 if (standalone)
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700836 return rc;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600837
838 elem = msm_rpm_get_entry_from_msg_id(msg_id);
839 if (!elem)
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700840 return rc;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600841
Michael Bohan5eb00812012-08-20 11:56:33 -0700842 wait_for_completion(&elem->ack);
Mahesh Sivasubramaniand69058b2012-10-01 16:49:26 -0600843 trace_rpm_ack_recd(0, msg_id);
844
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700845 rc = elem->errno;
Michael Bohan5eb00812012-08-20 11:56:33 -0700846 msm_rpm_free_list_entry(elem);
Mahesh Sivasubramanian55840772012-12-20 10:19:56 -0700847
848 return rc;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600849}
850EXPORT_SYMBOL(msm_rpm_wait_for_ack);
851
852int msm_rpm_wait_for_ack_noirq(uint32_t msg_id)
853{
854 struct msm_rpm_wait_data *elem;
855 unsigned long flags;
856 int rc = 0;
857 uint32_t id = 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600858
Mahesh Sivasubramanian7ef2aad2012-07-30 13:52:31 -0600859 if (!msg_id) {
860 pr_err("%s(): Invalid msg id\n", __func__);
861 return -ENOMEM;
862 }
863
864 if (msg_id == 1)
865 return 0;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600866
867 if (standalone)
868 return 0;
869
870 spin_lock_irqsave(&msm_rpm_data.smd_lock_read, flags);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600871
872 elem = msm_rpm_get_entry_from_msg_id(msg_id);
873
874 if (!elem)
875 /* Should this be a bug
876 * Is it ok for another thread to read the msg?
877 */
878 goto wait_ack_cleanup;
879
Praveen Chidambaram4647cdb2012-08-13 17:55:44 -0600880 if (elem->errno != INIT_ERROR) {
881 rc = elem->errno;
882 msm_rpm_free_list_entry(elem);
883 goto wait_ack_cleanup;
884 }
885
Mahesh Sivasubramanian2ff713d2012-09-12 09:37:28 -0600886 while (id != msg_id) {
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600887 if (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
888 int errno;
889 char buf[MAX_ERR_BUFFER_SIZE] = {};
890
891 msm_rpm_read_smd_data(buf);
892 id = msm_rpm_get_msg_id_from_ack(buf);
893 errno = msm_rpm_get_error_from_ack(buf);
894 msm_rpm_process_ack(id, errno);
Mahesh Sivasubramanian2ff713d2012-09-12 09:37:28 -0600895 }
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600896 }
897
Mahesh Sivasubramanian2ff713d2012-09-12 09:37:28 -0600898 rc = elem->errno;
Mahesh Sivasubramaniand69058b2012-10-01 16:49:26 -0600899 trace_rpm_ack_recd(1, msg_id);
900
Mahesh Sivasubramanian2ff713d2012-09-12 09:37:28 -0600901 msm_rpm_free_list_entry(elem);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600902wait_ack_cleanup:
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600903 spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -0700904
905 if (smd_is_pkt_avail(msm_rpm_data.ch_info))
906 complete(&data_ready);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600907 return rc;
908}
909EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq);
910
911int msm_rpm_send_message(enum msm_rpm_set set, uint32_t rsc_type,
912 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
913{
914 int i, rc;
915 struct msm_rpm_request *req =
916 msm_rpm_create_request(set, rsc_type, rsc_id, nelems);
917 if (!req)
918 return -ENOMEM;
919
920 for (i = 0; i < nelems; i++) {
921 rc = msm_rpm_add_kvp_data(req, kvp[i].key,
922 kvp[i].data, kvp[i].length);
923 if (rc)
924 goto bail;
925 }
926
927 rc = msm_rpm_wait_for_ack(msm_rpm_send_request(req));
928bail:
929 msm_rpm_free_request(req);
930 return rc;
931}
932EXPORT_SYMBOL(msm_rpm_send_message);
933
934int msm_rpm_send_message_noirq(enum msm_rpm_set set, uint32_t rsc_type,
935 uint32_t rsc_id, struct msm_rpm_kvp *kvp, int nelems)
936{
937 int i, rc;
938 struct msm_rpm_request *req =
939 msm_rpm_create_request_noirq(set, rsc_type, rsc_id, nelems);
940 if (!req)
941 return -ENOMEM;
942
943 for (i = 0; i < nelems; i++) {
944 rc = msm_rpm_add_kvp_data_noirq(req, kvp[i].key,
945 kvp[i].data, kvp[i].length);
946 if (rc)
947 goto bail;
948 }
949
950 rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(req));
951bail:
952 msm_rpm_free_request(req);
953 return rc;
954}
955EXPORT_SYMBOL(msm_rpm_send_message_noirq);
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600956
957/**
958 * During power collapse, the rpm driver disables the SMD interrupts to make
959 * sure that the interrupt doesn't wakes us from sleep.
960 */
961int msm_rpm_enter_sleep(void)
962{
Praveen Chidambaram51bb7962013-01-09 13:42:33 -0700963 if (standalone)
964 return 0;
965
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600966 return smd_mask_receive_interrupt(msm_rpm_data.ch_info, true);
967}
968EXPORT_SYMBOL(msm_rpm_enter_sleep);
969
970/**
971 * When the system resumes from power collapse, the SMD interrupt disabled by
972 * enter function has to reenabled to continue processing SMD message.
973 */
974void msm_rpm_exit_sleep(void)
975{
Praveen Chidambaram51bb7962013-01-09 13:42:33 -0700976 if (standalone)
977 return;
978
Mahesh Sivasubramanian11dad772012-07-13 14:00:01 -0600979 smd_mask_receive_interrupt(msm_rpm_data.ch_info, false);
980}
981EXPORT_SYMBOL(msm_rpm_exit_sleep);
982
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -0600983static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
984{
985 char *key = NULL;
986 int ret;
987
988 key = "rpm-channel-name";
989 ret = of_property_read_string(pdev->dev.of_node, key,
990 &msm_rpm_data.ch_name);
991 if (ret)
992 goto fail;
993
994 key = "rpm-channel-type";
995 ret = of_property_read_u32(pdev->dev.of_node, key,
996 &msm_rpm_data.ch_type);
997 if (ret)
998 goto fail;
999
Praveen Chidambaram51bb7962013-01-09 13:42:33 -07001000 key = "rpm-standalone";
1001 standalone = of_property_read_bool(pdev->dev.of_node, key);
1002
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001003 init_completion(&msm_rpm_data.smd_open);
1004 spin_lock_init(&msm_rpm_data.smd_lock_write);
1005 spin_lock_init(&msm_rpm_data.smd_lock_read);
1006 INIT_WORK(&msm_rpm_data.work, msm_rpm_smd_work);
1007
1008 if (smd_named_open_on_edge(msm_rpm_data.ch_name, msm_rpm_data.ch_type,
1009 &msm_rpm_data.ch_info, &msm_rpm_data,
1010 msm_rpm_notify)) {
1011 pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
1012 msm_rpm_data.ch_type);
1013
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001014 BUG_ON(!standalone);
1015 complete(&msm_rpm_data.smd_open);
Praveen Chidambaram51bb7962013-01-09 13:42:33 -07001016 } else {
1017 /*
1018 * Override DT's suggestion to try standalone; since we have an
1019 * SMD channel.
1020 */
1021 standalone = false;
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001022 }
1023
Michael Bohan5eb00812012-08-20 11:56:33 -07001024 wait_for_completion(&msm_rpm_data.smd_open);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001025
1026 smd_disable_read_intr(msm_rpm_data.ch_info);
1027
1028 if (!standalone) {
1029 msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
1030 if (!msm_rpm_smd_wq)
1031 return -EINVAL;
Mahesh Sivasubramanian06ff5d02012-11-09 15:47:12 -07001032 queue_work(msm_rpm_smd_wq, &msm_rpm_data.work);
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001033 }
1034
1035 of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
Praveen Chidambaram51bb7962013-01-09 13:42:33 -07001036
1037 if (standalone)
1038 pr_info("%s(): RPM running in standalone mode\n", __func__);
1039
Mahesh Sivasubramaniana8ff9922012-03-27 17:50:42 -06001040 return 0;
1041fail:
1042 pr_err("%s(): Failed to read node: %s, key=%s\n", __func__,
1043 pdev->dev.of_node->full_name, key);
1044 return -EINVAL;
1045}
1046
1047static struct of_device_id msm_rpm_match_table[] = {
1048 {.compatible = "qcom,rpm-smd"},
1049 {},
1050};
1051
1052static struct platform_driver msm_rpm_device_driver = {
1053 .probe = msm_rpm_dev_probe,
1054 .driver = {
1055 .name = "rpm-smd",
1056 .owner = THIS_MODULE,
1057 .of_match_table = msm_rpm_match_table,
1058 },
1059};
1060
1061int __init msm_rpm_driver_init(void)
1062{
1063 static bool registered;
1064
1065 if (registered)
1066 return 0;
1067 registered = true;
1068
1069 return platform_driver_register(&msm_rpm_device_driver);
1070}
1071EXPORT_SYMBOL(msm_rpm_driver_init);
1072late_initcall(msm_rpm_driver_init);