aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/mdm_hsic_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/mdm_hsic_pm.c')
-rw-r--r--arch/arm/mach-exynos/mdm_hsic_pm.c249
1 files changed, 226 insertions, 23 deletions
diff --git a/arch/arm/mach-exynos/mdm_hsic_pm.c b/arch/arm/mach-exynos/mdm_hsic_pm.c
index a8dd153..0ec7531 100644
--- a/arch/arm/mach-exynos/mdm_hsic_pm.c
+++ b/arch/arm/mach-exynos/mdm_hsic_pm.c
@@ -35,16 +35,24 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/ehci_def.h>
-#include <mach/mdm2.h>
-#include <linux/kernel.h>
#ifdef CONFIG_CPU_FREQ_TETHERING
+#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <mach/mdm2.h>
#endif
-
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
#include <linux/usb/android_composite.h>
#endif
+#ifdef CONFIG_USBIRQ_BALANCING_LTE_HIGHTP
+#include <mach/mdm2.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq_pegasusq.h>
+#define dev_put devput
+#include <linux/netdevice.h>
+#undef dev_put
+#include <mach/dev.h>
+#endif
#define EXTERNAL_MODEM "external_modem"
#define EHCI_REG_DUMP
@@ -96,6 +104,12 @@ struct mdm_hsic_pm_data {
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
struct notifier_block usb_composite_notifier;
#endif
+#ifdef CONFIG_USBIRQ_BALANCING_LTE_HIGHTP
+ struct notifier_block rndis_notifier;
+ struct notifier_block cpu_hotplug_notifier;
+ struct delayed_work hotplug_work;
+ bool is_rndis_running;
+#endif
bool block_request;
bool state_busy;
@@ -126,6 +140,9 @@ struct mdm_hsic_pm_data {
struct delayed_work fast_dormancy_work;
struct mdm_hsic_pm_platform_data *mdm_pdata;
+
+ /* QMICM mode value */
+ bool qmicm_mode;
};
/* indicate wakeup from lpa state */
@@ -288,16 +305,30 @@ int pm_dev_wait_lpa_wake(void)
return 0;
}
+void set_shutdown(void)
+{
+ struct mdm_hsic_pm_data *pm_data =
+ get_pm_data_by_dev_name("mdm_hsic_pm0");
+
+ pm_data->shutdown = true;
+}
+
void notify_modem_fatal(void)
{
struct mdm_hsic_pm_data *pm_data =
get_pm_data_by_dev_name("mdm_hsic_pm0");
pr_info("%s or shutdown\n", __func__);
+ print_mdm_gpio_state();
if (!pm_data || !pm_data->intf_cnt || !pm_data->udev)
return;
+ if (pm_data->shutdown == true) {
+ pr_info("During shutdown, return %s\n", __func__);
+ return;
+ }
+
pm_data->shutdown = true;
/* crash from sleep, ehci is in waking up, so do not control ehci */
@@ -373,7 +404,6 @@ void request_active_lock_release(const char *name)
pr_info("%s\n", __func__);
if (pm_data)
wake_unlock(&pm_data->l2_wake);
-
}
void request_boot_lock_set(const char *name)
@@ -411,13 +441,22 @@ void set_host_stat(const char *name, enum pwr_stat status)
return;
}
+ /* crash during kernel suspend/resume, do not control host ready pin */
+ /* and it has to be controlled when host driver initialized again */
+ if (pm_data->block_request && pm_data->shutdown)
+ return;
+
if (pm_data->gpio_host_ready) {
pr_info("dev rdy val = %d\n",
gpio_get_value(pm_data->gpio_device_ready));
pr_info("%s:set host port power status to [%d]\n",
__func__, status);
- /*10ms delay location moved*/
+ /*
+ * need get some delay for MDM9x15 suspend
+ * if L3 drive goes out to modem in suspending
+ * modem goes to unstable PM state. now 10 ms is enough
+ */
if(status == POWER_OFF)
mdelay(10);
@@ -438,6 +477,10 @@ int wait_dev_pwr_stat(const char *name, enum pwr_stat status)
return -ENODEV;
}
+ /* in shutdown(including modem fatal) do not need to wait dev ready */
+ if (pm_data->shutdown)
+ return 0;
+
pr_info("%s:[%s]...\n", __func__, status ? "PWR ON" : "PWR OFF");
if (pm_data->gpio_device_ready) {
@@ -452,8 +495,10 @@ int wait_dev_pwr_stat(const char *name, enum pwr_stat status)
if (gpio_get_value(pm_data->gpio_device_ready) == status)
pr_info(" done\n");
- else
+ else {
subsystem_restart(EXTERNAL_MODEM);
+ return -ETIMEDOUT;
+ }
return 0;
}
@@ -488,30 +533,23 @@ int check_udev_suspend_allowed(const char *name)
int set_hsic_lpa_states(int states)
{
+ struct mdm_hsic_pm_data *pm_data =
+ get_pm_data_by_dev_name("mdm_hsic_pm0");
/* if modem need to check survive, get status in variable */
int val = 1;
+ int ret = 0;
/* set state for LPA enter */
if (val) {
switch (states) {
case STATE_HSIC_LPA_ENTER:
- /*
- * need get some delay for MDM9x15 suspend
- * if L3 drive goes out to modem in suspending
- * modem goes to unstable PM state. now 10 ms is enough
- */
- /*10ms delay location moved*/
- //mdelay(10);
set_host_stat("mdm_hsic_pm0", POWER_OFF);
- wait_dev_pwr_stat("mdm_hsic_pm0", POWER_OFF);
+ ret = wait_dev_pwr_stat("mdm_hsic_pm0", POWER_OFF);
+ if (ret)
+ return ret;
pr_info("set hsic lpa enter\n");
break;
case STATE_HSIC_LPA_WAKE:
- /* host control is done by ehci runtime resume code */
- #if 0
- set_host_stat("mdm_hsic_pm0", POWER_ON);
- wait_dev_pwr_stat("mdm_hsic_pm0", POWER_ON);
- #endif
lpa_handling = true;
pr_info("%s: set lpa handling to true\n", __func__);
request_active_lock_set("mdm_hsic_pm0");
@@ -528,6 +566,13 @@ int set_hsic_lpa_states(int states)
return 1;
else
return 0;
+ case STATE_HSIC_LPA_ENABLE:
+ if (lpcharge)
+ return 0;
+ else if (pm_data)
+ return pm_data->shutdown;
+ else
+ return 1;
default:
pr_info("unknown lpa state\n");
break;
@@ -536,6 +581,24 @@ int set_hsic_lpa_states(int states)
return 0;
}
+bool mdm_check_main_connect(const char *name)
+{
+ /* find pm device from list by name */
+ struct mdm_hsic_pm_data *pm_data = get_pm_data_by_dev_name(name);
+
+ if (!pm_data) {
+ pr_err("%s:no pm device(%s)\n", __func__, name);
+ return false;
+ }
+
+ print_pm_dev_info(pm_data);
+
+ if (pm_data->intf_cnt >= 3)
+ return true;
+ else
+ return false;
+}
+
#define PM_START_DELAY_MS 3000
int register_udev_to_pm_dev(const char *name, struct usb_device *udev)
{
@@ -556,6 +619,7 @@ int register_udev_to_pm_dev(const char *name, struct usb_device *udev)
pm_data->udev = udev;
atomic_set(&pm_data->pmlock_cnt, 0);
usb_disable_autosuspend(udev);
+ pm_data->shutdown = false;
#ifdef CONFIG_SIM_DETECT
get_sim_state_at_boot();
#endif
@@ -574,6 +638,22 @@ int register_udev_to_pm_dev(const char *name, struct usb_device *udev)
return 0;
}
+int set_qmicm_mode(const char *name)
+{
+ /* find pm device from list by name */
+ struct mdm_hsic_pm_data *pm_data = get_pm_data_by_dev_name(name);
+
+ if (!pm_data) {
+ pr_err("%s:no pm device(%s) exist\n", __func__, name);
+ return -ENODEV;
+ }
+
+ pm_data->qmicm_mode = true;
+ pr_info("%s: set QMICM mode\n", __func__);
+
+ return 0;
+}
+
/* force fatal for debug when HSIC disconnect */
extern void mdm_force_fatal(void);
@@ -986,9 +1066,7 @@ static int link_pm_netdev_event(struct notifier_block *this,
}
return NOTIFY_DONE;
}
-#endif
-#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
static int usb_composite_notifier_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -1014,7 +1092,123 @@ static int usb_composite_notifier_event(struct notifier_block *this,
return NOTIFY_DONE;
}
#endif
+#ifdef CONFIG_USBIRQ_BALANCING_LTE_HIGHTP
+int boost_busfreq(struct device *dev, int enable)
+{
+ int ret = 0;
+ unsigned int busfreq = 440220; // T0
+ struct device *busdev = NULL;
+
+ if (dev == NULL)
+ return -ENODEV;
+
+ busdev = dev_get("exynos-busfreq");
+ if (busdev == NULL)
+ return -ENODEV;
+
+ if (enable)
+ ret = dev_lock(busdev, dev, busfreq);
+ else
+ ret = dev_unlock(busdev, dev);
+
+ return ret;
+}
+
+// only for T0 USB HOST
+int clear_cpu0_from_usbhost_irq(int enable)
+{
+ unsigned int irq = IRQ_USB_HOST;
+// unsigned int irq = IRQ_USB_HSOTG;
+
+ cpumask_var_t new_value;
+ int err = 0;
+
+ if (!irq_can_set_affinity(irq))
+ return -EIO;
+
+ if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
+ return -ENOMEM;
+
+ cpumask_setall(new_value);
+
+ if (enable) {
+ cpumask_and(new_value, new_value, cpu_online_mask);
+ cpumask_clear_cpu(0, new_value);
+ }
+
+ if (cpumask_intersects(new_value, cpu_online_mask)) {
+ err = irq_set_affinity(irq, new_value);
+ }
+
+ free_cpumask:
+ free_cpumask_var(new_value);
+ return err;
+}
+
+static int link_pm_rndis_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct mdm_hsic_pm_data *pm_data =
+ container_of(this, struct mdm_hsic_pm_data, rndis_notifier);
+ struct mdm_hsic_pm_platform_data *mdm_pdata = pm_data->mdm_pdata;
+ struct net_device *dev = ptr;
+
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+ if (!strncmp(dev->name, "rndis", 5)) {
+ switch (event) {
+ case NETDEV_UP:
+ if (mdm_pdata && mdm_pdata->dev)
+ boost_busfreq(mdm_pdata->dev, 1);
+ cpufreq_pegasusq_min_cpu_lock(2);
+ clear_cpu0_from_usbhost_irq(1);
+ pm_data->is_rndis_running = true;
+ pr_info("%s: %s UP\n", __func__, dev->name);
+ break;
+ case NETDEV_DOWN:
+ pm_data->is_rndis_running = false;
+ clear_cpu0_from_usbhost_irq(0);
+ cpufreq_pegasusq_min_cpu_unlock();
+ if (mdm_pdata && mdm_pdata->dev)
+ boost_busfreq(mdm_pdata->dev, 0);
+ pr_info("%s: %s DOWN\n", __func__, dev->name);
+ break;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static void hotplug_work_start(struct work_struct *work)
+{
+ struct mdm_hsic_pm_data *pm_data =
+ container_of(work, struct mdm_hsic_pm_data,
+ hotplug_work.work);
+ clear_cpu0_from_usbhost_irq(1);
+}
+
+static int hotplug_notify_callback(struct notifier_block *this,
+ unsigned long action, void *hcpu)
+{
+ struct mdm_hsic_pm_data *pm_data =
+ container_of(this, struct mdm_hsic_pm_data, cpu_hotplug_notifier);
+
+ if (pm_data->is_rndis_running) {
+ switch (action) {
+
+ case CPU_POST_DEAD:
+ if (1 == num_online_cpus())
+ {
+ cpufreq_pegasusq_min_cpu_lock(2);
+ queue_delayed_work(pm_data->wq, &pm_data->hotplug_work,
+ msecs_to_jiffies(100));
+ }
+ break;
+ }
+ }
+ return NOTIFY_OK;
+}
+#endif
static int mdm_hsic_pm_probe(struct platform_device *pdev)
{
int ret;
@@ -1077,18 +1271,27 @@ static int mdm_hsic_pm_probe(struct platform_device *pdev)
#ifdef CONFIG_CPU_FREQ_TETHERING
pm_data->netdev_notifier.notifier_call = link_pm_netdev_event;
register_netdevice_notifier(&pm_data->netdev_notifier);
-#endif
-#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
pm_data->usb_composite_notifier.notifier_call =
usb_composite_notifier_event;
register_usb_composite_notifier(&pm_data->usb_composite_notifier);
#endif
+#ifdef CONFIG_USBIRQ_BALANCING_LTE_HIGHTP
+ pm_data->is_rndis_running = false;
+ INIT_DELAYED_WORK(&pm_data->hotplug_work, hotplug_work_start);
+
+ pm_data->rndis_notifier.notifier_call = link_pm_rndis_event;
+ register_netdevice_notifier(&pm_data->rndis_notifier);
+
+ pm_data->cpu_hotplug_notifier.notifier_call = hotplug_notify_callback;
+ register_cpu_notifier(&pm_data->cpu_hotplug_notifier);
+#endif
wake_lock_init(&pm_data->l2_wake, WAKE_LOCK_SUSPEND, pm_data->name);
wake_lock_init(&pm_data->boot_wake, WAKE_LOCK_SUSPEND, "mdm_boot");
wake_lock_init(&pm_data->fd_wake, WAKE_LOCK_SUSPEND, "fast_dormancy");
pm_data->fd_wake_time = DEFAULT_RAW_WAKE_TIME;
+ pm_data->qmicm_mode = false;
print_pm_dev_info(pm_data);
list_add(&pm_data->list, &hsic_pm_dev_list);