xref: /linux/drivers/hwmon/lenovo-ec-sensors.c (revision 68a052239fc4b351e961f698b824f7654a346091)
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