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