diff options
Diffstat (limited to 'arch/arm/mach-exynos/mdm_hsic_pm.c')
-rw-r--r-- | arch/arm/mach-exynos/mdm_hsic_pm.c | 249 |
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); |