578 lines
14 KiB
C
578 lines
14 KiB
C
/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/module.h>
|
|
#include <asm/mach-types.h>
|
|
#include <mach/board.h>
|
|
#include <mach/rpc_pmapp.h>
|
|
#include <mach/msm_rpcrouter.h>
|
|
#include <mach/vreg.h>
|
|
|
|
#define PMAPP_RPC_PROG 0x30000060
|
|
#define PMAPP_RPC_VER_1_1 0x00010001
|
|
#define PMAPP_RPC_VER_1_2 0x00010002
|
|
#define PMAPP_RPC_VER_2_1 0x00020001
|
|
#define PMAPP_RPC_VER_3_1 0x00030001
|
|
#define PMAPP_RPC_VER_5_1 0x00050001
|
|
#define PMAPP_RPC_VER_6_1 0x00060001
|
|
#define PMAPP_RPC_VER_7_1 0x00070001
|
|
|
|
#define VBUS_SESS_VALID_CB_PROC 1
|
|
#define PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB (1 << 2)
|
|
#define PM_USB_PWR_SEL_SWITCH_ID 0
|
|
|
|
#define PMAPP_RPC_TIMEOUT (5*HZ)
|
|
|
|
#define PMAPP_DISPLAY_CLOCK_CONFIG_PROC 21
|
|
#define PMAPP_VREG_LEVEL_VOTE_PROC 23
|
|
#define PMAPP_SMPS_CLOCK_VOTE_PROC 26
|
|
#define PMAPP_CLOCK_VOTE_PROC 27
|
|
#define PMAPP_SMPS_MODE_VOTE_PROC 28
|
|
#define PMAPP_VREG_PINCNTRL_VOTE_PROC 30
|
|
#define PMAPP_DISP_BACKLIGHT_SET_PROC 31
|
|
#define PMAPP_DISP_BACKLIGHT_INIT_PROC 32
|
|
#define PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC 34
|
|
|
|
/* Clock voter name max length */
|
|
#define PMAPP_CLOCK_VOTER_ID_LEN 4
|
|
|
|
struct rpc_pmapp_ids {
|
|
unsigned long reg_for_vbus_valid;
|
|
unsigned long vote_for_vbus_valid_switch;
|
|
};
|
|
|
|
static struct rpc_pmapp_ids rpc_ids;
|
|
static struct msm_rpc_client *client;
|
|
|
|
/* Add newer versions at the top of array */
|
|
static const unsigned int rpc_vers[] = {
|
|
PMAPP_RPC_VER_7_1,
|
|
PMAPP_RPC_VER_6_1,
|
|
PMAPP_RPC_VER_5_1,
|
|
PMAPP_RPC_VER_3_1,
|
|
PMAPP_RPC_VER_2_1,
|
|
};
|
|
|
|
static void rpc_pmapp_init_rpc_ids(unsigned long vers)
|
|
{
|
|
if (vers == PMAPP_RPC_VER_1_1) {
|
|
rpc_ids.reg_for_vbus_valid = 5;
|
|
rpc_ids.vote_for_vbus_valid_switch = 6;
|
|
} else if (vers == PMAPP_RPC_VER_1_2) {
|
|
rpc_ids.reg_for_vbus_valid = 16;
|
|
rpc_ids.vote_for_vbus_valid_switch = 17;
|
|
} else if (vers == PMAPP_RPC_VER_2_1) {
|
|
rpc_ids.reg_for_vbus_valid = 0; /* NA */
|
|
rpc_ids.vote_for_vbus_valid_switch = 0; /* NA */
|
|
}
|
|
}
|
|
|
|
struct usb_pwr_sel_switch_args {
|
|
uint32_t cmd;
|
|
uint32_t switch_id;
|
|
uint32_t app_mask;
|
|
};
|
|
|
|
static int usb_pwr_sel_switch_arg_cb(struct msm_rpc_client *client,
|
|
void *buf, void *data)
|
|
{
|
|
struct usb_pwr_sel_switch_args *args = buf;
|
|
|
|
args->cmd = cpu_to_be32(*(uint32_t *)data);
|
|
args->switch_id = cpu_to_be32(PM_USB_PWR_SEL_SWITCH_ID);
|
|
args->app_mask = cpu_to_be32(PM_VOTE_USB_PWR_SEL_SWITCH_APP__HSUSB);
|
|
return sizeof(struct usb_pwr_sel_switch_args);
|
|
}
|
|
|
|
static int msm_pm_app_vote_usb_pwr_sel_switch(uint32_t cmd)
|
|
{
|
|
return msm_rpc_client_req(client,
|
|
rpc_ids.vote_for_vbus_valid_switch,
|
|
usb_pwr_sel_switch_arg_cb,
|
|
&cmd, NULL, NULL, -1);
|
|
}
|
|
|
|
struct vbus_sess_valid_args {
|
|
uint32_t cb_id;
|
|
};
|
|
|
|
static int vbus_sess_valid_arg_cb(struct msm_rpc_client *client,
|
|
void *buf, void *data)
|
|
{
|
|
struct vbus_sess_valid_args *args = buf;
|
|
|
|
args->cb_id = cpu_to_be32(*(uint32_t *)data);
|
|
return sizeof(struct vbus_sess_valid_args);
|
|
}
|
|
|
|
|
|
int pmic_vote_3p3_pwr_sel_switch(int boost)
|
|
{
|
|
int ret;
|
|
|
|
ret = msm_pm_app_vote_usb_pwr_sel_switch(boost);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(pmic_vote_3p3_pwr_sel_switch);
|
|
|
|
struct vbus_sn_notification_args {
|
|
uint32_t cb_id;
|
|
uint32_t vbus; /* vbus = 0 if VBUS is present */
|
|
};
|
|
|
|
static int vbus_notification_cb(struct msm_rpc_client *client,
|
|
void *buffer, int in_size)
|
|
{
|
|
struct vbus_sn_notification_args *args;
|
|
struct rpc_request_hdr *req = buffer;
|
|
int rc;
|
|
uint32_t accept_status;
|
|
void (*cb_func)(int);
|
|
uint32_t cb_id;
|
|
int vbus;
|
|
|
|
args = (struct vbus_sn_notification_args *) (req + 1);
|
|
cb_id = be32_to_cpu(args->cb_id);
|
|
vbus = be32_to_cpu(args->vbus);
|
|
|
|
cb_func = msm_rpc_get_cb_func(client, cb_id);
|
|
if (cb_func) {
|
|
cb_func(!vbus);
|
|
accept_status = RPC_ACCEPTSTAT_SUCCESS;
|
|
} else
|
|
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
|
|
|
|
msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
|
|
accept_status);
|
|
rc = msm_rpc_send_accepted_reply(client, 0);
|
|
if (rc)
|
|
pr_err("%s: send accepted reply failed: %d\n", __func__, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pm_app_usb_cb_func(struct msm_rpc_client *client,
|
|
void *buffer, int in_size)
|
|
{
|
|
int rc;
|
|
struct rpc_request_hdr *req = buffer;
|
|
|
|
switch (be32_to_cpu(req->procedure)) {
|
|
case VBUS_SESS_VALID_CB_PROC:
|
|
rc = vbus_notification_cb(client, buffer, in_size);
|
|
break;
|
|
default:
|
|
pr_err("%s: procedure not supported %d\n", __func__,
|
|
be32_to_cpu(req->procedure));
|
|
msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
|
|
RPC_ACCEPTSTAT_PROC_UNAVAIL);
|
|
rc = msm_rpc_send_accepted_reply(client, 0);
|
|
if (rc)
|
|
pr_err("%s: sending reply failed: %d\n", __func__, rc);
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int msm_pm_app_rpc_init(void (*callback)(int online))
|
|
{
|
|
uint32_t cb_id, rc;
|
|
|
|
if (!machine_is_qsd8x50_ffa() && !machine_is_msm7x27_ffa())
|
|
return -ENOTSUPP;
|
|
|
|
client = msm_rpc_register_client("pmapp_usb",
|
|
PMAPP_RPC_PROG,
|
|
PMAPP_RPC_VER_2_1, 1,
|
|
pm_app_usb_cb_func);
|
|
if (!IS_ERR(client)) {
|
|
rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_2_1);
|
|
goto done;
|
|
}
|
|
|
|
client = msm_rpc_register_client("pmapp_usb",
|
|
PMAPP_RPC_PROG,
|
|
PMAPP_RPC_VER_1_2, 1,
|
|
pm_app_usb_cb_func);
|
|
if (!IS_ERR(client)) {
|
|
rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_2);
|
|
goto done;
|
|
}
|
|
|
|
client = msm_rpc_register_client("pmapp_usb",
|
|
PMAPP_RPC_PROG,
|
|
PMAPP_RPC_VER_1_1, 1,
|
|
pm_app_usb_cb_func);
|
|
if (!IS_ERR(client))
|
|
rpc_pmapp_init_rpc_ids(PMAPP_RPC_VER_1_1);
|
|
else
|
|
return PTR_ERR(client);
|
|
|
|
done:
|
|
cb_id = msm_rpc_add_cb_func(client, (void *)callback);
|
|
/* In case of NULL callback funtion, cb_id would be -1 */
|
|
if ((int) cb_id < -1)
|
|
return cb_id;
|
|
rc = msm_rpc_client_req(client,
|
|
rpc_ids.reg_for_vbus_valid,
|
|
vbus_sess_valid_arg_cb,
|
|
&cb_id, NULL, NULL, -1);
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(msm_pm_app_rpc_init);
|
|
|
|
void msm_pm_app_rpc_deinit(void(*callback)(int online))
|
|
{
|
|
if (client) {
|
|
msm_rpc_remove_cb_func(client, (void *)callback);
|
|
msm_rpc_unregister_client(client);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(msm_pm_app_rpc_deinit);
|
|
|
|
/* error bit flags defined by modem side */
|
|
#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001)
|
|
#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002)
|
|
#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004)
|
|
#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008)
|
|
#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010)
|
|
|
|
#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) /* all 5 previous */
|
|
|
|
#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080)
|
|
#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100)
|
|
|
|
#define PMAPP_BUFF_SIZE 256
|
|
|
|
struct pmapp_buf {
|
|
char *start; /* buffer start addr */
|
|
char *end; /* buffer end addr */
|
|
int size; /* buffer size */
|
|
char *data; /* payload begin addr */
|
|
int len; /* payload len */
|
|
};
|
|
|
|
static DEFINE_MUTEX(pmapp_mtx);
|
|
|
|
struct pmapp_ctrl {
|
|
int inited;
|
|
struct pmapp_buf tbuf;
|
|
struct pmapp_buf rbuf;
|
|
struct msm_rpc_endpoint *endpoint;
|
|
};
|
|
|
|
static struct pmapp_ctrl pmapp_ctrl = {
|
|
.inited = -1,
|
|
};
|
|
|
|
|
|
static int pmapp_rpc_set_only(uint data0, uint data1, uint data2,
|
|
uint data3, int num, int proc);
|
|
|
|
static int pmapp_buf_init(void)
|
|
{
|
|
struct pmapp_ctrl *pm = &pmapp_ctrl;
|
|
|
|
memset(&pmapp_ctrl, 0, sizeof(pmapp_ctrl));
|
|
|
|
pm->tbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL);
|
|
if (pm->tbuf.start == NULL) {
|
|
printk(KERN_ERR "%s:%u\n", __func__, __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pm->tbuf.data = pm->tbuf.start;
|
|
pm->tbuf.size = PMAPP_BUFF_SIZE;
|
|
pm->tbuf.end = pm->tbuf.start + PMAPP_BUFF_SIZE;
|
|
pm->tbuf.len = 0;
|
|
|
|
pm->rbuf.start = kmalloc(PMAPP_BUFF_SIZE, GFP_KERNEL);
|
|
if (pm->rbuf.start == NULL) {
|
|
kfree(pm->tbuf.start);
|
|
printk(KERN_ERR "%s:%u\n", __func__, __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
pm->rbuf.data = pm->rbuf.start;
|
|
pm->rbuf.size = PMAPP_BUFF_SIZE;
|
|
pm->rbuf.end = pm->rbuf.start + PMAPP_BUFF_SIZE;
|
|
pm->rbuf.len = 0;
|
|
|
|
pm->inited = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void pmapp_buf_reserve(struct pmapp_buf *bp, int len)
|
|
{
|
|
bp->data += len;
|
|
}
|
|
|
|
static inline void pmapp_buf_reset(struct pmapp_buf *bp)
|
|
{
|
|
bp->data = bp->start;
|
|
bp->len = 0;
|
|
}
|
|
|
|
static int modem_to_linux_err(uint err)
|
|
{
|
|
if (err == 0)
|
|
return 0;
|
|
|
|
if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE)
|
|
return -EINVAL; /* PM_ERR_FLAG__PAR[1..5]_OUT_OF_RANGE */
|
|
|
|
if (err & PM_ERR_FLAG__SBI_OPT_ERR)
|
|
return -EIO;
|
|
|
|
if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED)
|
|
return -ENOSYS;
|
|
|
|
return -EPERM;
|
|
}
|
|
|
|
static int pmapp_put_tx_data(struct pmapp_buf *tp, uint datav)
|
|
{
|
|
uint *lp;
|
|
|
|
if ((tp->size - tp->len) < sizeof(datav)) {
|
|
printk(KERN_ERR "%s: OVERFLOW size=%d len=%d\n",
|
|
__func__, tp->size, tp->len);
|
|
return -1;
|
|
}
|
|
|
|
lp = (uint *)tp->data;
|
|
*lp = cpu_to_be32(datav);
|
|
tp->data += sizeof(datav);
|
|
tp->len += sizeof(datav);
|
|
|
|
return sizeof(datav);
|
|
}
|
|
|
|
static int pmapp_pull_rx_data(struct pmapp_buf *rp, uint *datap)
|
|
{
|
|
uint *lp;
|
|
|
|
if (rp->len < sizeof(*datap)) {
|
|
printk(KERN_ERR "%s: UNDERRUN len=%d\n", __func__, rp->len);
|
|
return -1;
|
|
}
|
|
lp = (uint *)rp->data;
|
|
*datap = be32_to_cpu(*lp);
|
|
rp->data += sizeof(*datap);
|
|
rp->len -= sizeof(*datap);
|
|
|
|
return sizeof(*datap);
|
|
}
|
|
|
|
|
|
static int pmapp_rpc_req_reply(struct pmapp_buf *tbuf, struct pmapp_buf *rbuf,
|
|
int proc)
|
|
{
|
|
struct pmapp_ctrl *pm = &pmapp_ctrl;
|
|
int ans, len, i;
|
|
|
|
|
|
if ((pm->endpoint == NULL) || IS_ERR(pm->endpoint)) {
|
|
for (i = 0; i < ARRAY_SIZE(rpc_vers); i++) {
|
|
pm->endpoint = msm_rpc_connect_compatible(
|
|
PMAPP_RPC_PROG, rpc_vers[i], 0);
|
|
|
|
if (IS_ERR(pm->endpoint)) {
|
|
ans = PTR_ERR(pm->endpoint);
|
|
printk(KERN_ERR "%s: init rpc failed! ans = %d"
|
|
" for 0x%x version, fallback\n",
|
|
__func__, ans, rpc_vers[i]);
|
|
} else {
|
|
printk(KERN_DEBUG "%s: successfully connected"
|
|
" to 0x%x rpc version\n",
|
|
__func__, rpc_vers[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IS_ERR(pm->endpoint)) {
|
|
ans = PTR_ERR(pm->endpoint);
|
|
return ans;
|
|
}
|
|
|
|
/*
|
|
* data is point to next available space at this moment,
|
|
* move it back to beginning of request header and increase
|
|
* the length
|
|
*/
|
|
tbuf->data = tbuf->start;
|
|
tbuf->len += sizeof(struct rpc_request_hdr);
|
|
|
|
len = msm_rpc_call_reply(pm->endpoint, proc,
|
|
tbuf->data, tbuf->len,
|
|
rbuf->data, rbuf->size,
|
|
PMAPP_RPC_TIMEOUT);
|
|
|
|
if (len <= 0) {
|
|
printk(KERN_ERR "%s: rpc failed! len = %d\n", __func__, len);
|
|
pm->endpoint = NULL; /* re-connect later ? */
|
|
return len;
|
|
}
|
|
|
|
rbuf->len = len;
|
|
/* strip off rpc_reply_hdr */
|
|
rbuf->data += sizeof(struct rpc_reply_hdr);
|
|
rbuf->len -= sizeof(struct rpc_reply_hdr);
|
|
|
|
return rbuf->len;
|
|
}
|
|
|
|
static int pmapp_rpc_set_only(uint data0, uint data1, uint data2, uint data3,
|
|
int num, int proc)
|
|
{
|
|
struct pmapp_ctrl *pm = &pmapp_ctrl;
|
|
struct pmapp_buf *tp;
|
|
struct pmapp_buf *rp;
|
|
int stat;
|
|
|
|
|
|
if (mutex_lock_interruptible(&pmapp_mtx))
|
|
return -ERESTARTSYS;
|
|
|
|
if (pm->inited <= 0) {
|
|
stat = pmapp_buf_init();
|
|
if (stat < 0) {
|
|
mutex_unlock(&pmapp_mtx);
|
|
return stat;
|
|
}
|
|
}
|
|
|
|
tp = &pm->tbuf;
|
|
rp = &pm->rbuf;
|
|
|
|
pmapp_buf_reset(tp);
|
|
pmapp_buf_reserve(tp, sizeof(struct rpc_request_hdr));
|
|
pmapp_buf_reset(rp);
|
|
|
|
if (num > 0)
|
|
pmapp_put_tx_data(tp, data0);
|
|
|
|
if (num > 1)
|
|
pmapp_put_tx_data(tp, data1);
|
|
|
|
if (num > 2)
|
|
pmapp_put_tx_data(tp, data2);
|
|
|
|
if (num > 3)
|
|
pmapp_put_tx_data(tp, data3);
|
|
|
|
stat = pmapp_rpc_req_reply(tp, rp, proc);
|
|
if (stat < 0) {
|
|
mutex_unlock(&pmapp_mtx);
|
|
return stat;
|
|
}
|
|
|
|
pmapp_pull_rx_data(rp, &stat); /* result from server */
|
|
|
|
mutex_unlock(&pmapp_mtx);
|
|
|
|
return modem_to_linux_err(stat);
|
|
}
|
|
|
|
int pmapp_display_clock_config(uint enable)
|
|
{
|
|
return pmapp_rpc_set_only(enable, 0, 0, 0, 1,
|
|
PMAPP_DISPLAY_CLOCK_CONFIG_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_display_clock_config);
|
|
|
|
int pmapp_clock_vote(const char *voter_id, uint clock_id, uint vote)
|
|
{
|
|
if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), clock_id, vote, 0, 3,
|
|
PMAPP_CLOCK_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_clock_vote);
|
|
|
|
int pmapp_smps_clock_vote(const char *voter_id, uint vreg_id, uint vote)
|
|
{
|
|
if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, vote, 0, 3,
|
|
PMAPP_SMPS_CLOCK_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_smps_clock_vote);
|
|
|
|
int pmapp_vreg_level_vote(const char *voter_id, uint vreg_id, uint level)
|
|
{
|
|
if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, level, 0, 3,
|
|
PMAPP_VREG_LEVEL_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_vreg_level_vote);
|
|
|
|
int pmapp_smps_mode_vote(const char *voter_id, uint vreg_id, uint mode)
|
|
{
|
|
if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, mode, 0, 3,
|
|
PMAPP_SMPS_MODE_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_smps_mode_vote);
|
|
|
|
int pmapp_vreg_pincntrl_vote(const char *voter_id, uint vreg_id,
|
|
uint clock_id, uint vote)
|
|
{
|
|
if (strlen(voter_id) != PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id,
|
|
vote, 4,
|
|
PMAPP_VREG_PINCNTRL_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_vreg_pincntrl_vote);
|
|
|
|
int pmapp_disp_backlight_set_brightness(int value)
|
|
{
|
|
if (value < 0 || value > 255)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(value, 0, 0, 0, 1,
|
|
PMAPP_DISP_BACKLIGHT_SET_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_disp_backlight_set_brightness);
|
|
|
|
void pmapp_disp_backlight_init(void)
|
|
{
|
|
pmapp_rpc_set_only(0, 0, 0, 0, 0, PMAPP_DISP_BACKLIGHT_INIT_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_disp_backlight_init);
|
|
|
|
int pmapp_vreg_lpm_pincntrl_vote(const char *voter_id, uint vreg_id,
|
|
uint clock_id, uint vote)
|
|
{
|
|
if (strnlen(voter_id, PMAPP_CLOCK_VOTER_ID_LEN)
|
|
!= PMAPP_CLOCK_VOTER_ID_LEN)
|
|
return -EINVAL;
|
|
|
|
return pmapp_rpc_set_only(*((uint *) voter_id), vreg_id, clock_id,
|
|
vote, 4,
|
|
PMAPP_VREG_LPM_PINCNTRL_VOTE_PROC);
|
|
}
|
|
EXPORT_SYMBOL(pmapp_vreg_lpm_pincntrl_vote);
|