1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ChromeOS EC driver for hwmon
5 * Copyright (C) 2024 Thomas Weißschuh <linux@weissschuh.net>
8 #include <linux/device.h>
9 #include <linux/hwmon.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/types.h>
16 #include <linux/units.h>
18 #define DRV_NAME "cros-ec-hwmon"
20 struct cros_ec_hwmon_priv
{
21 struct cros_ec_device
*cros_ec
;
22 const char *temp_sensor_names
[EC_TEMP_SENSOR_ENTRIES
+ EC_TEMP_SENSOR_B_ENTRIES
];
26 static int cros_ec_hwmon_read_fan_speed(struct cros_ec_device
*cros_ec
, u8 index
, u16
*speed
)
31 ret
= cros_ec_cmd_readmem(cros_ec
, EC_MEMMAP_FAN
+ index
* 2, 2, &__speed
);
35 *speed
= le16_to_cpu(__speed
);
39 static int cros_ec_hwmon_read_temp(struct cros_ec_device
*cros_ec
, u8 index
, u8
*temp
)
44 if (index
< EC_TEMP_SENSOR_ENTRIES
)
45 offset
= EC_MEMMAP_TEMP_SENSOR
+ index
;
47 offset
= EC_MEMMAP_TEMP_SENSOR_B
+ index
- EC_TEMP_SENSOR_ENTRIES
;
49 ret
= cros_ec_cmd_readmem(cros_ec
, offset
, 1, temp
);
55 static bool cros_ec_hwmon_is_error_fan(u16 speed
)
57 return speed
== EC_FAN_SPEED_NOT_PRESENT
|| speed
== EC_FAN_SPEED_STALLED
;
60 static bool cros_ec_hwmon_is_error_temp(u8 temp
)
62 return temp
== EC_TEMP_SENSOR_NOT_PRESENT
||
63 temp
== EC_TEMP_SENSOR_ERROR
||
64 temp
== EC_TEMP_SENSOR_NOT_POWERED
||
65 temp
== EC_TEMP_SENSOR_NOT_CALIBRATED
;
68 static long cros_ec_hwmon_temp_to_millicelsius(u8 temp
)
70 return kelvin_to_millicelsius((((long)temp
) + EC_TEMP_SENSOR_OFFSET
));
73 static int cros_ec_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
74 u32 attr
, int channel
, long *val
)
76 struct cros_ec_hwmon_priv
*priv
= dev_get_drvdata(dev
);
77 int ret
= -EOPNOTSUPP
;
81 if (type
== hwmon_fan
) {
82 if (attr
== hwmon_fan_input
) {
83 ret
= cros_ec_hwmon_read_fan_speed(priv
->cros_ec
, channel
, &speed
);
85 if (cros_ec_hwmon_is_error_fan(speed
))
90 } else if (attr
== hwmon_fan_fault
) {
91 ret
= cros_ec_hwmon_read_fan_speed(priv
->cros_ec
, channel
, &speed
);
93 *val
= cros_ec_hwmon_is_error_fan(speed
);
95 } else if (type
== hwmon_temp
) {
96 if (attr
== hwmon_temp_input
) {
97 ret
= cros_ec_hwmon_read_temp(priv
->cros_ec
, channel
, &temp
);
99 if (cros_ec_hwmon_is_error_temp(temp
))
102 *val
= cros_ec_hwmon_temp_to_millicelsius(temp
);
104 } else if (attr
== hwmon_temp_fault
) {
105 ret
= cros_ec_hwmon_read_temp(priv
->cros_ec
, channel
, &temp
);
107 *val
= cros_ec_hwmon_is_error_temp(temp
);
114 static int cros_ec_hwmon_read_string(struct device
*dev
, enum hwmon_sensor_types type
,
115 u32 attr
, int channel
, const char **str
)
117 struct cros_ec_hwmon_priv
*priv
= dev_get_drvdata(dev
);
119 if (type
== hwmon_temp
&& attr
== hwmon_temp_label
) {
120 *str
= priv
->temp_sensor_names
[channel
];
127 static umode_t
cros_ec_hwmon_is_visible(const void *data
, enum hwmon_sensor_types type
,
128 u32 attr
, int channel
)
130 const struct cros_ec_hwmon_priv
*priv
= data
;
132 if (type
== hwmon_fan
) {
133 if (priv
->usable_fans
& BIT(channel
))
135 } else if (type
== hwmon_temp
) {
136 if (priv
->temp_sensor_names
[channel
])
143 static const struct hwmon_channel_info
* const cros_ec_hwmon_info
[] = {
144 HWMON_CHANNEL_INFO(chip
, HWMON_C_REGISTER_TZ
),
145 HWMON_CHANNEL_INFO(fan
,
146 HWMON_F_INPUT
| HWMON_F_FAULT
,
147 HWMON_F_INPUT
| HWMON_F_FAULT
,
148 HWMON_F_INPUT
| HWMON_F_FAULT
,
149 HWMON_F_INPUT
| HWMON_F_FAULT
),
150 HWMON_CHANNEL_INFO(temp
,
151 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
152 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
153 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
154 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
155 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
156 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
157 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
158 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
159 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
160 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
161 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
162 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
163 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
164 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
165 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
166 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
167 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
168 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
169 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
170 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
171 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
172 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
173 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
,
174 HWMON_T_INPUT
| HWMON_T_FAULT
| HWMON_T_LABEL
),
178 static const struct hwmon_ops cros_ec_hwmon_ops
= {
179 .read
= cros_ec_hwmon_read
,
180 .read_string
= cros_ec_hwmon_read_string
,
181 .is_visible
= cros_ec_hwmon_is_visible
,
184 static const struct hwmon_chip_info cros_ec_hwmon_chip_info
= {
185 .ops
= &cros_ec_hwmon_ops
,
186 .info
= cros_ec_hwmon_info
,
189 static void cros_ec_hwmon_probe_temp_sensors(struct device
*dev
, struct cros_ec_hwmon_priv
*priv
,
192 struct ec_params_temp_sensor_get_info req
= {};
193 struct ec_response_temp_sensor_get_info resp
;
194 size_t candidates
, i
, sensor_name_size
;
198 if (thermal_version
< 2)
199 candidates
= EC_TEMP_SENSOR_ENTRIES
;
201 candidates
= ARRAY_SIZE(priv
->temp_sensor_names
);
203 for (i
= 0; i
< candidates
; i
++) {
204 if (cros_ec_hwmon_read_temp(priv
->cros_ec
, i
, &temp
) < 0)
207 if (temp
== EC_TEMP_SENSOR_NOT_PRESENT
)
211 ret
= cros_ec_cmd(priv
->cros_ec
, 0, EC_CMD_TEMP_SENSOR_GET_INFO
,
212 &req
, sizeof(req
), &resp
, sizeof(resp
));
216 sensor_name_size
= strnlen(resp
.sensor_name
, sizeof(resp
.sensor_name
));
217 priv
->temp_sensor_names
[i
] = devm_kasprintf(dev
, GFP_KERNEL
, "%.*s",
218 (int)sensor_name_size
,
223 static void cros_ec_hwmon_probe_fans(struct cros_ec_hwmon_priv
*priv
)
229 for (i
= 0; i
< EC_FAN_SPEED_ENTRIES
; i
++) {
230 ret
= cros_ec_hwmon_read_fan_speed(priv
->cros_ec
, i
, &speed
);
231 if (ret
== 0 && speed
!= EC_FAN_SPEED_NOT_PRESENT
)
232 priv
->usable_fans
|= BIT(i
);
236 static int cros_ec_hwmon_probe(struct platform_device
*pdev
)
238 struct device
*dev
= &pdev
->dev
;
239 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(dev
->parent
);
240 struct cros_ec_device
*cros_ec
= ec_dev
->ec_dev
;
241 struct cros_ec_hwmon_priv
*priv
;
242 struct device
*hwmon_dev
;
246 ret
= cros_ec_cmd_readmem(cros_ec
, EC_MEMMAP_THERMAL_VERSION
, 1, &thermal_version
);
250 /* Covers both fan and temp sensors */
251 if (thermal_version
== 0)
254 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
258 priv
->cros_ec
= cros_ec
;
260 cros_ec_hwmon_probe_temp_sensors(dev
, priv
, thermal_version
);
261 cros_ec_hwmon_probe_fans(priv
);
263 hwmon_dev
= devm_hwmon_device_register_with_info(dev
, "cros_ec", priv
,
264 &cros_ec_hwmon_chip_info
, NULL
);
266 return PTR_ERR_OR_ZERO(hwmon_dev
);
269 static const struct platform_device_id cros_ec_hwmon_id
[] = {
274 static struct platform_driver cros_ec_hwmon_driver
= {
275 .driver
.name
= DRV_NAME
,
276 .probe
= cros_ec_hwmon_probe
,
277 .id_table
= cros_ec_hwmon_id
,
279 module_platform_driver(cros_ec_hwmon_driver
);
281 MODULE_DEVICE_TABLE(platform
, cros_ec_hwmon_id
);
282 MODULE_DESCRIPTION("ChromeOS EC Hardware Monitoring Driver");
283 MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net");
284 MODULE_LICENSE("GPL");