From 8e0469a4f3e647059c0ef8db961140ee25246fbd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 28 Jun 2016 11:35:02 +0300 Subject: stm class: Add runtime power management handling Currently, there's no runtime pm in stm class devices, which makes it harder for the underlying hardware drivers to handle their power management. This patch applies the following runtime pm policy to stm class devices, which their parents can rely on for their power management tracking: * device is in use during character device writes, * delayed autosuspend is used to keep it active between adjacent writes, * device is in use while mmio regions are mapped, * device is is use while any stm_source devices are linked to it. Signed-off-by: Alexander Shishkin Reviewed-by: Mathieu Poirier Cc: Chunyan Zhang --- drivers/hwtracing/stm/core.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index ff31108b066f..51f81d64ca37 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -15,6 +15,7 @@ * as defined in MIPI STPv2 specification. */ +#include #include #include #include @@ -482,14 +483,40 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, return -EFAULT; } + pm_runtime_get_sync(&stm->dev); + count = stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf, count); + pm_runtime_mark_last_busy(&stm->dev); + pm_runtime_put_autosuspend(&stm->dev); kfree(kbuf); return count; } +static void stm_mmap_open(struct vm_area_struct *vma) +{ + struct stm_file *stmf = vma->vm_file->private_data; + struct stm_device *stm = stmf->stm; + + pm_runtime_get(&stm->dev); +} + +static void stm_mmap_close(struct vm_area_struct *vma) +{ + struct stm_file *stmf = vma->vm_file->private_data; + struct stm_device *stm = stmf->stm; + + pm_runtime_mark_last_busy(&stm->dev); + pm_runtime_put_autosuspend(&stm->dev); +} + +static const struct vm_operations_struct stm_mmap_vmops = { + .open = stm_mmap_open, + .close = stm_mmap_close, +}; + static int stm_char_mmap(struct file *file, struct vm_area_struct *vma) { struct stm_file *stmf = file->private_data; @@ -514,8 +541,11 @@ static int stm_char_mmap(struct file *file, struct vm_area_struct *vma) if (!phys) return -EINVAL; + pm_runtime_get_sync(&stm->dev); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = &stm_mmap_vmops; vm_iomap_memory(vma, phys, size); return 0; @@ -701,6 +731,17 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; + /* + * Use delayed autosuspend to avoid bouncing back and forth + * on recurring character device writes, with the initial + * delay time of 2 seconds. + */ + pm_runtime_no_callbacks(&stm->dev); + pm_runtime_use_autosuspend(&stm->dev); + pm_runtime_set_autosuspend_delay(&stm->dev, 2000); + pm_runtime_set_suspended(&stm->dev); + pm_runtime_enable(&stm->dev); + return 0; err_device: @@ -724,6 +765,9 @@ void stm_unregister_device(struct stm_data *stm_data) struct stm_source_device *src, *iter; int i, ret; + pm_runtime_dont_use_autosuspend(&stm->dev); + pm_runtime_disable(&stm->dev); + mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { ret = __stm_source_link_drop(src, stm); @@ -878,6 +922,8 @@ static int __stm_source_link_drop(struct stm_source_device *src, stm_output_free(link, &src->output); list_del_init(&src->link_entry); + pm_runtime_mark_last_busy(&link->dev); + pm_runtime_put_autosuspend(&link->dev); /* matches stm_find_device() from stm_source_link_store() */ stm_put_device(link); rcu_assign_pointer(src->link, NULL); @@ -971,8 +1017,11 @@ static ssize_t stm_source_link_store(struct device *dev, if (!link) return -EINVAL; + pm_runtime_get(&link->dev); + err = stm_source_link_add(src, link); if (err) { + pm_runtime_put_autosuspend(&link->dev); /* matches the stm_find_device() above */ stm_put_device(link); } @@ -1033,6 +1082,9 @@ int stm_source_register_device(struct device *parent, if (err) goto err; + pm_runtime_no_callbacks(&src->dev); + pm_runtime_forbid(&src->dev); + err = device_add(&src->dev); if (err) goto err; -- cgit v1.2.3 From 142dfeb20209607659ca85f15e7a3dd592a6dd20 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 22 Jun 2016 13:48:21 +0300 Subject: intel_th: Add runtime power management handling Currently, an Intel TH (pci) device will be always active, because the devices on the 'intel_th' bus don't implement runtime pm to track their usage. To address this, this patch adds runtime pm support to the 'intel_th' bus and some additional bits for the hub. The 'output' type device is in use while a capture is active; the 'source' type device (STH) relies on its child stm class device for runtime pm tracking. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 54 ++++++++++++++++++++++++++++++++------- drivers/hwtracing/intel_th/gth.c | 16 +++++++++++- 2 files changed, 60 insertions(+), 10 deletions(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 1be543e8e42f..fdd17636d109 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "intel_th.h" @@ -67,23 +68,33 @@ static int intel_th_probe(struct device *dev) hubdrv = to_intel_th_driver(hub->dev.driver); + pm_runtime_set_active(dev); + pm_runtime_no_callbacks(dev); + pm_runtime_enable(dev); + ret = thdrv->probe(to_intel_th_device(dev)); if (ret) - return ret; + goto out_pm; if (thdrv->attr_group) { ret = sysfs_create_group(&thdev->dev.kobj, thdrv->attr_group); - if (ret) { - thdrv->remove(thdev); - - return ret; - } + if (ret) + goto out; } if (thdev->type == INTEL_TH_OUTPUT && !intel_th_output_assigned(thdev)) + /* does not talk to hardware */ ret = hubdrv->assign(hub, thdev); +out: + if (ret) + thdrv->remove(thdev); + +out_pm: + if (ret) + pm_runtime_disable(dev); + return ret; } @@ -103,6 +114,8 @@ static int intel_th_remove(struct device *dev) if (thdrv->attr_group) sysfs_remove_group(&thdev->dev.kobj, thdrv->attr_group); + pm_runtime_get_sync(dev); + thdrv->remove(thdev); if (intel_th_output_assigned(thdev)) { @@ -110,9 +123,14 @@ static int intel_th_remove(struct device *dev) to_intel_th_driver(dev->parent->driver); if (hub->dev.driver) + /* does not talk to hardware */ hubdrv->unassign(hub, thdev); } + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; } @@ -185,6 +203,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + int ret = 0; if (!thdrv) return -ENODEV; @@ -192,12 +211,17 @@ static int intel_th_output_activate(struct intel_th_device *thdev) if (!try_module_get(thdrv->driver.owner)) return -ENODEV; + pm_runtime_get_sync(&thdev->dev); + if (thdrv->activate) - return thdrv->activate(thdev); + ret = thdrv->activate(thdev); + else + intel_th_trace_enable(thdev); - intel_th_trace_enable(thdev); + if (ret) + pm_runtime_put(&thdev->dev); - return 0; + return ret; } static void intel_th_output_deactivate(struct intel_th_device *thdev) @@ -213,6 +237,7 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) else intel_th_trace_disable(thdev); + pm_runtime_put(&thdev->dev); module_put(thdrv->driver.owner); } @@ -628,6 +653,10 @@ intel_th_alloc(struct device *dev, struct resource *devres, dev_set_drvdata(dev, th); + pm_runtime_no_callbacks(dev); + pm_runtime_put(dev); + pm_runtime_allow(dev); + err = intel_th_populate(th, devres, ndevres, irq); if (err) goto err_chrdev; @@ -635,6 +664,8 @@ intel_th_alloc(struct device *dev, struct resource *devres, return th; err_chrdev: + pm_runtime_forbid(dev); + __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, "intel_th/output"); @@ -658,6 +689,9 @@ void intel_th_free(struct intel_th *th) intel_th_device_remove(th->hub); + pm_runtime_get_sync(th->dev); + pm_runtime_forbid(th->dev); + __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, "intel_th/output"); @@ -682,6 +716,7 @@ int intel_th_trace_enable(struct intel_th_device *thdev) if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) return -EINVAL; + pm_runtime_get_sync(&thdev->dev); hubdrv->enable(hub, &thdev->output); return 0; @@ -702,6 +737,7 @@ int intel_th_trace_disable(struct intel_th_device *thdev) return -EINVAL; hubdrv->disable(hub, &thdev->output); + pm_runtime_put(&thdev->dev); return 0; } diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 9beea0b54231..4106eaf13123 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "intel_th.h" #include "gth.h" @@ -190,6 +191,11 @@ static ssize_t master_attr_store(struct device *dev, if (old_port >= 0) { gth->master[ma->master] = -1; clear_bit(ma->master, gth->output[old_port].master); + + /* + * if the port is active, program this setting, + * implies that runtime PM is on + */ if (gth->output[old_port].output->active) gth_master_set(gth, ma->master, -1); } @@ -204,7 +210,7 @@ static ssize_t master_attr_store(struct device *dev, set_bit(ma->master, gth->output[port].master); - /* if the port is active, program this setting */ + /* if the port is active, program this setting, see above */ if (gth->output[port].output->active) gth_master_set(gth, ma->master, port); } @@ -326,11 +332,15 @@ static ssize_t output_attr_show(struct device *dev, struct gth_device *gth = oa->gth; size_t count; + pm_runtime_get_sync(dev); + spin_lock(>h->gth_lock); count = snprintf(buf, PAGE_SIZE, "%x\n", gth_output_parm_get(gth, oa->port, oa->parm)); spin_unlock(>h->gth_lock); + pm_runtime_put(dev); + return count; } @@ -346,10 +356,14 @@ static ssize_t output_attr_store(struct device *dev, if (kstrtouint(buf, 16, &config) < 0) return -EINVAL; + pm_runtime_get_sync(dev); + spin_lock(>h->gth_lock); gth_output_parm_set(gth, oa->port, oa->parm, config); spin_unlock(>h->gth_lock); + pm_runtime_put(dev); + return count; } -- cgit v1.2.3 From 53c189f1762ba392b5c24a2f06382d5eb1399c25 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 28 Jun 2016 11:34:03 +0300 Subject: intel_th: gth: Fix a source comment There's a kerneldoc comment that'd been derived from another one by way of copying-and-pasting but hadn't been subsequently amended to reflect the purpose of the function. Fix this. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/gth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 4106eaf13123..33e09369a491 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -465,7 +465,7 @@ static int intel_th_output_attributes(struct gth_device *gth) } /** - * intel_th_gth_disable() - enable tracing to an output device + * intel_th_gth_disable() - disable tracing to an output device * @thdev: GTH device * @output: output device's descriptor * -- cgit v1.2.3 From bd581f239f2f190141820caa24c83bc72c82a347 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 29 Jun 2016 19:35:22 +0300 Subject: intel_th: Document output device callbacks 'output' type device callbacks are missing from the kerneldoc description of the 'intel_th_driver' structure. Fix this. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/intel_th.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/hwtracing') diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 0df22e30673d..20f57aa4d8f5 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -114,6 +114,9 @@ intel_th_output_assigned(struct intel_th_device *thdev) * @unassign: deassociate an output type device from an output port * @enable: enable tracing for a given output device * @disable: disable tracing for a given output device + * @irq: interrupt callback + * @activate: enable tracing on the output's side + * @deactivate: disable tracing on the output's side * @fops: file operations for device nodes * @attr_group: attributes provided by the driver * -- cgit v1.2.3