From 2489007e7d740ccbc3e0a202914e243ad5178787 Mon Sep 17 00:00:00 2001 From: codeworkx Date: Sat, 22 Sep 2012 09:48:20 +0200 Subject: merge opensource jb u5 Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2 --- drivers/misc/modem_if/modem_link_pm_usb.c | 183 +++++++++++++++++++++--------- 1 file changed, 129 insertions(+), 54 deletions(-) (limited to 'drivers/misc/modem_if/modem_link_pm_usb.c') diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index 244256f..1b2614e 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -27,15 +27,34 @@ #include "modem_link_pm_usb.h" +static inline void start_hub_work(struct link_pm_data *pm_data, int delay) +{ + if (pm_data->hub_work_running == false) { + pm_data->hub_work_running = true; + wake_lock(&pm_data->hub_lock); + mif_debug("link_pm_hub_work is started\n"); + } + + schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); +} + +static inline void end_hub_work(struct link_pm_data *pm_data) +{ + wake_unlock(&pm_data->hub_lock); + pm_data->hub_work_running = false; + mif_debug("link_pm_hub_work is done\n"); +} + bool link_pm_is_connected(struct usb_link_device *usb_ld) { if (has_hub(usb_ld)) { - if (usb_ld->link_pm_data->hub_init_lock) + struct link_pm_data *pm_data = usb_ld->link_pm_data; + if (pm_data->hub_init_lock) return false; - if (usb_ld->link_pm_data->hub_status != HUB_STATE_ACTIVE) { - schedule_delayed_work( - &usb_ld->link_pm_data->link_pm_hub, 0); + if (pm_data->hub_status == HUB_STATE_OFF) { + if (pm_data->hub_work_running == false) + start_hub_work(pm_data, 0); return false; } } @@ -48,27 +67,40 @@ bool link_pm_is_connected(struct usb_link_device *usb_ld) return true; } +void link_pm_preactive(struct link_pm_data *pm_data) +{ + if (pm_data->root_hub) { + mif_info("pre-active\n"); + pm_data->hub_on_retry_cnt = 0; + complete(&pm_data->hub_active); + pm_runtime_put_sync(pm_data->root_hub); + } + + pm_data->hub_status = HUB_STATE_ACTIVE; +} + static void link_pm_hub_work(struct work_struct *work) { int err; struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_hub.work); - if (pm_data->hub_status == HUB_STATE_ACTIVE) + if (pm_data->hub_status == HUB_STATE_ACTIVE) { + end_hub_work(pm_data); return; + } if (!pm_data->port_enable) { mif_err("mif: hub power func not assinged\n"); + end_hub_work(pm_data); return; } - wake_lock(&pm_data->hub_lock); /* If kernel if suspend, wait the ehci resume */ if (pm_data->dpm_suspending) { mif_info("dpm_suspending\n"); - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(500)); - goto exit; + start_hub_work(pm_data, 500); + return; } switch (pm_data->hub_status) { @@ -88,11 +120,11 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); - goto exit; + end_hub_work(pm_data); + } else { + /* resume root hub */ + start_hub_work(pm_data, 100); } - /* resume root hub */ - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(100)); break; case HUB_STATE_RESUMMING: if (pm_data->hub_on_retry_cnt++ > 50) { @@ -100,34 +132,26 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); + end_hub_work(pm_data); + } else { + mif_info("hub resumming: %d\n", + pm_data->hub_on_retry_cnt); + start_hub_work(pm_data, 200); } - mif_trace("hub resumming\n"); - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(200)); - break; - case HUB_STATE_PREACTIVE: - pm_data->hub_status = HUB_STATE_ACTIVE; - mif_trace("hub active\n"); - pm_data->hub_on_retry_cnt = 0; - wake_unlock(&pm_data->hub_lock); - complete(&pm_data->hub_active); - if (pm_data->root_hub) - pm_runtime_put_sync(pm_data->root_hub); break; } exit: return; } -static int link_pm_hub_standby(struct link_pm_data *pm_data) +static int link_pm_hub_standby(void *args) { + struct link_pm_data *pm_data = args; struct usb_link_device *usb_ld = pm_data->usb_ld; int err = 0; - mif_info("wait hub standby\n"); - if (!pm_data->port_enable) { - mif_err("hub power func not assinged\n"); + mif_err("port power func not assinged\n"); return -ENODEV; } @@ -136,6 +160,13 @@ static int link_pm_hub_standby(struct link_pm_data *pm_data) mif_err("hub off fail err=%d\n", err); pm_data->hub_status = HUB_STATE_OFF; + + /* this function is atomic. + * make force disconnect in workqueue.. + */ + if (pm_data->usb_ld->if_usb_connected) + schedule_work(&usb_ld->disconnect_work); + return err; } @@ -169,6 +200,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, { int value, err = 0; struct link_pm_data *pm_data = file->private_data; + struct usb_link_device *usb_ld = pm_data->usb_ld; mif_info("cmd: 0x%08x\n", cmd); @@ -182,15 +214,13 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); case IOCTL_LINK_CONNECTED: - return pm_data->usb_ld->if_usb_connected; - case IOCTL_LINK_PORT_ON: /* hub only */ + return usb_ld->if_usb_connected; + case IOCTL_LINK_PORT_ON: /* ignore cp host wakeup irq, set the hub_init_lock when AP try CP off and release hub_init_lock when CP boot done */ pm_data->hub_init_lock = 0; - if (pm_data->root_hub) { - pm_runtime_resume(pm_data->root_hub); - pm_runtime_forbid(pm_data->root_hub->parent); - } + if (pm_data->root_hub) + pm_runtime_get_sync(pm_data->root_hub); if (pm_data->port_enable) { err = pm_data->port_enable(2, 1); if (err < 0) { @@ -200,17 +230,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, pm_data->hub_status = HUB_STATE_RESUMMING; } break; - case IOCTL_LINK_PORT_OFF: /* hub only */ - if (pm_data->usb_ld->if_usb_connected) { - struct usb_device *udev = - pm_data->usb_ld->usbdev->parent; - pm_runtime_get_sync(&udev->dev); - if (udev->state != USB_STATE_NOTATTACHED) { - usb_force_disconnect(udev); - pr_info("force disconnect maybe cp-reset!!\n"); - } - pm_runtime_put_autosuspend(&udev->dev); - } + case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { mif_err("usb3503 active fail\n"); @@ -218,7 +238,6 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, } pm_data->hub_init_lock = 1; pm_data->hub_handshake_done = 0; - break; default: break; @@ -227,6 +246,50 @@ exit: return err; } +static ssize_t show_autosuspend(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct link_pm_data *pm_data = container_of(miscdev, + struct link_pm_data, miscdev); + struct usb_link_device *usb_ld = pm_data->usb_ld; + + p += sprintf(buf, "%s\n", pm_data->autosuspend ? "on" : "off"); + + return p - buf; +} + +static ssize_t store_autosuspend(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct link_pm_data *pm_data = container_of(miscdev, + struct link_pm_data, miscdev); + struct usb_link_device *usb_ld = pm_data->usb_ld; + struct task_struct *task = get_current(); + char taskname[TASK_COMM_LEN]; + + mif_info("autosuspend: %s: %s(%d)'\n", + buf, get_task_comm(taskname, task), task->pid); + + if (!strncmp(buf, "on", 2)) { + pm_data->autosuspend = true; + if (usb_ld->usbdev) + pm_runtime_allow(&usb_ld->usbdev->dev); + } else if (!strncmp(buf, "off", 3)) { + pm_data->autosuspend = false; + if (usb_ld->usbdev) + pm_runtime_forbid(&usb_ld->usbdev->dev); + } + + return count; +} + +static struct device_attribute attr_autosuspend = + __ATTR(autosuspend, S_IRUGO | S_IWUSR, + show_autosuspend, store_autosuspend); + static int link_pm_open(struct inode *inode, struct file *file) { struct link_pm_data *pm_data = @@ -253,11 +316,13 @@ static int link_pm_notifier_event(struct notifier_block *this, { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); + struct usb_link_device *usb_ld = pm_data->usb_ld; switch (event) { case PM_SUSPEND_PREPARE: pm_data->dpm_suspending = true; - link_pm_hub_standby(pm_data); + if (has_hub(usb_ld)) + link_pm_hub_standby(pm_data); return NOTIFY_OK; case PM_POST_SUSPEND: pm_data->dpm_suspending = false; @@ -286,12 +351,12 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->gpio_link_slavewake = pm_pdata->gpio_link_slavewake; pm_data->link_reconnect = pm_pdata->link_reconnect; pm_data->port_enable = pm_pdata->port_enable; - pm_data->cpufreq_lock = pm_pdata->cpufreq_lock; - pm_data->cpufreq_unlock = pm_pdata->cpufreq_unlock; + pm_data->freq_lock = pm_pdata->freq_lock; + pm_data->freq_unlock = pm_pdata->freq_unlock; pm_data->autosuspend_delay_ms = pm_pdata->autosuspend_delay_ms; + pm_data->autosuspend = true; pm_data->usb_ld = usb_ld; - pm_data->link_pm_active = false; usb_ld->link_pm_data = pm_data; pm_data->miscdev.minor = MISC_DYNAMIC_MINOR; @@ -304,6 +369,13 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) goto err_misc_register; } + err = device_create_file(pm_data->miscdev.this_device, + &attr_autosuspend); + if (err) { + mif_err("fail to create file: autosuspend: %d\n", err); + goto err_create_file; + } + pm_data->hub_init_lock = 1; irq = gpio_to_irq(usb_ld->pdata->gpio_host_wakeup); err = request_threaded_irq(irq, NULL, usb_resume_irq, @@ -319,12 +391,16 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) if (has_hub(usb_ld)) { init_completion(&pm_data->hub_active); pm_data->hub_status = HUB_STATE_OFF; - pm_pdata->p_hub_status = &pm_data->hub_status; pm_data->hub_handshake_done = 0; pm_data->root_hub = NULL; + + pm_pdata->hub_standby = link_pm_hub_standby; + pm_pdata->hub_pm_data = pm_data; + wake_lock_init(&pm_data->hub_lock, WAKE_LOCK_SUSPEND, "modem_hub_enum_lock"); INIT_DELAYED_WORK(&pm_data->link_pm_hub, link_pm_hub_work); + pm_data->hub_work_running = false; } pm_data->pm_notifier.notifier_call = link_pm_notifier_event; @@ -333,10 +409,9 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) return 0; err_request_irq: +err_create_file: misc_deregister(&pm_data->miscdev); err_misc_register: kfree(pm_data); return err; } - - -- cgit v1.1