diff options
author | Armin Wolf <W_Armin@gmx.de> | 2023-09-07 07:26:37 +0200 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2023-10-27 07:27:24 -0700 |
commit | a54fe61639d9f3b6765fee32edda7cfceb6d705a (patch) | |
tree | 9709cc7d9bec857e1cfdfc4f1a422ce0906078c0 /drivers/hwmon/sch5627.c | |
parent | 7da8a635436029957c5350da3acf51d78ed64071 (diff) | |
download | linux-stable-a54fe61639d9f3b6765fee32edda7cfceb6d705a.tar.gz linux-stable-a54fe61639d9f3b6765fee32edda7cfceb6d705a.tar.bz2 linux-stable-a54fe61639d9f3b6765fee32edda7cfceb6d705a.zip |
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/sch5627.c')
-rw-r--r-- | drivers/hwmon/sch5627.c | 72 |
1 files changed, 58 insertions, 14 deletions
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index bf408e35e2c3..85f8352cb3cf 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -9,7 +9,9 @@ #include <linux/bits.h> #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/pm.h> #include <linux/init.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/platform_device.h> @@ -72,6 +74,7 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { "VCC", "VTT", "VBAT", "VTR", "V_IN" }; struct sch5627_data { + struct regmap *regmap; unsigned short addr; u8 control; u8 temp_max[SCH5627_NO_TEMPS]; @@ -91,6 +94,26 @@ struct sch5627_data { u16 in[SCH5627_NO_IN]; }; +static const struct regmap_range sch5627_tunables_ranges[] = { + regmap_reg_range(0xA0, 0xA3), +}; + +static const struct regmap_access_table sch5627_tunables_table = { + .yes_ranges = sch5627_tunables_ranges, + .n_yes_ranges = ARRAY_SIZE(sch5627_tunables_ranges), +}; + +static const struct regmap_config sch5627_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .wr_table = &sch5627_tunables_table, + .rd_table = &sch5627_tunables_table, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, + .can_sleep = true, +}; + static int sch5627_update_temp(struct sch5627_data *data) { int ret = 0; @@ -250,7 +273,7 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at long *val) { struct sch5627_data *data = dev_get_drvdata(dev); - int ret; + int ret, value; switch (type) { case hwmon_temp: @@ -301,15 +324,11 @@ static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 at case hwmon_pwm: switch (attr) { case hwmon_pwm_auto_channels_temp: - mutex_lock(&data->update_lock); - ret = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel]); - mutex_unlock(&data->update_lock); - + ret = regmap_read(data->regmap, SCH5627_REG_PWM_MAP[channel], &value); if (ret < 0) return ret; - *val = ret; - + *val = value; return 0; default: break; @@ -359,7 +378,6 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a long val) { struct sch5627_data *data = dev_get_drvdata(dev); - int ret; switch (type) { case hwmon_pwm: @@ -369,12 +387,7 @@ static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 a if (val > U8_MAX || val < 0) return -EINVAL; - mutex_lock(&data->update_lock); - ret = sch56xx_write_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel], - val); - mutex_unlock(&data->update_lock); - - return ret; + return regmap_write(data->regmap, SCH5627_REG_PWM_MAP[channel], val); default: break; } @@ -501,6 +514,12 @@ static int sch5627_probe(struct platform_device *pdev) pr_err("hardware monitoring not enabled\n"); return -ENODEV; } + + data->regmap = devm_regmap_init_sch56xx(&pdev->dev, &data->update_lock, data->addr, + &sch5627_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + /* Trigger a Vbat voltage measurement, so that we get a valid reading the first time we read Vbat */ sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | SCH5627_CTRL_VBAT); @@ -531,6 +550,30 @@ static int sch5627_probe(struct platform_device *pdev) return 0; } +static int sch5627_suspend(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + + return 0; +} + +static int sch5627_resume(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + + regcache_cache_only(data->regmap, false); + /* We must not access the virtual registers when the lock bit is set */ + if (data->control & SCH5627_CTRL_LOCK) + return regcache_drop_region(data->regmap, 0, U16_MAX); + + return regcache_sync(data->regmap); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(sch5627_dev_pm_ops, sch5627_suspend, sch5627_resume); + static const struct platform_device_id sch5627_device_id[] = { { .name = "sch5627", @@ -542,6 +585,7 @@ MODULE_DEVICE_TABLE(platform, sch5627_device_id); static struct platform_driver sch5627_driver = { .driver = { .name = DRVNAME, + .pm = pm_sleep_ptr(&sch5627_dev_pm_ops), }, .probe = sch5627_probe, .id_table = sch5627_device_id, |