File Index
Files and directories
Sign in
FILE

lenovo-ec-sensors.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/lenovo-ec-sensors.c C
  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
 44static inline u8
 45get_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
 62enum systems {
 63	LENOVO_PX,
 64	LENOVO_P7,
 65	LENOVO_P5,
 66	LENOVO_P8,
 67};
 68
 69static int px_temp_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31, 32};
 70
 71static 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
 91static int p8_temp_map[] = {0, 1, 2, 8, 9, 13, 14, 15, 16, 17, 19, 20, 33};
 92
 93static 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
109static int gen_temp_map[] = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31};
110
111static 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
127static int px_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
128
129static 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
148static int p7_fan_map[] = {0, 2, 3, 4, 5, 6, 7, 8, 10, 11, 14};
149
150static 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
164static int p5_fan_map[] = {0, 5, 6, 7, 8, 10, 11, 14};
165
166static 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
177static int p8_fan_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14};
178
179static 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
196struct 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
204static int
205lenovo_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
223static int
224lenovo_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
249static int
250lenovo_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
267static int
268lenovo_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
283static umode_t
284lenovo_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
301static 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
340static 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
373static 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
403static 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
430static 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
436static struct hwmon_chip_info lenovo_ec_chip_info = {
437	.ops = &lenovo_ec_hwmon_ops,
438};
439
440static 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};
507MODULE_DEVICE_TABLE(dmi, thinkstation_dmi_table);
508
509static 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
591static struct platform_driver lenovo_ec_sensors_platform_driver = {
592	.driver = {
593		.name	= "lenovo-ec-sensors",
594	},
595	.probe = lenovo_ec_probe,
596};
597
598static struct platform_device *lenovo_ec_sensors_platform_device;
599
600static 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}
616module_init(lenovo_ec_init);
617
618static 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}
624module_exit(lenovo_ec_exit);
625
626MODULE_AUTHOR("David Ober <dober@lenovo.com>");
627MODULE_DESCRIPTION("HWMON driver for sensors accessible via EC in LENOVO motherboards");
628MODULE_LICENSE("GPL");
~/ Read-only file preview.