blob: 0828bb4da856e3b7b04a9da84fba1054b25a0d85 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2009-2011, 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/slab.h>
15#include <linux/err.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070016#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <asm/mach-types.h>
18#include <mach/board.h>
19#include <mach/rpc_pmapp.h>
20#include <mach/msm_rpcrouter.h>
21#include <mach/vreg.h>
22
23#define PMAPP_RPC_PROG 0x30000060
24#define PMAPP_RPC_VER_1_1 0x00010001
25#define PMAPP_RPC_VER_1_2 0x00010002
26#define PMAPP_RPC_VER_2_1 0x00020001
27#define PMAPP_RPC_VER_3_1 0x00030001
28#define PMAPP_RPC_VER_5_1 0x00050001
29#define PMAPP_RPC_VER_6_1 0x00060001
Trilok Sonif4f0f062011-08-29 00:49:41 +053030#define PMAPP_RPC_VER_7_1 0x00070001
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#define VBUS_SESS_VALID_CB_PROC 1
33#define PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB (1 << 2)
34#define PM_USB_PWR_SEL_SWITCH_ID 0
35
36#define PMAPP_RPC_TIMEOUT (5*HZ)
37
38#define PMAPP_DISPLAY_CLOCK_CONFIG_PROC 21
39#define PMAPP_VREG_LEVEL_VOTE_PROC 23
40#define PMAPP_SMPS_CLOCK_VOTE_PROC 26
41#define PMAPP_CLOCK_VOTE_PROC 27
42#define PMAPP_SMPS_MODE_VOTE_PROC 28
43#define PMAPP_VREG_PINCNTRL_VOTE_PROC 30
44#define PMAPP_DISP_BACKLIGHT_SET_PROC 31
45#define PMAPP_DISP_BACKLIGHT_INIT_PROC 32
Trilok Sonif4f0f062011-08-29 00:49:41 +053046#define PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC 34
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047
48/* Clock voter name max length */
49#define PMAPP_CLOCK_VOTER_ID_LEN 4
50
51struct rpc_pmapp_ids {
52 unsigned long reg_for_vbus_valid;
53 unsigned long vote_for_vbus_valid_switch;
54};
55
56static struct rpc_pmapp_ids rpc_ids;
57static struct msm_rpc_client *client;
58
59/* Add newer versions at the top of array */
60static const unsigned int rpc_vers[] = {
Trilok Sonif4f0f062011-08-29 00:49:41 +053061 PMAPP_RPC_VER_7_1,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062 PMAPP_RPC_VER_6_1,
63 PMAPP_RPC_VER_5_1,
64 PMAPP_RPC_VER_3_1,
65 PMAPP_RPC_VER_2_1,
66};
67
68static void rpc_pmapp_init_rpc_ids(unsigned long vers)
69{
70 if (vers == PMAPP_RPC_VER_1_1) {
71 rpc_ids.reg_for_vbus_valid = 5;
72 rpc_ids.vote_for_vbus_valid_switch = 6;
73 } else if (vers == PMAPP_RPC_VER_1_2) {
74 rpc_ids.reg_for_vbus_valid = 16;
75 rpc_ids.vote_for_vbus_valid_switch = 17;
76 } else if (vers == PMAPP_RPC_VER_2_1) {
77 rpc_ids.reg_for_vbus_valid = 0; /* NA */
78 rpc_ids.vote_for_vbus_valid_switch = 0; /* NA */
79 }
80}
81
82struct usb_pwr_sel_switch_args {
83 uint32_t cmd;
84 uint32_t switch_id;
85 uint32_t app_mask;
86};
87
88static int usb_pwr_sel_switch_arg_cb(struct msm_rpc_client *client,
89 void *buf, void *data)
90{
91 struct usb_pwr_sel_switch_args *args = buf;
92
93 args->cmd = cpu_to_be32(*(uint32_t *)data);
94 args->switch_id = cpu_to_be32(PM_USB_PWR_SEL_SWITCH_ID);
95 args->app_mask = cpu_to_be32(PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB);
96 return sizeof(struct usb_pwr_sel_switch_args);
97}
98
99static int msm_pm_app_vote_usb_pwr_sel_switch(uint32_t cmd)
100{
101 return msm_rpc_client_req(client,
102 rpc_ids.vote_for_vbus_valid_switch,
103 usb_pwr_sel_switch_arg_cb,
104 &cmd, NULL, NULL, -1);
105}
106
107struct vbus_sess_valid_args {
108 uint32_t cb_id;
109};
110
111static int vbus_sess_valid_arg_cb(struct msm_rpc_client *client,
112 void *buf, void *data)
113{
114 struct vbus_sess_valid_args *args = buf;
115
116 args->cb_id = cpu_to_be32(*(uint32_t *)data);
117 return sizeof(struct vbus_sess_valid_args);
118}
119
120
121int pmic_vote_3p3_pwr_sel_switch(int boost)
122{
123 int ret;
124
125 ret = msm_pm_app_vote_usb_pwr_sel_switch(boost);
126
127 return ret;
128}
129EXPORT_SYMBOL(pmic_vote_3p3_pwr_sel_switch);
130
131struct vbus_sn_notification_args {
132 uint32_t cb_id;
133 uint32_t vbus; /* vbus = 0 if VBUS is present */
134};
135
136static int vbus_notification_cb(struct msm_rpc_client *client,
137 void *buffer, int in_size)
138{
139 struct vbus_sn_notification_args *args;
140 struct rpc_request_hdr *req = buffer;
141 int rc;
142 uint32_t accept_status;
143 void (*cb_func)(int);
144 uint32_t cb_id;
145 int vbus;
146
147 args = (struct vbus_sn_notification_args *) (req + 1);
148 cb_id = be32_to_cpu(args->cb_id);
149 vbus = be32_to_cpu(args->vbus);
150
151 cb_func = msm_rpc_get_cb_func(client, cb_id);
152 if (cb_func) {
153 cb_func(!vbus);
154 accept_status = RPC_ACCEPTSTAT_SUCCESS;
155 } else
156 accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
157
158 msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
159 accept_status);
160 rc = msm_rpc_send_accepted_reply(client, 0);
161 if (rc)
162 pr_err("%s: send accepted reply failed: %d\n", __func__, rc);
163
164 return rc;
165}
166
167static int pm_app_usb_cb_func(struct msm_rpc_client *client,
168 void *buffer, int in_size)
169{
170 int rc;
171 struct rpc_request_hdr *req = buffer;
172
173 switch (be32_to_cpu(req->procedure)) {
174 case VBUS_SESS_VALID_CB_PROC:
175 rc = vbus_notification_cb(client, buffer, in_size);
176 break;
177 default:
178 pr_err("%s: procedure not supported %d\n", __func__,
179 be32_to_cpu(req->procedure));
180 msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
181 RPC_ACCEPTSTAT_PROC_UNAVAIL);
182 rc = msm_rpc_send_accepted_reply(client, 0);
183 if (rc)
184 pr_err("%s: sending reply failed: %d\n", __func__, rc);
185 break;
186 }
187 return rc;
188}
189
190int msm_pm_app_rpc_init(void (*callback)(int online))
191{
192 uint32_t cb_id, rc;
193
194 if (!machine_is_qsd8x50_ffa() && !machine_is_msm7x27_ffa())
195 return -ENOTSUPP;
196
197 client = msm_rpc_register_client("pmapp_usb",
198 PMAPP_RPC_PROG,
199 PMAPP_RPC_VER_2_1, 1,
200 pm_app_usb_cb_func);
201 if (!IS_ERR(client)) {
202 rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_2_1);
203 goto done;
204 }
205
206 client = msm_rpc_register_client("pmapp_usb",
207 PMAPP_RPC_PROG,
208 PMAPP_RPC_VER_1_2, 1,
209 pm_app_usb_cb_func);
210 if (!IS_ERR(client)) {
211 rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_2);
212 goto done;
213 }
214
215 client = msm_rpc_register_client("pmapp_usb",
216 PMAPP_RPC_PROG,
217 PMAPP_RPC_VER_1_1, 1,
218 pm_app_usb_cb_func);
219 if (!IS_ERR(client))
220 rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_1);
221 else
222 return PTR_ERR(client);
223
224done:
225 cb_id = msm_rpc_add_cb_func(client, (void *)callback);
226 /* In case of NULL callback funtion, cb_id would be -1 */
227 if ((int) cb_id < -1)
228 return cb_id;
229 rc = msm_rpc_client_req(client,
230 rpc_ids.reg_for_vbus_valid,
231 vbus_sess_valid_arg_cb,
232 &cb_id, NULL, NULL, -1);
233 return rc;
234}
235EXPORT_SYMBOL(msm_pm_app_rpc_init);
236
237void msm_pm_app_rpc_deinit(void(*callback)(int online))
238{
239 if (client) {
240 msm_rpc_remove_cb_func(client, (void *)callback);
241 msm_rpc_unregister_client(client);
242 }
243}
244EXPORT_SYMBOL(msm_pm_app_rpc_deinit);
245
246/* error bit flags defined by modem side */
247#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001)
248#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002)
249#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004)
250#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008)
251#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010)
252
253#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) /* all 5 previous */
254
255#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080)
256#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100)
257
258#define PMAPP_BUFF_SIZE 256
259
260struct pmapp_buf {
261 char *start; /* buffer start addr */
262 char *end; /* buffer end addr */
263 int size; /* buffer size */
264 char *data; /* payload begin addr */
265 int len; /* payload len */
266};
267
268static DEFINE_MUTEX(pmapp_mtx);
269
270struct pmapp_ctrl {
271 int inited;
272 struct pmapp_buf tbuf;
273 struct pmapp_buf rbuf;
274 struct msm_rpc_endpoint *endpoint;
275};
276
277static struct pmapp_ctrl pmapp_ctrl = {
278 .inited = -1,
279};
280
281
282static int pmapp_rpc_set_only(uint data0, uint data1, uint data2,
283 uint data3, int num, int proc);
284
285static int pmapp_buf_init(void)
286{
287 struct pmapp_ctrl *pm = &pmapp_ctrl;
288
289 memset(&pmapp_ctrl, 0, sizeof(pmapp_ctrl));
290
291 pm->tbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL);
292 if (pm->tbuf.start == NULL) {
293 printk(KERN_ERR "%s:%u\n", __func__, __LINE__);
294 return -ENOMEM;
295 }
296
297 pm->tbuf.data = pm->tbuf.start;
298 pm->tbuf.size = PMAPP_BUFF_SIZE;
299 pm->tbuf.end = pm->tbuf.start + PMAPP_BUFF_SIZE;
300 pm->tbuf.len = 0;
301
302 pm->rbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL);
303 if (pm->rbuf.start == NULL) {
304 kfree(pm->tbuf.start);
305 printk(KERN_ERR "%s:%u\n", __func__, __LINE__);
306 return -ENOMEM;
307 }
308 pm->rbuf.data = pm->rbuf.start;
309 pm->rbuf.size = PMAPP_BUFF_SIZE;
310 pm->rbuf.end = pm->rbuf.start + PMAPP_BUFF_SIZE;
311 pm->rbuf.len = 0;
312
313 pm->inited = 1;
314
315 return 0;
316}
317
318static inline void pmapp_buf_reserve(struct pmapp_buf *bp, int len)
319{
320 bp->data += len;
321}
322
323static inline void pmapp_buf_reset(struct pmapp_buf *bp)
324{
325 bp->data = bp->start;
326 bp->len = 0;
327}
328
329static int modem_to_linux_err(uint err)
330{
331 if (err == 0)
332 return 0;
333
334 if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE)
335 return -EINVAL; /* PM_ERR_FLAG__PAR[1..5]_OUT_OF_RANGE */
336
337 if (err & PM_ERR_FLAG__SBI_OPT_ERR)
338 return -EIO;
339
340 if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED)
341 return -ENOSYS;
342
343 return -EPERM;
344}
345
346static int pmapp_put_tx_data(struct pmapp_buf *tp, uint datav)
347{
348 uint *lp;
349
350 if ((tp->size - tp->len) < sizeof(datav)) {
351 printk(KERN_ERR "%s: OVERFLOW size=%d len=%d\n",
352 __func__, tp->size, tp->len);
353 return -1;
354 }
355
356 lp = (uint *)tp->data;
357 *lp = cpu_to_be32(datav);
358 tp->data += sizeof(datav);
359 tp->len += sizeof(datav);
360
361 return sizeof(datav);
362}
363
364static int pmapp_pull_rx_data(struct pmapp_buf *rp, uint *datap)
365{
366 uint *lp;
367
368 if (rp->len < sizeof(*datap)) {
369 printk(KERN_ERR "%s: UNDERRUN len=%d\n", __func__, rp->len);
370 return -1;
371 }
372 lp = (uint *)rp->data;
373 *datap = be32_to_cpu(*lp);
374 rp->data += sizeof(*datap);
375 rp->len -= sizeof(*datap);
376
377 return sizeof(*datap);
378}
379
380
381static int pmapp_rpc_req_reply(struct pmapp_buf *tbuf, struct pmapp_buf *rbuf,
382 int proc)
383{
384 struct pmapp_ctrl *pm = &pmapp_ctrl;
385 int ans, len, i;
386
387
388 if ((pm->endpoint == NULL) || IS_ERR(pm->endpoint)) {
389 for (i = 0; i < ARRAY_SIZE(rpc_vers); i++) {
390 pm->endpoint = msm_rpc_connect_compatible(
391 PMAPP_RPC_PROG, rpc_vers[i], 0);
392
393 if (IS_ERR(pm->endpoint)) {
394 ans = PTR_ERR(pm->endpoint);
395 printk(KERN_ERR "%s: init rpc failed! ans = %d"
396 " for 0x%x version, fallback\n",
397 __func__, ans, rpc_vers[i]);
398 } else {
399 printk(KERN_DEBUG "%s: successfully connected"
400 " to 0x%x rpc version\n",
401 __func__, rpc_vers[i]);
402 break;
403 }
404 }
405 }
406
407 if (IS_ERR(pm->endpoint)) {
408 ans = PTR_ERR(pm->endpoint);
409 return ans;
410 }
411
412 /*
413 * data is point to next available space at this moment,
414 * move it back to beginning of request header and increase
415 * the length
416 */
417 tbuf->data = tbuf->start;
418 tbuf->len += sizeof(struct rpc_request_hdr);
419
420 len = msm_rpc_call_reply(pm->endpoint, proc,
421 tbuf->data, tbuf->len,
422 rbuf->data, rbuf->size,
423 PMAPP_RPC_TIMEOUT);
424
425 if (len <= 0) {
426 printk(KERN_ERR "%s: rpc failed! len = %d\n", __func__, len);
427 pm->endpoint = NULL; /* re-connect later ? */
428 return len;
429 }
430
431 rbuf->len = len;
432 /* strip off rpc_reply_hdr */
433 rbuf->data += sizeof(struct rpc_reply_hdr);
434 rbuf->len -= sizeof(struct rpc_reply_hdr);
435
436 return rbuf->len;
437}
438
439static int pmapp_rpc_set_only(uint data0, uint data1, uint data2, uint data3,
440 int num, int proc)
441{
442 struct pmapp_ctrl *pm = &pmapp_ctrl;
443 struct pmapp_buf *tp;
444 struct pmapp_buf *rp;
445 int stat;
446
447
448 if (mutex_lock_interruptible(&pmapp_mtx))
449 return -ERESTARTSYS;
450
451 if (pm->inited <= 0) {
452 stat = pmapp_buf_init();
453 if (stat < 0) {
454 mutex_unlock(&pmapp_mtx);
455 return stat;
456 }
457 }
458
459 tp = &pm->tbuf;
460 rp = &pm->rbuf;
461
462 pmapp_buf_reset(tp);
463 pmapp_buf_reserve(tp, sizeof(struct rpc_request_hdr));
464 pmapp_buf_reset(rp);
465
466 if (num > 0)
467 pmapp_put_tx_data(tp, data0);
468
469 if (num > 1)
470 pmapp_put_tx_data(tp, data1);
471
472 if (num > 2)
473 pmapp_put_tx_data(tp, data2);
474
475 if (num > 3)
476 pmapp_put_tx_data(tp, data3);
477
478 stat = pmapp_rpc_req_reply(tp, rp, proc);
479 if (stat < 0) {
480 mutex_unlock(&pmapp_mtx);
481 return stat;
482 }
483
484 pmapp_pull_rx_data(rp, &stat); /* result from server */
485
486 mutex_unlock(&pmapp_mtx);
487
488 return modem_to_linux_err(stat);
489}
490
491int pmapp_display_clock_config(uint enable)
492{
493 return pmapp_rpc_set_only(enable, 0, 0, 0, 1,
494 PMAPP_DISPLAY_CLOCK_CONFIG_PROC);
495}
496EXPORT_SYMBOL(pmapp_display_clock_config);
497
498int pmapp_clock_vote(const char *voter_id, uint clock_id, uint vote)
499{
500 if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
501 return -EINVAL;
502
503 return pmapp_rpc_set_only(*((uint *) voter_id), clock_id, vote, 0, 3,
504 PMAPP_CLOCK_VOTE_PROC);
505}
506EXPORT_SYMBOL(pmapp_clock_vote);
507
508int pmapp_smps_clock_vote(const char *voter_id, uint vreg_id, uint vote)
509{
510 if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
511 return -EINVAL;
512
513 return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, vote, 0, 3,
514 PMAPP_SMPS_CLOCK_VOTE_PROC);
515}
516EXPORT_SYMBOL(pmapp_smps_clock_vote);
517
518int pmapp_vreg_level_vote(const char *voter_id, uint vreg_id, uint level)
519{
520 if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
521 return -EINVAL;
522
523 return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, level, 0, 3,
524 PMAPP_VREG_LEVEL_VOTE_PROC);
525}
526EXPORT_SYMBOL(pmapp_vreg_level_vote);
527
528int pmapp_smps_mode_vote(const char *voter_id, uint vreg_id, uint mode)
529{
530 if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
531 return -EINVAL;
532
533 return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, mode, 0, 3,
534 PMAPP_SMPS_MODE_VOTE_PROC);
535}
536EXPORT_SYMBOL(pmapp_smps_mode_vote);
537
538int pmapp_vreg_pincntrl_vote(const char *voter_id, uint vreg_id,
539 uint clock_id, uint vote)
540{
541 if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
542 return -EINVAL;
543
544 return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id,
545 vote, 4,
546 PMAPP_VREG_PINCNTRL_VOTE_PROC);
547}
548EXPORT_SYMBOL(pmapp_vreg_pincntrl_vote);
549
550int pmapp_disp_backlight_set_brightness(int value)
551{
Padmanabhan Komanduruc35fdc72012-05-25 19:15:45 +0530552 if (value < 0 || value > 255)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553 return -EINVAL;
554
555 return pmapp_rpc_set_only(value, 0, 0, 0, 1,
556 PMAPP_DISP_BACKLIGHT_SET_PROC);
557}
558EXPORT_SYMBOL(pmapp_disp_backlight_set_brightness);
559
560void pmapp_disp_backlight_init(void)
561{
562 pmapp_rpc_set_only(0, 0, 0, 0, 0, PMAPP_DISP_BACKLIGHT_INIT_PROC);
563}
564EXPORT_SYMBOL(pmapp_disp_backlight_init);
Trilok Sonif4f0f062011-08-29 00:49:41 +0530565
566int pmapp_vreg_lpm_pincntrl_vote(const char *voter_id, uint vreg_id,
567 uint clock_id, uint vote)
568{
569 if (strnlen(voter_id, PMAPP_CLOCK_VOTER_ID_LEN)
570 != PMAPP_CLOCK_VOTER_ID_LEN)
571 return -EINVAL;
572
573 return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id,
574 vote, 4,
575 PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC);
576}
577EXPORT_SYMBOL(pmapp_vreg_lpm_pincntrl_vote);