aboutsummaryrefslogtreecommitdiff
path: root/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch
blob: 205806cdfd97751188480ce96ef643a93430ec7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
From 96a8a4776cb142f5d2bb7f6379df9af40e727c0b Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
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 <phil@raspberrypi.com>
---
 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 <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regulator/consumer.h>
@@ -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)