diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /drivers/input/touchscreen/ts4800-ts.c | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/input/touchscreen/ts4800-ts.c')
| -rw-r--r-- | drivers/input/touchscreen/ts4800-ts.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c new file mode 100644 index 000000000..98422d1e8 --- /dev/null +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -0,0 +1,222 @@ +/* + * Touchscreen driver for the TS-4800 board + * + * Copyright (c) 2015 - Savoir-faire Linux + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* polling interval in ms */ +#define POLL_INTERVAL 3 + +#define DEBOUNCE_COUNT 1 + +/* sensor values are 12-bit wide */ +#define MAX_12BIT ((1 << 12) - 1) + +#define PENDOWN_MASK 0x1 + +#define X_OFFSET 0x0 +#define Y_OFFSET 0x2 + +struct ts4800_ts { + struct input_dev *input; + struct device *dev; + char phys[32]; + + void __iomem *base; + struct regmap *regmap; + unsigned int reg; + unsigned int bit; + + bool pendown; + int debounce; +}; + +static int ts4800_ts_open(struct input_dev *input_dev) +{ + struct ts4800_ts *ts = input_get_drvdata(input_dev); + int error; + + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + + error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (error) { + dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error); + return error; + } + + return 0; +} + +static void ts4800_ts_close(struct input_dev *input_dev) +{ + struct ts4800_ts *ts = input_get_drvdata(input_dev); + int ret; + + ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); + if (ret) + dev_warn(ts->dev, "Failed to disable touchscreen\n"); + +} + +static void ts4800_ts_poll(struct input_dev *input_dev) +{ + struct ts4800_ts *ts = input_get_drvdata(input_dev); + u16 last_x = readw(ts->base + X_OFFSET); + u16 last_y = readw(ts->base + Y_OFFSET); + bool pendown = last_x & PENDOWN_MASK; + + if (pendown) { + if (ts->debounce) { + ts->debounce--; + return; + } + + if (!ts->pendown) { + input_report_key(input_dev, BTN_TOUCH, 1); + ts->pendown = true; + } + + last_x = ((~last_x) >> 4) & MAX_12BIT; + last_y = ((~last_y) >> 4) & MAX_12BIT; + + input_report_abs(input_dev, ABS_X, last_x); + input_report_abs(input_dev, ABS_Y, last_y); + input_sync(input_dev); + } else if (ts->pendown) { + ts->pendown = false; + ts->debounce = DEBOUNCE_COUNT; + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static int ts4800_parse_dt(struct platform_device *pdev, + struct ts4800_ts *ts) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + u32 reg, bit; + int error; + + struct device_node *syscon_np __free(device_node) = + of_parse_phandle(np, "syscon", 0); + if (!syscon_np) { + dev_err(dev, "no syscon property\n"); + return -ENODEV; + } + + ts->regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(ts->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(ts->regmap); + } + + error = of_property_read_u32_index(np, "syscon", 1, ®); + if (error < 0) { + dev_err(dev, "no offset in syscon\n"); + return error; + } + + ts->reg = reg; + + error = of_property_read_u32_index(np, "syscon", 2, &bit); + if (error < 0) { + dev_err(dev, "no bit in syscon\n"); + return error; + } + + ts->bit = BIT(bit); + + return 0; +} + +static int ts4800_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct ts4800_ts *ts; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + error = ts4800_parse_dt(pdev, ts); + if (error) + return error; + + ts->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ts->base)) + return PTR_ERR(ts->base); + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); + ts->input = input_dev; + ts->dev = &pdev->dev; + + input_set_drvdata(input_dev, ts); + + input_dev->name = "TS-4800 Touchscreen"; + input_dev->phys = ts->phys; + + input_dev->open = ts4800_ts_open; + input_dev->close = ts4800_ts_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + + error = input_setup_polling(input_dev, ts4800_ts_poll); + if (error) { + dev_err(&pdev->dev, "Unable to set up polling: %d\n", error); + return error; + } + + input_set_poll_interval(input_dev, POLL_INTERVAL); + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "Unable to register input device: %d\n", error); + return error; + } + + return 0; +} + +static const struct of_device_id ts4800_ts_of_match[] = { + { .compatible = "technologic,ts4800-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts4800_ts_of_match); + +static struct platform_driver ts4800_ts_driver = { + .driver = { + .name = "ts4800-ts", + .of_match_table = ts4800_ts_of_match, + }, + .probe = ts4800_ts_probe, +}; +module_platform_driver(ts4800_ts_driver); + +MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ts4800_ts"); |
