1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * HWMON driver for Lenovo ThinkStation based workstations 4 * via the embedded controller registers 5 * 6 * Copyright (C) 2024 David Ober (Lenovo) <dober@lenovo.com> 7 * 8 * EC provides: 9 * - CPU temperature 10 * - DIMM temperature 11 * - Chassis zone temperatures 12 * - CPU fan RPM 13 * - DIMM fan RPM 14 * - Chassis fans RPM 15 */ 16 17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 19 #include <linux/acpi.h> 20 #include <linux/bits.h> 21 #include <linux/delay.h> 22 #include <linux/device.h> 23 #include <linux/dmi.h> 24 #include <linux/err.h> 25 #include <linux/hwmon.h> 26 #include <linux/io.h> 27 #include <linux/ioport.h> 28 #include <linux/module.h> 29 #include <linux/mutex.h> 30 #include <linux/platform_device.h> 31 #include <linux/types.h> 32 #include <linux/units.h> 33 34 #define MCHP_SING_IDX 0x0000 35 #define MCHP_EMI0_APPLICATION_ID 0x090C 36 #define MCHP_EMI0_EC_ADDRESS 0x0902 37 #define MCHP_EMI0_EC_DATA_BYTE0 0x0904 38 #define MCHP_EMI0_EC_DATA_BYTE1 0x0905 39 #define MCHP_EMI0_EC_DATA_BYTE2 0x0906 40 #define MCHP_EMI0_EC_DATA_BYTE3 0x0907 41 #define IO_REGION_START 0x0900 42 #define IO_REGION_LENGTH 0xD 43 44 static inline u8 45 get_ec_reg(unsigned char page, unsigned char index) 46 { 47 u8 onebyte; 48 unsigned short m_index; 49 unsigned short phy_index = page * 256 + index; 50 51 outb_p(0x01, MCHP_EMI0_APPLICATION_ID); 52 53 m_index = phy_index & GENMASK(14, 2); 54 outw_p(m_index, MCHP_EMI0_EC_ADDRESS); 55 56 onebyte = inb_p(MCHP_EMI0_EC_DATA_BYTE0 + (phy_index & GENMASK(1, 0))); 57 58 outb_p(0x01, MCHP_EMI0_APPLICATION_ID); /* write 0x01 again to clean */ 59 return onebyte; 60 } 61 62 enum systems { 63 LENOVO_PX, 64 LENOVO_P7, 65 LENOVO_P5, 66 LENOVO_P8, 67 }; 68 69 static int px_temp_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31, 32}; 70 71 static const char * const lenovo_px_ec_temp_label[] = { 72 "CPU1", 73 "CPU2", 74 "R_DIMM1", 75 "L_DIMM1", 76 "R_DIMM2", 77 "L_DIMM2", 78 "PCH", 79 "M2_R", 80 "M2_Z1R", 81 "M2_Z2R", 82 "PCI_Z1", 83 "PCI_Z2", 84 "PCI_Z3", 85 "PCI_Z4", 86 "AMB", 87 "PSU1", 88 "PSU2", 89 }; 90 91 static int p8_temp_map[] = {0, 1, 2, 8, 9, 13, 14, 15, 16, 17, 19, 20, 33}; 92 93 static const char * const lenovo_p8_ec_temp_label[] = { 94 "CPU1", 95 "CPU_DIMM_BANK1", 96 "CPU_DIMM_BANK2", 97 "M2_Z2R", 98 "M2_Z3R", 99 "DIMM_RIGHT", 100 "DIMM_LEFT", 101 "PCI_Z1", 102 "PCI_Z2", 103 "PCI_Z3", 104 "AMB", 105 "REAR_VR", 106 "PSU", 107 }; 108 109 static int gen_temp_map[] = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31}; 110 111 static const char * const lenovo_gen_ec_temp_label[] = { 112 "CPU1", 113 "R_DIMM", 114 "L_DIMM", 115 "PCH", 116 "M2_R", 117 "M2_Z1R", 118 "M2_Z2R", 119 "PCI_Z1", 120 "PCI_Z2", 121 "PCI_Z3", 122 "PCI_Z4", 123 "AMB", 124 "PSU", 125 }; 126 127 static int px_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 128 129 static const char * const px_ec_fan_label[] = { 130 "CPU1_Fan", 131 "CPU2_Fan", 132 "Front_Fan1-1", 133 "Front_Fan1-2", 134 "Front_Fan2", 135 "Front_Fan3", 136 "MEM_Fan1", 137 "MEM_Fan2", 138 "Rear_Fan1", 139 "Rear_Fan2", 140 "Flex_Bay_Fan1", 141 "Flex_Bay_Fan2", 142 "Flex_Bay_Fan2", 143 "PSU_HDD_Fan", 144 "PSU1_Fan", 145 "PSU2_Fan", 146 }; 147 148 static int p7_fan_map[] = {0, 2, 3, 4, 5, 6, 7, 8, 10, 11, 14}; 149 150 static const char * const p7_ec_fan_label[] = { 151 "CPU1_Fan", 152 "HP_CPU_Fan1", 153 "HP_CPU_Fan2", 154 "PCIE1_4_Fan", 155 "PCIE5_7_Fan", 156 "MEM_Fan1", 157 "MEM_Fan2", 158 "Rear_Fan1", 159 "BCB_Fan", 160 "Flex_Bay_Fan", 161 "PSU_Fan", 162 }; 163 164 static int p5_fan_map[] = {0, 5, 6, 7, 8, 10, 11, 14}; 165 166 static const char * const p5_ec_fan_label[] = { 167 "CPU_Fan", 168 "HDD_Fan", 169 "Duct_Fan1", 170 "MEM_Fan", 171 "Rear_Fan", 172 "Front_Fan", 173 "Flex_Bay_Fan", 174 "PSU_Fan", 175 }; 176 177 static int p8_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14}; 178 179 static const char * const p8_ec_fan_label[] = { 180 "CPU1_Fan", 181 "CPU2_Fan", 182 "HP_CPU_Fan1", 183 "HP_CPU_Fan2", 184 "PCIE1_4_Fan", 185 "PCIE5_7_Fan", 186 "DIMM1_Fan1", 187 "DIMM1_Fan2", 188 "DIMM2_Fan1", 189 "DIMM2_Fan2", 190 "Rear_Fan", 191 "HDD_Bay_Fan", 192 "Flex_Bay_Fan", 193 "PSU_Fan", 194 }; 195 196 struct ec_sensors_data { 197 struct mutex mec_mutex; /* lock for sensor data access */ 198 const char *const *fan_labels; 199 const char *const *temp_labels; 200 const int *fan_map; 201 const int *temp_map; 202 }; 203 204 static int 205 lenovo_ec_do_read_temp(struct ec_sensors_data *data, u32 attr, int channel, long *val) 206 { 207 u8 lsb; 208 209 switch (attr) { 210 case hwmon_temp_input: 211 mutex_lock(&data->mec_mutex); 212 lsb = get_ec_reg(2, 0x81 + channel); 213 mutex_unlock(&data->mec_mutex); 214 if (lsb <= 0x40) 215 return -ENODATA; 216 *val = (lsb - 0x40) * 1000; 217 return 0; 218 default: 219 return -EOPNOTSUPP; 220 } 221 } 222 223 static int 224 lenovo_ec_do_read_fan(struct ec_sensors_data *data, u32 attr, int channel, long *val) 225 { 226 u8 lsb, msb; 227 228 channel *= 2; 229 switch (attr) { 230 case hwmon_fan_input: 231 mutex_lock(&data->mec_mutex); 232 lsb = get_ec_reg(4, 0x20 + channel); 233 msb = get_ec_reg(4, 0x21 + channel); 234 mutex_unlock(&data->mec_mutex); 235 *val = (msb << 8) + lsb; 236 return 0; 237 case hwmon_fan_max: 238 mutex_lock(&data->mec_mutex); 239 lsb = get_ec_reg(4, 0x40 + channel); 240 msb = get_ec_reg(4, 0x41 + channel); 241 mutex_unlock(&data->mec_mutex); 242 *val = (msb << 8) + lsb; 243 return 0; 244 default: 245 return -EOPNOTSUPP; 246 } 247 } 248 249 static int 250 lenovo_ec_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, 251 u32 attr, int channel, const char **str) 252 { 253 struct ec_sensors_data *state = dev_get_drvdata(dev); 254 255 switch (type) { 256 case hwmon_temp: 257 *str = state->temp_labels[channel]; 258 return 0; 259 case hwmon_fan: 260 *str = state->fan_labels[channel]; 261 return 0; 262 default: 263 return -EOPNOTSUPP; 264 } 265 } 266 267 static int 268 lenovo_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 269 u32 attr, int channel, long *val) 270 { 271 struct ec_sensors_data *data = dev_get_drvdata(dev); 272 273 switch (type) { 274 case hwmon_temp: 275 return lenovo_ec_do_read_temp(data, attr, data->temp_map[channel], val); 276 case hwmon_fan: 277 return lenovo_ec_do_read_fan(data, attr, data->fan_map[channel], val); 278 default: 279 return -EOPNOTSUPP; 280 } 281 } 282 283 static umode_t 284 lenovo_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, 285 u32 attr, int channel) 286 { 287 switch (type) { 288 case hwmon_temp: 289 if (attr == hwmon_temp_input || attr == hwmon_temp_label) 290 return 0444; 291 return 0; 292 case hwmon_fan: 293 if (attr == hwmon_fan_input || attr == hwmon_fan_max || attr == hwmon_fan_label) 294 return 0444; 295 return 0; 296 default: 297 return 0; 298 } 299 } 300 301 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_px[] = { 302 HWMON_CHANNEL_INFO(temp, 303 HWMON_T_INPUT | HWMON_T_LABEL, 304 HWMON_T_INPUT | HWMON_T_LABEL, 305 HWMON_T_INPUT | HWMON_T_LABEL, 306 HWMON_T_INPUT | HWMON_T_LABEL, 307 HWMON_T_INPUT | HWMON_T_LABEL, 308 HWMON_T_INPUT | HWMON_T_LABEL, 309 HWMON_T_INPUT | HWMON_T_LABEL, 310 HWMON_T_INPUT | HWMON_T_LABEL, 311 HWMON_T_INPUT | HWMON_T_LABEL, 312 HWMON_T_INPUT | HWMON_T_LABEL, 313 HWMON_T_INPUT | HWMON_T_LABEL, 314 HWMON_T_INPUT | HWMON_T_LABEL, 315 HWMON_T_INPUT | HWMON_T_LABEL, 316 HWMON_T_INPUT | HWMON_T_LABEL, 317 HWMON_T_INPUT | HWMON_T_LABEL, 318 HWMON_T_INPUT | HWMON_T_LABEL, 319 HWMON_T_INPUT | HWMON_T_LABEL), 320 HWMON_CHANNEL_INFO(fan, 321 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 322 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 323 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 324 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 325 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 326 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 327 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 328 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 329 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 330 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 331 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 332 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 333 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 334 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 335 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 336 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX), 337 NULL 338 }; 339 340 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p8[] = { 341 HWMON_CHANNEL_INFO(temp, 342 HWMON_T_INPUT | HWMON_T_LABEL, 343 HWMON_T_INPUT | HWMON_T_LABEL, 344 HWMON_T_INPUT | HWMON_T_LABEL, 345 HWMON_T_INPUT | HWMON_T_LABEL, 346 HWMON_T_INPUT | HWMON_T_LABEL, 347 HWMON_T_INPUT | HWMON_T_LABEL, 348 HWMON_T_INPUT | HWMON_T_LABEL, 349 HWMON_T_INPUT | HWMON_T_LABEL, 350 HWMON_T_INPUT | HWMON_T_LABEL, 351 HWMON_T_INPUT | HWMON_T_LABEL, 352 HWMON_T_INPUT | HWMON_T_LABEL, 353 HWMON_T_INPUT | HWMON_T_LABEL, 354 HWMON_T_INPUT | HWMON_T_LABEL), 355 HWMON_CHANNEL_INFO(fan, 356 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 357 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 358 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 359 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 360 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 361 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 362 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 363 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 364 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 365 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 366 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 367 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 368 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 369 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX), 370 NULL 371 }; 372 373 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p7[] = { 374 HWMON_CHANNEL_INFO(temp, 375 HWMON_T_INPUT | HWMON_T_LABEL, 376 HWMON_T_INPUT | HWMON_T_LABEL, 377 HWMON_T_INPUT | HWMON_T_LABEL, 378 HWMON_T_INPUT | HWMON_T_LABEL, 379 HWMON_T_INPUT | HWMON_T_LABEL, 380 HWMON_T_INPUT | HWMON_T_LABEL, 381 HWMON_T_INPUT | HWMON_T_LABEL, 382 HWMON_T_INPUT | HWMON_T_LABEL, 383 HWMON_T_INPUT | HWMON_T_LABEL, 384 HWMON_T_INPUT | HWMON_T_LABEL, 385 HWMON_T_INPUT | HWMON_T_LABEL, 386 HWMON_T_INPUT | HWMON_T_LABEL, 387 HWMON_T_INPUT | HWMON_T_LABEL), 388 HWMON_CHANNEL_INFO(fan, 389 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 390 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 391 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 392 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 393 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 394 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 395 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 396 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 397 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 398 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 399 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX), 400 NULL 401 }; 402 403 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p5[] = { 404 HWMON_CHANNEL_INFO(temp, 405 HWMON_T_INPUT | HWMON_T_LABEL, 406 HWMON_T_INPUT | HWMON_T_LABEL, 407 HWMON_T_INPUT | HWMON_T_LABEL, 408 HWMON_T_INPUT | HWMON_T_LABEL, 409 HWMON_T_INPUT | HWMON_T_LABEL, 410 HWMON_T_INPUT | HWMON_T_LABEL, 411 HWMON_T_INPUT | HWMON_T_LABEL, 412 HWMON_T_INPUT | HWMON_T_LABEL, 413 HWMON_T_INPUT | HWMON_T_LABEL, 414 HWMON_T_INPUT | HWMON_T_LABEL, 415 HWMON_T_INPUT | HWMON_T_LABEL, 416 HWMON_T_INPUT | HWMON_T_LABEL, 417 HWMON_T_INPUT | HWMON_T_LABEL), 418 HWMON_CHANNEL_INFO(fan, 419 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 420 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 421 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 422 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 423 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 424 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 425 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX, 426 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX), 427 NULL 428 }; 429 430 static const struct hwmon_ops lenovo_ec_hwmon_ops = { 431 .is_visible = lenovo_ec_hwmon_is_visible, 432 .read = lenovo_ec_hwmon_read, 433 .read_string = lenovo_ec_hwmon_read_string, 434 }; 435 436 static struct hwmon_chip_info lenovo_ec_chip_info = { 437 .ops = &lenovo_ec_hwmon_ops, 438 }; 439 440 static const struct dmi_system_id thinkstation_dmi_table[] = { 441 { 442 .ident = "LENOVO_PX", 443 .driver_data = (void *)(long)LENOVO_PX, 444 .matches = { 445 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 446 DMI_MATCH(DMI_PRODUCT_NAME, "30EU"), 447 }, 448 }, 449 { 450 .ident = "LENOVO_PX", 451 .driver_data = (void *)(long)LENOVO_PX, 452 .matches = { 453 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 454 DMI_MATCH(DMI_PRODUCT_NAME, "30EV"), 455 }, 456 }, 457 { 458 .ident = "LENOVO_P7", 459 .driver_data = (void *)(long)LENOVO_P7, 460 .matches = { 461 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 462 DMI_MATCH(DMI_PRODUCT_NAME, "30F2"), 463 }, 464 }, 465 { 466 .ident = "LENOVO_P7", 467 .driver_data = (void *)(long)LENOVO_P7, 468 .matches = { 469 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 470 DMI_MATCH(DMI_PRODUCT_NAME, "30F3"), 471 }, 472 }, 473 { 474 .ident = "LENOVO_P5", 475 .driver_data = (void *)(long)LENOVO_P5, 476 .matches = { 477 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 478 DMI_MATCH(DMI_PRODUCT_NAME, "30G9"), 479 }, 480 }, 481 { 482 .ident = "LENOVO_P5", 483 .driver_data = (void *)(long)LENOVO_P5, 484 .matches = { 485 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 486 DMI_MATCH(DMI_PRODUCT_NAME, "30GA"), 487 }, 488 }, 489 { 490 .ident = "LENOVO_P8", 491 .driver_data = (void *)(long)LENOVO_P8, 492 .matches = { 493 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 494 DMI_MATCH(DMI_PRODUCT_NAME, "30HH"), 495 }, 496 }, 497 { 498 .ident = "LENOVO_P8", 499 .driver_data = (void *)(long)LENOVO_P8, 500 .matches = { 501 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 502 DMI_MATCH(DMI_PRODUCT_NAME, "30HJ"), 503 }, 504 }, 505 {} 506 }; 507 MODULE_DEVICE_TABLE(dmi, thinkstation_dmi_table); 508 509 static int lenovo_ec_probe(struct platform_device *pdev) 510 { 511 struct device *hwdev; 512 struct ec_sensors_data *ec_data; 513 const struct hwmon_chip_info *chip_info; 514 struct device *dev = &pdev->dev; 515 const struct dmi_system_id *dmi_id; 516 int app_id; 517 518 ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data), GFP_KERNEL); 519 if (!ec_data) 520 return -ENOMEM; 521 522 if (!request_region(IO_REGION_START, IO_REGION_LENGTH, "LNV-WKS")) { 523 pr_err(":request fail\n"); 524 return -EIO; 525 } 526 527 dev_set_drvdata(dev, ec_data); 528 529 chip_info = &lenovo_ec_chip_info; 530 531 mutex_init(&ec_data->mec_mutex); 532 533 mutex_lock(&ec_data->mec_mutex); 534 app_id = inb_p(MCHP_EMI0_APPLICATION_ID); 535 if (app_id) /* check EMI Application ID Value */ 536 outb_p(app_id, MCHP_EMI0_APPLICATION_ID); /* set EMI Application ID to 0 */ 537 outw_p(MCHP_SING_IDX, MCHP_EMI0_EC_ADDRESS); 538 mutex_unlock(&ec_data->mec_mutex); 539 540 if ((inb_p(MCHP_EMI0_EC_DATA_BYTE0) != 'M') && 541 (inb_p(MCHP_EMI0_EC_DATA_BYTE1) != 'C') && 542 (inb_p(MCHP_EMI0_EC_DATA_BYTE2) != 'H') && 543 (inb_p(MCHP_EMI0_EC_DATA_BYTE3) != 'P')) { 544 release_region(IO_REGION_START, IO_REGION_LENGTH); 545 return -ENODEV; 546 } 547 548 dmi_id = dmi_first_match(thinkstation_dmi_table); 549 550 switch ((long)dmi_id->driver_data) { 551 case 0: 552 ec_data->fan_labels = px_ec_fan_label; 553 ec_data->temp_labels = lenovo_px_ec_temp_label; 554 ec_data->fan_map = px_fan_map; 555 ec_data->temp_map = px_temp_map; 556 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_px; 557 break; 558 case 1: 559 ec_data->fan_labels = p7_ec_fan_label; 560 ec_data->temp_labels = lenovo_gen_ec_temp_label; 561 ec_data->fan_map = p7_fan_map; 562 ec_data->temp_map = gen_temp_map; 563 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p7; 564 break; 565 case 2: 566 ec_data->fan_labels = p5_ec_fan_label; 567 ec_data->temp_labels = lenovo_gen_ec_temp_label; 568 ec_data->fan_map = p5_fan_map; 569 ec_data->temp_map = gen_temp_map; 570 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p5; 571 break; 572 case 3: 573 ec_data->fan_labels = p8_ec_fan_label; 574 ec_data->temp_labels = lenovo_p8_ec_temp_label; 575 ec_data->fan_map = p8_fan_map; 576 ec_data->temp_map = p8_temp_map; 577 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p8; 578 break; 579 default: 580 release_region(IO_REGION_START, IO_REGION_LENGTH); 581 return -ENODEV; 582 } 583 584 hwdev = devm_hwmon_device_register_with_info(dev, "lenovo_ec", 585 ec_data, 586 chip_info, NULL); 587 588 return PTR_ERR_OR_ZERO(hwdev); 589 } 590 591 static struct platform_driver lenovo_ec_sensors_platform_driver = { 592 .driver = { 593 .name = "lenovo-ec-sensors", 594 }, 595 .probe = lenovo_ec_probe, 596 }; 597 598 static struct platform_device *lenovo_ec_sensors_platform_device; 599 600 static int __init lenovo_ec_init(void) 601 { 602 if (!dmi_check_system(thinkstation_dmi_table)) 603 return -ENODEV; 604 605 lenovo_ec_sensors_platform_device = 606 platform_create_bundle(&lenovo_ec_sensors_platform_driver, 607 lenovo_ec_probe, NULL, 0, NULL, 0); 608 609 if (IS_ERR(lenovo_ec_sensors_platform_device)) { 610 release_region(IO_REGION_START, IO_REGION_LENGTH); 611 return PTR_ERR(lenovo_ec_sensors_platform_device); 612 } 613 614 return 0; 615 } 616 module_init(lenovo_ec_init); 617 618 static void __exit lenovo_ec_exit(void) 619 { 620 release_region(IO_REGION_START, IO_REGION_LENGTH); 621 platform_device_unregister(lenovo_ec_sensors_platform_device); 622 platform_driver_unregister(&lenovo_ec_sensors_platform_driver); 623 } 624 module_exit(lenovo_ec_exit); 625 626 MODULE_AUTHOR("David Ober <dober@lenovo.com>"); 627 MODULE_DESCRIPTION("HWMON driver for sensors accessible via EC in LENOVO motherboards"); 628 MODULE_LICENSE("GPL"); 629