From 96a8a4776cb142f5d2bb7f6379df9af40e727c0b Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Jul 2023 10:17:29 +0100 Subject: [PATCH] hwmon: (pwm-fan) Add fan speed register support Some platforms include a fan-speed register that reports RPM directly as an alternative to counting interrupts from the fan tachometer input. Add support for reading a register at a given offset (rpm-offset) within a block declared in another node (rpm-regmap). This indirection allows the usual address mapping to be performed, and for address sharing with another driver. Signed-off-by: Phil Elwell --- drivers/hwmon/pwm-fan.c | 59 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,9 @@ struct pwm_fan_ctx { ktime_t sample_start; struct timer_list rpm_timer; + void __iomem *rpm_regbase; + unsigned int rpm_offset; + unsigned int pwm_value; unsigned int pwm_fan_state; unsigned int pwm_fan_max_state; @@ -61,6 +65,10 @@ struct pwm_fan_ctx { struct hwmon_channel_info fan_channel; }; +static const u32 rpm_reg_channel_config[] = { + HWMON_F_INPUT, 0 +}; + /* This handler assumes self resetting edge triggered interrupt. */ static irqreturn_t pulse_handler(int irq, void *dev_id) { @@ -335,7 +343,10 @@ static int pwm_fan_read(struct device *d } return -EOPNOTSUPP; case hwmon_fan: - *val = ctx->tachs[channel].rpm; + if (ctx->rpm_regbase) + *val = (long)readl(ctx->rpm_regbase + ctx->rpm_offset); + else + *val = ctx->tachs[channel].rpm; return 0; default: @@ -470,6 +481,7 @@ static void pwm_fan_cleanup(void *__ctx) /* Switch off everything */ ctx->enable_mode = pwm_disable_reg_disable; pwm_fan_power_off(ctx); + iounmap(ctx->rpm_regbase); } static int pwm_fan_probe(struct platform_device *pdev) @@ -534,10 +546,23 @@ static int pwm_fan_probe(struct platform return ret; ctx->tach_count = platform_irq_count(pdev); + if (ctx->tach_count == 0) { + struct device_node *rpm_node; + + rpm_node = of_parse_phandle(dev->of_node, "rpm-regmap", 0); + if (rpm_node) + ctx->rpm_regbase = of_iomap(rpm_node, 0); + } + if (ctx->tach_count < 0) return dev_err_probe(dev, ctx->tach_count, "Could not get number of fan tachometer inputs\n"); - dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count); + if (IS_ERR(ctx->rpm_regbase)) + return dev_err_probe(dev, PTR_ERR(ctx->rpm_regbase), + "Could not get rpm reg\n"); + + dev_dbg(dev, "%d fan tachometer inputs, %d rpm regmap\n", ctx->tach_count, + !!ctx->rpm_regbase); if (ctx->tach_count) { channel_count++; /* We also have a FAN channel. */ @@ -554,12 +579,24 @@ static int pwm_fan_probe(struct platform if (!fan_channel_config) return -ENOMEM; ctx->fan_channel.config = fan_channel_config; + } else if (ctx->rpm_regbase) { + channel_count++; /* We also have a FAN channel. */ + ctx->fan_channel.type = hwmon_fan; + ctx->fan_channel.config = rpm_reg_channel_config; + + if (of_property_read_u32(pdev->dev.of_node, "rpm-offset", &ctx->rpm_offset)) { + dev_err(&pdev->dev, "unable to read 'rpm-offset'"); + ret = -EINVAL; + goto error; + } } channels = devm_kcalloc(dev, channel_count + 1, sizeof(struct hwmon_channel_info *), GFP_KERNEL); - if (!channels) - return -ENOMEM; + if (!channels) { + ret = -ENOMEM; + goto error; + } channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE); @@ -602,6 +639,8 @@ static int pwm_fan_probe(struct platform mod_timer(&ctx->rpm_timer, jiffies + HZ); channels[1] = &ctx->fan_channel; + } else if (ctx->rpm_regbase) { + channels[1] = &ctx->fan_channel; } ctx->info.ops = &pwm_fan_hwmon_ops; @@ -611,12 +650,13 @@ static int pwm_fan_probe(struct platform ctx, &ctx->info, NULL); if (IS_ERR(hwmon)) { dev_err(dev, "Failed to register hwmon device\n"); - return PTR_ERR(hwmon); + ret = PTR_ERR(hwmon); + goto error; } ret = pwm_fan_of_get_cooling_data(dev, ctx); if (ret) - return ret; + goto error; ctx->pwm_fan_state = ctx->pwm_fan_max_state; if (IS_ENABLED(CONFIG_THERMAL)) { @@ -627,12 +667,17 @@ static int pwm_fan_probe(struct platform dev_err(dev, "Failed to register pwm-fan as cooling device: %d\n", ret); - return ret; + goto error; } ctx->cdev = cdev; } return 0; + +error: + if (ctx->rpm_regbase) + iounmap(ctx->rpm_regbase); + return ret; } static void pwm_fan_shutdown(struct platform_device *pdev)