2019-05-29 07:17:58 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-02-28 01:51:40 -03:00
|
|
|
/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/interrupt.h>
|
2017-10-05 09:11:06 -03:00
|
|
|
#include <linux/gpio/consumer.h>
|
2012-02-28 01:51:40 -03:00
|
|
|
#include <linux/slab.h>
|
2013-10-18 00:07:15 -03:00
|
|
|
#include <linux/of.h>
|
2013-02-08 18:47:30 -03:00
|
|
|
#include <linux/of_gpio.h>
|
2012-02-28 01:51:40 -03:00
|
|
|
#include <linux/platform_device.h>
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include <linux/pm_qos.h>
|
2012-02-28 01:51:40 -03:00
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <media/rc-core.h>
|
|
|
|
|
|
|
|
#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
|
|
|
|
|
|
|
|
struct gpio_rc_dev {
|
|
|
|
struct rc_dev *rcdev;
|
2017-09-07 20:41:32 -03:00
|
|
|
struct gpio_desc *gpiod;
|
|
|
|
int irq;
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
struct device *pmdev;
|
|
|
|
struct pm_qos_request qos;
|
2012-02-28 01:51:40 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
|
|
|
|
{
|
2017-09-07 20:41:32 -03:00
|
|
|
int val;
|
2012-02-28 01:51:40 -03:00
|
|
|
struct gpio_rc_dev *gpio_dev = dev_id;
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
struct device *pmdev = gpio_dev->pmdev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For some cpuidle systems, not all:
|
|
|
|
* Respond to interrupt taking more latency when cpu in idle.
|
|
|
|
* Invoke asynchronous pm runtime get from interrupt context,
|
|
|
|
* this may introduce a millisecond delay to call resume callback,
|
|
|
|
* where to disable cpuilde.
|
|
|
|
*
|
|
|
|
* Two issues lead to fail to decode first frame, one is latency to
|
|
|
|
* respond to interrupt, another is delay introduced by async api.
|
|
|
|
*/
|
|
|
|
if (pmdev)
|
|
|
|
pm_runtime_get(pmdev);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
2017-09-07 20:41:32 -03:00
|
|
|
val = gpiod_get_value(gpio_dev->gpiod);
|
|
|
|
if (val >= 0)
|
|
|
|
ir_raw_event_store_edge(gpio_dev->rcdev, val == 1);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
if (pmdev) {
|
|
|
|
pm_runtime_mark_last_busy(pmdev);
|
|
|
|
pm_runtime_put_autosuspend(pmdev);
|
|
|
|
}
|
|
|
|
|
2012-02-28 01:51:40 -03:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2012-12-21 13:17:53 -08:00
|
|
|
static int gpio_ir_recv_probe(struct platform_device *pdev)
|
2012-02-28 01:51:40 -03:00
|
|
|
{
|
2017-09-07 20:34:35 -03:00
|
|
|
struct device *dev = &pdev->dev;
|
2017-09-07 20:39:45 -03:00
|
|
|
struct device_node *np = dev->of_node;
|
2012-02-28 01:51:40 -03:00
|
|
|
struct gpio_rc_dev *gpio_dev;
|
|
|
|
struct rc_dev *rcdev;
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
u32 period = 0;
|
2012-02-28 01:51:40 -03:00
|
|
|
int rc;
|
|
|
|
|
2017-09-07 20:39:45 -03:00
|
|
|
if (!np)
|
|
|
|
return -ENODEV;
|
2012-02-28 01:51:40 -03:00
|
|
|
|
2017-09-07 20:35:22 -03:00
|
|
|
gpio_dev = devm_kzalloc(dev, sizeof(*gpio_dev), GFP_KERNEL);
|
2012-02-28 01:51:40 -03:00
|
|
|
if (!gpio_dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-09-07 20:41:32 -03:00
|
|
|
gpio_dev->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
|
2022-09-19 23:58:43 +08:00
|
|
|
if (IS_ERR(gpio_dev->gpiod))
|
|
|
|
return dev_err_probe(dev, PTR_ERR(gpio_dev->gpiod),
|
|
|
|
"error getting gpio\n");
|
2017-09-07 20:41:32 -03:00
|
|
|
gpio_dev->irq = gpiod_to_irq(gpio_dev->gpiod);
|
|
|
|
if (gpio_dev->irq < 0)
|
|
|
|
return gpio_dev->irq;
|
2017-09-07 20:39:45 -03:00
|
|
|
|
2017-09-07 20:36:11 -03:00
|
|
|
rcdev = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW);
|
2017-09-07 20:35:22 -03:00
|
|
|
if (!rcdev)
|
|
|
|
return -ENOMEM;
|
2012-02-28 01:51:40 -03:00
|
|
|
|
2012-06-18 15:02:20 -03:00
|
|
|
rcdev->priv = gpio_dev;
|
2017-07-01 12:13:19 -04:00
|
|
|
rcdev->device_name = GPIO_IR_DEVICE_NAME;
|
2012-06-18 15:02:20 -03:00
|
|
|
rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
|
2012-02-28 01:51:40 -03:00
|
|
|
rcdev->input_id.bustype = BUS_HOST;
|
2012-06-18 15:02:20 -03:00
|
|
|
rcdev->input_id.vendor = 0x0001;
|
|
|
|
rcdev->input_id.product = 0x0001;
|
|
|
|
rcdev->input_id.version = 0x0100;
|
2017-09-07 20:34:35 -03:00
|
|
|
rcdev->dev.parent = dev;
|
2017-09-07 20:39:14 -03:00
|
|
|
rcdev->driver_name = KBUILD_MODNAME;
|
2017-01-26 14:37:33 -02:00
|
|
|
rcdev->min_timeout = 1;
|
2015-09-23 11:07:08 -03:00
|
|
|
rcdev->timeout = IR_DEFAULT_TIMEOUT;
|
|
|
|
rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
|
2017-09-07 20:39:45 -03:00
|
|
|
rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
|
|
|
|
rcdev->map_name = of_get_property(np, "linux,rc-map-name", NULL);
|
|
|
|
if (!rcdev->map_name)
|
|
|
|
rcdev->map_name = RC_MAP_EMPTY;
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
gpio_dev->rcdev = rcdev;
|
|
|
|
|
2017-09-07 20:37:07 -03:00
|
|
|
rc = devm_rc_register_device(dev, rcdev);
|
2012-02-28 01:51:40 -03:00
|
|
|
if (rc < 0) {
|
2017-09-07 20:34:35 -03:00
|
|
|
dev_err(dev, "failed to register rc device (%d)\n", rc);
|
2017-09-07 20:37:07 -03:00
|
|
|
return rc;
|
2012-02-28 01:51:40 -03:00
|
|
|
}
|
|
|
|
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
of_property_read_u32(np, "linux,autosuspend-period", &period);
|
|
|
|
if (period) {
|
|
|
|
gpio_dev->pmdev = dev;
|
|
|
|
pm_runtime_set_autosuspend_delay(dev, period);
|
|
|
|
pm_runtime_use_autosuspend(dev);
|
|
|
|
pm_runtime_set_suspended(dev);
|
|
|
|
pm_runtime_enable(dev);
|
|
|
|
}
|
|
|
|
|
2012-02-28 01:51:40 -03:00
|
|
|
platform_set_drvdata(pdev, gpio_dev);
|
|
|
|
|
2017-09-07 20:41:32 -03:00
|
|
|
return devm_request_irq(dev, gpio_dev->irq, gpio_ir_recv_irq,
|
2017-09-07 20:38:20 -03:00
|
|
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
|
|
|
"gpio-ir-recv-irq", gpio_dev);
|
2012-02-28 01:51:40 -03:00
|
|
|
}
|
|
|
|
|
media: rc: gpio-ir-recv: add remove function
In case runtime PM is enabled, do runtime PM clean up to remove
cpu latency qos request, otherwise driver removal may have below
kernel dump:
[ 19.463299] Unable to handle kernel NULL pointer dereference at
virtual address 0000000000000048
[ 19.472161] Mem abort info:
[ 19.474985] ESR = 0x0000000096000004
[ 19.478754] EC = 0x25: DABT (current EL), IL = 32 bits
[ 19.484081] SET = 0, FnV = 0
[ 19.487149] EA = 0, S1PTW = 0
[ 19.490361] FSC = 0x04: level 0 translation fault
[ 19.495256] Data abort info:
[ 19.498149] ISV = 0, ISS = 0x00000004
[ 19.501997] CM = 0, WnR = 0
[ 19.504977] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000049f81000
[ 19.511432] [0000000000000048] pgd=0000000000000000,
p4d=0000000000000000
[ 19.518245] Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
[ 19.524520] Modules linked in: gpio_ir_recv(+) rc_core [last
unloaded: rc_core]
[ 19.531845] CPU: 0 PID: 445 Comm: insmod Not tainted
6.2.0-rc1-00028-g2c397a46d47c #72
[ 19.531854] Hardware name: FSL i.MX8MM EVK board (DT)
[ 19.531859] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS
BTYPE=--)
[ 19.551777] pc : cpu_latency_qos_remove_request+0x20/0x110
[ 19.557277] lr : gpio_ir_recv_runtime_suspend+0x18/0x30
[gpio_ir_recv]
[ 19.557294] sp : ffff800008ce3740
[ 19.557297] x29: ffff800008ce3740 x28: 0000000000000000 x27:
ffff800008ce3d50
[ 19.574270] x26: ffffc7e3e9cea100 x25: 00000000000f4240 x24:
ffffc7e3f9ef0e30
[ 19.574284] x23: 0000000000000000 x22: ffff0061803820f4 x21:
0000000000000008
[ 19.574296] x20: ffffc7e3fa75df30 x19: 0000000000000020 x18:
ffffffffffffffff
[ 19.588570] x17: 0000000000000000 x16: ffffc7e3f9efab70 x15:
ffffffffffffffff
[ 19.595712] x14: ffff800008ce37b8 x13: ffff800008ce37aa x12:
0000000000000001
[ 19.602853] x11: 0000000000000001 x10: ffffcbe3ec0dff87 x9 :
0000000000000008
[ 19.609991] x8 : 0101010101010101 x7 : 0000000000000000 x6 :
000000000f0bfe9f
[ 19.624261] x5 : 00ffffffffffffff x4 : 0025ab8e00000000 x3 :
ffff006180382010
[ 19.631405] x2 : ffffc7e3e9ce8030 x1 : ffffc7e3fc3eb810 x0 :
0000000000000020
[ 19.638548] Call trace:
[ 19.640995] cpu_latency_qos_remove_request+0x20/0x110
[ 19.646142] gpio_ir_recv_runtime_suspend+0x18/0x30 [gpio_ir_recv]
[ 19.652339] pm_generic_runtime_suspend+0x2c/0x44
[ 19.657055] __rpm_callback+0x48/0x1dc
[ 19.660807] rpm_callback+0x6c/0x80
[ 19.664301] rpm_suspend+0x10c/0x640
[ 19.667880] rpm_idle+0x250/0x2d0
[ 19.671198] update_autosuspend+0x38/0xe0
[ 19.675213] pm_runtime_set_autosuspend_delay+0x40/0x60
[ 19.680442] gpio_ir_recv_probe+0x1b4/0x21c [gpio_ir_recv]
[ 19.685941] platform_probe+0x68/0xc0
[ 19.689610] really_probe+0xc0/0x3dc
[ 19.693189] __driver_probe_device+0x7c/0x190
[ 19.697550] driver_probe_device+0x3c/0x110
[ 19.701739] __driver_attach+0xf4/0x200
[ 19.705578] bus_for_each_dev+0x70/0xd0
[ 19.709417] driver_attach+0x24/0x30
[ 19.712998] bus_add_driver+0x17c/0x240
[ 19.716834] driver_register+0x78/0x130
[ 19.720676] __platform_driver_register+0x28/0x34
[ 19.725386] gpio_ir_recv_driver_init+0x20/0x1000 [gpio_ir_recv]
[ 19.731404] do_one_initcall+0x44/0x2ac
[ 19.735243] do_init_module+0x48/0x1d0
[ 19.739003] load_module+0x19fc/0x2034
[ 19.742759] __do_sys_finit_module+0xac/0x12c
[ 19.747124] __arm64_sys_finit_module+0x20/0x30
[ 19.751664] invoke_syscall+0x48/0x114
[ 19.755420] el0_svc_common.constprop.0+0xcc/0xec
[ 19.760132] do_el0_svc+0x38/0xb0
[ 19.763456] el0_svc+0x2c/0x84
[ 19.766516] el0t_64_sync_handler+0xf4/0x120
[ 19.770789] el0t_64_sync+0x190/0x194
[ 19.774460] Code: 910003fd a90153f3 aa0003f3 91204021 (f9401400)
[ 19.780556] ---[ end trace 0000000000000000 ]---
Signed-off-by: Li Jun <jun.li@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-01-11 10:39:21 +01:00
|
|
|
static int gpio_ir_recv_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
|
|
|
|
struct device *pmdev = gpio_dev->pmdev;
|
|
|
|
|
|
|
|
if (pmdev) {
|
|
|
|
pm_runtime_get_sync(pmdev);
|
|
|
|
cpu_latency_qos_remove_request(&gpio_dev->qos);
|
|
|
|
|
|
|
|
pm_runtime_disable(pmdev);
|
|
|
|
pm_runtime_put_noidle(pmdev);
|
|
|
|
pm_runtime_set_suspended(pmdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-28 01:51:40 -03:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int gpio_ir_recv_suspend(struct device *dev)
|
|
|
|
{
|
2017-09-07 20:41:32 -03:00
|
|
|
struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
if (device_may_wakeup(dev))
|
2017-09-07 20:41:32 -03:00
|
|
|
enable_irq_wake(gpio_dev->irq);
|
2012-02-28 01:51:40 -03:00
|
|
|
else
|
2017-09-07 20:41:32 -03:00
|
|
|
disable_irq(gpio_dev->irq);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpio_ir_recv_resume(struct device *dev)
|
|
|
|
{
|
2017-09-07 20:41:32 -03:00
|
|
|
struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
if (device_may_wakeup(dev))
|
2017-09-07 20:41:32 -03:00
|
|
|
disable_irq_wake(gpio_dev->irq);
|
2012-02-28 01:51:40 -03:00
|
|
|
else
|
2017-09-07 20:41:32 -03:00
|
|
|
enable_irq(gpio_dev->irq);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
static int gpio_ir_recv_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
cpu_latency_qos_remove_request(&gpio_dev->qos);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpio_ir_recv_runtime_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
cpu_latency_qos_add_request(&gpio_dev->qos, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-28 01:51:40 -03:00
|
|
|
static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
|
|
|
|
.suspend = gpio_ir_recv_suspend,
|
|
|
|
.resume = gpio_ir_recv_resume,
|
media: rc: gpio-ir-recv: add QoS support for cpuidle system
GPIO IR receive is much rely on interrupt response, uneven interrupt
latency will lead to incorrect timing, so the decoder fails to decode
it. The issue is particularly acute on some systems which support
cpuidle, not all, dynamically disable and enable cpuidle can solve this
problem to a great extent.
However, there is a downside to this approach, the measurement of header
on the first frame may incorrect. Test on i.MX8M serials, when enable
cpuidle, interrupt latency could be about 500us.
With this patch:
1. has no side effect on non-cpuidle system, even runtime pm api won't
be invoked to avoid a bunch of pm busy work for devices that do not need
it, including spinlocks, ktime, etc.
2. latency is still much longer for the first gpio interrupt on cpuidle
system, so the first frame may not be decoded. Generally, RC would transmit
multiple frames at once press, we can sacrifice the first frame.
3. add "linux,autosuspend-period" property in device tree if you also
suffer this cpuidle issue.
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-22 21:08:07 +02:00
|
|
|
.runtime_suspend = gpio_ir_recv_runtime_suspend,
|
|
|
|
.runtime_resume = gpio_ir_recv_runtime_resume,
|
2012-02-28 01:51:40 -03:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2017-09-07 20:39:45 -03:00
|
|
|
static const struct of_device_id gpio_ir_recv_of_match[] = {
|
|
|
|
{ .compatible = "gpio-ir-receiver", },
|
|
|
|
{ },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);
|
|
|
|
|
2012-02-28 01:51:40 -03:00
|
|
|
static struct platform_driver gpio_ir_recv_driver = {
|
|
|
|
.probe = gpio_ir_recv_probe,
|
media: rc: gpio-ir-recv: add remove function
In case runtime PM is enabled, do runtime PM clean up to remove
cpu latency qos request, otherwise driver removal may have below
kernel dump:
[ 19.463299] Unable to handle kernel NULL pointer dereference at
virtual address 0000000000000048
[ 19.472161] Mem abort info:
[ 19.474985] ESR = 0x0000000096000004
[ 19.478754] EC = 0x25: DABT (current EL), IL = 32 bits
[ 19.484081] SET = 0, FnV = 0
[ 19.487149] EA = 0, S1PTW = 0
[ 19.490361] FSC = 0x04: level 0 translation fault
[ 19.495256] Data abort info:
[ 19.498149] ISV = 0, ISS = 0x00000004
[ 19.501997] CM = 0, WnR = 0
[ 19.504977] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000049f81000
[ 19.511432] [0000000000000048] pgd=0000000000000000,
p4d=0000000000000000
[ 19.518245] Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
[ 19.524520] Modules linked in: gpio_ir_recv(+) rc_core [last
unloaded: rc_core]
[ 19.531845] CPU: 0 PID: 445 Comm: insmod Not tainted
6.2.0-rc1-00028-g2c397a46d47c #72
[ 19.531854] Hardware name: FSL i.MX8MM EVK board (DT)
[ 19.531859] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS
BTYPE=--)
[ 19.551777] pc : cpu_latency_qos_remove_request+0x20/0x110
[ 19.557277] lr : gpio_ir_recv_runtime_suspend+0x18/0x30
[gpio_ir_recv]
[ 19.557294] sp : ffff800008ce3740
[ 19.557297] x29: ffff800008ce3740 x28: 0000000000000000 x27:
ffff800008ce3d50
[ 19.574270] x26: ffffc7e3e9cea100 x25: 00000000000f4240 x24:
ffffc7e3f9ef0e30
[ 19.574284] x23: 0000000000000000 x22: ffff0061803820f4 x21:
0000000000000008
[ 19.574296] x20: ffffc7e3fa75df30 x19: 0000000000000020 x18:
ffffffffffffffff
[ 19.588570] x17: 0000000000000000 x16: ffffc7e3f9efab70 x15:
ffffffffffffffff
[ 19.595712] x14: ffff800008ce37b8 x13: ffff800008ce37aa x12:
0000000000000001
[ 19.602853] x11: 0000000000000001 x10: ffffcbe3ec0dff87 x9 :
0000000000000008
[ 19.609991] x8 : 0101010101010101 x7 : 0000000000000000 x6 :
000000000f0bfe9f
[ 19.624261] x5 : 00ffffffffffffff x4 : 0025ab8e00000000 x3 :
ffff006180382010
[ 19.631405] x2 : ffffc7e3e9ce8030 x1 : ffffc7e3fc3eb810 x0 :
0000000000000020
[ 19.638548] Call trace:
[ 19.640995] cpu_latency_qos_remove_request+0x20/0x110
[ 19.646142] gpio_ir_recv_runtime_suspend+0x18/0x30 [gpio_ir_recv]
[ 19.652339] pm_generic_runtime_suspend+0x2c/0x44
[ 19.657055] __rpm_callback+0x48/0x1dc
[ 19.660807] rpm_callback+0x6c/0x80
[ 19.664301] rpm_suspend+0x10c/0x640
[ 19.667880] rpm_idle+0x250/0x2d0
[ 19.671198] update_autosuspend+0x38/0xe0
[ 19.675213] pm_runtime_set_autosuspend_delay+0x40/0x60
[ 19.680442] gpio_ir_recv_probe+0x1b4/0x21c [gpio_ir_recv]
[ 19.685941] platform_probe+0x68/0xc0
[ 19.689610] really_probe+0xc0/0x3dc
[ 19.693189] __driver_probe_device+0x7c/0x190
[ 19.697550] driver_probe_device+0x3c/0x110
[ 19.701739] __driver_attach+0xf4/0x200
[ 19.705578] bus_for_each_dev+0x70/0xd0
[ 19.709417] driver_attach+0x24/0x30
[ 19.712998] bus_add_driver+0x17c/0x240
[ 19.716834] driver_register+0x78/0x130
[ 19.720676] __platform_driver_register+0x28/0x34
[ 19.725386] gpio_ir_recv_driver_init+0x20/0x1000 [gpio_ir_recv]
[ 19.731404] do_one_initcall+0x44/0x2ac
[ 19.735243] do_init_module+0x48/0x1d0
[ 19.739003] load_module+0x19fc/0x2034
[ 19.742759] __do_sys_finit_module+0xac/0x12c
[ 19.747124] __arm64_sys_finit_module+0x20/0x30
[ 19.751664] invoke_syscall+0x48/0x114
[ 19.755420] el0_svc_common.constprop.0+0xcc/0xec
[ 19.760132] do_el0_svc+0x38/0xb0
[ 19.763456] el0_svc+0x2c/0x84
[ 19.766516] el0t_64_sync_handler+0xf4/0x120
[ 19.770789] el0t_64_sync+0x190/0x194
[ 19.774460] Code: 910003fd a90153f3 aa0003f3 91204021 (f9401400)
[ 19.780556] ---[ end trace 0000000000000000 ]---
Signed-off-by: Li Jun <jun.li@nxp.com>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-01-11 10:39:21 +01:00
|
|
|
.remove = gpio_ir_recv_remove,
|
2012-02-28 01:51:40 -03:00
|
|
|
.driver = {
|
2017-09-07 20:39:14 -03:00
|
|
|
.name = KBUILD_MODNAME,
|
2013-02-08 18:47:30 -03:00
|
|
|
.of_match_table = of_match_ptr(gpio_ir_recv_of_match),
|
2012-02-28 01:51:40 -03:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.pm = &gpio_ir_recv_pm_ops,
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
};
|
2012-06-18 15:03:06 -03:00
|
|
|
module_platform_driver(gpio_ir_recv_driver);
|
2012-02-28 01:51:40 -03:00
|
|
|
|
|
|
|
MODULE_DESCRIPTION("GPIO IR Receiver driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|