Merge changes I24bee29b,Ia543bb06,I50a8acf9 into msm-3.0
* changes:
n_smux: smux_ctl: Disable local loopback
n_smux: Enable SMUX driver for 8960 Fusion 4
msm: rmnet_smux: Add RMNET over SMUX
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 39b6c7d..eb28379 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -945,14 +945,6 @@
help
Say Y here if high speed MSM UART v1.4 is present.
-config DEBUG_MSM8930_UART
- bool "Kernel low-level debugging messages via MSM 8930 UART"
- depends on ARCH_MSM8930 && DEBUG_LL
- select MSM_HAS_DEBUG_UART_HS
- help
- Say Y here if you want the debug print routines to direct
- their output to the serial port on MSM 8930 devices.
-
config MSM_DEBUG_UART_PHYS
hex
default 0xA9A00000 if (ARCH_MSM7X27 || ARCH_QSD8X50) && DEBUG_MSM_UART1
@@ -999,13 +991,29 @@
config DEBUG_MSM8960_UART
bool "Kernel low-level debugging messages via MSM 8960 UART"
- depends on ARCH_MSM8960
+ depends on ARCH_MSM8960 && DEBUG_LL
select DEBUG_MSM8930_UART
select MSM_HAS_DEBUG_UART_HS
help
Say Y here if you want the debug print routines to direct
their output to the serial port on MSM 8960 devices.
+ config DEBUG_MSM8930_UART
+ bool "Kernel low-level debugging messages via MSM 8930 UART"
+ depends on ARCH_MSM8930 && DEBUG_LL
+ select MSM_HAS_DEBUG_UART_HS
+ help
+ Say Y here if you want the debug print routines to direct
+ their output to the serial port on MSM 8930 devices.
+
+ config DEBUG_APQ8064_UART
+ bool "Kernel low-level debugging messages via APQ 8064 UART"
+ depends on ARCH_APQ8064 && DEBUG_LL
+ select MSM_HAS_DEBUG_UART_HS
+ help
+ Say Y here if you want the debug print routines to direct
+ their output to the serial port on APQ 8064 devices.
+
config DEBUG_MSMCOPPER_UART
bool "Kernel low-level debugging messages via MSM Copper UART"
depends on ARCH_MSMCOPPER
diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h
index 78ca88f..997b3be 100644
--- a/arch/arm/mach-msm/include/mach/mdm2.h
+++ b/arch/arm/mach-msm/include/mach/mdm2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -16,6 +16,9 @@
struct mdm_platform_data {
char *mdm_version;
int ramdump_delay_ms;
+ int soft_reset_inverted;
+ int early_power_on;
+ int sfr_query;
struct platform_device *peripheral_platform_device;
};
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8064.h b/arch/arm/mach-msm/include/mach/msm_iomap-8064.h
index 96bc35e..10e2b74 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8064.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8064.h
@@ -106,4 +106,9 @@
#define APQ8064_HDMI_PHYS 0x04A00000
#define APQ8064_HDMI_SIZE SZ_4K
+#ifdef CONFIG_DEBUG_APQ8064_UART
+#define MSM_DEBUG_UART_BASE IOMEM(0xFA740000)
+#define MSM_DEBUG_UART_PHYS 0x16640000
+#endif
+
#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 59d3a96..2a0d34a 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -281,6 +281,9 @@
},
MSM_CHIP_DEVICE(QFPROM, APQ8064),
MSM_CHIP_DEVICE(SIC_NON_SECURE, APQ8064),
+#ifdef CONFIG_DEBUG_APQ8064_UART
+ MSM_DEVICE(DEBUG_UART),
+#endif
};
void __init msm_map_apq8064_io(void)
diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c
index b4b7ea3..bd7bd9e 100644
--- a/arch/arm/mach-msm/mdm2.c
+++ b/arch/arm/mach-msm/mdm2.c
@@ -53,11 +53,13 @@
static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv)
{
+ if (!mdm_drv->pdata->peripheral_platform_device)
+ return;
+
mutex_lock(&hsic_status_lock);
if (hsic_peripheral_status)
goto out;
- if (mdm_drv->pdata->peripheral_platform_device)
- platform_device_add(mdm_drv->pdata->peripheral_platform_device);
+ platform_device_add(mdm_drv->pdata->peripheral_platform_device);
hsic_peripheral_status = 1;
out:
mutex_unlock(&hsic_status_lock);
@@ -65,84 +67,106 @@
static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
{
+ if (!mdm_drv->pdata->peripheral_platform_device)
+ return;
+
mutex_lock(&hsic_status_lock);
if (!hsic_peripheral_status)
goto out;
- if (mdm_drv->pdata->peripheral_platform_device)
- platform_device_del(mdm_drv->pdata->peripheral_platform_device);
+ platform_device_del(mdm_drv->pdata->peripheral_platform_device);
hsic_peripheral_status = 0;
out:
mutex_unlock(&hsic_status_lock);
}
-static void power_on_mdm(struct mdm_modem_drv *mdm_drv)
+static void mdm_power_down_common(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 1 : 0;
+
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+ mdm_peripheral_disconnect(mdm_drv);
+}
+
+static void mdm_do_first_power_on(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 0 : 1;
+
+ if (power_on_count != 1) {
+ pr_err("%s: Calling fn when power_on_count != 1\n",
+ __func__);
+ return;
+ }
+
+ pr_err("%s: Powering on modem for the first time\n", __func__);
+ mdm_peripheral_disconnect(mdm_drv);
+
+ /* If the device has a kpd pwr gpio then toggle it. */
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) {
+ /* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle,
+ * then pull it back low.
+ */
+ pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
+ msleep(1000);
+ gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
+ }
+
+ /* De-assert the soft reset line. */
+ pr_debug("%s: De-asserting soft reset gpio\n", __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+
+ mdm_peripheral_connect(mdm_drv);
+ msleep(200);
+}
+
+static void mdm_do_soft_power_on(struct mdm_modem_drv *mdm_drv)
+{
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 0 : 1;
+
+ /* De-assert the soft reset line. */
+ pr_err("%s: soft resetting mdm modem\n", __func__);
+
+ mdm_peripheral_disconnect(mdm_drv);
+
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction == 1 ? 0 : 1);
+ usleep_range(5000, 10000);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction == 1 ? 1 : 0);
+
+ mdm_peripheral_connect(mdm_drv);
+ msleep(200);
+}
+
+static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv)
{
power_on_count++;
/* this gpio will be used to indicate apq readiness,
- * de-assert it now so that it can asserted later
+ * de-assert it now so that it can be asserted later.
+ * May not be used.
*/
- gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
- /* The second attempt to power-on the mdm is the first attempt
- * from user space, but we're already powered on. Ignore this.
- * Subsequent attempts are from SSR or if something failed, in
- * which case we must always reset the modem.
+ /*
+ * If we did an "early power on" then ignore the very next
+ * power-on request because it would the be first request from
+ * user space but we're already powered on. Ignore it.
*/
- if (power_on_count == 2)
+ if (mdm_drv->pdata->early_power_on &&
+ (power_on_count == 2))
return;
- mdm_peripheral_disconnect(mdm_drv);
-
- /* Pull RESET gpio low and wait for it to settle. */
- pr_debug("Pulling RESET gpio low\n");
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 0);
- usleep_range(5000, 10000);
-
- /* Deassert RESET first and wait for it to settle. */
- pr_debug("%s: Pulling RESET gpio high\n", __func__);
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 1);
- usleep(20000);
-
- /* Pull PWR gpio high and wait for it to settle, but only
- * the first time the mdm is powered up.
- * Some targets do not use ap2mdm_kpdpwr_n_gpio.
- */
- if (power_on_count == 1) {
- if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0) {
- pr_debug("%s: Powering on mdm modem\n", __func__);
- gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
- usleep(1000);
- }
- }
- mdm_peripheral_connect(mdm_drv);
-
- msleep(200);
-}
-
-static void power_down_mdm(struct mdm_modem_drv *mdm_drv)
-{
- int i;
-
- for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
- pet_watchdog();
- msleep(MDM_MODEM_DELTA);
- if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
- break;
- }
- if (i <= 0) {
- pr_err("%s: MDM2AP_STATUS never went low.\n",
- __func__);
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 0);
-
- for (i = MDM_HOLD_TIME; i > 0; i -= MDM_MODEM_DELTA) {
- pet_watchdog();
- msleep(MDM_MODEM_DELTA);
- }
- }
- if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
- gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
- mdm_peripheral_disconnect(mdm_drv);
+ if (power_on_count == 1)
+ mdm_do_first_power_on(mdm_drv);
+ else
+ mdm_do_soft_power_on(mdm_drv);
}
static void debug_state_changed(int value)
@@ -157,13 +181,15 @@
if (value) {
mdm_peripheral_disconnect(mdm_drv);
mdm_peripheral_connect(mdm_drv);
- gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1);
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1);
}
}
static struct mdm_ops mdm_cb = {
- .power_on_mdm_cb = power_on_mdm,
- .power_down_mdm_cb = power_down_mdm,
+ .power_on_mdm_cb = mdm_power_on_common,
+ .reset_mdm_cb = mdm_power_on_common,
+ .power_down_mdm_cb = mdm_power_down_common,
.debug_state_changed_cb = debug_state_changed,
.status_cb = mdm_status_changed,
};
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index f8e187d..ffff782 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -152,6 +152,14 @@
(unsigned long __user *) arg);
INIT_COMPLETION(mdm_needs_reload);
break;
+ case GET_DLOAD_STATUS:
+ pr_debug("getting status of mdm2ap_errfatal_gpio\n");
+ if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
+ !mdm_drv->mdm_ready)
+ put_user(1, (unsigned long __user *) arg);
+ else
+ put_user(0, (unsigned long __user *) arg);
+ break;
default:
pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
ret = -EINVAL;
@@ -161,31 +169,13 @@
return ret;
}
-static void mdm_fatal_fn(struct work_struct *work)
-{
- pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
- subsystem_restart(EXTERNAL_MODEM);
-}
-
-static DECLARE_WORK(mdm_fatal_work, mdm_fatal_fn);
-
static void mdm_status_fn(struct work_struct *work)
{
int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
- if (!mdm_drv->mdm_ready)
- return;
-
- mdm_drv->ops->status_cb(mdm_drv, value);
-
pr_debug("%s: status:%d\n", __func__, value);
-
- if ((value == 0)) {
- pr_info("%s: unexpected reset external modem\n", __func__);
- subsystem_restart(EXTERNAL_MODEM);
- } else if (value == 1) {
- pr_info("%s: status = 1: mdm is now ready\n", __func__);
- }
+ if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
+ mdm_drv->ops->status_cb(mdm_drv, value);
}
static DECLARE_WORK(mdm_status_work, mdm_status_fn);
@@ -194,7 +184,6 @@
{
disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
disable_irq_nosync(mdm_drv->mdm_status_irq);
-
}
static irqreturn_t mdm_errfatal(int irq, void *dev_id)
@@ -202,8 +191,9 @@
pr_debug("%s: mdm got errfatal interrupt\n", __func__);
if (mdm_drv->mdm_ready &&
(gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
- pr_debug("%s: scheduling work now\n", __func__);
- queue_work(mdm_queue, &mdm_fatal_work);
+ pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
+ mdm_drv->mdm_ready = 0;
+ subsystem_restart(EXTERNAL_MODEM);
}
return IRQ_HANDLED;
}
@@ -242,8 +232,12 @@
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
break;
}
- if (i <= 0)
+ if (i <= 0) {
pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
+ /* Reset the modem so that it will go into download mode. */
+ if (mdm_drv && mdm_drv->ops->reset_mdm_cb)
+ mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ }
return NOTIFY_DONE;
}
@@ -253,16 +247,23 @@
static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
+ int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
+
pr_debug("%s: mdm sent status change interrupt\n", __func__);
-
- queue_work(mdm_queue, &mdm_status_work);
-
+ if (value == 0 && mdm_drv->mdm_ready == 1) {
+ pr_info("%s: unexpected reset external modem\n", __func__);
+ mdm_drv->mdm_unexpected_reset_occurred = 1;
+ mdm_drv->mdm_ready = 0;
+ subsystem_restart(EXTERNAL_MODEM);
+ } else if (value == 1) {
+ pr_info("%s: status = 1: mdm is now ready\n", __func__);
+ queue_work(mdm_queue, &mdm_status_work);
+ }
return IRQ_HANDLED;
}
static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
{
- mdm_drv->mdm_ready = 0;
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
if (mdm_drv->pdata->ramdump_delay_ms > 0) {
/* Wait for the external modem to complete
@@ -270,7 +271,11 @@
*/
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ if (!mdm_drv->mdm_unexpected_reset_occurred)
+ mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ else
+ mdm_drv->mdm_unexpected_reset_occurred = 0;
+
return 0;
}
@@ -287,8 +292,10 @@
pr_info("%s: mdm modem restart timed out.\n", __func__);
} else {
pr_info("%s: mdm modem has been restarted\n", __func__);
+
/* Log the reason for the restart */
- queue_work(mdm_sfr_queue, &sfr_reason_work);
+ if (mdm_drv->pdata->sfr_query)
+ queue_work(mdm_sfr_queue, &sfr_reason_work);
}
INIT_COMPLETION(mdm_boot);
return mdm_drv->mdm_boot_status;
@@ -310,7 +317,6 @@
pr_info("%s: mdm modem ramdumps completed.\n",
__func__);
INIT_COMPLETION(mdm_ram_dumps);
- gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
mdm_drv->ops->power_down_mdm_cb(mdm_drv);
}
return mdm_drv->mdm_ram_dump_status;
@@ -395,11 +401,11 @@
if (pres)
mdm_drv->ap2mdm_wakeup_gpio = pres->start;
- /* AP2MDM_PMIC_RESET_N */
+ /* AP2MDM_SOFT_RESET */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
- "AP2MDM_PMIC_RESET_N");
+ "AP2MDM_SOFT_RESET");
if (pres)
- mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
+ mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
/* AP2MDM_KPDPWR_N */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
@@ -407,6 +413,12 @@
if (pres)
mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
+ /* AP2MDM_PMIC_PWR_EN */
+ pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
+ "AP2MDM_PMIC_PWR_EN");
+ if (pres)
+ mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
+
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
mdm_drv->ops = mdm_ops;
@@ -430,11 +442,18 @@
gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
- gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
- gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
+ "AP2MDM_PMIC_PWR_EN");
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
+ "AP2MDM_SOFT_RESET");
+
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
@@ -515,10 +534,18 @@
mdm_drv->mdm_status_irq = irq;
status_err:
+ /*
+ * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
+ * high until the whole phone is shut down.
+ */
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
+
/* Perform early powerup of the external modem in order to
* allow tabla devices to be found.
*/
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
+ if (mdm_drv->pdata->early_power_on)
+ mdm_drv->ops->power_on_mdm_cb(mdm_drv);
pr_info("%s: Registering mdm modem\n", __func__);
return misc_register(&mdm_modem_misc);
@@ -526,10 +553,14 @@
fatal_err:
gpio_free(mdm_drv->ap2mdm_status_gpio);
gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
@@ -547,10 +578,14 @@
gpio_free(mdm_drv->ap2mdm_status_gpio);
gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
+ if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
gpio_free(mdm_drv->mdm2ap_status_gpio);
gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
if (mdm_drv->ap2mdm_wakeup_gpio > 0)
gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
@@ -566,5 +601,7 @@
mdm_disable_irqs();
mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
}
diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h
index 206bd8b..f157d88 100644
--- a/arch/arm/mach-msm/mdm_private.h
+++ b/arch/arm/mach-msm/mdm_private.h
@@ -17,6 +17,7 @@
struct mdm_ops {
void (*power_on_mdm_cb)(struct mdm_modem_drv *mdm_drv);
+ void (*reset_mdm_cb)(struct mdm_modem_drv *mdm_drv);
void (*normal_boot_done_cb)(struct mdm_modem_drv *mdm_drv);
void (*power_down_mdm_cb)(struct mdm_modem_drv *mdm_drv);
void (*debug_state_changed_cb)(int value);
@@ -31,8 +32,9 @@
unsigned ap2mdm_status_gpio;
unsigned mdm2ap_wakeup_gpio;
unsigned ap2mdm_wakeup_gpio;
- unsigned ap2mdm_pmic_reset_n_gpio;
unsigned ap2mdm_kpdpwr_n_gpio;
+ unsigned ap2mdm_soft_reset_gpio;
+ unsigned ap2mdm_pmic_pwr_en_gpio;
int mdm_errfatal_irq;
int mdm_status_irq;
@@ -41,6 +43,7 @@
int mdm_ram_dump_status;
enum charm_boot_type boot_type;
int mdm_debug_on;
+ int mdm_unexpected_reset_occurred;
struct mdm_ops *ops;
struct mdm_platform_data *pdata;
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
index f9414a5..2b72021 100644
--- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -396,7 +396,7 @@
void (*getevent)(void *ptr, size_t len))
{
uint32_t evt_buf[3];
- void *data;
+ void *data = NULL;
struct buf_info *outch = NULL;
uint32_t y_phy, cbcr_phy;
struct table_cmd *table_pending = NULL;
@@ -432,6 +432,7 @@
vfe_7x_ops(driver_data, MSG_OUTPUT_T,
len, getevent);
vfe2x_send_isp_msg(vfe2x_ctrl, MSG_ID_SNAPSHOT_DONE);
+ kfree(data);
return;
case MSG_OUTPUT_S:
outch = &vfe2x_ctrl->snap;
@@ -712,24 +713,30 @@
vfe2x_ctrl->update_pending = 0;
}
spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+ kfree(data);
return;
}
table_pending = list_first_entry(&vfe2x_ctrl->table_q,
struct table_cmd, list);
if (!table_pending) {
spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
+ kfree(data);
return;
}
msm_adsp_write(vfe_mod, table_pending->queue,
table_pending->cmd, table_pending->size);
list_del(&table_pending->list);
kfree(table_pending->cmd);
+ kfree(table_pending);
vfe2x_ctrl->tableack_pending = 1;
spin_unlock_irqrestore(&vfe2x_ctrl->table_lock, flags);
} else if (!vfe2x_ctrl->tableack_pending) {
- if (!list_empty(&vfe2x_ctrl->table_q))
+ if (!list_empty(&vfe2x_ctrl->table_q)) {
+ kfree(data);
return;
+ }
}
+ kfree(data);
}
static struct msm_adsp_ops vfe_7x_sync = {
@@ -1640,6 +1647,7 @@
config_failure:
kfree(scfg);
kfree(axio);
+ kfree(sfcfg);
return rc;
}
diff --git a/drivers/usb/gadget/u_ctrl_hsuart.c b/drivers/usb/gadget/u_ctrl_hsuart.c
new file mode 100644
index 0000000..7102d81
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsuart.c
@@ -0,0 +1,576 @@
+/* Copyright (c) 2012, 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+static unsigned int num_ctrl_ports;
+
+static const char *ghsuart_ctrl_names[] = {
+ "SMUX_RMNET_CTL_HSUART"
+};
+
+struct ghsuart_ctrl_port {
+ /* port */
+ unsigned port_num;
+ /* gadget */
+ enum gadget_type gtype;
+ spinlock_t port_lock;
+ void *port_usb;
+ /* work queue*/
+ struct workqueue_struct *wq;
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ /*ctrl pkt response cb*/
+ int (*send_cpkt_response)(void *g, void *buf, size_t len);
+ void *ctxt;
+ unsigned int ch_id;
+ /* flow control bits */
+ unsigned long flags;
+ int (*send_pkt)(void *, void *, size_t actual);
+ /* Channel status */
+ unsigned long channel_sts;
+ /* control bits */
+ unsigned cbits_tomodem;
+ /* counters */
+ unsigned long to_modem;
+ unsigned long to_host;
+ unsigned long drp_cpkt_cnt;
+};
+
+static struct {
+ struct ghsuart_ctrl_port *port;
+ struct platform_driver pdrv;
+} ghsuart_ctrl_ports[NUM_HSUART_PORTS];
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual);
+
+static void smux_control_event(void *priv, int event_type, const void *metadata)
+{
+ struct grmnet *gr = NULL;
+ struct ghsuart_ctrl_port *port = priv;
+ void *buf;
+ unsigned long flags;
+ size_t len;
+
+ switch (event_type) {
+ case SMUX_CONNECTED:
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ set_bit(CH_OPENED, &port->channel_sts);
+ if (port->gtype == USB_GADGET_RMNET) {
+ gr = port->port_usb;
+ if (gr && gr->connect)
+ gr->connect(gr);
+ }
+ break;
+ case SMUX_DISCONNECTED:
+ clear_bit(CH_OPENED, &port->channel_sts);
+ break;
+ case SMUX_READ_DONE:
+ len = ((struct smux_meta_read *)metadata)->len;
+ buf = ((struct smux_meta_read *)metadata)->buffer;
+ ghsuart_ctrl_receive(port, buf, len);
+ break;
+ case SMUX_READ_FAIL:
+ buf = ((struct smux_meta_read *)metadata)->buffer;
+ kfree(buf);
+ break;
+ case SMUX_WRITE_DONE:
+ case SMUX_WRITE_FAIL:
+ buf = ((struct smux_meta_write *)metadata)->buffer;
+ kfree(buf);
+ break;
+ case SMUX_LOW_WM_HIT:
+ case SMUX_HIGH_WM_HIT:
+ case SMUX_TIOCM_UPDATE:
+ break;
+ default:
+ pr_err("%s Event %d not supported\n", __func__, event_type);
+ };
+}
+
+static int rx_control_buffer(void *priv, void **pkt_priv, void **buffer,
+ int size)
+{
+ void *rx_buf;
+
+ rx_buf = kmalloc(size, GFP_KERNEL);
+ if (!rx_buf)
+ return -EAGAIN;
+ *buffer = rx_buf;
+ *pkt_priv = NULL;
+
+ return 0;
+}
+
+static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+ struct ghsuart_ctrl_port *port = dev;
+ int retval = 0;
+
+ pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+ __func__, actual);
+
+ /* send it to USB here */
+ if (port && port->send_cpkt_response) {
+ retval = port->send_cpkt_response(port->port_usb, buf, actual);
+ port->to_host++;
+ }
+ kfree(buf);
+ return retval;
+}
+
+static int
+ghsuart_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+ void *cbuf;
+ struct ghsuart_ctrl_port *port;
+ int ret;
+
+ if (portno >= num_ctrl_ports) {
+ pr_err("%s: Invalid portno#%d\n", __func__, portno);
+ return -ENODEV;
+ }
+
+ port = ghsuart_ctrl_ports[portno].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+ /* drop cpkt if ch is not open */
+ if (!test_bit(CH_OPENED, &port->channel_sts)) {
+ port->drp_cpkt_cnt++;
+ return 0;
+ }
+ cbuf = kmalloc(len, GFP_ATOMIC);
+ if (!cbuf)
+ return -ENOMEM;
+
+ memcpy(cbuf, buf, len);
+
+ pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+ ret = msm_smux_write(port->ch_id, port, (void *)cbuf, len);
+ if (ret < 0) {
+ pr_err_ratelimited("%s: write error:%d\n",
+ __func__, ret);
+ port->drp_cpkt_cnt++;
+ kfree(cbuf);
+ return ret;
+ }
+ port->to_modem++;
+
+ return 0;
+}
+
+static void
+ghsuart_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+ struct ghsuart_ctrl_port *port;
+
+ if (portno >= num_ctrl_ports || !gptr) {
+ pr_err("%s: Invalid portno#%d\n", __func__, portno);
+ return;
+ }
+
+ port = ghsuart_ctrl_ports[portno].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return;
+ }
+
+ if (cbits == port->cbits_tomodem)
+ return;
+
+ port->cbits_tomodem = cbits;
+
+ if (!test_bit(CH_OPENED, &port->channel_sts))
+ return;
+
+ pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+ /* Send the control bits to the Modem */
+ msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static void ghsuart_ctrl_connect_w(struct work_struct *w)
+{
+ struct ghsuart_ctrl_port *port =
+ container_of(w, struct ghsuart_ctrl_port, connect_w);
+ int retval;
+
+ if (!port || !test_bit(CH_READY, &port->channel_sts))
+ return;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ retval = msm_smux_open(port->ch_id, port->ctxt, smux_control_event,
+ rx_control_buffer);
+ if (retval < 0) {
+ pr_err(" %s smux_open failed\n", __func__);
+ return;
+ }
+
+}
+
+int ghsuart_ctrl_connect(void *gptr, int port_num)
+{
+ struct ghsuart_ctrl_port *port;
+ struct grmnet *gr;
+ unsigned long flags;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ if (port_num > num_ctrl_ports || !gptr) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ port = ghsuart_ctrl_ports[port_num].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ gr = gptr;
+ port->send_cpkt_response = gr->send_cpkt_response;
+ gr->send_encap_cmd = ghsuart_send_cpkt_tomodem;
+ gr->notify_modem = ghsuart_send_cbits_tomodem;
+
+ port->port_usb = gptr;
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->drp_cpkt_cnt = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (test_bit(CH_READY, &port->channel_sts))
+ queue_work(port->wq, &port->connect_w);
+
+ return 0;
+}
+
+static void ghsuart_ctrl_disconnect_w(struct work_struct *w)
+{
+ struct ghsuart_ctrl_port *port =
+ container_of(w, struct ghsuart_ctrl_port, disconnect_w);
+
+ if (!test_bit(CH_OPENED, &port->channel_sts))
+ return;
+
+ msm_smux_close(port->ch_id);
+ clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+void ghsuart_ctrl_disconnect(void *gptr, int port_num)
+{
+ struct gctrl_port *port;
+ struct grmnet *gr = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ if (port_num > num_ctrl_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return;
+ }
+
+ port = gctrl_ports[port_num].port;
+
+ if (!gptr || !port) {
+ pr_err("%s: grmnet port is null\n", __func__);
+ return;
+ }
+
+ gr = gptr;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ gr->send_encap_cmd = 0;
+ gr->notify_modem = 0;
+ port->cbits_tomodem = 0;
+ port->port_usb = 0;
+ port->send_cpkt_response = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->disconnect_w);
+}
+
+static int ghsuart_ctrl_probe(struct platform_device *pdev)
+{
+ struct ghsuart_ctrl_port *port;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ port = ghsuart_ctrl_ports[pdev->id].port;
+ set_bit(CH_READY, &port->channel_sts);
+
+ /* if usb is online, start read */
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ queue_work(port->wq, &port->connect_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return 0;
+}
+
+static int ghsuart_ctrl_remove(struct platform_device *pdev)
+{
+ struct ghsuart_ctrl_port *port;
+ struct grmnet *gr = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ port = ghsuart_ctrl_ports[pdev->id].port;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ goto not_ready;
+ }
+
+ gr = port->port_usb;
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (gr && gr->disconnect)
+ gr->disconnect(gr);
+
+ clear_bit(CH_OPENED, &port->channel_sts);
+not_ready:
+ clear_bit(CH_READY, &port->channel_sts);
+
+ return 0;
+}
+
+static void ghsuart_ctrl_port_free(int portno)
+{
+ struct ghsuart_ctrl_port *port = ghsuart_ctrl_ports[portno].port;
+ struct platform_driver *pdrv = &gctrl_ports[portno].pdrv;
+
+ destroy_workqueue(port->wq);
+ if (pdrv)
+ platform_driver_unregister(pdrv);
+ kfree(port);
+}
+
+static int ghsuart_ctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+ struct ghsuart_ctrl_port *port;
+ struct platform_driver *pdrv;
+ int err;
+
+ port = kzalloc(sizeof(struct ghsuart_ctrl_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->wq = create_singlethread_workqueue(ghsuart_ctrl_names[portno]);
+ if (!port->wq) {
+ pr_err("%s: Unable to create workqueue:%s\n",
+ __func__, ghsuart_ctrl_names[portno]);
+ kfree(port);
+ return -ENOMEM;
+ }
+
+ port->port_num = portno;
+ port->gtype = gtype;
+
+ spin_lock_init(&port->port_lock);
+
+ INIT_WORK(&port->connect_w, ghsuart_ctrl_connect_w);
+ INIT_WORK(&port->disconnect_w, ghsuart_ctrl_disconnect_w);
+
+ port->ch_id = SMUX_USB_RMNET_CTL_0;
+ port->ctxt = port;
+ port->send_pkt = ghsuart_ctrl_receive;
+ ghsuart_ctrl_ports[portno].port = port;
+
+ pdrv = &ghsuart_ctrl_ports[portno].pdrv;
+ pdrv->probe = ghsuart_ctrl_probe;
+ pdrv->remove = ghsuart_ctrl_remove;
+ pdrv->driver.name = ghsuart_ctrl_names[portno];
+ pdrv->driver.owner = THIS_MODULE;
+
+ err = platform_driver_register(pdrv);
+ if (unlikely(err < 0))
+ return err;
+ pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+ return 0;
+}
+
+int ghsuart_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+ int first_port_id = num_ctrl_ports;
+ int total_num_ports = num_ports + num_ctrl_ports;
+ int i;
+ int ret = 0;
+
+ if (!num_ports || total_num_ports > NUM_HSUART_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, num_ports);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+ for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+ num_ctrl_ports++;
+ ret = ghsuart_ctrl_port_alloc(i, gtype);
+ if (ret) {
+ num_ctrl_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_ports;
+ }
+ }
+
+ return first_port_id;
+
+free_ports:
+ for (i = first_port_id; i < num_ctrl_ports; i++)
+ ghsuart_ctrl_port_free(i);
+ num_ctrl_ports = first_port_id;
+ return ret;
+}
+
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ghsuart_ctrl_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ghsuart_ctrl_port *port;
+ char *buf;
+ unsigned long flags;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < num_ctrl_ports; i++) {
+ port = ghsuart_ctrl_ports[i].port;
+ if (!port)
+ continue;
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "#PORT:%d port: %p\n"
+ "to_usbhost: %lu\n"
+ "to_modem: %lu\n"
+ "cpkt_drp_cnt: %lu\n"
+ "DTR: %s\n",
+ i, port,
+ port->to_host, port->to_modem,
+ port->drp_cpkt_cnt,
+ port->cbits_tomodem ? "HIGH" : "LOW");
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t ghsuart_ctrl_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ghsuart_ctrl_port *port;
+ int i;
+ unsigned long flags;
+
+ for (i = 0; i < num_ctrl_ports; i++) {
+ port = ghsuart_ctrl_ports[i].port;
+ if (!port)
+ continue;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->drp_cpkt_cnt = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+ return count;
+}
+
+static const struct file_operations ghsuart_ctrl_stats_ops = {
+ .read = ghsuart_ctrl_read_stats,
+ .write = ghsuart_ctrl_reset_stats,
+};
+
+static struct dentry *ghsuart_ctrl_dent;
+static int ghsuart_ctrl_debugfs_init(void)
+{
+ struct dentry *ghsuart_ctrl_dfile;
+
+ ghsuart_ctrl_dent = debugfs_create_dir("ghsuart_ctrl_xport", 0);
+ if (!ghsuart_ctrl_dent || IS_ERR(ghsuart_ctrl_dent))
+ return -ENODEV;
+
+ ghsuart_ctrl_dfile =
+ debugfs_create_file("status", S_IRUGO | S_IWUSR,
+ ghsuart_ctrl_dent, 0, &gctrl_stats_ops);
+ if (!ghsuart_ctrl_dfile || IS_ERR(ghsuart_ctrl_dfile)) {
+ debugfs_remove(ghsuart_ctrl_dent);
+ ghsuart_ctrl_dent = NULL;
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void ghsuart_ctrl_debugfs_exit(void)
+{
+ debugfs_remove_recursive(ghsuart_ctrl_dent);
+}
+
+static int __init ghsuart_ctrl_init(void)
+{
+ int ret;
+
+ ret = ghsuart_ctrl_debugfs_init();
+ if (ret) {
+ pr_debug("mode debugfs file is not available\n");
+ return ret;
+ }
+ return 0;
+}
+module_init(ghsuart_ctrl_init);
+
+static void __exit ghsuart_ctrl_exit(void)
+{
+ ghsuart_ctrl_debugfs_exit();
+}
+module_exit(ghsuart_ctrl_exit);
+
+MODULE_DESCRIPTION("HSUART control xport for RmNet");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsuart.c b/drivers/usb/gadget/u_data_hsuart.c
new file mode 100644
index 0000000..b2c57c4
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsuart.c
@@ -0,0 +1,1142 @@
+/* Copyright (c) 2012, 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/smux.h>
+
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int num_data_ports;
+
+static const char *ghsuart_data_names[] = {
+ "SMUX_DUN_DATA_HSUART",
+ "SMUX_RMNET_DATA_HSUART"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN 20
+
+#define GHSUART_DATA_RMNET_RX_Q_SIZE 10
+#define GHSUART_DATA_RMNET_TX_Q_SIZE 20
+#define GHSUART_DATA_SERIAL_RX_Q_SIZE 5
+#define GHSUART_DATA_SERIAL_TX_Q_SIZE 5
+#define GHSUART_DATA_RX_REQ_SIZE 2048
+#define GHSUART_DATA_TX_INTR_THRESHOLD 1
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
+
+static unsigned int ghsuart_data_rmnet_tx_q_size = GHSUART_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsuart_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rmnet_rx_q_size = GHSUART_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsuart_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_tx_q_size =
+ GHSUART_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsuart_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_serial_rx_q_size =
+ GHSUART_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsuart_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsuart_data_rx_req_size = GHSUART_DATA_RX_REQ_SIZE;
+module_param(ghsuart_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+unsigned int ghsuart_data_tx_intr_thld = GHSUART_DATA_TX_INTR_THRESHOLD;
+module_param(ghsuart_data_tx_intr_thld, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct ghsuart_data_port {
+ /* port */
+ unsigned port_num;
+
+ /* gadget */
+ atomic_t connected;
+ struct usb_ep *in;
+ struct usb_ep *out;
+
+ enum gadget_type gtype;
+ spinlock_t port_lock;
+ void *port_usb;
+
+ /* data transfer queues */
+ unsigned int tx_q_size;
+ struct list_head tx_idle;
+ struct sk_buff_head tx_skb_q;
+ spinlock_t tx_lock;
+
+ unsigned int rx_q_size;
+ struct list_head rx_idle;
+ struct sk_buff_head rx_skb_q;
+ spinlock_t rx_lock;
+
+ /* work */
+ struct workqueue_struct *wq;
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ struct work_struct write_tomdm_w;
+ struct work_struct write_tohost_w;
+ void *ctx;
+ unsigned int ch_id;
+ /* flow control bits */
+ unsigned long flags;
+ /* channel status */
+ unsigned long channel_sts;
+
+ unsigned int n_tx_req_queued;
+
+ /* control bits */
+ unsigned cbits_tomodem;
+ unsigned cbits_tohost;
+
+ /* counters */
+ unsigned long to_modem;
+ unsigned long to_host;
+ unsigned int tomodem_drp_cnt;
+};
+
+static struct {
+ struct ghsuart_data_port *port;
+ struct platform_driver pdrv;
+} ghsuart_data_ports[NUM_HSUART_PORTS];
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port);
+
+static void ghsuart_data_free_requests(struct usb_ep *ep,
+ struct list_head *head)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static int ghsuart_data_alloc_requests(struct usb_ep *ep,
+ struct list_head *head,
+ int num,
+ void (*cb)(struct usb_ep *ep, struct usb_request *),
+ gfp_t flags)
+{
+ int i;
+ struct usb_request *req;
+
+ pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+ ep->name, head, num, cb);
+
+ for (i = 0; i < num; i++) {
+ req = usb_ep_alloc_request(ep, flags);
+ if (!req) {
+ pr_err("%s: req allocated:%d\n", __func__, i);
+ return list_empty(head) ? -ENOMEM : 0;
+ }
+ req->complete = cb;
+ list_add(&req->list, head);
+ }
+
+ return 0;
+}
+
+static void ghsuart_data_write_tohost(struct work_struct *w)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ int ret;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ struct ghsuart_data_port *port;
+
+ port = container_of(w, struct ghsuart_data_port, write_tohost_w);
+
+ if (!port || !atomic_read(&port->connected))
+ return;
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ ep = port->in;
+ if (!ep) {
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ return;
+ }
+
+ while (!list_empty(&port->tx_idle)) {
+ skb = __skb_dequeue(&port->tx_skb_q);
+ if (!skb)
+ break;
+
+ req = list_first_entry(&port->tx_idle, struct usb_request,
+ list);
+ req->context = skb;
+ req->buf = skb->data;
+ req->length = skb->len;
+
+ port->n_tx_req_queued++;
+ if (port->n_tx_req_queued == ghsuart_data_tx_intr_thld) {
+ req->no_interrupt = 0;
+ port->n_tx_req_queued = 0;
+ } else {
+ req->no_interrupt = 1;
+ }
+
+ list_del(&req->list);
+
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ ret = usb_ep_queue(ep, req, GFP_KERNEL);
+ spin_lock_irqsave(&port->tx_lock, flags);
+ if (ret) {
+ pr_err("%s: usb epIn failed\n", __func__);
+ list_add(&req->list, &port->tx_idle);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ port->to_host++;
+ }
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static void ghsuart_data_write_tomdm(struct work_struct *w)
+{
+ struct ghsuart_data_port *port;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int ret;
+
+ port = container_of(w, struct ghsuart_data_port, write_tomdm_w);
+
+ if (!port || !atomic_read(&port->connected))
+ return;
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ if (test_bit(TX_THROTTLED, &port->flags)) {
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+
+ while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+ pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+ port, port->to_modem, port->port_num);
+
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ ret = msm_smux_write(port->ch_id, skb, skb->data, skb->len);
+ spin_lock_irqsave(&port->rx_lock, flags);
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ /*flow control*/
+ set_bit(TX_THROTTLED, &port->flags);
+ __skb_queue_head(&port->rx_skb_q, skb);
+ break;
+ }
+ pr_err_ratelimited("%s: write error:%d\n",
+ __func__, ret);
+ port->tomodem_drp_cnt++;
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ port->to_modem++;
+ }
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_data_epin_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct ghsuart_data_port *port = ep->driver_data;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+
+ switch (status) {
+ case 0:
+ /* successful completion */
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* connection gone */
+ dev_kfree_skb_any(skb);
+ req->buf = 0;
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ pr_err("%s: data tx ep error %d\n", __func__, status);
+ break;
+ }
+
+ dev_kfree_skb_any(skb);
+
+ spin_lock(&port->tx_lock);
+ list_add_tail(&req->list, &port->tx_idle);
+ spin_unlock(&port->tx_lock);
+
+ queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsuart_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ghsuart_data_port *port = ep->driver_data;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+ int queue = 0;
+
+ switch (status) {
+ case 0:
+ skb_put(skb, req->actual);
+ queue = 1;
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* cable disconnection */
+ dev_kfree_skb_any(skb);
+ req->buf = 0;
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+ __func__, ep->name, status,
+ req->actual, req->length);
+ dev_kfree_skb_any(skb);
+ list_add_tail(&req->list, &port->rx_idle);
+ return;
+ }
+
+ spin_lock(&port->rx_lock);
+ if (queue) {
+ __skb_queue_tail(&port->rx_skb_q, skb);
+ list_add_tail(&req->list, &port->rx_idle);
+ queue_work(port->wq, &port->write_tomdm_w);
+ }
+ spin_unlock(&port->rx_lock);
+}
+
+static void ghsuart_data_start_rx(struct ghsuart_data_port *port)
+{
+ struct usb_request *req;
+ struct usb_ep *ep;
+ unsigned long flags;
+ int ret;
+ struct sk_buff *skb;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ep = port->out;
+ if (!ep) {
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+
+ if (test_bit(TX_THROTTLED, &port->flags)) {
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+
+ while (atomic_read(&port->connected) && !list_empty(&port->rx_idle)) {
+
+ req = list_first_entry(&port->rx_idle,
+ struct usb_request, list);
+
+ skb = alloc_skb(ghsuart_data_rx_req_size, GFP_ATOMIC);
+ if (!skb)
+ break;
+ list_del(&req->list);
+ req->buf = skb->data;
+ req->length = ghsuart_data_rx_req_size;
+ req->context = skb;
+
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ ret = usb_ep_queue(ep, req, GFP_KERNEL);
+ spin_lock_irqsave(&port->rx_lock, flags);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+
+ pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+ if (atomic_read(&port->connected))
+ list_add(&req->list, &port->rx_idle);
+ else
+ usb_ep_free_request(ep, req);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static void ghsuart_data_start_io(struct ghsuart_data_port *port)
+{
+ unsigned long flags;
+ struct usb_ep *ep;
+ int ret;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ep = port->out;
+ if (!ep) {
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+
+ ret = ghsuart_data_alloc_requests(ep, &port->rx_idle,
+ port->rx_q_size, ghsuart_data_epout_complete, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s: rx req allocation failed\n", __func__);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ ep = port->in;
+ if (!ep) {
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ return;
+ }
+
+ ret = ghsuart_data_alloc_requests(ep, &port->tx_idle,
+ port->tx_q_size, ghsuart_data_epin_complete, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s: tx req allocation failed\n", __func__);
+ ghsuart_data_free_requests(ep, &port->rx_idle);
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ /* queue out requests */
+ ghsuart_data_start_rx(port);
+}
+
+static void ghsuart_dunctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+ struct ghsuart_data_port *port = ctxt;
+ struct gserial *gser;
+ unsigned long flags;
+
+ pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+ "ring%c framing%c parity%c overrun%c\n", __func__,
+ ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+ ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+ ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+ ctrl_bits & ACM_CTRL_RI ? '+' : '-',
+ ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+ ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+ ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->cbits_tohost = ctrl_bits;
+ gser = port->port_usb;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ if (gser && gser->send_modem_ctrl_bits)
+ gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+const char *event_string(int event_type)
+{
+ switch (event_type) {
+ case SMUX_CONNECTED:
+ return "SMUX_CONNECTED";
+ case SMUX_DISCONNECTED:
+ return "SMUX_DISCONNECTED";
+ case SMUX_READ_DONE:
+ return "SMUX_READ_DONE";
+ case SMUX_READ_FAIL:
+ return "SMUX_READ_FAIL";
+ case SMUX_WRITE_DONE:
+ return "SMUX_WRITE_DONE";
+ case SMUX_WRITE_FAIL:
+ return "SMUX_WRITE_FAIL";
+ case SMUX_HIGH_WM_HIT:
+ return "SMUX_HIGH_WM_HIT";
+ case SMUX_LOW_WM_HIT:
+ return "SMUX_LOW_WM_HIT";
+ case SMUX_TIOCM_UPDATE:
+ return "SMUX_TIOCM_UPDATE";
+ default:
+ return "UNDEFINED";
+ }
+}
+
+static void ghsuart_notify_event(void *priv, int event_type,
+ const void *metadata)
+{
+ struct ghsuart_data_port *port = priv;
+ struct smux_meta_write *meta_write =
+ (struct smux_meta_write *) metadata;
+ struct smux_meta_read *meta_read =
+ (struct smux_meta_read *) metadata;
+ struct sk_buff *skb;
+ unsigned long flags;
+ unsigned int cbits;
+ struct gserial *gser;
+
+ pr_debug("%s: event type: %s ", __func__, event_string(event_type));
+ switch (event_type) {
+ case SMUX_CONNECTED:
+ set_bit(CH_OPENED, &port->channel_sts);
+ if (port->gtype == USB_GADGET_SERIAL) {
+ cbits = msm_smux_tiocm_get(port->ch_id);
+ if (cbits & ACM_CTRL_DCD) {
+ gser = port->port_usb;
+ if (gser && gser->connect)
+ gser->connect(gser);
+ }
+ }
+ ghsuart_data_start_io(port);
+ break;
+ case SMUX_DISCONNECTED:
+ clear_bit(CH_OPENED, &port->channel_sts);
+ break;
+ case SMUX_READ_DONE:
+ skb = meta_read->pkt_priv;
+ skb->data = meta_read->buffer;
+ skb->len = meta_read->len;
+ spin_lock_irqsave(&port->tx_lock, flags);
+ __skb_queue_tail(&port->tx_skb_q, skb);
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ queue_work(port->wq, &port->write_tohost_w);
+ break;
+ case SMUX_WRITE_DONE:
+ skb = meta_write->pkt_priv;
+ skb->data = meta_write->buffer;
+ dev_kfree_skb_any(skb);
+ queue_work(port->wq, &port->write_tomdm_w);
+ break;
+ case SMUX_READ_FAIL:
+ skb = meta_read->pkt_priv;
+ skb->data = meta_read->buffer;
+ dev_kfree_skb_any(skb);
+ break;
+ case SMUX_WRITE_FAIL:
+ skb = meta_write->pkt_priv;
+ skb->data = meta_write->buffer;
+ dev_kfree_skb_any(skb);
+ break;
+ case SMUX_HIGH_WM_HIT:
+ spin_lock_irqsave(&port->rx_lock, flags);
+ set_bit(TX_THROTTLED, &port->flags);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ case SMUX_LOW_WM_HIT:
+ spin_lock_irqsave(&port->rx_lock, flags);
+ clear_bit(TX_THROTTLED, &port->flags);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ queue_work(port->wq, &port->write_tomdm_w);
+ break;
+ case SMUX_TIOCM_UPDATE:
+ if (port->gtype == USB_GADGET_SERIAL) {
+ cbits = msm_smux_tiocm_get(port->ch_id);
+ ghsuart_dunctrl_status(port, cbits);
+ }
+ break;
+ default:
+ pr_err("%s:wrong event recieved\n", __func__);
+ }
+}
+
+static int ghsuart_get_rx_buffer(void *priv, void **pkt_priv,
+ void **buffer, int size)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+ *pkt_priv = skb;
+ *buffer = skb->data;
+
+ return 0;
+}
+
+static void ghsuart_data_connect_w(struct work_struct *w)
+{
+ struct ghsuart_data_port *port =
+ container_of(w, struct ghsuart_data_port, connect_w);
+ int ret;
+
+ if (!port || !atomic_read(&port->connected) ||
+ !test_bit(CH_READY, &port->channel_sts))
+ return;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ ret = msm_smux_open(port->ch_id, port, &ghsuart_notify_event,
+ &ghsuart_get_rx_buffer);
+ if (ret) {
+ pr_err("%s: unable to open smux ch:%d err:%d\n",
+ __func__, port->ch_id, ret);
+ return;
+ }
+}
+
+static void ghsuart_data_disconnect_w(struct work_struct *w)
+{
+ struct ghsuart_data_port *port =
+ container_of(w, struct ghsuart_data_port, disconnect_w);
+
+ if (!test_bit(CH_OPENED, &port->channel_sts))
+ return;
+
+ msm_smux_close(port->ch_id);
+ clear_bit(CH_OPENED, &port->channel_sts);
+}
+
+static void ghsuart_data_free_buffers(struct ghsuart_data_port *port)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ if (!port->in) {
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ return;
+ }
+
+ ghsuart_data_free_requests(port->in, &port->tx_idle);
+
+ while ((skb = __skb_dequeue(&port->tx_skb_q)))
+ dev_kfree_skb_any(skb);
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ if (!port->out) {
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ return;
+ }
+
+ ghsuart_data_free_requests(port->out, &port->rx_idle);
+
+ while ((skb = __skb_dequeue(&port->rx_skb_q)))
+ dev_kfree_skb_any(skb);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+}
+
+static int ghsuart_data_probe(struct platform_device *pdev)
+{
+ struct ghsuart_data_port *port;
+
+ pr_debug("%s: name:%s num_data_ports= %d\n",
+ __func__, pdev->name, num_data_ports);
+
+ if (pdev->id >= num_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = ghsuart_data_ports[pdev->id].port;
+ set_bit(CH_READY, &port->channel_sts);
+
+ /* if usb is online, try opening bridge */
+ if (atomic_read(&port->connected))
+ queue_work(port->wq, &port->connect_w);
+
+ return 0;
+}
+
+/* mdm disconnect */
+static int ghsuart_data_remove(struct platform_device *pdev)
+{
+ struct ghsuart_data_port *port;
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+ int ret;
+ struct gserial *gser = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ if (pdev->id >= num_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = ghsuart_data_ports[pdev->id].port;
+
+ ep_in = port->in;
+ if (ep_in)
+ usb_ep_fifo_flush(ep_in);
+
+ ep_out = port->out;
+ if (ep_out)
+ usb_ep_fifo_flush(ep_out);
+
+ ghsuart_data_free_buffers(port);
+
+ if (port->gtype == USB_GADGET_SERIAL) {
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser = port->port_usb;
+ port->cbits_tohost = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ if (gser && gser->disconnect)
+ gser->disconnect(gser);
+ }
+
+ ret = msm_smux_close(port->ch_id);
+ if (ret < 0)
+ pr_err("%s:Unable to close smux channel: %d\n",
+ __func__, port->ch_id);
+
+ clear_bit(CH_READY, &port->channel_sts);
+ clear_bit(CH_OPENED, &port->channel_sts);
+
+ return 0;
+}
+
+static void ghsuart_data_port_free(int portno)
+{
+ struct ghsuart_data_port *port = ghsuart_data_ports[portno].port;
+ struct platform_driver *pdrv = &ghsuart_data_ports[portno].pdrv;
+
+ destroy_workqueue(port->wq);
+ kfree(port);
+
+ if (pdrv)
+ platform_driver_unregister(pdrv);
+}
+
+static void
+ghsuart_send_controlbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+ struct ghsuart_data_port *port;
+
+ if (portno >= num_ctrl_ports || !gptr) {
+ pr_err("%s: Invalid portno#%d\n", __func__, portno);
+ return;
+ }
+
+ port = ghsuart_data_ports[portno].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return;
+ }
+
+ if (cbits == port->cbits_tomodem)
+ return;
+
+ port->cbits_tomodem = cbits;
+
+ if (!test_bit(CH_OPENED, &port->channel_sts))
+ return;
+
+ /* if DTR is high, update latest modem info to Host */
+ if (port->cbits_tomodem & ACM_CTRL_DTR) {
+ unsigned int i;
+
+ i = msm_smux_tiocm_get(port->ch_id);
+ ghsuart_dunctrl_status(port, i);
+ }
+
+ pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+ /* Send the control bits to the Modem */
+ msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
+}
+
+static int ghsuart_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+ struct ghsuart_data_port *port;
+ struct platform_driver *pdrv;
+
+ port = kzalloc(sizeof(struct ghsuart_data_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->wq = create_singlethread_workqueue(ghsuart_data_names[port_num]);
+ if (!port->wq) {
+ pr_err("%s: Unable to create workqueue:%s\n",
+ __func__, ghsuart_data_names[port_num]);
+ kfree(port);
+ return -ENOMEM;
+ }
+ port->port_num = port_num;
+
+ /* port initialization */
+ spin_lock_init(&port->port_lock);
+ spin_lock_init(&port->rx_lock);
+ spin_lock_init(&port->tx_lock);
+
+ INIT_WORK(&port->connect_w, ghsuart_data_connect_w);
+ INIT_WORK(&port->disconnect_w, ghsuart_data_disconnect_w);
+ INIT_WORK(&port->write_tohost_w, ghsuart_data_write_tohost);
+ INIT_WORK(&port->write_tomdm_w, ghsuart_data_write_tomdm);
+
+ INIT_LIST_HEAD(&port->tx_idle);
+ INIT_LIST_HEAD(&port->rx_idle);
+
+ skb_queue_head_init(&port->tx_skb_q);
+ skb_queue_head_init(&port->rx_skb_q);
+
+ port->gtype = gtype;
+ if (port->gtype == USB_GADGET_SERIAL)
+ port->ch_id = SMUX_USB_DUN_0;
+ else
+ port->ch_id = SMUX_USB_RMNET_DATA_0;
+ port->ctx = port;
+ ghsuart_data_ports[port_num].port = port;
+
+ pdrv = &ghsuart_data_ports[port_num].pdrv;
+ pdrv->probe = ghsuart_data_probe;
+ pdrv->remove = ghsuart_data_remove;
+ pdrv->driver.name = ghsuart_data_names[port_num];
+ pdrv->driver.owner = THIS_MODULE;
+
+ platform_driver_register(pdrv);
+
+ pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+ return 0;
+}
+
+void ghsuart_data_disconnect(void *gptr, int port_num)
+{
+ struct ghsuart_data_port *port;
+ unsigned long flags;
+ struct gserial *gser = NULL;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ port = ghsuart_data_ports[port_num].port;
+
+ if (port_num > num_data_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return;
+ }
+
+ if (!gptr || !port) {
+ pr_err("%s: port is null\n", __func__);
+ return;
+ }
+
+ ghsuart_data_free_buffers(port);
+
+ /* disable endpoints */
+ if (port->in)
+ usb_ep_disable(port->in);
+
+ if (port->out)
+ usb_ep_disable(port->out);
+
+ atomic_set(&port->connected, 0);
+
+ if (port->gtype == USB_GADGET_SERIAL) {
+ gser = gptr;
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser->notify_modem = 0;
+ port->cbits_tomodem = 0;
+ port->port_usb = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ port->in = NULL;
+ port->n_tx_req_queued = 0;
+ clear_bit(RX_THROTTLED, &port->flags);
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ port->out = NULL;
+ clear_bit(TX_THROTTLED, &port->flags);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsuart_data_connect(void *gptr, int port_num)
+{
+ struct ghsuart_data_port *port;
+ struct gserial *gser;
+ struct grmnet *gr;
+ unsigned long flags;
+ int ret = 0;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ port = ghsuart_data_ports[port_num].port;
+
+ if (port_num > num_data_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ if (!gptr || !port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ if (port->gtype == USB_GADGET_SERIAL) {
+ gser = gptr;
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ port->in = gser->in;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ port->out = gser->out;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+
+ port->tx_q_size = ghsuart_data_serial_tx_q_size;
+ port->rx_q_size = ghsuart_data_serial_rx_q_size;
+ gser->in->driver_data = port;
+ gser->out->driver_data = port;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser->notify_modem = ghsuart_send_controlbits_tomodem;
+ port->port_usb = gptr;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ } else {
+ gr = gptr;
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ port->in = gr->in;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ port->out = gr->out;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ port->tx_q_size = ghsuart_data_rmnet_tx_q_size;
+ port->rx_q_size = ghsuart_data_rmnet_rx_q_size;
+ gr->in->driver_data = port;
+ gr->out->driver_data = port;
+ }
+
+ ret = usb_ep_enable(port->in);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+ __func__, port->in);
+ goto fail;
+ }
+
+ ret = usb_ep_enable(port->out);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+ __func__, port->out);
+ usb_ep_disable(port->in);
+ goto fail;
+ }
+
+ atomic_set(&port->connected, 1);
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ port->to_host = 0;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ port->to_modem = 0;
+ port->tomodem_drp_cnt = 0;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ queue_work(port->wq, &port->connect_w);
+fail:
+ return ret;
+}
+
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ghsuart_data_read_stats(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct ghsuart_data_port *port;
+ struct platform_driver *pdrv;
+ char *buf;
+ unsigned long flags;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < num_data_ports; i++) {
+ port = ghsuart_data_ports[i].port;
+ if (!port)
+ continue;
+ pdrv = &ghsuart_data_ports[i].pdrv;
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\nName: %s\n"
+ "#PORT:%d port#: %p\n"
+ "data_ch_open: %d\n"
+ "data_ch_ready: %d\n"
+ "\n******UL INFO*****\n\n"
+ "dpkts_to_modem: %lu\n"
+ "tomodem_drp_cnt: %u\n"
+ "rx_buf_len: %u\n"
+ "TX_THROTTLED %d\n",
+ pdrv->driver.name,
+ i, port,
+ test_bit(CH_OPENED, &port->channel_sts),
+ test_bit(CH_READY, &port->channel_sts),
+ port->to_modem,
+ port->tomodem_drp_cnt,
+ port->rx_skb_q.qlen,
+ test_bit(TX_THROTTLED, &port->flags));
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\n******DL INFO******\n\n"
+ "dpkts_to_usbhost: %lu\n"
+ "tx_buf_len: %u\n"
+ "RX_THROTTLED %d\n",
+ port->to_host,
+ port->tx_skb_q.qlen,
+ test_bit(RX_THROTTLED, &port->flags));
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t ghsuart_data_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ghsuart_data_port *port;
+ int i;
+ unsigned long flags;
+
+ for (i = 0; i < num_data_ports; i++) {
+ port = ghsuart_data_ports[i].port;
+ if (!port)
+ continue;
+
+ spin_lock_irqsave(&port->rx_lock, flags);
+ port->to_modem = 0;
+ port->tomodem_drp_cnt = 0;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ port->to_host = 0;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ }
+ return count;
+}
+
+const struct file_operations ghsuart_data_stats_ops = {
+ .read = ghsuart_data_read_stats,
+ .write = ghsuart_data_reset_stats,
+};
+
+static struct dentry *ghsuart_data_dent;
+static int ghsuart_data_debugfs_init(void)
+{
+ struct dentry *ghsuart_data_dfile;
+
+ ghsuart_data_dent = debugfs_create_dir("ghsic_data_xport", 0);
+ if (!ghsuart_data_dent || IS_ERR(ghsuart_data_dent))
+ return -ENODEV;
+
+ ghsuart_data_dfile = debugfs_create_file("status", S_IRUGO | S_IWUSR,
+ ghsuart_data_dent, 0, &ghsuart_data_stats_ops);
+ if (!ghsuart_data_dfile || IS_ERR(ghsuart_data_dfile)) {
+ debugfs_remove(ghsuart_data_dent);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void ghsuart_data_debugfs_exit(void)
+{
+ debugfs_remove_recursive(ghsuart_data_dent);
+}
+
+int ghsuart_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+ int first_port_id = num_data_ports;
+ int total_num_ports = num_ports + num_data_ports;
+ int ret = 0;
+ int i;
+
+ if (!num_ports || total_num_ports > NUM_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, num_ports);
+ return -EINVAL;
+ }
+ pr_debug("%s: count: %d\n", __func__, num_ports);
+
+ for (i = first_port_id; i < total_num_ports; i++) {
+
+ /*probe can be called while port_alloc,so update no_data_ports*/
+ num_data_ports++;
+ ret = ghsuart_data_port_alloc(i, gtype);
+ if (ret) {
+ num_data_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_ports;
+ }
+ }
+
+ /*return the starting index*/
+ return first_port_id;
+
+free_ports:
+ for (i = first_port_id; i < num_data_ports; i++)
+ ghsuart_data_port_free(i);
+ num_data_ports = first_port_id;
+
+ return ret;
+}
+
+static int __init ghsuart_data_init(void)
+{
+ int ret;
+
+ ret = ghsuart_data_debugfs_init();
+ if (ret) {
+ pr_debug("mode debugfs file is not available");
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ghsuart_data_init);
+
+static void __exit ghsuart_data_exit(void)
+{
+ ghsuart_data_debugfs_exit();
+}
+module_exit(ghsuart_data_exit);
+
+MODULE_DESCRIPTION("hsuart data xport driver for DUN and RMNET");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/msm_charm.h b/include/linux/msm_charm.h
index 779fd38..c31e493 100644
--- a/include/linux/msm_charm.h
+++ b/include/linux/msm_charm.h
@@ -10,6 +10,7 @@
#define NORMAL_BOOT_DONE _IOW(CHARM_CODE, 5, int)
#define RAM_DUMP_DONE _IOW(CHARM_CODE, 6, int)
#define WAIT_FOR_RESTART _IOR(CHARM_CODE, 7, int)
+#define GET_DLOAD_STATUS _IOR(CHARM_CODE, 8, int)
enum charm_boot_type {
CHARM_NORMAL_BOOT = 0,