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 <weimaraner@gmail.com> 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 70 static bool force; 71 module_param(force, bool, 0444); 72 MODULE_PARM_DESC(force, 73 "Force loading on untested BIOS versions (default: false)"); 74 75 static struct platform_device *lps_ec_pdev; 76 77 static 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 89 static 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 101 static 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 */ 135 static 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 162 static int 163 lps_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 180 static umode_t 181 lps_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 200 static int 201 lps_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 237 static 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 245 static 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 251 static const struct hwmon_chip_info lps_ec_chip_info = { 252 .ops = &lps_ec_ops, 253 .info = lps_ec_info, 254 }; 255 256 static 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) */ 291 static 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 }; 302 MODULE_DEVICE_TABLE(dmi, lps_ec_dmi_table); 303 304 /* Loose table (vendor + product only) for use with force=1 */ 305 static 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 316 static struct platform_driver lps_ec_driver = { 317 .probe = lps_ec_probe, 318 .driver = { 319 .name = DRIVER_NAME, 320 }, 321 }; 322 323 static 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 348 static void __exit lps_ec_exit(void) 349 { 350 platform_device_unregister(lps_ec_pdev); 351 platform_driver_unregister(&lps_ec_driver); 352 } 353 354 module_init(lps_ec_init); 355 module_exit(lps_ec_exit); 356 357 MODULE_AUTHOR("Mariano Abad <weimaraner@gmail.com>"); 358 MODULE_DESCRIPTION("Hardware monitoring driver for LattePanda Sigma EC"); 359 MODULE_LICENSE("GPL"); 360