aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/mdm2.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/mdm2.c')
-rw-r--r--arch/arm/mach-exynos/mdm2.c273
1 files changed, 188 insertions, 85 deletions
diff --git a/arch/arm/mach-exynos/mdm2.c b/arch/arm/mach-exynos/mdm2.c
index 249b7cf..bb66e2e 100644
--- a/arch/arm/mach-exynos/mdm2.c
+++ b/arch/arm/mach-exynos/mdm2.c
@@ -36,7 +36,6 @@
#include <asm/uaccess.h>
#include <mach/mdm2.h>
#include <mach/restart.h>
-#include <mach/subsystem_notif.h>
#include <mach/subsystem_restart.h>
#include <linux/msm_charm.h>
#ifndef CONFIG_ARCH_EXYNOS
@@ -47,119 +46,227 @@
#include "mdm_private.h"
#include <linux/wakelock.h>
-#define MDM_MODEM_TIMEOUT 6000
-#define MDM_HOLD_TIME 4000
-#define MDM_MODEM_DELTA 100
+#define MDM_PBLRDY_CNT 20
static int mdm_debug_on;
static int power_on_count;
+/* ehci driver already loaded by kernel init */
static int hsic_peripheral_status = 1;
static DEFINE_MUTEX(hsic_status_lock);
static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv)
{
- pr_err("%s\n", __func__);
+ struct mdm_platform_data *pdata;
+
+ pr_info("%s\n", __func__);
+
+ if (!mdm_drv || !mdm_drv->pdata)
+ return;
+
+ pdata = mdm_drv->pdata;
+
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);
+
+ /* exynos usb phy contgrol seq
+ * ehci on -> ohci on -> ohci off -> ehci on
+ */
+ if (pdata->peripheral_platform_device_ehci)
+ platform_device_add(pdata->peripheral_platform_device_ehci);
+
+ /* timing problem occurs when it is controlled after ohci on
+ * control ap2mdm_wake signal right after ehci host power on
+ */
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1);
+
+ if (pdata->peripheral_platform_device_ohci)
+ platform_device_add(pdata->peripheral_platform_device_ohci);
+
hsic_peripheral_status = 1;
out:
mutex_unlock(&hsic_status_lock);
- pr_err("%s : ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
}
-static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
+void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
{
- pr_err("%s\n", __func__);
+ struct mdm_platform_data *pdata;
+
+ pr_info("%s\n", __func__);
+
+ if (!mdm_drv || !mdm_drv->pdata)
+ return;
+
+ pdata = mdm_drv->pdata;
+
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);
+
+ /* exynos usb phy contgrol seq
+ * ehci on -> ohci on -> ohci off -> ehci on
+ */
+ if (pdata->peripheral_platform_device_ohci)
+ platform_device_del(pdata->peripheral_platform_device_ohci);
+ if (pdata->peripheral_platform_device_ehci)
+ platform_device_del(pdata->peripheral_platform_device_ehci);
+
hsic_peripheral_status = 0;
out:
mutex_unlock(&hsic_status_lock);
- pr_err("%s : ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
}
-static void power_on_mdm(struct mdm_modem_drv *mdm_drv)
+static void mdm_toggle_soft_reset(struct mdm_modem_drv *mdm_drv)
{
- power_on_count++;
+ int soft_reset_direction_assert = 0,
+ soft_reset_direction_de_assert = 1;
- pr_err("%s: power count %d\n", __func__, power_on_count);
- /* this gpio will be used to indicate apq readiness,
- * de-assert it now so that it can asserted later
- */
- gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
+ if (mdm_drv->pdata->soft_reset_inverted) {
+ soft_reset_direction_assert = 1;
+ soft_reset_direction_de_assert = 0;
+ }
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction_assert);
+ usleep_range(5000, 10000);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction_de_assert);
+ msleep(20);
+}
- /* 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 (power_on_count == 2)
- return;
+static void mdm_power_down_common(struct mdm_modem_drv *mdm_drv)
+{
+ int i;
+ int soft_reset_direction =
+ mdm_drv->pdata->soft_reset_inverted ? 1 : 0;
+
+ pr_info("%s\n", __func__);
+ /* Wait for the modem to complete its power down actions. */
+ for (i = 20; i > 0; i--) {
+ if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
+ break;
+ msleep(100);
+ }
+ if (i == 0) {
+ pr_err("%s: MDM2AP_STATUS never went low. Doing a hard reset\n",
+ __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
+ soft_reset_direction);
+ /*
+ * Currently, there is a debounce timer on the charm PMIC. It is
+ * necessary to hold the PMIC RESET low for ~3.5 seconds
+ * for the reset to fully take place. Sleep here to ensure the
+ * reset has occured before the function exits.
+ */
+ msleep(4000);
+ }
mdm_peripheral_disconnect(mdm_drv);
+}
- /* Pull RESET gpio low and wait for it to settle. */
- pr_info("Pulling RESET gpio low\n");
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 0);
- usleep_range(5000, 10000);
+static void mdm_do_first_power_on(struct mdm_modem_drv *mdm_drv)
+{
+ int i;
+ int pblrdy;
- /* Deassert RESET first and wait for ir to settle. */
- pr_info("%s: Pulling RESET gpio high\n", __func__);
- gpio_direction_output(mdm_drv->ap2mdm_pmic_reset_n_gpio, 1);
- msleep(20);
+ if (power_on_count != 1) {
+ pr_err("%s: Calling fn when power_on_count != 1\n",
+ __func__);
+ return;
+ }
- /* 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.
+ pr_err("%s: Powering on modem for the first time\n", __func__);
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
+ mdm_peripheral_disconnect(mdm_drv);
+
+ /* If this is the first power-up after a panic, the modem may still
+ * be in a power-on state, in which case we need to toggle the gpio
+ * instead of just de-asserting it. No harm done if the modem was
+ * powered down.
*/
- 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_range(1000, 1000);
- }
+ mdm_toggle_soft_reset(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);
}
-#ifdef CONFIG_ARCH_EXYNOS
+ /* first power charged after 10ms */
+ usleep_range(10000, 15000);
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
-#endif
- mdm_peripheral_connect(mdm_drv);
+ if (!mdm_drv->mdm2ap_pblrdy)
+ goto start_mdm_peripheral;
+
+ for (i = 0; i < MDM_PBLRDY_CNT; i++) {
+ pblrdy = gpio_get_value(mdm_drv->mdm2ap_pblrdy);
+ if (pblrdy)
+ break;
+ usleep_range(5000, 5000);
+ }
+
+ pr_debug("%s: i:%d\n", __func__, i);
+
+start_mdm_peripheral:
+ mdm_peripheral_connect(mdm_drv);
msleep(200);
}
-static void power_down_mdm(struct mdm_modem_drv *mdm_drv)
+static void mdm_do_soft_power_on(struct mdm_modem_drv *mdm_drv)
{
int i;
+ int pblrdy;
- pr_err("%s\n", __func__);
- 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)
+ pr_err("%s: soft resetting mdm modem\n", __func__);
+ mdm_peripheral_disconnect(mdm_drv);
+ mdm_toggle_soft_reset(mdm_drv);
+
+ if (!mdm_drv->mdm2ap_pblrdy)
+ goto start_mdm_peripheral;
+
+ for (i = 0; i < MDM_PBLRDY_CNT; i++) {
+ pblrdy = gpio_get_value(mdm_drv->mdm2ap_pblrdy);
+ if (pblrdy)
break;
+ usleep_range(5000, 5000);
}
- 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);
+
+ pr_debug("%s: i:%d\n", __func__, i);
+
+start_mdm_peripheral:
+ 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 be asserted later.
+ * May not be used.
+ */
+ if (mdm_drv->ap2mdm_wakeup_gpio > 0)
+ gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
+
+ /*
+ * 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 (mdm_drv->pdata->early_power_on &&
+ (power_on_count == 2))
+ return;
+
+ if (power_on_count == 1)
+ mdm_do_first_power_on(mdm_drv);
+ else
+ mdm_do_soft_power_on(mdm_drv);
}
#ifdef CONFIG_ARCH_EXYNOS
@@ -179,18 +286,18 @@ static void mdm_status_changed(struct mdm_modem_drv *mdm_drv, int value)
{
pr_debug("%s: value:%d\n", __func__, value);
- pr_err("%s: ap2mdm_status = %d\n", __func__,
- gpio_get_value(mdm_drv->ap2mdm_status_gpio));
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,
#ifdef CONFIG_ARCH_EXYNOS
@@ -198,19 +305,9 @@ static struct mdm_ops mdm_cb = {
#endif
};
-/* temprary wakelock, remove when L3 state implemented */
-#ifdef CONFIG_ARCH_EXYNOS
-static struct wake_lock mdm_wake;
-#endif
-
static int __init mdm_modem_probe(struct platform_device *pdev)
{
- pr_err("%s\n", __func__);
-/* temprary wakelock, remove when L3 state implemented */
-#ifdef CONFIG_ARCH_EXYNOS
- wake_lock_init(&mdm_wake, WAKE_LOCK_SUSPEND, "mdm_wake");
- wake_lock(&mdm_wake);
-#endif
+ pr_info("%s\n", __func__);
return mdm_common_create(pdev, &mdm_cb);
}
@@ -226,7 +323,10 @@ static void mdm_modem_shutdown(struct platform_device *pdev)
static struct platform_driver mdm_modem_driver = {
.remove = mdm_modem_remove,
- .shutdown = mdm_modem_shutdown,
+ /**
+ * shutdown has done at reboot notifier
+ *.shutdown = mdm_modem_shutdown,
+ */
.driver = {
.name = "mdm2_modem",
.owner = THIS_MODULE
@@ -235,6 +335,9 @@ static struct platform_driver mdm_modem_driver = {
static int __init mdm_modem_init(void)
{
+ /* in lpm mode, do not load modem driver */
+ if (lpcharge)
+ return 0;
return platform_driver_probe(&mdm_modem_driver, mdm_modem_probe);
}