summaryrefslogtreecommitdiff
path: root/arch/arm/mach-msm/rpc_hsusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-msm/rpc_hsusb.c')
-rw-r--r--arch/arm/mach-msm/rpc_hsusb.c574
1 files changed, 574 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/rpc_hsusb.c b/arch/arm/mach-msm/rpc_hsusb.c
new file mode 100644
index 000000000000..22dee1c3322b
--- /dev/null
+++ b/arch/arm/mach-msm/rpc_hsusb.c
@@ -0,0 +1,574 @@
+/* linux/arch/arm/mach-msm/rpc_hsusb.c
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/err.h>
+#include <mach/rpc_hsusb.h>
+#include <asm/mach-types.h>
+
+static struct msm_rpc_endpoint *usb_ep;
+static struct msm_rpc_endpoint *chg_ep;
+
+struct msm_chg_rpc_ids {
+ unsigned long prog;
+ unsigned long vers_comp;
+ unsigned chg_usb_charger_connected_proc;
+ unsigned chg_usb_charger_disconnected_proc;
+ unsigned chg_usb_i_is_available_proc;
+ unsigned chg_usb_i_is_not_available_proc;
+};
+
+struct msm_hsusb_rpc_ids {
+ unsigned long prog;
+ unsigned long vers_comp;
+ unsigned long init_phy;
+ unsigned long vbus_pwr_up;
+ unsigned long vbus_pwr_down;
+ unsigned long update_product_id;
+ unsigned long update_serial_num;
+ unsigned long update_is_serial_num_null;
+ unsigned long reset_rework_installed;
+ unsigned long enable_pmic_ulpi_data0;
+ unsigned long disable_pmic_ulpi_data0;
+};
+
+static struct msm_hsusb_rpc_ids usb_rpc_ids;
+static struct msm_chg_rpc_ids chg_rpc_ids;
+
+static int msm_hsusb_init_rpc_ids(unsigned long vers)
+{
+ if (vers == 0x00010001) {
+ usb_rpc_ids.prog = 0x30000064;
+ usb_rpc_ids.vers_comp = 0x00010001;
+ usb_rpc_ids.init_phy = 2;
+ usb_rpc_ids.vbus_pwr_up = 6;
+ usb_rpc_ids.vbus_pwr_down = 7;
+ usb_rpc_ids.update_product_id = 8;
+ usb_rpc_ids.update_serial_num = 9;
+ usb_rpc_ids.update_is_serial_num_null = 10;
+ usb_rpc_ids.reset_rework_installed = 17;
+ usb_rpc_ids.enable_pmic_ulpi_data0 = 18;
+ usb_rpc_ids.disable_pmic_ulpi_data0 = 19;
+ return 0;
+ } else if (vers == 0x00010002) {
+ usb_rpc_ids.prog = 0x30000064;
+ usb_rpc_ids.vers_comp = 0x00010002;
+ usb_rpc_ids.init_phy = 2;
+ usb_rpc_ids.vbus_pwr_up = 6;
+ usb_rpc_ids.vbus_pwr_down = 7;
+ usb_rpc_ids.update_product_id = 8;
+ usb_rpc_ids.update_serial_num = 9;
+ usb_rpc_ids.update_is_serial_num_null = 10;
+ usb_rpc_ids.reset_rework_installed = 17;
+ usb_rpc_ids.enable_pmic_ulpi_data0 = 18;
+ usb_rpc_ids.disable_pmic_ulpi_data0 = 19;
+ return 0;
+ } else {
+ printk(KERN_INFO "%s: no matches found for version\n",
+ __func__);
+ return -ENODATA;
+ }
+}
+
+static int msm_chg_init_rpc_ids(unsigned long vers)
+{
+ if (vers == 0x00010001) {
+ chg_rpc_ids.prog = 0x3000001a;
+ chg_rpc_ids.vers_comp = 0x00010001;
+ chg_rpc_ids.chg_usb_charger_connected_proc = 7;
+ chg_rpc_ids.chg_usb_charger_disconnected_proc = 8;
+ chg_rpc_ids.chg_usb_i_is_available_proc = 9;
+ chg_rpc_ids.chg_usb_i_is_not_available_proc = 10;
+ return 0;
+ } else {
+ printk(KERN_INFO "%s: no matches found for version\n",
+ __func__);
+ return -ENODATA;
+ }
+}
+EXPORT_SYMBOL(msm_chg_init_rpc_ids);
+
+/* rpc connect for hsusb */
+int msm_hsusb_rpc_connect(void)
+{
+
+ if (usb_ep && !IS_ERR(usb_ep)) {
+ printk(KERN_INFO "%s: usb_ep already connected\n", __func__);
+ return 0;
+ }
+
+ /* Initialize rpc ids */
+ if (msm_hsusb_init_rpc_ids(0x00010001)) {
+ printk(KERN_ERR "%s: rpc ids initialization failed\n"
+ , __func__);
+ return -ENODATA;
+ }
+
+ usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog,
+ usb_rpc_ids.vers_comp, 0);
+
+ if (IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: connect compatible failed vers = %lx\n",
+ __func__, usb_rpc_ids.vers_comp);
+
+ /* Initialize rpc ids */
+ if (msm_hsusb_init_rpc_ids(0x00010002)) {
+ printk(KERN_ERR "%s: rpc ids initialization failed\n",
+ __func__);
+ return -ENODATA;
+ }
+ usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog,
+ usb_rpc_ids.vers_comp, 0);
+ }
+
+ if (IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: connect compatible failed vers = %lx\n",
+ __func__, usb_rpc_ids.vers_comp);
+ return -EAGAIN;
+ } else
+ printk(KERN_INFO "%s: rpc connect success vers = %lx\n",
+ __func__, usb_rpc_ids.vers_comp);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_hsusb_rpc_connect);
+
+/* rpc connect for charging */
+int msm_chg_rpc_connect(void)
+{
+
+ if (machine_is_msm7201a_surf() || machine_is_msm7x27_surf() ||
+ machine_is_qsd8x50_surf())
+ return -ENOTSUPP;
+
+ if (chg_ep && !IS_ERR(chg_ep)) {
+ printk(KERN_INFO "%s: chg_ep already connected\n", __func__);
+ return 0;
+ }
+
+ /* Initialize rpc ids */
+ if (msm_chg_init_rpc_ids(0x00010001)) {
+ printk(KERN_ERR "%s: rpc ids initialization failed\n"
+ , __func__);
+ return -ENODATA;
+ }
+
+ chg_ep = msm_rpc_connect_compatible(chg_rpc_ids.prog,
+ chg_rpc_ids.vers_comp, 0);
+
+ if (IS_ERR(chg_ep)) {
+ printk(KERN_ERR "%s: connect compatible failed vers = %lx\n",
+ __func__, chg_rpc_ids.vers_comp);
+ return -EAGAIN;
+ } else
+ printk(KERN_INFO "%s: rpc connect success vers = %lx\n",
+ __func__, chg_rpc_ids.vers_comp);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_chg_rpc_connect);
+
+/* rpc call for phy_reset */
+int msm_hsusb_phy_reset(void)
+{
+ int rc = 0;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: phy_reset rpc failed before call,"
+ "rc = %ld\n", __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.init_phy,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: phy_reset rpc failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_hsusb_phy_reset\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_phy_reset);
+
+/* rpc call for vbus powerup */
+int msm_hsusb_vbus_powerup(void)
+{
+ int rc = 0;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: vbus_powerup rpc failed before call,"
+ "rc = %ld\n", __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_up,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: vbus_powerup failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_hsusb_vbus_powerup\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_vbus_powerup);
+
+/* rpc call for vbus shutdown */
+int msm_hsusb_vbus_shutdown(void)
+{
+ int rc = 0;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: vbus_shutdown rpc failed before call,"
+ "rc = %ld\n", __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_down,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: vbus_shutdown failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_hsusb_vbus_shutdown\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_vbus_shutdown);
+
+int msm_hsusb_send_productID(uint32_t product_id)
+{
+ int rc = 0;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t product_id;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ req.product_id = cpu_to_be32(product_id);
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_product_id,
+ &req, sizeof(req),
+ 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: rpc call failed! error: %d\n",
+ __func__, rc);
+ else
+ printk(KERN_ERR "%s: rpc call success\n" ,
+ __func__);
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_send_productID);
+
+int msm_hsusb_send_serial_number(char *serial_number)
+{
+ int rc = 0, serial_len;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t length;
+ char serial_num[20];
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ serial_len = strlen(serial_number)+1;
+ strncpy(req.serial_num, serial_number, 20);
+ req.length = cpu_to_be32(serial_len);
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_serial_num,
+ &req, sizeof(req),
+ 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: rpc call failed! error: %d\n",
+ __func__, rc);
+ else
+ printk(KERN_ERR "%s: rpc call success\n" ,
+ __func__);
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_send_serial_number);
+
+int msm_hsusb_is_serial_num_null(uint32_t val)
+{
+ int rc = 0;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t value;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+ if (!usb_rpc_ids.update_is_serial_num_null) {
+ printk(KERN_ERR "%s: proc id not supported \n", __func__);
+ return -ENODATA;
+ }
+
+ req.value = cpu_to_be32(val);
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_is_serial_num_null,
+ &req, sizeof(req),
+ 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: rpc call failed! error: %d\n" ,
+ __func__, rc);
+ else
+ printk(KERN_ERR "%s: rpc call success\n" ,
+ __func__);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_is_serial_num_null);
+
+int msm_chg_usb_charger_connected(uint32_t device)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t otg_dev;
+ } req;
+
+ if (!chg_ep || IS_ERR(chg_ep))
+ return -EAGAIN;
+ req.otg_dev = cpu_to_be32(device);
+ rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_connected_proc,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: charger_connected failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_chg_usb_charger_connected\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_chg_usb_charger_connected);
+
+int msm_chg_usb_i_is_available(uint32_t sample)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t i_ma;
+ } req;
+
+ if (!chg_ep || IS_ERR(chg_ep))
+ return -EAGAIN;
+ req.i_ma = cpu_to_be32(sample);
+ rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_available_proc,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: charger_i_available failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_chg_usb_i_is_available\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_chg_usb_i_is_available);
+
+int msm_chg_usb_i_is_not_available(void)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!chg_ep || IS_ERR(chg_ep))
+ return -EAGAIN;
+ rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_not_available_proc,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: charger_i_not_available failed! rc ="
+ "%d \n", __func__, rc);
+ } else
+ printk(KERN_INFO "msm_chg_usb_i_is_not_available\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_chg_usb_i_is_not_available);
+
+int msm_chg_usb_charger_disconnected(void)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!chg_ep || IS_ERR(chg_ep))
+ return -EAGAIN;
+ rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_disconnected_proc,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: charger_disconnected failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "msm_chg_usb_charger_disconnected\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_chg_usb_charger_disconnected);
+
+/* rpc call to close connection */
+int msm_hsusb_rpc_close(void)
+{
+ int rc = 0;
+
+ if (IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: rpc_close failed before call, rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_close(usb_ep);
+ usb_ep = NULL;
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
+ __func__, rc);
+ return -EAGAIN;
+ } else
+ printk(KERN_INFO "rpc close success\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_hsusb_rpc_close);
+
+/* rpc call to close charging connection */
+int msm_chg_rpc_close(void)
+{
+ int rc = 0;
+
+ if (IS_ERR(chg_ep)) {
+ printk(KERN_ERR "%s: rpc_close failed before call, rc = %ld\n",
+ __func__, PTR_ERR(chg_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_close(chg_ep);
+ chg_ep = NULL;
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
+ __func__, rc);
+ return -EAGAIN;
+ } else
+ printk(KERN_INFO "rpc close success\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_chg_rpc_close);
+
+int msm_hsusb_reset_rework_installed(void)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+ struct hsusb_rpc_rep {
+ struct rpc_reply_hdr hdr;
+ uint32_t rework;
+ } rep;
+
+ memset(&rep, 0, sizeof(rep));
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_call_reply(usb_ep, usb_rpc_ids.reset_rework_installed,
+ &req, sizeof(req),
+ &rep, sizeof(rep), 5 * HZ);
+
+ if (rc < 0) {
+ pr_err("%s: rpc call failed! error: (%d)"
+ "proc id: (%lx)\n",
+ __func__, rc,
+ usb_rpc_ids.reset_rework_installed);
+ return rc;
+ }
+
+ pr_info("%s: rework: (%d)\n", __func__, rep.rework);
+ return be32_to_cpu(rep.rework);
+}
+EXPORT_SYMBOL(msm_hsusb_reset_rework_installed);
+
+static int msm_hsusb_pmic_ulpidata0_config(int enable)
+{
+ int rc = 0;
+ struct hsusb_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ if (!usb_ep || IS_ERR(usb_ep)) {
+ pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ return -EAGAIN;
+ }
+
+ if (enable)
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.enable_pmic_ulpi_data0,
+ &req, sizeof(req), 5 * HZ);
+ else
+ rc = msm_rpc_call(usb_ep, usb_rpc_ids.disable_pmic_ulpi_data0,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0)
+ pr_err("%s: rpc call failed! error: %d\n",
+ __func__, rc);
+ return rc;
+}
+
+int msm_hsusb_enable_pmic_ulpidata0(void)
+{
+ return msm_hsusb_pmic_ulpidata0_config(1);
+}
+EXPORT_SYMBOL(msm_hsusb_enable_pmic_ulpidata0);
+
+int msm_hsusb_disable_pmic_ulpidata0(void)
+{
+ return msm_hsusb_pmic_ulpidata0_config(0);
+}
+EXPORT_SYMBOL(msm_hsusb_disable_pmic_ulpidata0);