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