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, 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
lenovo_ec_do_read_temp(struct ec_sensors_data * data,u32 attr,int channel,long * val)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
lenovo_ec_do_read_fan(struct ec_sensors_data * data,u32 attr,int channel,long * val)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
lenovo_ec_hwmon_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)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
lenovo_ec_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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
lenovo_ec_hwmon_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)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
lenovo_ec_probe(struct platform_device * pdev)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
lenovo_ec_init(void)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
lenovo_ec_exit(void)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