projects/linux·blob master · refs

359 lines · 8.7 KiB

1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Hardware monitoring driver for LattePanda Sigma EC.
4 *
5 * The LattePanda Sigma is an x86 SBC made by DFRobot with an ITE IT8613E
6 * Embedded Controller that manages a CPU fan and thermal sensors.
7 *
8 * The BIOS declares the ACPI Embedded Controller (PNP0C09) with _STA
9 * returning 0 and provides only stub ECRD/ECWT methods that return Zero
10 * for all registers. Since the kernel's ACPI EC subsystem never initializes,
11 * ec_read() is not available and direct port I/O to the standard ACPI EC
12 * ports (0x62/0x66) is used instead.
13 *
14 * Because ACPI never initializes the EC, there is no concurrent firmware
15 * access to these ports, and no ACPI Global Lock or namespace mutex is
16 * required. The hwmon with_info API serializes all sysfs callbacks,
17 * so no additional driver-level locking is needed.
18 *
19 * The EC register map was discovered by dumping all 256 registers,
20 * identifying those that change in real-time, and validating by physically
21 * stopping the fan and observing the RPM register drop to zero. The map
22 * has been verified on BIOS version 5.27; other versions may differ.
23 *
24 * Copyright (c) 2026 Mariano Abad <[email protected]>
25 */
26
27#include <linux/delay.h>
28#include <linux/dmi.h>
29#include <linux/hwmon.h>
30#include <linux/io.h>
31#include <linux/ioport.h>
32#include <linux/module.h>
33#include <linux/platform_device.h>
34
35#define DRIVER_NAME "lattepanda_sigma_ec"
36
37/* EC I/O ports (standard ACPI EC interface) */
38#define EC_DATA_PORT 0x62
39#define EC_CMD_PORT 0x66 /* also status port */
40
41/* EC commands */
42#define EC_CMD_READ 0x80
43
44/* EC status register bits */
45#define EC_STATUS_OBF 0x01 /* Output Buffer Full */
46#define EC_STATUS_IBF 0x02 /* Input Buffer Full */
47
48/* EC register offsets for LattePanda Sigma (BIOS 5.27) */
49#define EC_REG_FAN_RPM_HI 0x2E
50#define EC_REG_FAN_RPM_LO 0x2F
51#define EC_REG_TEMP_BOARD 0x60
52#define EC_REG_TEMP_CPU 0x70
53#define EC_REG_FAN_DUTY 0x93
54
55/*
56 * EC polling uses udelay() because the EC typically responds within a
57 * few microseconds. The kernel's own ACPI EC driver (drivers/acpi/ec.c)
58 * likewise uses udelay() for busy-polling with a per-poll delay of 550us.
59 *
60 * usleep_range() was tested but caused EC protocol failures: the EC
61 * clears its status flags within microseconds, and sleeping for 50-100us
62 * between polls allowed the flags to transition past the expected state.
63 *
64 * The worst-case total busy-wait of 25ms covers EC recovery after errors.
65 * In practice the EC responds within 10us so the loop exits immediately.
66 */
67#define EC_TIMEOUT_US 25000
68#define EC_POLL_US 1
69
70static bool force;
71module_param(force, bool, 0444);
72MODULE_PARM_DESC(force,
73 "Force loading on untested BIOS versions (default: false)");
74
75static struct platform_device *lps_ec_pdev;
76
77static int ec_wait_ibf_clear(void)
78{
79 int i;
80
81 for (i = 0; i < EC_TIMEOUT_US; i++) {
82 if (!(inb(EC_CMD_PORT) & EC_STATUS_IBF))
83 return 0;
84 udelay(EC_POLL_US);
85 }
86 return -ETIMEDOUT;
87}
88
89static int ec_wait_obf_set(void)
90{
91 int i;
92
93 for (i = 0; i < EC_TIMEOUT_US; i++) {
94 if (inb(EC_CMD_PORT) & EC_STATUS_OBF)
95 return 0;
96 udelay(EC_POLL_US);
97 }
98 return -ETIMEDOUT;
99}
100
101static int ec_read_reg(u8 reg, u8 *val)
102{
103 int ret;
104
105 ret = ec_wait_ibf_clear();
106 if (ret)
107 return ret;
108
109 outb(EC_CMD_READ, EC_CMD_PORT);
110
111 ret = ec_wait_ibf_clear();
112 if (ret)
113 return ret;
114
115 outb(reg, EC_DATA_PORT);
116
117 ret = ec_wait_obf_set();
118 if (ret)
119 return ret;
120
121 *val = inb(EC_DATA_PORT);
122 return 0;
123}
124
125/*
126 * Read a 16-bit big-endian value from two consecutive EC registers.
127 *
128 * The EC may update the register pair between reading the high and low
129 * bytes, which could produce a corrupted value if the high byte rolls
130 * over (e.g., 0x0100 -> 0x00FF read as 0x01FF). Guard against this by
131 * re-reading the high byte after reading the low byte. If the high byte
132 * changed, re-read the low byte to get a consistent pair.
133 * See also lm90_read16() which uses the same approach.
134 */
135static int ec_read_reg16(u8 reg_hi, u8 reg_lo, u16 *val)
136{
137 int ret;
138 u8 oldh, newh, lo;
139
140 ret = ec_read_reg(reg_hi, &oldh);
141 if (ret)
142 return ret;
143
144 ret = ec_read_reg(reg_lo, &lo);
145 if (ret)
146 return ret;
147
148 ret = ec_read_reg(reg_hi, &newh);
149 if (ret)
150 return ret;
151
152 if (oldh != newh) {
153 ret = ec_read_reg(reg_lo, &lo);
154 if (ret)
155 return ret;
156 }
157
158 *val = ((u16)newh << 8) | lo;
159 return 0;
160}
161
162static int
163lps_ec_read_string(struct device *dev,
164 enum hwmon_sensor_types type,
165 u32 attr, int channel,
166 const char **str)
167{
168 switch (type) {
169 case hwmon_fan:
170 *str = "CPU Fan";
171 return 0;
172 case hwmon_temp:
173 *str = channel == 0 ? "Board Temp" : "CPU Temp";
174 return 0;
175 default:
176 return -EOPNOTSUPP;
177 }
178}
179
180static umode_t
181lps_ec_is_visible(const void *drvdata,
182 enum hwmon_sensor_types type,
183 u32 attr, int channel)
184{
185 switch (type) {
186 case hwmon_fan:
187 if (attr == hwmon_fan_input || attr == hwmon_fan_label)
188 return 0444;
189 break;
190 case hwmon_temp:
191 if (attr == hwmon_temp_input || attr == hwmon_temp_label)
192 return 0444;
193 break;
194 default:
195 break;
196 }
197 return 0;
198}
199
200static int
201lps_ec_read(struct device *dev,
202 enum hwmon_sensor_types type,
203 u32 attr, int channel, long *val)
204{
205 u16 rpm;
206 u8 v;
207 int ret;
208
209 switch (type) {
210 case hwmon_fan:
211 if (attr != hwmon_fan_input)
212 return -EOPNOTSUPP;
213 ret = ec_read_reg16(EC_REG_FAN_RPM_HI,
214 EC_REG_FAN_RPM_LO, &rpm);
215 if (ret)
216 return ret;
217 *val = rpm;
218 return 0;
219
220 case hwmon_temp:
221 if (attr != hwmon_temp_input)
222 return -EOPNOTSUPP;
223 ret = ec_read_reg(channel == 0 ? EC_REG_TEMP_BOARD
224 : EC_REG_TEMP_CPU,
225 &v);
226 if (ret)
227 return ret;
228 /* EC reports unsigned 8-bit temperature in degrees Celsius */
229 *val = (unsigned long)v * 1000;
230 return 0;
231
232 default:
233 return -EOPNOTSUPP;
234 }
235}
236
237static const struct hwmon_channel_info * const lps_ec_info[] = {
238 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
239 HWMON_CHANNEL_INFO(temp,
240 HWMON_T_INPUT | HWMON_T_LABEL,
241 HWMON_T_INPUT | HWMON_T_LABEL),
242 NULL
243};
244
245static const struct hwmon_ops lps_ec_ops = {
246 .is_visible = lps_ec_is_visible,
247 .read = lps_ec_read,
248 .read_string = lps_ec_read_string,
249};
250
251static const struct hwmon_chip_info lps_ec_chip_info = {
252 .ops = &lps_ec_ops,
253 .info = lps_ec_info,
254};
255
256static int lps_ec_probe(struct platform_device *pdev)
257{
258 struct device *dev = &pdev->dev;
259 struct device *hwmon;
260 u8 test;
261 int ret;
262
263 if (!devm_request_region(dev, EC_DATA_PORT, 1, DRIVER_NAME))
264 return dev_err_probe(dev, -EBUSY,
265 "Failed to request EC data port 0x%x\n",
266 EC_DATA_PORT);
267
268 if (!devm_request_region(dev, EC_CMD_PORT, 1, DRIVER_NAME))
269 return dev_err_probe(dev, -EBUSY,
270 "Failed to request EC cmd port 0x%x\n",
271 EC_CMD_PORT);
272
273 /* Sanity check: verify EC is responsive */
274 ret = ec_read_reg(EC_REG_FAN_DUTY, &test);
275 if (ret)
276 return dev_err_probe(dev, ret,
277 "EC not responding on ports 0x%x/0x%x\n",
278 EC_DATA_PORT, EC_CMD_PORT);
279
280 hwmon = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, NULL,
281 &lps_ec_chip_info, NULL);
282 if (IS_ERR(hwmon))
283 return dev_err_probe(dev, PTR_ERR(hwmon),
284 "Failed to register hwmon device\n");
285
286 dev_info(dev, "EC hwmon registered (fan duty: %u%%)\n", test);
287 return 0;
288}
289
290/* DMI table with strict BIOS version match (override with force=1) */
291static const struct dmi_system_id lps_ec_dmi_table[] = {
292 {
293 .ident = "LattePanda Sigma",
294 .matches = {
295 DMI_MATCH(DMI_SYS_VENDOR, "LattePanda"),
296 DMI_MATCH(DMI_PRODUCT_NAME, "LattePanda Sigma"),
297 DMI_MATCH(DMI_BIOS_VERSION, "5.27"),
298 },
299 },
300 { } /* terminator */
301};
302MODULE_DEVICE_TABLE(dmi, lps_ec_dmi_table);
303
304/* Loose table (vendor + product only) for use with force=1 */
305static const struct dmi_system_id lps_ec_dmi_table_force[] = {
306 {
307 .ident = "LattePanda Sigma",
308 .matches = {
309 DMI_MATCH(DMI_SYS_VENDOR, "LattePanda"),
310 DMI_MATCH(DMI_PRODUCT_NAME, "LattePanda Sigma"),
311 },
312 },
313 { } /* terminator */
314};
315
316static struct platform_driver lps_ec_driver = {
317 .probe = lps_ec_probe,
318 .driver = {
319 .name = DRIVER_NAME,
320 },
321};
322
323static int __init lps_ec_init(void)
324{
325 int ret;
326
327 if (!dmi_check_system(lps_ec_dmi_table)) {
328 if (!force || !dmi_check_system(lps_ec_dmi_table_force))
329 return -ENODEV;
330 pr_warn("%s: BIOS version not verified, loading due to force=1\n",
331 DRIVER_NAME);
332 }
333
334 ret = platform_driver_register(&lps_ec_driver);
335 if (ret)
336 return ret;
337
338 lps_ec_pdev = platform_device_register_simple(DRIVER_NAME, -1,
339 NULL, 0);
340 if (IS_ERR(lps_ec_pdev)) {
341 platform_driver_unregister(&lps_ec_driver);
342 return PTR_ERR(lps_ec_pdev);
343 }
344
345 return 0;
346}
347
348static void __exit lps_ec_exit(void)
349{
350 platform_device_unregister(lps_ec_pdev);
351 platform_driver_unregister(&lps_ec_driver);
352}
353
354module_init(lps_ec_init);
355module_exit(lps_ec_exit);
356
357MODULE_AUTHOR("Mariano Abad <[email protected]>");
358MODULE_DESCRIPTION("Hardware monitoring driver for LattePanda Sigma EC");
359MODULE_LICENSE("GPL");