diff options
author | Armin Wolf <W_Armin@gmx.de> | 2023-09-07 08:26:37 +0300 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2023-10-27 17:27:24 +0300 |
commit | a54fe61639d9f3b6765fee32edda7cfceb6d705a (patch) | |
tree | 9709cc7d9bec857e1cfdfc4f1a422ce0906078c0 /drivers/hwmon/sch56xx-common.c | |
parent | 7da8a635436029957c5350da3acf51d78ed64071 (diff) | |
download | linux-a54fe61639d9f3b6765fee32edda7cfceb6d705a.tar.xz |
hwmon: (sch5627) Use regmap for pwm map register caching
Accessing virtual registers is very inefficient, so pwm map values
should be cached when possible, else userspace could effectively do
a DOS attack by reading pwm map values in a while loop.
Use the regmap cache to cache those values.
Tested on a Fujitsu Esprimo P720.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://lore.kernel.org/r/20230907052639.16491-4-W_Armin@gmx.de
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon/sch56xx-common.c')
-rw-r--r-- | drivers/hwmon/sch56xx-common.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index ac1f72580715..ebdeb3a20b87 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/err.h> #include <linux/io.h> #include <linux/acpi.h> @@ -59,6 +60,11 @@ struct sch56xx_watchdog_data { u8 watchdog_output_enable; }; +struct sch56xx_bus_context { + struct mutex *lock; /* Used to serialize access to the mailbox registers */ + u16 addr; +}; + static struct platform_device *sch56xx_pdev; /* Super I/O functions */ @@ -239,6 +245,76 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, EXPORT_SYMBOL(sch56xx_read_virtual_reg12); /* + * Regmap support + */ + +static int sch56xx_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct sch56xx_bus_context *bus = context; + int ret; + + mutex_lock(bus->lock); + ret = sch56xx_write_virtual_reg(bus->addr, (u16)reg, (u8)val); + mutex_unlock(bus->lock); + + return ret; +} + +static int sch56xx_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sch56xx_bus_context *bus = context; + int ret; + + mutex_lock(bus->lock); + ret = sch56xx_read_virtual_reg(bus->addr, (u16)reg); + mutex_unlock(bus->lock); + + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static void sch56xx_free_context(void *context) +{ + kfree(context); +} + +static const struct regmap_bus sch56xx_bus = { + .reg_write = sch56xx_reg_write, + .reg_read = sch56xx_reg_read, + .free_context = sch56xx_free_context, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +struct regmap *devm_regmap_init_sch56xx(struct device *dev, struct mutex *lock, u16 addr, + const struct regmap_config *config) +{ + struct sch56xx_bus_context *context; + struct regmap *map; + + if (config->reg_bits != 16 && config->val_bits != 8) + return ERR_PTR(-EOPNOTSUPP); + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return ERR_PTR(-ENOMEM); + + context->lock = lock; + context->addr = addr; + + map = devm_regmap_init(dev, &sch56xx_bus, context, config); + if (IS_ERR(map)) + kfree(context); + + return map; +} +EXPORT_SYMBOL(devm_regmap_init_sch56xx); + +/* * Watchdog routines */ |