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
get_ec_reg(unsigned char page,unsigned char index)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, 15};
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 };
88
89 static int gen_temp_map[] = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
90
91 static const char * const lenovo_gen_ec_temp_label[] = {
92 "CPU1",
93 "R_DIMM",
94 "L_DIMM",
95 "PCH",
96 "M2_R",
97 "M2_Z1R",
98 "M2_Z2R",
99 "PCI_Z1",
100 "PCI_Z2",
101 "PCI_Z3",
102 "PCI_Z4",
103 "AMB",
104 };
105
106 static int px_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
107
108 static const char * const px_ec_fan_label[] = {
109 "CPU1_Fan",
110 "CPU2_Fan",
111 "Front_Fan1-1",
112 "Front_Fan1-2",
113 "Front_Fan2",
114 "Front_Fan3",
115 "MEM_Fan1",
116 "MEM_Fan2",
117 "Rear_Fan1",
118 "Rear_Fan2",
119 "Flex_Bay_Fan1",
120 "Flex_Bay_Fan2",
121 "Flex_Bay_Fan2",
122 "PSU_HDD_Fan",
123 "PSU1_Fan",
124 "PSU2_Fan",
125 };
126
127 static int p7_fan_map[] = {0, 2, 3, 4, 5, 6, 7, 8, 10, 11, 14};
128
129 static const char * const p7_ec_fan_label[] = {
130 "CPU1_Fan",
131 "HP_CPU_Fan1",
132 "HP_CPU_Fan2",
133 "PCIE1_4_Fan",
134 "PCIE5_7_Fan",
135 "MEM_Fan1",
136 "MEM_Fan2",
137 "Rear_Fan1",
138 "BCB_Fan",
139 "Flex_Bay_Fan",
140 "PSU_Fan",
141 };
142
143 static int p5_fan_map[] = {0, 5, 6, 7, 8, 10, 11, 14};
144
145 static const char * const p5_ec_fan_label[] = {
146 "CPU_Fan",
147 "HDD_Fan",
148 "Duct_Fan1",
149 "MEM_Fan",
150 "Rear_Fan",
151 "Front_Fan",
152 "Flex_Bay_Fan",
153 "PSU_Fan",
154 };
155
156 static int p8_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14};
157
158 static const char * const p8_ec_fan_label[] = {
159 "CPU1_Fan",
160 "CPU2_Fan",
161 "HP_CPU_Fan1",
162 "HP_CPU_Fan2",
163 "PCIE1_4_Fan",
164 "PCIE5_7_Fan",
165 "DIMM1_Fan1",
166 "DIMM1_Fan2",
167 "DIMM2_Fan1",
168 "DIMM2_Fan2",
169 "Rear_Fan",
170 "HDD_Bay_Fan",
171 "Flex_Bay_Fan",
172 "PSU_Fan",
173 };
174
175 struct ec_sensors_data {
176 struct mutex mec_mutex; /* lock for sensor data access */
177 const char *const *fan_labels;
178 const char *const *temp_labels;
179 const int *fan_map;
180 const int *temp_map;
181 };
182
183 static int
lenovo_ec_do_read_temp(struct ec_sensors_data * data,u32 attr,int channel,long * val)184 lenovo_ec_do_read_temp(struct ec_sensors_data *data, u32 attr, int channel, long *val)
185 {
186 u8 lsb;
187
188 switch (attr) {
189 case hwmon_temp_input:
190 mutex_lock(&data->mec_mutex);
191 lsb = get_ec_reg(2, 0x81 + channel);
192 mutex_unlock(&data->mec_mutex);
193 if (lsb <= 0x40)
194 return -ENODATA;
195 *val = (lsb - 0x40) * 1000;
196 return 0;
197 default:
198 return -EOPNOTSUPP;
199 }
200 }
201
202 static int
lenovo_ec_do_read_fan(struct ec_sensors_data * data,u32 attr,int channel,long * val)203 lenovo_ec_do_read_fan(struct ec_sensors_data *data, u32 attr, int channel, long *val)
204 {
205 u8 lsb, msb;
206
207 channel *= 2;
208 switch (attr) {
209 case hwmon_fan_input:
210 mutex_lock(&data->mec_mutex);
211 lsb = get_ec_reg(4, 0x20 + channel);
212 msb = get_ec_reg(4, 0x21 + channel);
213 mutex_unlock(&data->mec_mutex);
214 *val = (msb << 8) + lsb;
215 return 0;
216 case hwmon_fan_max:
217 mutex_lock(&data->mec_mutex);
218 lsb = get_ec_reg(4, 0x40 + channel);
219 msb = get_ec_reg(4, 0x41 + channel);
220 mutex_unlock(&data->mec_mutex);
221 *val = (msb << 8) + lsb;
222 return 0;
223 default:
224 return -EOPNOTSUPP;
225 }
226 }
227
228 static int
lenovo_ec_hwmon_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)229 lenovo_ec_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
230 u32 attr, int channel, const char **str)
231 {
232 struct ec_sensors_data *state = dev_get_drvdata(dev);
233
234 switch (type) {
235 case hwmon_temp:
236 *str = state->temp_labels[channel];
237 return 0;
238 case hwmon_fan:
239 *str = state->fan_labels[channel];
240 return 0;
241 default:
242 return -EOPNOTSUPP;
243 }
244 }
245
246 static int
lenovo_ec_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)247 lenovo_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
248 u32 attr, int channel, long *val)
249 {
250 struct ec_sensors_data *data = dev_get_drvdata(dev);
251
252 switch (type) {
253 case hwmon_temp:
254 return lenovo_ec_do_read_temp(data, attr, data->temp_map[channel], val);
255 case hwmon_fan:
256 return lenovo_ec_do_read_fan(data, attr, data->fan_map[channel], val);
257 default:
258 return -EOPNOTSUPP;
259 }
260 }
261
262 static umode_t
lenovo_ec_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)263 lenovo_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
264 u32 attr, int channel)
265 {
266 switch (type) {
267 case hwmon_temp:
268 if (attr == hwmon_temp_input || attr == hwmon_temp_label)
269 return 0444;
270 return 0;
271 case hwmon_fan:
272 if (attr == hwmon_fan_input || attr == hwmon_fan_max || attr == hwmon_fan_label)
273 return 0444;
274 return 0;
275 default:
276 return 0;
277 }
278 }
279
280 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_px[] = {
281 HWMON_CHANNEL_INFO(temp,
282 HWMON_T_INPUT | HWMON_T_LABEL,
283 HWMON_T_INPUT | HWMON_T_LABEL,
284 HWMON_T_INPUT | HWMON_T_LABEL,
285 HWMON_T_INPUT | HWMON_T_LABEL,
286 HWMON_T_INPUT | HWMON_T_LABEL,
287 HWMON_T_INPUT | HWMON_T_LABEL,
288 HWMON_T_INPUT | HWMON_T_LABEL,
289 HWMON_T_INPUT | HWMON_T_LABEL,
290 HWMON_T_INPUT | HWMON_T_LABEL,
291 HWMON_T_INPUT | HWMON_T_LABEL,
292 HWMON_T_INPUT | HWMON_T_LABEL,
293 HWMON_T_INPUT | HWMON_T_LABEL,
294 HWMON_T_INPUT | HWMON_T_LABEL,
295 HWMON_T_INPUT | HWMON_T_LABEL,
296 HWMON_T_INPUT | HWMON_T_LABEL),
297 HWMON_CHANNEL_INFO(fan,
298 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
299 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
300 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
301 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
302 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
303 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
304 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
305 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
306 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
307 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
308 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
309 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
310 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
311 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
312 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
313 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX),
314 NULL
315 };
316
317 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p8[] = {
318 HWMON_CHANNEL_INFO(temp,
319 HWMON_T_INPUT | HWMON_T_LABEL,
320 HWMON_T_INPUT | HWMON_T_LABEL,
321 HWMON_T_INPUT | HWMON_T_LABEL,
322 HWMON_T_INPUT | HWMON_T_LABEL,
323 HWMON_T_INPUT | HWMON_T_LABEL,
324 HWMON_T_INPUT | HWMON_T_LABEL,
325 HWMON_T_INPUT | HWMON_T_LABEL,
326 HWMON_T_INPUT | HWMON_T_LABEL,
327 HWMON_T_INPUT | HWMON_T_LABEL,
328 HWMON_T_INPUT | HWMON_T_LABEL,
329 HWMON_T_INPUT | HWMON_T_LABEL,
330 HWMON_T_INPUT | HWMON_T_LABEL),
331 HWMON_CHANNEL_INFO(fan,
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 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
338 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
339 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
340 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
341 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
342 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
343 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
344 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
345 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX),
346 NULL
347 };
348
349 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p7[] = {
350 HWMON_CHANNEL_INFO(temp,
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_T_INPUT | HWMON_T_LABEL,
356 HWMON_T_INPUT | HWMON_T_LABEL,
357 HWMON_T_INPUT | HWMON_T_LABEL,
358 HWMON_T_INPUT | HWMON_T_LABEL,
359 HWMON_T_INPUT | HWMON_T_LABEL,
360 HWMON_T_INPUT | HWMON_T_LABEL,
361 HWMON_T_INPUT | HWMON_T_LABEL,
362 HWMON_T_INPUT | HWMON_T_LABEL),
363 HWMON_CHANNEL_INFO(fan,
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 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
371 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
372 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
373 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX,
374 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX),
375 NULL
376 };
377
378 static const struct hwmon_channel_info *lenovo_ec_hwmon_info_p5[] = {
379 HWMON_CHANNEL_INFO(temp,
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_T_INPUT | HWMON_T_LABEL,
389 HWMON_T_INPUT | HWMON_T_LABEL,
390 HWMON_T_INPUT | HWMON_T_LABEL,
391 HWMON_T_INPUT | HWMON_T_LABEL),
392 HWMON_CHANNEL_INFO(fan,
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 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MAX),
401 NULL
402 };
403
404 static const struct hwmon_ops lenovo_ec_hwmon_ops = {
405 .is_visible = lenovo_ec_hwmon_is_visible,
406 .read = lenovo_ec_hwmon_read,
407 .read_string = lenovo_ec_hwmon_read_string,
408 };
409
410 static struct hwmon_chip_info lenovo_ec_chip_info = {
411 .ops = &lenovo_ec_hwmon_ops,
412 };
413
414 static const struct dmi_system_id thinkstation_dmi_table[] = {
415 {
416 .ident = "LENOVO_PX",
417 .driver_data = (void *)(long)LENOVO_PX,
418 .matches = {
419 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
420 DMI_MATCH(DMI_PRODUCT_NAME, "30EU"),
421 },
422 },
423 {
424 .ident = "LENOVO_PX",
425 .driver_data = (void *)(long)LENOVO_PX,
426 .matches = {
427 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
428 DMI_MATCH(DMI_PRODUCT_NAME, "30EV"),
429 },
430 },
431 {
432 .ident = "LENOVO_P7",
433 .driver_data = (void *)(long)LENOVO_P7,
434 .matches = {
435 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
436 DMI_MATCH(DMI_PRODUCT_NAME, "30F2"),
437 },
438 },
439 {
440 .ident = "LENOVO_P7",
441 .driver_data = (void *)(long)LENOVO_P7,
442 .matches = {
443 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
444 DMI_MATCH(DMI_PRODUCT_NAME, "30F3"),
445 },
446 },
447 {
448 .ident = "LENOVO_P5",
449 .driver_data = (void *)(long)LENOVO_P5,
450 .matches = {
451 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
452 DMI_MATCH(DMI_PRODUCT_NAME, "30G9"),
453 },
454 },
455 {
456 .ident = "LENOVO_P5",
457 .driver_data = (void *)(long)LENOVO_P5,
458 .matches = {
459 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
460 DMI_MATCH(DMI_PRODUCT_NAME, "30GA"),
461 },
462 },
463 {
464 .ident = "LENOVO_P8",
465 .driver_data = (void *)(long)LENOVO_P8,
466 .matches = {
467 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
468 DMI_MATCH(DMI_PRODUCT_NAME, "30HH"),
469 },
470 },
471 {
472 .ident = "LENOVO_P8",
473 .driver_data = (void *)(long)LENOVO_P8,
474 .matches = {
475 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
476 DMI_MATCH(DMI_PRODUCT_NAME, "30HJ"),
477 },
478 },
479 {}
480 };
481 MODULE_DEVICE_TABLE(dmi, thinkstation_dmi_table);
482
lenovo_ec_probe(struct platform_device * pdev)483 static int lenovo_ec_probe(struct platform_device *pdev)
484 {
485 struct device *hwdev;
486 struct ec_sensors_data *ec_data;
487 const struct hwmon_chip_info *chip_info;
488 struct device *dev = &pdev->dev;
489 const struct dmi_system_id *dmi_id;
490 int app_id;
491
492 ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data), GFP_KERNEL);
493 if (!ec_data)
494 return -ENOMEM;
495
496 if (!request_region(IO_REGION_START, IO_REGION_LENGTH, "LNV-WKS")) {
497 pr_err(":request fail\n");
498 return -EIO;
499 }
500
501 dev_set_drvdata(dev, ec_data);
502
503 chip_info = &lenovo_ec_chip_info;
504
505 mutex_init(&ec_data->mec_mutex);
506
507 mutex_lock(&ec_data->mec_mutex);
508 app_id = inb_p(MCHP_EMI0_APPLICATION_ID);
509 if (app_id) /* check EMI Application ID Value */
510 outb_p(app_id, MCHP_EMI0_APPLICATION_ID); /* set EMI Application ID to 0 */
511 outw_p(MCHP_SING_IDX, MCHP_EMI0_EC_ADDRESS);
512 mutex_unlock(&ec_data->mec_mutex);
513
514 if ((inb_p(MCHP_EMI0_EC_DATA_BYTE0) != 'M') &&
515 (inb_p(MCHP_EMI0_EC_DATA_BYTE1) != 'C') &&
516 (inb_p(MCHP_EMI0_EC_DATA_BYTE2) != 'H') &&
517 (inb_p(MCHP_EMI0_EC_DATA_BYTE3) != 'P')) {
518 release_region(IO_REGION_START, IO_REGION_LENGTH);
519 return -ENODEV;
520 }
521
522 dmi_id = dmi_first_match(thinkstation_dmi_table);
523
524 switch ((long)dmi_id->driver_data) {
525 case 0:
526 ec_data->fan_labels = px_ec_fan_label;
527 ec_data->temp_labels = lenovo_px_ec_temp_label;
528 ec_data->fan_map = px_fan_map;
529 ec_data->temp_map = px_temp_map;
530 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_px;
531 break;
532 case 1:
533 ec_data->fan_labels = p7_ec_fan_label;
534 ec_data->temp_labels = lenovo_gen_ec_temp_label;
535 ec_data->fan_map = p7_fan_map;
536 ec_data->temp_map = gen_temp_map;
537 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p7;
538 break;
539 case 2:
540 ec_data->fan_labels = p5_ec_fan_label;
541 ec_data->temp_labels = lenovo_gen_ec_temp_label;
542 ec_data->fan_map = p5_fan_map;
543 ec_data->temp_map = gen_temp_map;
544 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p5;
545 break;
546 case 3:
547 ec_data->fan_labels = p8_ec_fan_label;
548 ec_data->temp_labels = lenovo_gen_ec_temp_label;
549 ec_data->fan_map = p8_fan_map;
550 ec_data->temp_map = gen_temp_map;
551 lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p8;
552 break;
553 default:
554 release_region(IO_REGION_START, IO_REGION_LENGTH);
555 return -ENODEV;
556 }
557
558 hwdev = devm_hwmon_device_register_with_info(dev, "lenovo_ec",
559 ec_data,
560 chip_info, NULL);
561
562 return PTR_ERR_OR_ZERO(hwdev);
563 }
564
565 static struct platform_driver lenovo_ec_sensors_platform_driver = {
566 .driver = {
567 .name = "lenovo-ec-sensors",
568 },
569 .probe = lenovo_ec_probe,
570 };
571
572 static struct platform_device *lenovo_ec_sensors_platform_device;
573
lenovo_ec_init(void)574 static int __init lenovo_ec_init(void)
575 {
576 if (!dmi_check_system(thinkstation_dmi_table))
577 return -ENODEV;
578
579 lenovo_ec_sensors_platform_device =
580 platform_create_bundle(&lenovo_ec_sensors_platform_driver,
581 lenovo_ec_probe, NULL, 0, NULL, 0);
582
583 if (IS_ERR(lenovo_ec_sensors_platform_device)) {
584 release_region(IO_REGION_START, IO_REGION_LENGTH);
585 return PTR_ERR(lenovo_ec_sensors_platform_device);
586 }
587
588 return 0;
589 }
590 module_init(lenovo_ec_init);
591
lenovo_ec_exit(void)592 static void __exit lenovo_ec_exit(void)
593 {
594 release_region(IO_REGION_START, IO_REGION_LENGTH);
595 platform_device_unregister(lenovo_ec_sensors_platform_device);
596 platform_driver_unregister(&lenovo_ec_sensors_platform_driver);
597 }
598 module_exit(lenovo_ec_exit);
599
600 MODULE_AUTHOR("David Ober <dober@lenovo.com>");
601 MODULE_DESCRIPTION("HWMON driver for sensors accessible via EC in LENOVO motherboards");
602 MODULE_LICENSE("GPL");
603