@@ -22,15 +22,22 @@
#define MAX_PWM 255
+struct pwm_fan_tach {
+ int irq;
+ atomic_t pulses;
+ unsigned int rpm;
+ u8 pulses_per_revolution;
+
+ struct sensor_device_attribute sensor_attr;
+};
+
struct pwm_fan_ctx {
struct mutex lock;
struct pwm_device *pwm;
struct regulator *reg_en;
- int irq;
- atomic_t pulses;
- unsigned int rpm;
- u8 pulses_per_revolution;
+ int tach_count;
+ struct pwm_fan_tach *tachs;
ktime_t sample_start;
struct timer_list rpm_timer;
@@ -39,14 +46,17 @@ struct pwm_fan_ctx {
unsigned int pwm_fan_max_state;
unsigned int *pwm_fan_cooling_levels;
struct thermal_cooling_device *cdev;
+
+ struct attribute_group fan_group;
+ struct attribute_group *fan_groups[2];
};
/* This handler assumes self resetting edge triggered interrupt. */
static irqreturn_t pulse_handler(int irq, void *dev_id)
{
- struct pwm_fan_ctx *ctx = dev_id;
+ struct pwm_fan_tach *tach = dev_id;
- atomic_inc(&ctx->pulses);
+ atomic_inc(&tach->pulses);
return IRQ_HANDLED;
}
@@ -54,16 +64,23 @@ static irqreturn_t pulse_handler(int irq, void *dev_id)
static void sample_timer(struct timer_list *t)
{
struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
- int pulses;
- u64 tmp;
+ ktime_t now = ktime_get();
+ int i;
- pulses = atomic_read(&ctx->pulses);
- atomic_sub(pulses, &ctx->pulses);
- tmp = (u64)pulses * ktime_ms_delta(ktime_get(), ctx->sample_start) * 60;
- do_div(tmp, ctx->pulses_per_revolution * 1000);
- ctx->rpm = tmp;
+ for (i = 0; i < ctx->tach_count; i++) {
+ struct pwm_fan_tach *tach;
+ int pulses;
+ u64 tmp;
+
+ tach = &ctx->tachs[i];
+ pulses = atomic_read(&tach->pulses);
+ atomic_sub(pulses, &tach->pulses);
+ tmp = (u64)pulses * ktime_ms_delta(now, ctx->sample_start) * 60;
+ do_div(tmp, tach->pulses_per_revolution * 1000);
+ tach->rpm = tmp;
+ }
- ctx->sample_start = ktime_get();
+ ctx->sample_start = now;
mod_timer(&ctx->rpm_timer, jiffies + HZ);
}
@@ -131,41 +148,13 @@ static ssize_t rpm_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct pwm_fan_tach *tach = &ctx->tachs[sensor_attr->index];
- return sprintf(buf, "%u\n", ctx->rpm);
+ return sprintf(buf, "%u\n", tach->rpm);
}
static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
-static SENSOR_DEVICE_ATTR_RO(fan1_input, rpm, 0);
-
-static struct attribute *pwm_fan_attrs[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- NULL,
-};
-
-static umode_t pwm_fan_attrs_visible(struct kobject *kobj, struct attribute *a,
- int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
-
- /* Hide fan_input in case no interrupt is available */
- if (n == 1 && ctx->irq <= 0)
- return 0;
-
- return a->mode;
-}
-
-static const struct attribute_group pwm_fan_group = {
- .attrs = pwm_fan_attrs,
- .is_visible = pwm_fan_attrs_visible,
-};
-
-static const struct attribute_group *pwm_fan_groups[] = {
- &pwm_fan_group,
- NULL,
-};
/* thermal cooling device callbacks */
static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
@@ -284,7 +273,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
struct device *hwmon;
int ret;
struct pwm_state state = { };
- u32 ppr = 2;
+ size_t sz;
+ unsigned int i;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -298,10 +288,6 @@ static int pwm_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- ctx->irq = platform_get_irq_optional(pdev, 0);
- if (ctx->irq == -EPROBE_DEFER)
- return ctx->irq;
-
ctx->reg_en = devm_regulator_get_optional(dev, "fan");
if (IS_ERR(ctx->reg_en)) {
if (PTR_ERR(ctx->reg_en) != -ENODEV)
@@ -337,26 +323,77 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ret)
return ret;
- of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr);
- ctx->pulses_per_revolution = ppr;
- if (!ctx->pulses_per_revolution) {
- dev_err(dev, "pulses-per-revolution can't be zero.\n");
- return -EINVAL;
- }
+ ctx->tach_count = platform_irq_count(pdev);
+ 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 (ctx->irq > 0) {
- ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
- pdev->name, ctx);
- if (ret) {
- dev_err(dev, "Failed to request interrupt: %d\n", ret);
- return ret;
+ sz = (2 + ctx->tach_count) * sizeof(struct attribute *);
+ ctx->fan_group.attrs = devm_kzalloc(dev, sz, GFP_KERNEL);
+ if (!ctx->fan_group.attrs)
+ return -ENOMEM;
+
+ sz = ctx->tach_count * sizeof(struct pwm_fan_tach);
+ ctx->tachs = devm_kzalloc(dev, sz, GFP_KERNEL);
+ if (!ctx->tachs)
+ return -ENOMEM;
+
+ ctx->fan_group.attrs[0] = &sensor_dev_attr_pwm1.dev_attr.attr;
+ for (i = 0; i < ctx->tach_count; i++) {
+ struct pwm_fan_tach *tach = &ctx->tachs[i];
+ u32 ppr = 2;
+ char *name;
+
+ tach->irq = platform_get_irq(pdev, i);
+ if (tach->irq == -EPROBE_DEFER)
+ return tach->irq;
+
+ of_property_read_u32_index(dev->of_node,
+ "pulses-per-revolution",
+ i,
+ &ppr);
+ tach->pulses_per_revolution = ppr;
+ if (!tach->pulses_per_revolution) {
+ dev_err(dev, "pulses-per-revolution can't be zero.\n");
+ return -EINVAL;
}
+
+ if (tach->irq > 0) {
+ ret = devm_request_irq(dev, tach->irq, pulse_handler, 0,
+ pdev->name, tach);
+ if (ret) {
+ dev_err(dev,
+ "Failed to request interrupt: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ sysfs_attr_init(&tach->sensor_attr.dev_attr.attr);
+
+ name = devm_kzalloc(dev, 16, GFP_KERNEL);
+ snprintf(name, 16, "fan%d_input", i + 1);
+ tach->sensor_attr.dev_attr.attr.name = name;
+ tach->sensor_attr.dev_attr.attr.mode = 0444;
+ tach->sensor_attr.dev_attr.show = rpm_show;
+ tach->sensor_attr.index = i;
+ ctx->fan_group.attrs[i + 1] = &tach->sensor_attr.dev_attr.attr;
+
+ dev_dbg(dev, "%s: irq=%d, pulses_per_revolution=%d",
+ name, tach->irq, tach->pulses_per_revolution);
+ }
+
+ if (ctx->tach_count > 0) {
ctx->sample_start = ktime_get();
mod_timer(&ctx->rpm_timer, jiffies + HZ);
}
- hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan",
- ctx, pwm_fan_groups);
+ ctx->fan_groups[0] = &ctx->fan_group;
+
+ hwmon = devm_hwmon_device_register_with_groups(dev,
+ "pwmfan", ctx,
+ (const struct attribute_group **)ctx->fan_groups);
if (IS_ERR(hwmon)) {
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(hwmon);
The pwm-fan driver is extended to support multiple fan tachometer signals connected to GPIO inputs. This is intended to support the case where a single PWM output signal is routed to multiple fans, each of which have a tachometer output connected back to a GPIO pin. The number of fan tachometer inputs is determined by the number of interrupt sources configured for the pwm-fan device. The number of pulses-per-revolution entries should match the number of interrupt sources so that each input has a value assigned. The fan tachometer measurements are exposed as sysfs files fan1_input, fan2_input, etc up to the number of configured inputs. If no inputs are configured then no corresponding sysfs files will be created. This avoids the need for an is_visible function to hide unused fanN_input sysfs files. Signed-off-by: Paul Barker <pbarker@konsulko.com> --- drivers/hwmon/pwm-fan.c | 165 ++++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 64 deletions(-)