Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/rpc_pmapp.c b/arch/arm/mach-msm/rpc_pmapp.c
new file mode 100644
index 0000000..3f80760
--- /dev/null
+++ b/arch/arm/mach-msm/rpc_pmapp.c
@@ -0,0 +1,560 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. 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 <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 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
+
+/* 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_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 > 100)
+ 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);