summaryrefslogtreecommitdiff
path: root/drivers/hwmon/sch56xx-common.c
diff options
context:
space:
mode:
authorArmin Wolf <W_Armin@gmx.de>2023-09-07 08:26:37 +0300
committerGuenter Roeck <linux@roeck-us.net>2023-10-27 17:27:24 +0300
commita54fe61639d9f3b6765fee32edda7cfceb6d705a (patch)
tree9709cc7d9bec857e1cfdfc4f1a422ce0906078c0 /drivers/hwmon/sch56xx-common.c
parent7da8a635436029957c5350da3acf51d78ed64071 (diff)
downloadlinux-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.c76
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
*/