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);