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