xref: /linux/drivers/hwmon/nct6775-platform.c (revision c05403e64d7feb12ac31cf785a61b665970cc821)
1c3963bc0SZev Weiss // SPDX-License-Identifier: GPL-2.0-or-later
2c3963bc0SZev Weiss /*
3c3963bc0SZev Weiss  * nct6775 - Platform driver for the hardware monitoring
4c3963bc0SZev Weiss  *	     functionality of Nuvoton NCT677x Super-I/O chips
5c3963bc0SZev Weiss  *
6c3963bc0SZev Weiss  * Copyright (C) 2012  Guenter Roeck <linux@roeck-us.net>
7c3963bc0SZev Weiss  */
8c3963bc0SZev Weiss 
9c3963bc0SZev Weiss #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10c3963bc0SZev Weiss 
11c3963bc0SZev Weiss #include <linux/acpi.h>
12c3963bc0SZev Weiss #include <linux/dmi.h>
13c3963bc0SZev Weiss #include <linux/hwmon-sysfs.h>
14c3963bc0SZev Weiss #include <linux/hwmon-vid.h>
15c3963bc0SZev Weiss #include <linux/init.h>
16c3963bc0SZev Weiss #include <linux/io.h>
17c3963bc0SZev Weiss #include <linux/module.h>
18c3963bc0SZev Weiss #include <linux/platform_device.h>
19c3963bc0SZev Weiss #include <linux/regmap.h>
20c3963bc0SZev Weiss 
21c3963bc0SZev Weiss #include "nct6775.h"
22c3963bc0SZev Weiss 
23c3963bc0SZev Weiss enum sensor_access { access_direct, access_asuswmi };
24c3963bc0SZev Weiss 
25c3963bc0SZev Weiss static const char * const nct6775_sio_names[] __initconst = {
26c3963bc0SZev Weiss 	"NCT6106D",
27c3963bc0SZev Weiss 	"NCT6116D",
28c3963bc0SZev Weiss 	"NCT6775F",
29c3963bc0SZev Weiss 	"NCT6776D/F",
30c3963bc0SZev Weiss 	"NCT6779D",
31c3963bc0SZev Weiss 	"NCT6791D",
32c3963bc0SZev Weiss 	"NCT6792D",
33c3963bc0SZev Weiss 	"NCT6793D",
34c3963bc0SZev Weiss 	"NCT6795D",
35c3963bc0SZev Weiss 	"NCT6796D",
36c3963bc0SZev Weiss 	"NCT6797D",
37c3963bc0SZev Weiss 	"NCT6798D",
38c3963bc0SZev Weiss };
39c3963bc0SZev Weiss 
40c3963bc0SZev Weiss static unsigned short force_id;
41c3963bc0SZev Weiss module_param(force_id, ushort, 0);
42c3963bc0SZev Weiss MODULE_PARM_DESC(force_id, "Override the detected device ID");
43c3963bc0SZev Weiss 
44c3963bc0SZev Weiss static unsigned short fan_debounce;
45c3963bc0SZev Weiss module_param(fan_debounce, ushort, 0);
46c3963bc0SZev Weiss MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
47c3963bc0SZev Weiss 
48c3963bc0SZev Weiss #define DRVNAME "nct6775"
49c3963bc0SZev Weiss 
50c3963bc0SZev Weiss #define NCT6775_PORT_CHIPID	0x58
51c3963bc0SZev Weiss 
52c3963bc0SZev Weiss /*
53c3963bc0SZev Weiss  * ISA constants
54c3963bc0SZev Weiss  */
55c3963bc0SZev Weiss 
56c3963bc0SZev Weiss #define IOREGION_ALIGNMENT	(~7)
57c3963bc0SZev Weiss #define IOREGION_OFFSET		5
58c3963bc0SZev Weiss #define IOREGION_LENGTH		2
59c3963bc0SZev Weiss #define ADDR_REG_OFFSET		0
60c3963bc0SZev Weiss #define DATA_REG_OFFSET		1
61c3963bc0SZev Weiss 
62c3963bc0SZev Weiss /*
63c3963bc0SZev Weiss  * Super-I/O constants and functions
64c3963bc0SZev Weiss  */
65c3963bc0SZev Weiss 
66c3963bc0SZev Weiss #define NCT6775_LD_ACPI		0x0a
67c3963bc0SZev Weiss #define NCT6775_LD_HWM		0x0b
68c3963bc0SZev Weiss #define NCT6775_LD_VID		0x0d
69c3963bc0SZev Weiss #define NCT6775_LD_12		0x12
70c3963bc0SZev Weiss 
71c3963bc0SZev Weiss #define SIO_REG_LDSEL		0x07	/* Logical device select */
72c3963bc0SZev Weiss #define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
73c3963bc0SZev Weiss #define SIO_REG_ENABLE		0x30	/* Logical device enable */
74c3963bc0SZev Weiss #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
75c3963bc0SZev Weiss 
76c3963bc0SZev Weiss #define SIO_NCT6106_ID		0xc450
77c3963bc0SZev Weiss #define SIO_NCT6116_ID		0xd280
78c3963bc0SZev Weiss #define SIO_NCT6775_ID		0xb470
79c3963bc0SZev Weiss #define SIO_NCT6776_ID		0xc330
80c3963bc0SZev Weiss #define SIO_NCT6779_ID		0xc560
81c3963bc0SZev Weiss #define SIO_NCT6791_ID		0xc800
82c3963bc0SZev Weiss #define SIO_NCT6792_ID		0xc910
83c3963bc0SZev Weiss #define SIO_NCT6793_ID		0xd120
84c3963bc0SZev Weiss #define SIO_NCT6795_ID		0xd350
85c3963bc0SZev Weiss #define SIO_NCT6796_ID		0xd420
86c3963bc0SZev Weiss #define SIO_NCT6797_ID		0xd450
87c3963bc0SZev Weiss #define SIO_NCT6798_ID		0xd428
88c3963bc0SZev Weiss #define SIO_ID_MASK		0xFFF8
89c3963bc0SZev Weiss 
90c3963bc0SZev Weiss /*
91c3963bc0SZev Weiss  * Control registers
92c3963bc0SZev Weiss  */
93c3963bc0SZev Weiss #define NCT6775_REG_CR_FAN_DEBOUNCE	0xf0
94c3963bc0SZev Weiss 
95c3963bc0SZev Weiss struct nct6775_sio_data {
96c3963bc0SZev Weiss 	int sioreg;
97c3963bc0SZev Weiss 	int ld;
98c3963bc0SZev Weiss 	enum kinds kind;
99c3963bc0SZev Weiss 	enum sensor_access access;
100c3963bc0SZev Weiss 
101c3963bc0SZev Weiss 	/* superio_() callbacks  */
102c3963bc0SZev Weiss 	void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
103c3963bc0SZev Weiss 	int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
104c3963bc0SZev Weiss 	void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
105c3963bc0SZev Weiss 	int (*sio_enter)(struct nct6775_sio_data *sio_data);
106c3963bc0SZev Weiss 	void (*sio_exit)(struct nct6775_sio_data *sio_data);
107c3963bc0SZev Weiss };
108c3963bc0SZev Weiss 
109c3b3747dSDenis Pauk #define ASUSWMI_METHOD			"WMBD"
110c3963bc0SZev Weiss #define ASUSWMI_METHODID_RSIO		0x5253494F
111c3963bc0SZev Weiss #define ASUSWMI_METHODID_WSIO		0x5753494F
112c3963bc0SZev Weiss #define ASUSWMI_METHODID_RHWM		0x5248574D
113c3963bc0SZev Weiss #define ASUSWMI_METHODID_WHWM		0x5748574D
114c3963bc0SZev Weiss #define ASUSWMI_UNSUPPORTED_METHOD	0xFFFFFFFE
115c3b3747dSDenis Pauk #define ASUSWMI_DEVICE_HID		"PNP0C14"
116c3b3747dSDenis Pauk #define ASUSWMI_DEVICE_UID		"ASUSWMI"
117e2e09989SDenis Pauk #define ASUSMSI_DEVICE_UID		"AsusMbSwInterface"
118c3b3747dSDenis Pauk 
119c3b3747dSDenis Pauk #if IS_ENABLED(CONFIG_ACPI)
120c3b3747dSDenis Pauk /*
121c3b3747dSDenis Pauk  * ASUS boards have only one device with WMI "WMBD" method and have provided
122c3b3747dSDenis Pauk  * access to only one SuperIO chip at 0x0290.
123c3b3747dSDenis Pauk  */
124c3b3747dSDenis Pauk static struct acpi_device *asus_acpi_dev;
125c3b3747dSDenis Pauk #endif
126c3963bc0SZev Weiss 
127c3963bc0SZev Weiss static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
128c3963bc0SZev Weiss {
129c3b3747dSDenis Pauk #if IS_ENABLED(CONFIG_ACPI)
130c3b3747dSDenis Pauk 	acpi_handle handle = acpi_device_handle(asus_acpi_dev);
131c3963bc0SZev Weiss 	u32 args = bank | (reg << 8) | (val << 16);
132c3b3747dSDenis Pauk 	struct acpi_object_list input;
133c3b3747dSDenis Pauk 	union acpi_object params[3];
134c3b3747dSDenis Pauk 	unsigned long long result;
135c3963bc0SZev Weiss 	acpi_status status;
136c3963bc0SZev Weiss 
137c3b3747dSDenis Pauk 	params[0].type = ACPI_TYPE_INTEGER;
138c3b3747dSDenis Pauk 	params[0].integer.value = 0;
139c3b3747dSDenis Pauk 	params[1].type = ACPI_TYPE_INTEGER;
140c3b3747dSDenis Pauk 	params[1].integer.value = method_id;
141c3b3747dSDenis Pauk 	params[2].type = ACPI_TYPE_BUFFER;
142c3b3747dSDenis Pauk 	params[2].buffer.length = sizeof(args);
143c3b3747dSDenis Pauk 	params[2].buffer.pointer = (void *)&args;
144c3b3747dSDenis Pauk 	input.count = 3;
145c3b3747dSDenis Pauk 	input.pointer = params;
146c3963bc0SZev Weiss 
147c3b3747dSDenis Pauk 	status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, &input, &result);
148c3963bc0SZev Weiss 	if (ACPI_FAILURE(status))
149c3963bc0SZev Weiss 		return -EIO;
150c3963bc0SZev Weiss 
151c3963bc0SZev Weiss 	if (retval)
15208d40c1dSAndy Shevchenko 		*retval = result;
153c3963bc0SZev Weiss 
154c3963bc0SZev Weiss 	return 0;
155c3963bc0SZev Weiss #else
156c3963bc0SZev Weiss 	return -EOPNOTSUPP;
157c3963bc0SZev Weiss #endif
158c3963bc0SZev Weiss }
159c3963bc0SZev Weiss 
160c3963bc0SZev Weiss static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val)
161c3963bc0SZev Weiss {
162c3963bc0SZev Weiss 	return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank,
163c3963bc0SZev Weiss 					      reg, val, NULL);
164c3963bc0SZev Weiss }
165c3963bc0SZev Weiss 
166c3963bc0SZev Weiss static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val)
167c3963bc0SZev Weiss {
168c3963bc0SZev Weiss 	u32 ret, tmp = 0;
169c3963bc0SZev Weiss 
170c3963bc0SZev Weiss 	ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank,
171c3963bc0SZev Weiss 					      reg, 0, &tmp);
172c3963bc0SZev Weiss 	*val = tmp;
173c3963bc0SZev Weiss 	return ret;
174c3963bc0SZev Weiss }
175c3963bc0SZev Weiss 
176c3963bc0SZev Weiss static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg)
177c3963bc0SZev Weiss {
178c3963bc0SZev Weiss 	int tmp = 0;
179c3963bc0SZev Weiss 
180c3963bc0SZev Weiss 	nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld,
181c3963bc0SZev Weiss 					reg, 0, &tmp);
182c3963bc0SZev Weiss 	return tmp;
183c3963bc0SZev Weiss }
184c3963bc0SZev Weiss 
185c3963bc0SZev Weiss static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val)
186c3963bc0SZev Weiss {
187c3963bc0SZev Weiss 	nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld,
188c3963bc0SZev Weiss 					reg, val, NULL);
189c3963bc0SZev Weiss }
190c3963bc0SZev Weiss 
191c3963bc0SZev Weiss static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld)
192c3963bc0SZev Weiss {
193c3963bc0SZev Weiss 	sio_data->ld = ld;
194c3963bc0SZev Weiss }
195c3963bc0SZev Weiss 
196c3963bc0SZev Weiss static int superio_wmi_enter(struct nct6775_sio_data *sio_data)
197c3963bc0SZev Weiss {
198c3963bc0SZev Weiss 	return 0;
199c3963bc0SZev Weiss }
200c3963bc0SZev Weiss 
201c3963bc0SZev Weiss static void superio_wmi_exit(struct nct6775_sio_data *sio_data)
202c3963bc0SZev Weiss {
203c3963bc0SZev Weiss }
204c3963bc0SZev Weiss 
205c3963bc0SZev Weiss static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val)
206c3963bc0SZev Weiss {
207c3963bc0SZev Weiss 	int ioreg = sio_data->sioreg;
208c3963bc0SZev Weiss 
209c3963bc0SZev Weiss 	outb(reg, ioreg);
210c3963bc0SZev Weiss 	outb(val, ioreg + 1);
211c3963bc0SZev Weiss }
212c3963bc0SZev Weiss 
213c3963bc0SZev Weiss static int superio_inb(struct nct6775_sio_data *sio_data, int reg)
214c3963bc0SZev Weiss {
215c3963bc0SZev Weiss 	int ioreg = sio_data->sioreg;
216c3963bc0SZev Weiss 
217c3963bc0SZev Weiss 	outb(reg, ioreg);
218c3963bc0SZev Weiss 	return inb(ioreg + 1);
219c3963bc0SZev Weiss }
220c3963bc0SZev Weiss 
221c3963bc0SZev Weiss static void superio_select(struct nct6775_sio_data *sio_data, int ld)
222c3963bc0SZev Weiss {
223c3963bc0SZev Weiss 	int ioreg = sio_data->sioreg;
224c3963bc0SZev Weiss 
225c3963bc0SZev Weiss 	outb(SIO_REG_LDSEL, ioreg);
226c3963bc0SZev Weiss 	outb(ld, ioreg + 1);
227c3963bc0SZev Weiss }
228c3963bc0SZev Weiss 
229c3963bc0SZev Weiss static int superio_enter(struct nct6775_sio_data *sio_data)
230c3963bc0SZev Weiss {
231c3963bc0SZev Weiss 	int ioreg = sio_data->sioreg;
232c3963bc0SZev Weiss 
233c3963bc0SZev Weiss 	/*
234c3963bc0SZev Weiss 	 * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
235c3963bc0SZev Weiss 	 */
236c3963bc0SZev Weiss 	if (!request_muxed_region(ioreg, 2, DRVNAME))
237c3963bc0SZev Weiss 		return -EBUSY;
238c3963bc0SZev Weiss 
239c3963bc0SZev Weiss 	outb(0x87, ioreg);
240c3963bc0SZev Weiss 	outb(0x87, ioreg);
241c3963bc0SZev Weiss 
242c3963bc0SZev Weiss 	return 0;
243c3963bc0SZev Weiss }
244c3963bc0SZev Weiss 
245c3963bc0SZev Weiss static void superio_exit(struct nct6775_sio_data *sio_data)
246c3963bc0SZev Weiss {
247c3963bc0SZev Weiss 	int ioreg = sio_data->sioreg;
248c3963bc0SZev Weiss 
249c3963bc0SZev Weiss 	outb(0xaa, ioreg);
250c3963bc0SZev Weiss 	outb(0x02, ioreg);
251c3963bc0SZev Weiss 	outb(0x02, ioreg + 1);
252c3963bc0SZev Weiss 	release_region(ioreg, 2);
253c3963bc0SZev Weiss }
254c3963bc0SZev Weiss 
255c3963bc0SZev Weiss static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg)
256c3963bc0SZev Weiss {
257c3963bc0SZev Weiss 	u8 bank = reg >> 8;
258c3963bc0SZev Weiss 
259c3963bc0SZev Weiss 	data->bank = bank;
260c3963bc0SZev Weiss }
261c3963bc0SZev Weiss 
262c3963bc0SZev Weiss static int nct6775_wmi_reg_read(void *ctx, unsigned int reg, unsigned int *val)
263c3963bc0SZev Weiss {
264c3963bc0SZev Weiss 	struct nct6775_data *data = ctx;
265c3963bc0SZev Weiss 	int err, word_sized = nct6775_reg_is_word_sized(data, reg);
266c3963bc0SZev Weiss 	u8 tmp = 0;
267c3963bc0SZev Weiss 	u16 res;
268c3963bc0SZev Weiss 
269c3963bc0SZev Weiss 	nct6775_wmi_set_bank(data, reg);
270c3963bc0SZev Weiss 
271c3963bc0SZev Weiss 	err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp);
272c3963bc0SZev Weiss 	if (err)
273c3963bc0SZev Weiss 		return err;
274c3963bc0SZev Weiss 
275c3963bc0SZev Weiss 	res = tmp;
276c3963bc0SZev Weiss 	if (word_sized) {
277c3963bc0SZev Weiss 		err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp);
278c3963bc0SZev Weiss 		if (err)
279c3963bc0SZev Weiss 			return err;
280c3963bc0SZev Weiss 
281c3963bc0SZev Weiss 		res = (res << 8) + tmp;
282c3963bc0SZev Weiss 	}
283c3963bc0SZev Weiss 	*val = res;
284c3963bc0SZev Weiss 	return 0;
285c3963bc0SZev Weiss }
286c3963bc0SZev Weiss 
287c3963bc0SZev Weiss static int nct6775_wmi_reg_write(void *ctx, unsigned int reg, unsigned int value)
288c3963bc0SZev Weiss {
289c3963bc0SZev Weiss 	struct nct6775_data *data = ctx;
290c3963bc0SZev Weiss 	int res, word_sized = nct6775_reg_is_word_sized(data, reg);
291c3963bc0SZev Weiss 
292c3963bc0SZev Weiss 	nct6775_wmi_set_bank(data, reg);
293c3963bc0SZev Weiss 
294c3963bc0SZev Weiss 	if (word_sized) {
295c3963bc0SZev Weiss 		res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8);
296c3963bc0SZev Weiss 		if (res)
297c3963bc0SZev Weiss 			return res;
298c3963bc0SZev Weiss 
299c3963bc0SZev Weiss 		res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value);
300c3963bc0SZev Weiss 	} else {
301c3963bc0SZev Weiss 		res = nct6775_asuswmi_write(data->bank, reg & 0xff, value);
302c3963bc0SZev Weiss 	}
303c3963bc0SZev Weiss 
304c3963bc0SZev Weiss 	return res;
305c3963bc0SZev Weiss }
306c3963bc0SZev Weiss 
307c3963bc0SZev Weiss /*
308c3963bc0SZev Weiss  * On older chips, only registers 0x50-0x5f are banked.
309c3963bc0SZev Weiss  * On more recent chips, all registers are banked.
310c3963bc0SZev Weiss  * Assume that is the case and set the bank number for each access.
311c3963bc0SZev Weiss  * Cache the bank number so it only needs to be set if it changes.
312c3963bc0SZev Weiss  */
313c3963bc0SZev Weiss static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
314c3963bc0SZev Weiss {
315c3963bc0SZev Weiss 	u8 bank = reg >> 8;
316c3963bc0SZev Weiss 
317c3963bc0SZev Weiss 	if (data->bank != bank) {
318c3963bc0SZev Weiss 		outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
319c3963bc0SZev Weiss 		outb_p(bank, data->addr + DATA_REG_OFFSET);
320c3963bc0SZev Weiss 		data->bank = bank;
321c3963bc0SZev Weiss 	}
322c3963bc0SZev Weiss }
323c3963bc0SZev Weiss 
324c3963bc0SZev Weiss static int nct6775_reg_read(void *ctx, unsigned int reg, unsigned int *val)
325c3963bc0SZev Weiss {
326c3963bc0SZev Weiss 	struct nct6775_data *data = ctx;
327c3963bc0SZev Weiss 	int word_sized = nct6775_reg_is_word_sized(data, reg);
328c3963bc0SZev Weiss 
329c3963bc0SZev Weiss 	nct6775_set_bank(data, reg);
330c3963bc0SZev Weiss 	outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
331c3963bc0SZev Weiss 	*val = inb_p(data->addr + DATA_REG_OFFSET);
332c3963bc0SZev Weiss 	if (word_sized) {
333c3963bc0SZev Weiss 		outb_p((reg & 0xff) + 1,
334c3963bc0SZev Weiss 		       data->addr + ADDR_REG_OFFSET);
335c3963bc0SZev Weiss 		*val = (*val << 8) + inb_p(data->addr + DATA_REG_OFFSET);
336c3963bc0SZev Weiss 	}
337c3963bc0SZev Weiss 	return 0;
338c3963bc0SZev Weiss }
339c3963bc0SZev Weiss 
340c3963bc0SZev Weiss static int nct6775_reg_write(void *ctx, unsigned int reg, unsigned int value)
341c3963bc0SZev Weiss {
342c3963bc0SZev Weiss 	struct nct6775_data *data = ctx;
343c3963bc0SZev Weiss 	int word_sized = nct6775_reg_is_word_sized(data, reg);
344c3963bc0SZev Weiss 
345c3963bc0SZev Weiss 	nct6775_set_bank(data, reg);
346c3963bc0SZev Weiss 	outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
347c3963bc0SZev Weiss 	if (word_sized) {
348c3963bc0SZev Weiss 		outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
349c3963bc0SZev Weiss 		outb_p((reg & 0xff) + 1,
350c3963bc0SZev Weiss 		       data->addr + ADDR_REG_OFFSET);
351c3963bc0SZev Weiss 	}
352c3963bc0SZev Weiss 	outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
353c3963bc0SZev Weiss 	return 0;
354c3963bc0SZev Weiss }
355c3963bc0SZev Weiss 
356c3963bc0SZev Weiss static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
357c3963bc0SZev Weiss {
358c3963bc0SZev Weiss 	int val;
359c3963bc0SZev Weiss 
360c3963bc0SZev Weiss 	val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
361c3963bc0SZev Weiss 	if (val & 0x10) {
362c3963bc0SZev Weiss 		pr_info("Enabling hardware monitor logical device mappings.\n");
363c3963bc0SZev Weiss 		sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
364c3963bc0SZev Weiss 			       val & ~0x10);
365c3963bc0SZev Weiss 	}
366c3963bc0SZev Weiss }
367c3963bc0SZev Weiss 
3688de7295cSJonathan Cameron static int nct6775_suspend(struct device *dev)
369c3963bc0SZev Weiss {
370c3963bc0SZev Weiss 	int err;
371c3963bc0SZev Weiss 	u16 tmp;
372f4e6960fSZev Weiss 	struct nct6775_data *data = nct6775_update_device(dev);
373c3963bc0SZev Weiss 
374c3963bc0SZev Weiss 	if (IS_ERR(data))
375c3963bc0SZev Weiss 		return PTR_ERR(data);
376c3963bc0SZev Weiss 
377c3963bc0SZev Weiss 	mutex_lock(&data->update_lock);
378c3963bc0SZev Weiss 	err = nct6775_read_value(data, data->REG_VBAT, &tmp);
379c3963bc0SZev Weiss 	if (err)
380c3963bc0SZev Weiss 		goto out;
381c3963bc0SZev Weiss 	data->vbat = tmp;
382c3963bc0SZev Weiss 	if (data->kind == nct6775) {
383c3963bc0SZev Weiss 		err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &tmp);
384c3963bc0SZev Weiss 		if (err)
385c3963bc0SZev Weiss 			goto out;
386c3963bc0SZev Weiss 		data->fandiv1 = tmp;
387c3963bc0SZev Weiss 
388c3963bc0SZev Weiss 		err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &tmp);
389c3963bc0SZev Weiss 		if (err)
390c3963bc0SZev Weiss 			goto out;
391c3963bc0SZev Weiss 		data->fandiv2 = tmp;
392c3963bc0SZev Weiss 	}
393c3963bc0SZev Weiss out:
394c3963bc0SZev Weiss 	mutex_unlock(&data->update_lock);
395c3963bc0SZev Weiss 
396c3963bc0SZev Weiss 	return err;
397c3963bc0SZev Weiss }
398c3963bc0SZev Weiss 
3998de7295cSJonathan Cameron static int nct6775_resume(struct device *dev)
400c3963bc0SZev Weiss {
401c3963bc0SZev Weiss 	struct nct6775_data *data = dev_get_drvdata(dev);
402c3963bc0SZev Weiss 	struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
403c3963bc0SZev Weiss 	int i, j, err = 0;
404c3963bc0SZev Weiss 	u8 reg;
405c3963bc0SZev Weiss 
406c3963bc0SZev Weiss 	mutex_lock(&data->update_lock);
407c3963bc0SZev Weiss 	data->bank = 0xff;		/* Force initial bank selection */
408c3963bc0SZev Weiss 
409c3963bc0SZev Weiss 	err = sio_data->sio_enter(sio_data);
410c3963bc0SZev Weiss 	if (err)
411c3963bc0SZev Weiss 		goto abort;
412c3963bc0SZev Weiss 
413c3963bc0SZev Weiss 	sio_data->sio_select(sio_data, NCT6775_LD_HWM);
414c3963bc0SZev Weiss 	reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
415c3963bc0SZev Weiss 	if (reg != data->sio_reg_enable)
416c3963bc0SZev Weiss 		sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable);
417c3963bc0SZev Weiss 
418c3963bc0SZev Weiss 	if (data->kind == nct6791 || data->kind == nct6792 ||
419c3963bc0SZev Weiss 	    data->kind == nct6793 || data->kind == nct6795 ||
420c3963bc0SZev Weiss 	    data->kind == nct6796 || data->kind == nct6797 ||
421c3963bc0SZev Weiss 	    data->kind == nct6798)
422c3963bc0SZev Weiss 		nct6791_enable_io_mapping(sio_data);
423c3963bc0SZev Weiss 
424c3963bc0SZev Weiss 	sio_data->sio_exit(sio_data);
425c3963bc0SZev Weiss 
426c3963bc0SZev Weiss 	/* Restore limits */
427c3963bc0SZev Weiss 	for (i = 0; i < data->in_num; i++) {
428c3963bc0SZev Weiss 		if (!(data->have_in & BIT(i)))
429c3963bc0SZev Weiss 			continue;
430c3963bc0SZev Weiss 
431c3963bc0SZev Weiss 		err = nct6775_write_value(data, data->REG_IN_MINMAX[0][i], data->in[i][1]);
432c3963bc0SZev Weiss 		if (err)
433c3963bc0SZev Weiss 			goto abort;
434c3963bc0SZev Weiss 		err = nct6775_write_value(data, data->REG_IN_MINMAX[1][i], data->in[i][2]);
435c3963bc0SZev Weiss 		if (err)
436c3963bc0SZev Weiss 			goto abort;
437c3963bc0SZev Weiss 	}
438c3963bc0SZev Weiss 
439c3963bc0SZev Weiss 	for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
440c3963bc0SZev Weiss 		if (!(data->has_fan_min & BIT(i)))
441c3963bc0SZev Weiss 			continue;
442c3963bc0SZev Weiss 
443c3963bc0SZev Weiss 		err = nct6775_write_value(data, data->REG_FAN_MIN[i], data->fan_min[i]);
444c3963bc0SZev Weiss 		if (err)
445c3963bc0SZev Weiss 			goto abort;
446c3963bc0SZev Weiss 	}
447c3963bc0SZev Weiss 
448c3963bc0SZev Weiss 	for (i = 0; i < NUM_TEMP; i++) {
449c3963bc0SZev Weiss 		if (!(data->have_temp & BIT(i)))
450c3963bc0SZev Weiss 			continue;
451c3963bc0SZev Weiss 
452c3963bc0SZev Weiss 		for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
453c3963bc0SZev Weiss 			if (data->reg_temp[j][i]) {
454c3963bc0SZev Weiss 				err = nct6775_write_temp(data, data->reg_temp[j][i],
455c3963bc0SZev Weiss 							 data->temp[j][i]);
456c3963bc0SZev Weiss 				if (err)
457c3963bc0SZev Weiss 					goto abort;
458c3963bc0SZev Weiss 			}
459c3963bc0SZev Weiss 	}
460c3963bc0SZev Weiss 
461c3963bc0SZev Weiss 	/* Restore other settings */
462c3963bc0SZev Weiss 	err = nct6775_write_value(data, data->REG_VBAT, data->vbat);
463c3963bc0SZev Weiss 	if (err)
464c3963bc0SZev Weiss 		goto abort;
465c3963bc0SZev Weiss 	if (data->kind == nct6775) {
466c3963bc0SZev Weiss 		err = nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
467c3963bc0SZev Weiss 		if (err)
468c3963bc0SZev Weiss 			goto abort;
469c3963bc0SZev Weiss 		err = nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
470c3963bc0SZev Weiss 	}
471c3963bc0SZev Weiss 
472c3963bc0SZev Weiss abort:
473c3963bc0SZev Weiss 	/* Force re-reading all values */
474c3963bc0SZev Weiss 	data->valid = false;
475c3963bc0SZev Weiss 	mutex_unlock(&data->update_lock);
476c3963bc0SZev Weiss 
477c3963bc0SZev Weiss 	return err;
478c3963bc0SZev Weiss }
479c3963bc0SZev Weiss 
4808de7295cSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
481c3963bc0SZev Weiss 
482c3963bc0SZev Weiss static void
483c3963bc0SZev Weiss nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
484c3963bc0SZev Weiss {
485c3963bc0SZev Weiss 	bool fan3pin = false, fan4pin = false, fan4min = false;
486c3963bc0SZev Weiss 	bool fan5pin = false, fan6pin = false, fan7pin = false;
487c3963bc0SZev Weiss 	bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
488c3963bc0SZev Weiss 	bool pwm6pin = false, pwm7pin = false;
489c3963bc0SZev Weiss 
490c3963bc0SZev Weiss 	/* Store SIO_REG_ENABLE for use during resume */
491c3963bc0SZev Weiss 	sio_data->sio_select(sio_data, NCT6775_LD_HWM);
492c3963bc0SZev Weiss 	data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
493c3963bc0SZev Weiss 
494c3963bc0SZev Weiss 	/* fan4 and fan5 share some pins with the GPIO and serial flash */
495c3963bc0SZev Weiss 	if (data->kind == nct6775) {
496c3963bc0SZev Weiss 		int cr2c = sio_data->sio_inb(sio_data, 0x2c);
497c3963bc0SZev Weiss 
498c3963bc0SZev Weiss 		fan3pin = cr2c & BIT(6);
499c3963bc0SZev Weiss 		pwm3pin = cr2c & BIT(7);
500c3963bc0SZev Weiss 
501c3963bc0SZev Weiss 		/* On NCT6775, fan4 shares pins with the fdc interface */
502c3963bc0SZev Weiss 		fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80);
503c3963bc0SZev Weiss 	} else if (data->kind == nct6776) {
504c3963bc0SZev Weiss 		bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80;
505c3963bc0SZev Weiss 		const char *board_vendor, *board_name;
506c3963bc0SZev Weiss 
507c3963bc0SZev Weiss 		board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
508c3963bc0SZev Weiss 		board_name = dmi_get_system_info(DMI_BOARD_NAME);
509c3963bc0SZev Weiss 
510c3963bc0SZev Weiss 		if (board_name && board_vendor &&
511c3963bc0SZev Weiss 		    !strcmp(board_vendor, "ASRock")) {
512c3963bc0SZev Weiss 			/*
513c3963bc0SZev Weiss 			 * Auxiliary fan monitoring is not enabled on ASRock
514c3963bc0SZev Weiss 			 * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
515c3963bc0SZev Weiss 			 * Observed with BIOS version 2.00.
516c3963bc0SZev Weiss 			 */
517c3963bc0SZev Weiss 			if (!strcmp(board_name, "Z77 Pro4-M")) {
518c3963bc0SZev Weiss 				if ((data->sio_reg_enable & 0xe0) != 0xe0) {
519c3963bc0SZev Weiss 					data->sio_reg_enable |= 0xe0;
520c3963bc0SZev Weiss 					sio_data->sio_outb(sio_data, SIO_REG_ENABLE,
521c3963bc0SZev Weiss 						     data->sio_reg_enable);
522c3963bc0SZev Weiss 				}
523c3963bc0SZev Weiss 			}
524c3963bc0SZev Weiss 		}
525c3963bc0SZev Weiss 
526c3963bc0SZev Weiss 		if (data->sio_reg_enable & 0x80)
527c3963bc0SZev Weiss 			fan3pin = gpok;
528c3963bc0SZev Weiss 		else
529c3963bc0SZev Weiss 			fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40);
530c3963bc0SZev Weiss 
531c3963bc0SZev Weiss 		if (data->sio_reg_enable & 0x40)
532c3963bc0SZev Weiss 			fan4pin = gpok;
533c3963bc0SZev Weiss 		else
534c3963bc0SZev Weiss 			fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01;
535c3963bc0SZev Weiss 
536c3963bc0SZev Weiss 		if (data->sio_reg_enable & 0x20)
537c3963bc0SZev Weiss 			fan5pin = gpok;
538c3963bc0SZev Weiss 		else
539c3963bc0SZev Weiss 			fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02;
540c3963bc0SZev Weiss 
541c3963bc0SZev Weiss 		fan4min = fan4pin;
542c3963bc0SZev Weiss 		pwm3pin = fan3pin;
543c3963bc0SZev Weiss 	} else if (data->kind == nct6106) {
544c3963bc0SZev Weiss 		int cr24 = sio_data->sio_inb(sio_data, 0x24);
545c3963bc0SZev Weiss 
546c3963bc0SZev Weiss 		fan3pin = !(cr24 & 0x80);
547c3963bc0SZev Weiss 		pwm3pin = cr24 & 0x08;
548c3963bc0SZev Weiss 	} else if (data->kind == nct6116) {
549c3963bc0SZev Weiss 		int cr1a = sio_data->sio_inb(sio_data, 0x1a);
550c3963bc0SZev Weiss 		int cr1b = sio_data->sio_inb(sio_data, 0x1b);
551c3963bc0SZev Weiss 		int cr24 = sio_data->sio_inb(sio_data, 0x24);
552c3963bc0SZev Weiss 		int cr2a = sio_data->sio_inb(sio_data, 0x2a);
553c3963bc0SZev Weiss 		int cr2b = sio_data->sio_inb(sio_data, 0x2b);
554c3963bc0SZev Weiss 		int cr2f = sio_data->sio_inb(sio_data, 0x2f);
555c3963bc0SZev Weiss 
556c3963bc0SZev Weiss 		fan3pin = !(cr2b & 0x10);
557c3963bc0SZev Weiss 		fan4pin = (cr2b & 0x80) ||			// pin 1(2)
558c3963bc0SZev Weiss 			(!(cr2f & 0x10) && (cr1a & 0x04));	// pin 65(66)
559c3963bc0SZev Weiss 		fan5pin = (cr2b & 0x80) ||			// pin 126(127)
560c3963bc0SZev Weiss 			(!(cr1b & 0x03) && (cr2a & 0x02));	// pin 94(96)
561c3963bc0SZev Weiss 
562c3963bc0SZev Weiss 		pwm3pin = fan3pin && (cr24 & 0x08);
563c3963bc0SZev Weiss 		pwm4pin = fan4pin;
564c3963bc0SZev Weiss 		pwm5pin = fan5pin;
565c3963bc0SZev Weiss 	} else {
566c3963bc0SZev Weiss 		/*
567c3963bc0SZev Weiss 		 * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
568c3963bc0SZev Weiss 		 * NCT6797D, NCT6798D
569c3963bc0SZev Weiss 		 */
570c3963bc0SZev Weiss 		int cr1a = sio_data->sio_inb(sio_data, 0x1a);
571c3963bc0SZev Weiss 		int cr1b = sio_data->sio_inb(sio_data, 0x1b);
572c3963bc0SZev Weiss 		int cr1c = sio_data->sio_inb(sio_data, 0x1c);
573c3963bc0SZev Weiss 		int cr1d = sio_data->sio_inb(sio_data, 0x1d);
574c3963bc0SZev Weiss 		int cr2a = sio_data->sio_inb(sio_data, 0x2a);
575c3963bc0SZev Weiss 		int cr2b = sio_data->sio_inb(sio_data, 0x2b);
576c3963bc0SZev Weiss 		int cr2d = sio_data->sio_inb(sio_data, 0x2d);
577c3963bc0SZev Weiss 		int cr2f = sio_data->sio_inb(sio_data, 0x2f);
578c3963bc0SZev Weiss 		bool dsw_en = cr2f & BIT(3);
579c3963bc0SZev Weiss 		bool ddr4_en = cr2f & BIT(4);
580c3963bc0SZev Weiss 		int cre0;
581c3963bc0SZev Weiss 		int creb;
582c3963bc0SZev Weiss 		int cred;
583c3963bc0SZev Weiss 
584c3963bc0SZev Weiss 		sio_data->sio_select(sio_data, NCT6775_LD_12);
585c3963bc0SZev Weiss 		cre0 = sio_data->sio_inb(sio_data, 0xe0);
586c3963bc0SZev Weiss 		creb = sio_data->sio_inb(sio_data, 0xeb);
587c3963bc0SZev Weiss 		cred = sio_data->sio_inb(sio_data, 0xed);
588c3963bc0SZev Weiss 
589c3963bc0SZev Weiss 		fan3pin = !(cr1c & BIT(5));
590c3963bc0SZev Weiss 		fan4pin = !(cr1c & BIT(6));
591c3963bc0SZev Weiss 		fan5pin = !(cr1c & BIT(7));
592c3963bc0SZev Weiss 
593c3963bc0SZev Weiss 		pwm3pin = !(cr1c & BIT(0));
594c3963bc0SZev Weiss 		pwm4pin = !(cr1c & BIT(1));
595c3963bc0SZev Weiss 		pwm5pin = !(cr1c & BIT(2));
596c3963bc0SZev Weiss 
597c3963bc0SZev Weiss 		switch (data->kind) {
598c3963bc0SZev Weiss 		case nct6791:
599c3963bc0SZev Weiss 			fan6pin = cr2d & BIT(1);
600c3963bc0SZev Weiss 			pwm6pin = cr2d & BIT(0);
601c3963bc0SZev Weiss 			break;
602c3963bc0SZev Weiss 		case nct6792:
603c3963bc0SZev Weiss 			fan6pin = !dsw_en && (cr2d & BIT(1));
604c3963bc0SZev Weiss 			pwm6pin = !dsw_en && (cr2d & BIT(0));
605c3963bc0SZev Weiss 			break;
606c3963bc0SZev Weiss 		case nct6793:
607c3963bc0SZev Weiss 			fan5pin |= cr1b & BIT(5);
608c3963bc0SZev Weiss 			fan5pin |= creb & BIT(5);
609c3963bc0SZev Weiss 
610c3963bc0SZev Weiss 			fan6pin = !dsw_en && (cr2d & BIT(1));
611c3963bc0SZev Weiss 			fan6pin |= creb & BIT(3);
612c3963bc0SZev Weiss 
613c3963bc0SZev Weiss 			pwm5pin |= cr2d & BIT(7);
614c3963bc0SZev Weiss 			pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
615c3963bc0SZev Weiss 
616c3963bc0SZev Weiss 			pwm6pin = !dsw_en && (cr2d & BIT(0));
617c3963bc0SZev Weiss 			pwm6pin |= creb & BIT(2);
618c3963bc0SZev Weiss 			break;
619c3963bc0SZev Weiss 		case nct6795:
620c3963bc0SZev Weiss 			fan5pin |= cr1b & BIT(5);
621c3963bc0SZev Weiss 			fan5pin |= creb & BIT(5);
622c3963bc0SZev Weiss 
623c3963bc0SZev Weiss 			fan6pin = (cr2a & BIT(4)) &&
624c3963bc0SZev Weiss 					(!dsw_en || (cred & BIT(4)));
625c3963bc0SZev Weiss 			fan6pin |= creb & BIT(3);
626c3963bc0SZev Weiss 
627c3963bc0SZev Weiss 			pwm5pin |= cr2d & BIT(7);
628c3963bc0SZev Weiss 			pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
629c3963bc0SZev Weiss 
630c3963bc0SZev Weiss 			pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
631c3963bc0SZev Weiss 			pwm6pin |= creb & BIT(2);
632c3963bc0SZev Weiss 			break;
633c3963bc0SZev Weiss 		case nct6796:
634c3963bc0SZev Weiss 			fan5pin |= cr1b & BIT(5);
635c3963bc0SZev Weiss 			fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0));
636c3963bc0SZev Weiss 			fan5pin |= creb & BIT(5);
637c3963bc0SZev Weiss 
638c3963bc0SZev Weiss 			fan6pin = (cr2a & BIT(4)) &&
639c3963bc0SZev Weiss 					(!dsw_en || (cred & BIT(4)));
640c3963bc0SZev Weiss 			fan6pin |= creb & BIT(3);
641c3963bc0SZev Weiss 
642c3963bc0SZev Weiss 			fan7pin = !(cr2b & BIT(2));
643c3963bc0SZev Weiss 
644c3963bc0SZev Weiss 			pwm5pin |= cr2d & BIT(7);
645c3963bc0SZev Weiss 			pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0));
646c3963bc0SZev Weiss 			pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
647c3963bc0SZev Weiss 
648c3963bc0SZev Weiss 			pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
649c3963bc0SZev Weiss 			pwm6pin |= creb & BIT(2);
650c3963bc0SZev Weiss 
651c3963bc0SZev Weiss 			pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
652c3963bc0SZev Weiss 			break;
653c3963bc0SZev Weiss 		case nct6797:
654c3963bc0SZev Weiss 			fan5pin |= !ddr4_en && (cr1b & BIT(5));
655c3963bc0SZev Weiss 			fan5pin |= creb & BIT(5);
656c3963bc0SZev Weiss 
657c3963bc0SZev Weiss 			fan6pin = cr2a & BIT(4);
658c3963bc0SZev Weiss 			fan6pin |= creb & BIT(3);
659c3963bc0SZev Weiss 
660c3963bc0SZev Weiss 			fan7pin = cr1a & BIT(1);
661c3963bc0SZev Weiss 
662c3963bc0SZev Weiss 			pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
663c3963bc0SZev Weiss 			pwm5pin |= !ddr4_en && (cr2d & BIT(7));
664c3963bc0SZev Weiss 
665c3963bc0SZev Weiss 			pwm6pin = creb & BIT(2);
666c3963bc0SZev Weiss 			pwm6pin |= cred & BIT(2);
667c3963bc0SZev Weiss 
668c3963bc0SZev Weiss 			pwm7pin = cr1d & BIT(4);
669c3963bc0SZev Weiss 			break;
670c3963bc0SZev Weiss 		case nct6798:
671c3963bc0SZev Weiss 			fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
672c3963bc0SZev Weiss 			fan6pin |= cr2a & BIT(4);
673c3963bc0SZev Weiss 			fan6pin |= creb & BIT(5);
674c3963bc0SZev Weiss 
675c3963bc0SZev Weiss 			fan7pin = cr1b & BIT(5);
676c3963bc0SZev Weiss 			fan7pin |= !(cr2b & BIT(2));
677c3963bc0SZev Weiss 			fan7pin |= creb & BIT(3);
678c3963bc0SZev Weiss 
679c3963bc0SZev Weiss 			pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
680c3963bc0SZev Weiss 			pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3));
681c3963bc0SZev Weiss 			pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
682c3963bc0SZev Weiss 
683c3963bc0SZev Weiss 			pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
684c3963bc0SZev Weiss 			pwm7pin |= cr2d & BIT(7);
685c3963bc0SZev Weiss 			pwm7pin |= creb & BIT(2);
686c3963bc0SZev Weiss 			break;
687c3963bc0SZev Weiss 		default:	/* NCT6779D */
688c3963bc0SZev Weiss 			break;
689c3963bc0SZev Weiss 		}
690c3963bc0SZev Weiss 
691c3963bc0SZev Weiss 		fan4min = fan4pin;
692c3963bc0SZev Weiss 	}
693c3963bc0SZev Weiss 
694c3963bc0SZev Weiss 	/* fan 1 and 2 (0x03) are always present */
695c3963bc0SZev Weiss 	data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
696c3963bc0SZev Weiss 		(fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
697c3963bc0SZev Weiss 	data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
698c3963bc0SZev Weiss 		(fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
699c3963bc0SZev Weiss 	data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
700c3963bc0SZev Weiss 		(pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
701c3963bc0SZev Weiss }
702c3963bc0SZev Weiss 
703c3963bc0SZev Weiss static ssize_t
704c3963bc0SZev Weiss cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
705c3963bc0SZev Weiss {
706c3963bc0SZev Weiss 	struct nct6775_data *data = dev_get_drvdata(dev);
707c3963bc0SZev Weiss 
708c3963bc0SZev Weiss 	return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
709c3963bc0SZev Weiss }
710c3963bc0SZev Weiss 
711c3963bc0SZev Weiss static DEVICE_ATTR_RO(cpu0_vid);
712c3963bc0SZev Weiss 
713c3963bc0SZev Weiss /* Case open detection */
714c3963bc0SZev Weiss 
715c3963bc0SZev Weiss static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
716c3963bc0SZev Weiss static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
717c3963bc0SZev Weiss 
718c3963bc0SZev Weiss static ssize_t
719c3963bc0SZev Weiss clear_caseopen(struct device *dev, struct device_attribute *attr,
720c3963bc0SZev Weiss 	       const char *buf, size_t count)
721c3963bc0SZev Weiss {
722c3963bc0SZev Weiss 	struct nct6775_data *data = dev_get_drvdata(dev);
723c3963bc0SZev Weiss 	struct nct6775_sio_data *sio_data = data->driver_data;
724c3963bc0SZev Weiss 	int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
725c3963bc0SZev Weiss 	unsigned long val;
726c3963bc0SZev Weiss 	u8 reg;
727c3963bc0SZev Weiss 	int ret;
728c3963bc0SZev Weiss 
729c3963bc0SZev Weiss 	if (kstrtoul(buf, 10, &val) || val != 0)
730c3963bc0SZev Weiss 		return -EINVAL;
731c3963bc0SZev Weiss 
732c3963bc0SZev Weiss 	mutex_lock(&data->update_lock);
733c3963bc0SZev Weiss 
734c3963bc0SZev Weiss 	/*
735c3963bc0SZev Weiss 	 * Use CR registers to clear caseopen status.
736c3963bc0SZev Weiss 	 * The CR registers are the same for all chips, and not all chips
737c3963bc0SZev Weiss 	 * support clearing the caseopen status through "regular" registers.
738c3963bc0SZev Weiss 	 */
739c3963bc0SZev Weiss 	ret = sio_data->sio_enter(sio_data);
740c3963bc0SZev Weiss 	if (ret) {
741c3963bc0SZev Weiss 		count = ret;
742c3963bc0SZev Weiss 		goto error;
743c3963bc0SZev Weiss 	}
744c3963bc0SZev Weiss 
745c3963bc0SZev Weiss 	sio_data->sio_select(sio_data, NCT6775_LD_ACPI);
746c3963bc0SZev Weiss 	reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
747c3963bc0SZev Weiss 	reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
748c3963bc0SZev Weiss 	sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
749c3963bc0SZev Weiss 	reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
750c3963bc0SZev Weiss 	sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
751c3963bc0SZev Weiss 	sio_data->sio_exit(sio_data);
752c3963bc0SZev Weiss 
753c3963bc0SZev Weiss 	data->valid = false;	/* Force cache refresh */
754c3963bc0SZev Weiss error:
755c3963bc0SZev Weiss 	mutex_unlock(&data->update_lock);
756c3963bc0SZev Weiss 	return count;
757c3963bc0SZev Weiss }
758c3963bc0SZev Weiss 
759c3963bc0SZev Weiss static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, nct6775_show_alarm,
760c3963bc0SZev Weiss 			  clear_caseopen, INTRUSION_ALARM_BASE);
761c3963bc0SZev Weiss static SENSOR_DEVICE_ATTR(intrusion1_alarm, 0644, nct6775_show_alarm,
762c3963bc0SZev Weiss 			  clear_caseopen, INTRUSION_ALARM_BASE + 1);
763c3963bc0SZev Weiss static SENSOR_DEVICE_ATTR(intrusion0_beep, 0644, nct6775_show_beep,
764c3963bc0SZev Weiss 			  nct6775_store_beep, INTRUSION_ALARM_BASE);
765c3963bc0SZev Weiss static SENSOR_DEVICE_ATTR(intrusion1_beep, 0644, nct6775_show_beep,
766c3963bc0SZev Weiss 			  nct6775_store_beep, INTRUSION_ALARM_BASE + 1);
767c3963bc0SZev Weiss static SENSOR_DEVICE_ATTR(beep_enable, 0644, nct6775_show_beep,
768c3963bc0SZev Weiss 			  nct6775_store_beep, BEEP_ENABLE_BASE);
769c3963bc0SZev Weiss 
770c3963bc0SZev Weiss static umode_t nct6775_other_is_visible(struct kobject *kobj,
771c3963bc0SZev Weiss 					struct attribute *attr, int index)
772c3963bc0SZev Weiss {
773c3963bc0SZev Weiss 	struct device *dev = kobj_to_dev(kobj);
774c3963bc0SZev Weiss 	struct nct6775_data *data = dev_get_drvdata(dev);
775c3963bc0SZev Weiss 
776c3963bc0SZev Weiss 	if (index == 0 && !data->have_vid)
777c3963bc0SZev Weiss 		return 0;
778c3963bc0SZev Weiss 
779c3963bc0SZev Weiss 	if (index == 1 || index == 2) {
780c3963bc0SZev Weiss 		if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
781c3963bc0SZev Weiss 			return 0;
782c3963bc0SZev Weiss 	}
783c3963bc0SZev Weiss 
784c3963bc0SZev Weiss 	if (index == 3 || index == 4) {
785c3963bc0SZev Weiss 		if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
786c3963bc0SZev Weiss 			return 0;
787c3963bc0SZev Weiss 	}
788c3963bc0SZev Weiss 
789c3963bc0SZev Weiss 	return nct6775_attr_mode(data, attr);
790c3963bc0SZev Weiss }
791c3963bc0SZev Weiss 
792c3963bc0SZev Weiss /*
793c3963bc0SZev Weiss  * nct6775_other_is_visible uses the index into the following array
794c3963bc0SZev Weiss  * to determine if attributes should be created or not.
795c3963bc0SZev Weiss  * Any change in order or content must be matched.
796c3963bc0SZev Weiss  */
797c3963bc0SZev Weiss static struct attribute *nct6775_attributes_other[] = {
798c3963bc0SZev Weiss 	&dev_attr_cpu0_vid.attr,				/* 0 */
799c3963bc0SZev Weiss 	&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,	/* 1 */
800c3963bc0SZev Weiss 	&sensor_dev_attr_intrusion1_alarm.dev_attr.attr,	/* 2 */
801c3963bc0SZev Weiss 	&sensor_dev_attr_intrusion0_beep.dev_attr.attr,		/* 3 */
802c3963bc0SZev Weiss 	&sensor_dev_attr_intrusion1_beep.dev_attr.attr,		/* 4 */
803c3963bc0SZev Weiss 	&sensor_dev_attr_beep_enable.dev_attr.attr,		/* 5 */
804c3963bc0SZev Weiss 
805c3963bc0SZev Weiss 	NULL
806c3963bc0SZev Weiss };
807c3963bc0SZev Weiss 
808c3963bc0SZev Weiss static const struct attribute_group nct6775_group_other = {
809c3963bc0SZev Weiss 	.attrs = nct6775_attributes_other,
810c3963bc0SZev Weiss 	.is_visible = nct6775_other_is_visible,
811c3963bc0SZev Weiss };
812c3963bc0SZev Weiss 
813c3963bc0SZev Weiss static int nct6775_platform_probe_init(struct nct6775_data *data)
814c3963bc0SZev Weiss {
815c3963bc0SZev Weiss 	int err;
816c3963bc0SZev Weiss 	u8 cr2a;
817c3963bc0SZev Weiss 	struct nct6775_sio_data *sio_data = data->driver_data;
818c3963bc0SZev Weiss 
819c3963bc0SZev Weiss 	err = sio_data->sio_enter(sio_data);
820c3963bc0SZev Weiss 	if (err)
821c3963bc0SZev Weiss 		return err;
822c3963bc0SZev Weiss 
823c3963bc0SZev Weiss 	cr2a = sio_data->sio_inb(sio_data, 0x2a);
824c3963bc0SZev Weiss 	switch (data->kind) {
825c3963bc0SZev Weiss 	case nct6775:
826c3963bc0SZev Weiss 		data->have_vid = (cr2a & 0x40);
827c3963bc0SZev Weiss 		break;
828c3963bc0SZev Weiss 	case nct6776:
829c3963bc0SZev Weiss 		data->have_vid = (cr2a & 0x60) == 0x40;
830c3963bc0SZev Weiss 		break;
831c3963bc0SZev Weiss 	case nct6106:
832c3963bc0SZev Weiss 	case nct6116:
833c3963bc0SZev Weiss 	case nct6779:
834c3963bc0SZev Weiss 	case nct6791:
835c3963bc0SZev Weiss 	case nct6792:
836c3963bc0SZev Weiss 	case nct6793:
837c3963bc0SZev Weiss 	case nct6795:
838c3963bc0SZev Weiss 	case nct6796:
839c3963bc0SZev Weiss 	case nct6797:
840c3963bc0SZev Weiss 	case nct6798:
841c3963bc0SZev Weiss 		break;
842c3963bc0SZev Weiss 	}
843c3963bc0SZev Weiss 
844c3963bc0SZev Weiss 	/*
845c3963bc0SZev Weiss 	 * Read VID value
846c3963bc0SZev Weiss 	 * We can get the VID input values directly at logical device D 0xe3.
847c3963bc0SZev Weiss 	 */
848c3963bc0SZev Weiss 	if (data->have_vid) {
849c3963bc0SZev Weiss 		sio_data->sio_select(sio_data, NCT6775_LD_VID);
850c3963bc0SZev Weiss 		data->vid = sio_data->sio_inb(sio_data, 0xe3);
851c3963bc0SZev Weiss 		data->vrm = vid_which_vrm();
852c3963bc0SZev Weiss 	}
853c3963bc0SZev Weiss 
854c3963bc0SZev Weiss 	if (fan_debounce) {
855c3963bc0SZev Weiss 		u8 tmp;
856c3963bc0SZev Weiss 
857c3963bc0SZev Weiss 		sio_data->sio_select(sio_data, NCT6775_LD_HWM);
858c3963bc0SZev Weiss 		tmp = sio_data->sio_inb(sio_data,
859c3963bc0SZev Weiss 				    NCT6775_REG_CR_FAN_DEBOUNCE);
860c3963bc0SZev Weiss 		switch (data->kind) {
861c3963bc0SZev Weiss 		case nct6106:
862c3963bc0SZev Weiss 		case nct6116:
863c3963bc0SZev Weiss 			tmp |= 0xe0;
864c3963bc0SZev Weiss 			break;
865c3963bc0SZev Weiss 		case nct6775:
866c3963bc0SZev Weiss 			tmp |= 0x1e;
867c3963bc0SZev Weiss 			break;
868c3963bc0SZev Weiss 		case nct6776:
869c3963bc0SZev Weiss 		case nct6779:
870c3963bc0SZev Weiss 			tmp |= 0x3e;
871c3963bc0SZev Weiss 			break;
872c3963bc0SZev Weiss 		case nct6791:
873c3963bc0SZev Weiss 		case nct6792:
874c3963bc0SZev Weiss 		case nct6793:
875c3963bc0SZev Weiss 		case nct6795:
876c3963bc0SZev Weiss 		case nct6796:
877c3963bc0SZev Weiss 		case nct6797:
878c3963bc0SZev Weiss 		case nct6798:
879c3963bc0SZev Weiss 			tmp |= 0x7e;
880c3963bc0SZev Weiss 			break;
881c3963bc0SZev Weiss 		}
882c3963bc0SZev Weiss 		sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE,
883c3963bc0SZev Weiss 			     tmp);
884c3963bc0SZev Weiss 		pr_info("Enabled fan debounce for chip %s\n", data->name);
885c3963bc0SZev Weiss 	}
886c3963bc0SZev Weiss 
887c3963bc0SZev Weiss 	nct6775_check_fan_inputs(data, sio_data);
888c3963bc0SZev Weiss 
889c3963bc0SZev Weiss 	sio_data->sio_exit(sio_data);
890c3963bc0SZev Weiss 
891c3963bc0SZev Weiss 	return nct6775_add_attr_group(data, &nct6775_group_other);
892c3963bc0SZev Weiss }
893c3963bc0SZev Weiss 
894c3963bc0SZev Weiss static const struct regmap_config nct6775_regmap_config = {
895c3963bc0SZev Weiss 	.reg_bits = 16,
896c3963bc0SZev Weiss 	.val_bits = 16,
897c3963bc0SZev Weiss 	.reg_read = nct6775_reg_read,
898c3963bc0SZev Weiss 	.reg_write = nct6775_reg_write,
899c3963bc0SZev Weiss };
900c3963bc0SZev Weiss 
901c3963bc0SZev Weiss static const struct regmap_config nct6775_wmi_regmap_config = {
902c3963bc0SZev Weiss 	.reg_bits = 16,
903c3963bc0SZev Weiss 	.val_bits = 16,
904c3963bc0SZev Weiss 	.reg_read = nct6775_wmi_reg_read,
905c3963bc0SZev Weiss 	.reg_write = nct6775_wmi_reg_write,
906c3963bc0SZev Weiss };
907c3963bc0SZev Weiss 
908c3963bc0SZev Weiss static int nct6775_platform_probe(struct platform_device *pdev)
909c3963bc0SZev Weiss {
910c3963bc0SZev Weiss 	struct device *dev = &pdev->dev;
911c3963bc0SZev Weiss 	struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
912c3963bc0SZev Weiss 	struct nct6775_data *data;
913c3963bc0SZev Weiss 	struct resource *res;
914c3963bc0SZev Weiss 	const struct regmap_config *regmapcfg;
915c3963bc0SZev Weiss 
916c3963bc0SZev Weiss 	if (sio_data->access == access_direct) {
917c3963bc0SZev Weiss 		res = platform_get_resource(pdev, IORESOURCE_IO, 0);
918c3963bc0SZev Weiss 		if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, DRVNAME))
919c3963bc0SZev Weiss 			return -EBUSY;
920c3963bc0SZev Weiss 	}
921c3963bc0SZev Weiss 
922c3963bc0SZev Weiss 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
923c3963bc0SZev Weiss 	if (!data)
924c3963bc0SZev Weiss 		return -ENOMEM;
925c3963bc0SZev Weiss 
926c3963bc0SZev Weiss 	data->kind = sio_data->kind;
927c3963bc0SZev Weiss 	data->sioreg = sio_data->sioreg;
928c3963bc0SZev Weiss 
929c3963bc0SZev Weiss 	if (sio_data->access == access_direct) {
930c3963bc0SZev Weiss 		data->addr = res->start;
931c3963bc0SZev Weiss 		regmapcfg = &nct6775_regmap_config;
932c3963bc0SZev Weiss 	} else {
933c3963bc0SZev Weiss 		regmapcfg = &nct6775_wmi_regmap_config;
934c3963bc0SZev Weiss 	}
935c3963bc0SZev Weiss 
936c3963bc0SZev Weiss 	platform_set_drvdata(pdev, data);
937c3963bc0SZev Weiss 
938c3963bc0SZev Weiss 	data->driver_data = sio_data;
939c3963bc0SZev Weiss 	data->driver_init = nct6775_platform_probe_init;
940c3963bc0SZev Weiss 
941c3963bc0SZev Weiss 	return nct6775_probe(&pdev->dev, data, regmapcfg);
942c3963bc0SZev Weiss }
943c3963bc0SZev Weiss 
944c3963bc0SZev Weiss static struct platform_driver nct6775_driver = {
945c3963bc0SZev Weiss 	.driver = {
946c3963bc0SZev Weiss 		.name	= DRVNAME,
9478de7295cSJonathan Cameron 		.pm	= pm_sleep_ptr(&nct6775_dev_pm_ops),
948c3963bc0SZev Weiss 	},
949c3963bc0SZev Weiss 	.probe		= nct6775_platform_probe,
950c3963bc0SZev Weiss };
951c3963bc0SZev Weiss 
952c3963bc0SZev Weiss /* nct6775_find() looks for a '627 in the Super-I/O config space */
953c3963bc0SZev Weiss static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
954c3963bc0SZev Weiss {
955c3963bc0SZev Weiss 	u16 val;
956c3963bc0SZev Weiss 	int err;
957c3963bc0SZev Weiss 	int addr;
958c3963bc0SZev Weiss 
959c3963bc0SZev Weiss 	sio_data->access = access_direct;
960c3963bc0SZev Weiss 	sio_data->sioreg = sioaddr;
961c3963bc0SZev Weiss 
962c3963bc0SZev Weiss 	err = sio_data->sio_enter(sio_data);
963c3963bc0SZev Weiss 	if (err)
964c3963bc0SZev Weiss 		return err;
965c3963bc0SZev Weiss 
966c3963bc0SZev Weiss 	val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) |
967c3963bc0SZev Weiss 		sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1);
968c3963bc0SZev Weiss 	if (force_id && val != 0xffff)
969c3963bc0SZev Weiss 		val = force_id;
970c3963bc0SZev Weiss 
971c3963bc0SZev Weiss 	switch (val & SIO_ID_MASK) {
972c3963bc0SZev Weiss 	case SIO_NCT6106_ID:
973c3963bc0SZev Weiss 		sio_data->kind = nct6106;
974c3963bc0SZev Weiss 		break;
975c3963bc0SZev Weiss 	case SIO_NCT6116_ID:
976c3963bc0SZev Weiss 		sio_data->kind = nct6116;
977c3963bc0SZev Weiss 		break;
978c3963bc0SZev Weiss 	case SIO_NCT6775_ID:
979c3963bc0SZev Weiss 		sio_data->kind = nct6775;
980c3963bc0SZev Weiss 		break;
981c3963bc0SZev Weiss 	case SIO_NCT6776_ID:
982c3963bc0SZev Weiss 		sio_data->kind = nct6776;
983c3963bc0SZev Weiss 		break;
984c3963bc0SZev Weiss 	case SIO_NCT6779_ID:
985c3963bc0SZev Weiss 		sio_data->kind = nct6779;
986c3963bc0SZev Weiss 		break;
987c3963bc0SZev Weiss 	case SIO_NCT6791_ID:
988c3963bc0SZev Weiss 		sio_data->kind = nct6791;
989c3963bc0SZev Weiss 		break;
990c3963bc0SZev Weiss 	case SIO_NCT6792_ID:
991c3963bc0SZev Weiss 		sio_data->kind = nct6792;
992c3963bc0SZev Weiss 		break;
993c3963bc0SZev Weiss 	case SIO_NCT6793_ID:
994c3963bc0SZev Weiss 		sio_data->kind = nct6793;
995c3963bc0SZev Weiss 		break;
996c3963bc0SZev Weiss 	case SIO_NCT6795_ID:
997c3963bc0SZev Weiss 		sio_data->kind = nct6795;
998c3963bc0SZev Weiss 		break;
999c3963bc0SZev Weiss 	case SIO_NCT6796_ID:
1000c3963bc0SZev Weiss 		sio_data->kind = nct6796;
1001c3963bc0SZev Weiss 		break;
1002c3963bc0SZev Weiss 	case SIO_NCT6797_ID:
1003c3963bc0SZev Weiss 		sio_data->kind = nct6797;
1004c3963bc0SZev Weiss 		break;
1005c3963bc0SZev Weiss 	case SIO_NCT6798_ID:
1006c3963bc0SZev Weiss 		sio_data->kind = nct6798;
1007c3963bc0SZev Weiss 		break;
1008c3963bc0SZev Weiss 	default:
1009c3963bc0SZev Weiss 		if (val != 0xffff)
1010c3963bc0SZev Weiss 			pr_debug("unsupported chip ID: 0x%04x\n", val);
1011c3963bc0SZev Weiss 		sio_data->sio_exit(sio_data);
1012c3963bc0SZev Weiss 		return -ENODEV;
1013c3963bc0SZev Weiss 	}
1014c3963bc0SZev Weiss 
1015c3963bc0SZev Weiss 	/* We have a known chip, find the HWM I/O address */
1016c3963bc0SZev Weiss 	sio_data->sio_select(sio_data, NCT6775_LD_HWM);
1017c3963bc0SZev Weiss 	val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8)
1018c3963bc0SZev Weiss 	    | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1);
1019c3963bc0SZev Weiss 	addr = val & IOREGION_ALIGNMENT;
1020c3963bc0SZev Weiss 	if (addr == 0) {
1021c3963bc0SZev Weiss 		pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
1022c3963bc0SZev Weiss 		sio_data->sio_exit(sio_data);
1023c3963bc0SZev Weiss 		return -ENODEV;
1024c3963bc0SZev Weiss 	}
1025c3963bc0SZev Weiss 
1026c3963bc0SZev Weiss 	/* Activate logical device if needed */
1027c3963bc0SZev Weiss 	val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
1028c3963bc0SZev Weiss 	if (!(val & 0x01)) {
1029c3963bc0SZev Weiss 		pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
1030c3963bc0SZev Weiss 		sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01);
1031c3963bc0SZev Weiss 	}
1032c3963bc0SZev Weiss 
1033c3963bc0SZev Weiss 	if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
1034c3963bc0SZev Weiss 	    sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
1035c3963bc0SZev Weiss 	    sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
1036c3963bc0SZev Weiss 	    sio_data->kind == nct6798)
1037c3963bc0SZev Weiss 		nct6791_enable_io_mapping(sio_data);
1038c3963bc0SZev Weiss 
1039c3963bc0SZev Weiss 	sio_data->sio_exit(sio_data);
1040c3963bc0SZev Weiss 	pr_info("Found %s or compatible chip at %#x:%#x\n",
1041c3963bc0SZev Weiss 		nct6775_sio_names[sio_data->kind], sioaddr, addr);
1042c3963bc0SZev Weiss 
1043c3963bc0SZev Weiss 	return addr;
1044c3963bc0SZev Weiss }
1045c3963bc0SZev Weiss 
1046c3963bc0SZev Weiss /*
1047c3963bc0SZev Weiss  * when Super-I/O functions move to a separate file, the Super-I/O
1048c3963bc0SZev Weiss  * bus will manage the lifetime of the device and this module will only keep
1049c3963bc0SZev Weiss  * track of the nct6775 driver. But since we use platform_device_alloc(), we
1050c3963bc0SZev Weiss  * must keep track of the device
1051c3963bc0SZev Weiss  */
1052c3963bc0SZev Weiss static struct platform_device *pdev[2];
1053c3963bc0SZev Weiss 
1054c3963bc0SZev Weiss static const char * const asus_wmi_boards[] = {
1055bcd2fbecSDenis Pauk 	"PRIME A520M-A",
1056bcd2fbecSDenis Pauk 	"PRIME A520M-A II",
1057bcd2fbecSDenis Pauk 	"PRIME A520M-E",
1058bcd2fbecSDenis Pauk 	"PRIME A520M-K",
1059c3963bc0SZev Weiss 	"PRIME B360-PLUS",
1060bcd2fbecSDenis Pauk 	"PRIME B360M-A",
1061bcd2fbecSDenis Pauk 	"PRIME B360M-C",
1062c3963bc0SZev Weiss 	"PRIME B460-PLUS",
1063bcd2fbecSDenis Pauk 	"PRIME B460M-A R2.0",
1064c3963bc0SZev Weiss 	"PRIME B550-PLUS",
1065c3963bc0SZev Weiss 	"PRIME B550M-A",
1066c3963bc0SZev Weiss 	"PRIME B550M-A (WI-FI)",
1067bcd2fbecSDenis Pauk 	"PRIME B550M-A AC",
1068bcd2fbecSDenis Pauk 	"PRIME B550M-A WIFI II",
1069bcd2fbecSDenis Pauk 	"PRIME B550M-K",
107076412408SDenis Pauk 	"PRIME H410M-R",
1071c3963bc0SZev Weiss 	"PRIME X570-P",
1072c3963bc0SZev Weiss 	"PRIME X570-PRO",
107302825926SDenis Pauk 	"PRO H410T",
107402825926SDenis Pauk 	"Pro A520M-C",
107502825926SDenis Pauk 	"Pro A520M-C II",
107602825926SDenis Pauk 	"Pro B550M-C",
107702825926SDenis Pauk 	"Pro WS X570-ACE",
107802825926SDenis Pauk 	"ProArt B550-CREATOR",
107902825926SDenis Pauk 	"ProArt X570-CREATOR WIFI",
108002825926SDenis Pauk 	"ProArt Z490-CREATOR 10G",
1081c3963bc0SZev Weiss 	"ROG CROSSHAIR VIII DARK HERO",
10821864069cSDenis Pauk 	"ROG CROSSHAIR VIII EXTREME",
1083c3963bc0SZev Weiss 	"ROG CROSSHAIR VIII FORMULA",
1084c3963bc0SZev Weiss 	"ROG CROSSHAIR VIII HERO",
10851864069cSDenis Pauk 	"ROG CROSSHAIR VIII HERO (WI-FI)",
1086c3963bc0SZev Weiss 	"ROG CROSSHAIR VIII IMPACT",
1087c3963bc0SZev Weiss 	"ROG STRIX B550-A GAMING",
1088c3963bc0SZev Weiss 	"ROG STRIX B550-E GAMING",
1089c3963bc0SZev Weiss 	"ROG STRIX B550-F GAMING",
1090c3963bc0SZev Weiss 	"ROG STRIX B550-F GAMING (WI-FI)",
1091c3963bc0SZev Weiss 	"ROG STRIX B550-F GAMING WIFI II",
1092c3963bc0SZev Weiss 	"ROG STRIX B550-I GAMING",
1093*c05403e6SDenis Pauk 	"ROG STRIX B550-XE GAMING WIFI",
1094c3963bc0SZev Weiss 	"ROG STRIX X570-E GAMING",
109576412408SDenis Pauk 	"ROG STRIX X570-E GAMING WIFI II",
1096c3963bc0SZev Weiss 	"ROG STRIX X570-F GAMING",
1097c3963bc0SZev Weiss 	"ROG STRIX X570-I GAMING",
1098c3963bc0SZev Weiss 	"ROG STRIX Z390-E GAMING",
1099c3963bc0SZev Weiss 	"ROG STRIX Z390-F GAMING",
1100c3963bc0SZev Weiss 	"ROG STRIX Z390-H GAMING",
1101c3963bc0SZev Weiss 	"ROG STRIX Z390-I GAMING",
1102c3963bc0SZev Weiss 	"ROG STRIX Z490-A GAMING",
1103c3963bc0SZev Weiss 	"ROG STRIX Z490-E GAMING",
1104c3963bc0SZev Weiss 	"ROG STRIX Z490-F GAMING",
1105c3963bc0SZev Weiss 	"ROG STRIX Z490-G GAMING",
1106c3963bc0SZev Weiss 	"ROG STRIX Z490-G GAMING (WI-FI)",
1107c3963bc0SZev Weiss 	"ROG STRIX Z490-H GAMING",
1108c3963bc0SZev Weiss 	"ROG STRIX Z490-I GAMING",
1109bcd2fbecSDenis Pauk 	"TUF GAMING A520M-PLUS",
1110bcd2fbecSDenis Pauk 	"TUF GAMING A520M-PLUS II",
1111bcd2fbecSDenis Pauk 	"TUF GAMING A520M-PLUS WIFI",
111202825926SDenis Pauk 	"TUF GAMING B550-PLUS",
111302825926SDenis Pauk 	"TUF GAMING B550-PLUS WIFI II",
111402825926SDenis Pauk 	"TUF GAMING B550-PRO",
11151864069cSDenis Pauk 	"TUF GAMING B550M-E",
111623e8a379SDenis Pauk 	"TUF GAMING B550M-E WIFI",
1117c3963bc0SZev Weiss 	"TUF GAMING B550M-PLUS",
1118c3963bc0SZev Weiss 	"TUF GAMING B550M-PLUS (WI-FI)",
11191864069cSDenis Pauk 	"TUF GAMING B550M-PLUS WIFI II",
1120c3963bc0SZev Weiss 	"TUF GAMING X570-PLUS",
1121c3963bc0SZev Weiss 	"TUF GAMING X570-PLUS (WI-FI)",
1122bcd2fbecSDenis Pauk 	"TUF GAMING X570-PLUS_BR",
1123c3963bc0SZev Weiss 	"TUF GAMING X570-PRO (WI-FI)",
1124c3963bc0SZev Weiss 	"TUF GAMING Z490-PLUS",
1125c3963bc0SZev Weiss 	"TUF GAMING Z490-PLUS (WI-FI)",
1126bcd2fbecSDenis Pauk 	"Z490-GUNDAM (WI-FI)",
1127c3963bc0SZev Weiss };
1128c3963bc0SZev Weiss 
1129e2e09989SDenis Pauk static const char * const asus_msi_boards[] = {
1130bcd2fbecSDenis Pauk 	"EX-B660M-V5 D4",
1131e2e09989SDenis Pauk 	"EX-B660M-V5 PRO D4",
1132e2e09989SDenis Pauk 	"PRIME B650-PLUS",
1133e2e09989SDenis Pauk 	"PRIME B650M-A",
1134e2e09989SDenis Pauk 	"PRIME B650M-A AX",
1135bcd2fbecSDenis Pauk 	"PRIME B650M-A AX II",
1136e2e09989SDenis Pauk 	"PRIME B650M-A II",
1137e2e09989SDenis Pauk 	"PRIME B650M-A WIFI",
1138e2e09989SDenis Pauk 	"PRIME B650M-A WIFI II",
1139e2e09989SDenis Pauk 	"PRIME B660M-A D4",
1140e2e09989SDenis Pauk 	"PRIME B660M-A WIFI D4",
1141e2e09989SDenis Pauk 	"PRIME X670-P",
1142e2e09989SDenis Pauk 	"PRIME X670-P WIFI",
1143e2e09989SDenis Pauk 	"PRIME X670E-PRO WIFI",
114490b86248SErik Ekman 	"PRIME Z590-A",
114590b86248SErik Ekman 	"PRIME Z590-P",
1146bcd2fbecSDenis Pauk 	"PRIME Z590-P WIFI",
1147bcd2fbecSDenis Pauk 	"PRIME Z590-V",
114890b86248SErik Ekman 	"PRIME Z590M-PLUS",
1149bcd2fbecSDenis Pauk 	"Pro B660M-C",
1150*c05403e6SDenis Pauk 	"Pro B660M-C D4",
1151bcd2fbecSDenis Pauk 	"Pro WS W680-ACE",
1152bcd2fbecSDenis Pauk 	"Pro WS W680-ACE IPMI",
1153bcd2fbecSDenis Pauk 	"ProArt B650-CREATOR",
1154e2e09989SDenis Pauk 	"ProArt B660-CREATOR D4",
1155e2e09989SDenis Pauk 	"ProArt X670E-CREATOR WIFI",
1156bcd2fbecSDenis Pauk 	"ProArt Z790-CREATOR WIFI",
1157e2e09989SDenis Pauk 	"ROG CROSSHAIR X670E EXTREME",
1158e2e09989SDenis Pauk 	"ROG CROSSHAIR X670E GENE",
1159e2e09989SDenis Pauk 	"ROG CROSSHAIR X670E HERO",
1160bcd2fbecSDenis Pauk 	"ROG MAXIMUS XIII APEX",
1161bcd2fbecSDenis Pauk 	"ROG MAXIMUS XIII EXTREME",
1162e2e09989SDenis Pauk 	"ROG MAXIMUS XIII EXTREME GLACIAL",
1163bcd2fbecSDenis Pauk 	"ROG MAXIMUS XIII HERO",
1164bcd2fbecSDenis Pauk 	"ROG MAXIMUS Z690 APEX",
1165e2e09989SDenis Pauk 	"ROG MAXIMUS Z690 EXTREME",
1166e2e09989SDenis Pauk 	"ROG MAXIMUS Z690 EXTREME GLACIAL",
1167bcd2fbecSDenis Pauk 	"ROG MAXIMUS Z790 EXTREME",
1168e2e09989SDenis Pauk 	"ROG STRIX B650-A GAMING WIFI",
1169e2e09989SDenis Pauk 	"ROG STRIX B650E-E GAMING WIFI",
1170e2e09989SDenis Pauk 	"ROG STRIX B650E-F GAMING WIFI",
1171e2e09989SDenis Pauk 	"ROG STRIX B650E-I GAMING WIFI",
1172bcd2fbecSDenis Pauk 	"ROG STRIX B660-A GAMING WIFI",
1173e2e09989SDenis Pauk 	"ROG STRIX B660-A GAMING WIFI D4",
1174e2e09989SDenis Pauk 	"ROG STRIX B660-F GAMING WIFI",
1175e2e09989SDenis Pauk 	"ROG STRIX B660-G GAMING WIFI",
1176e2e09989SDenis Pauk 	"ROG STRIX B660-I GAMING WIFI",
1177e2e09989SDenis Pauk 	"ROG STRIX X670E-A GAMING WIFI",
1178e2e09989SDenis Pauk 	"ROG STRIX X670E-E GAMING WIFI",
1179e2e09989SDenis Pauk 	"ROG STRIX X670E-F GAMING WIFI",
1180e2e09989SDenis Pauk 	"ROG STRIX X670E-I GAMING WIFI",
1181bcd2fbecSDenis Pauk 	"ROG STRIX Z590-A GAMING WIFI",
1182e2e09989SDenis Pauk 	"ROG STRIX Z590-A GAMING WIFI II",
1183bcd2fbecSDenis Pauk 	"ROG STRIX Z590-E GAMING WIFI",
1184bcd2fbecSDenis Pauk 	"ROG STRIX Z590-F GAMING WIFI",
1185bcd2fbecSDenis Pauk 	"ROG STRIX Z590-I GAMING WIFI",
1186e2e09989SDenis Pauk 	"ROG STRIX Z690-A GAMING WIFI D4",
1187e2e09989SDenis Pauk 	"TUF GAMING B650-PLUS",
1188e2e09989SDenis Pauk 	"TUF GAMING B650-PLUS WIFI",
1189e2e09989SDenis Pauk 	"TUF GAMING B650M-PLUS",
1190e2e09989SDenis Pauk 	"TUF GAMING B650M-PLUS WIFI",
1191bcd2fbecSDenis Pauk 	"TUF GAMING B660-PLUS WIFI D4",
1192bcd2fbecSDenis Pauk 	"TUF GAMING B660M-E D4",
1193e2e09989SDenis Pauk 	"TUF GAMING B660M-PLUS WIFI",
1194e2e09989SDenis Pauk 	"TUF GAMING X670E-PLUS",
1195e2e09989SDenis Pauk 	"TUF GAMING X670E-PLUS WIFI",
1196bcd2fbecSDenis Pauk 	"TUF GAMING Z590-PLUS",
1197e2e09989SDenis Pauk 	"TUF GAMING Z590-PLUS WIFI",
1198bcd2fbecSDenis Pauk 	"Z590 WIFI GUNDAM EDITION",
1199e2e09989SDenis Pauk };
1200e2e09989SDenis Pauk 
1201c3b3747dSDenis Pauk #if IS_ENABLED(CONFIG_ACPI)
1202c3b3747dSDenis Pauk /*
1203c3b3747dSDenis Pauk  * Callback for acpi_bus_for_each_dev() to find the right device
1204c3b3747dSDenis Pauk  * by _UID and _HID and return 1 to stop iteration.
1205c3b3747dSDenis Pauk  */
1206c3b3747dSDenis Pauk static int nct6775_asuswmi_device_match(struct device *dev, void *data)
1207c3b3747dSDenis Pauk {
1208c3b3747dSDenis Pauk 	struct acpi_device *adev = to_acpi_device(dev);
1209c3b3747dSDenis Pauk 	const char *uid = acpi_device_uid(adev);
1210c3b3747dSDenis Pauk 	const char *hid = acpi_device_hid(adev);
1211c3b3747dSDenis Pauk 
1212c3b3747dSDenis Pauk 	if (hid && !strcmp(hid, ASUSWMI_DEVICE_HID) && uid && !strcmp(uid, data)) {
1213c3b3747dSDenis Pauk 		asus_acpi_dev = adev;
1214c3b3747dSDenis Pauk 		return 1;
1215c3b3747dSDenis Pauk 	}
1216c3b3747dSDenis Pauk 
1217c3b3747dSDenis Pauk 	return 0;
1218c3b3747dSDenis Pauk }
1219c3b3747dSDenis Pauk #endif
1220c3b3747dSDenis Pauk 
1221c3b3747dSDenis Pauk static enum sensor_access nct6775_determine_access(const char *device_uid)
1222c3b3747dSDenis Pauk {
1223c3b3747dSDenis Pauk #if IS_ENABLED(CONFIG_ACPI)
1224c3b3747dSDenis Pauk 	u8 tmp;
1225c3b3747dSDenis Pauk 
1226c3b3747dSDenis Pauk 	acpi_bus_for_each_dev(nct6775_asuswmi_device_match, (void *)device_uid);
1227c3b3747dSDenis Pauk 	if (!asus_acpi_dev)
1228c3b3747dSDenis Pauk 		return access_direct;
1229c3b3747dSDenis Pauk 
1230c3b3747dSDenis Pauk 	/* if reading chip id via ACPI succeeds, use WMI "WMBD" method for access */
1231c3b3747dSDenis Pauk 	if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
1232c3b3747dSDenis Pauk 		pr_debug("Using Asus WMBD method of %s to access %#x chip.\n", device_uid, tmp);
1233c3b3747dSDenis Pauk 		return access_asuswmi;
1234c3b3747dSDenis Pauk 	}
1235c3b3747dSDenis Pauk #endif
1236c3b3747dSDenis Pauk 
1237c3b3747dSDenis Pauk 	return access_direct;
1238c3b3747dSDenis Pauk }
1239c3b3747dSDenis Pauk 
1240c3963bc0SZev Weiss static int __init sensors_nct6775_platform_init(void)
1241c3963bc0SZev Weiss {
1242c3963bc0SZev Weiss 	int i, err;
1243c3963bc0SZev Weiss 	bool found = false;
1244c3963bc0SZev Weiss 	int address;
1245c3963bc0SZev Weiss 	struct resource res;
1246c3963bc0SZev Weiss 	struct nct6775_sio_data sio_data;
1247c3963bc0SZev Weiss 	int sioaddr[2] = { 0x2e, 0x4e };
1248c3963bc0SZev Weiss 	enum sensor_access access = access_direct;
1249c3963bc0SZev Weiss 	const char *board_vendor, *board_name;
1250c3963bc0SZev Weiss 
1251c3963bc0SZev Weiss 	err = platform_driver_register(&nct6775_driver);
1252c3963bc0SZev Weiss 	if (err)
1253c3963bc0SZev Weiss 		return err;
1254c3963bc0SZev Weiss 
1255c3963bc0SZev Weiss 	board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
1256c3963bc0SZev Weiss 	board_name = dmi_get_system_info(DMI_BOARD_NAME);
1257c3963bc0SZev Weiss 
1258c3963bc0SZev Weiss 	if (board_name && board_vendor &&
1259c3963bc0SZev Weiss 	    !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) {
1260c3963bc0SZev Weiss 		err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards),
1261c3963bc0SZev Weiss 				   board_name);
1262c3b3747dSDenis Pauk 		if (err >= 0)
1263c3b3747dSDenis Pauk 			access = nct6775_determine_access(ASUSWMI_DEVICE_UID);
1264e2e09989SDenis Pauk 
1265e2e09989SDenis Pauk 		err = match_string(asus_msi_boards, ARRAY_SIZE(asus_msi_boards),
1266e2e09989SDenis Pauk 				   board_name);
1267e2e09989SDenis Pauk 		if (err >= 0)
1268e2e09989SDenis Pauk 			access = nct6775_determine_access(ASUSMSI_DEVICE_UID);
1269c3963bc0SZev Weiss 	}
1270c3963bc0SZev Weiss 
1271c3963bc0SZev Weiss 	/*
1272c3963bc0SZev Weiss 	 * initialize sio_data->kind and sio_data->sioreg.
1273c3963bc0SZev Weiss 	 *
1274c3963bc0SZev Weiss 	 * when Super-I/O functions move to a separate file, the Super-I/O
1275c3963bc0SZev Weiss 	 * driver will probe 0x2e and 0x4e and auto-detect the presence of a
1276c3963bc0SZev Weiss 	 * nct6775 hardware monitor, and call probe()
1277c3963bc0SZev Weiss 	 */
1278c3963bc0SZev Weiss 	for (i = 0; i < ARRAY_SIZE(pdev); i++) {
1279c3963bc0SZev Weiss 		sio_data.sio_outb = superio_outb;
1280c3963bc0SZev Weiss 		sio_data.sio_inb = superio_inb;
1281c3963bc0SZev Weiss 		sio_data.sio_select = superio_select;
1282c3963bc0SZev Weiss 		sio_data.sio_enter = superio_enter;
1283c3963bc0SZev Weiss 		sio_data.sio_exit = superio_exit;
1284c3963bc0SZev Weiss 
1285c3963bc0SZev Weiss 		address = nct6775_find(sioaddr[i], &sio_data);
1286c3963bc0SZev Weiss 		if (address <= 0)
1287c3963bc0SZev Weiss 			continue;
1288c3963bc0SZev Weiss 
1289c3963bc0SZev Weiss 		found = true;
1290c3963bc0SZev Weiss 
1291c3963bc0SZev Weiss 		sio_data.access = access;
1292c3963bc0SZev Weiss 
1293c3963bc0SZev Weiss 		if (access == access_asuswmi) {
1294c3963bc0SZev Weiss 			sio_data.sio_outb = superio_wmi_outb;
1295c3963bc0SZev Weiss 			sio_data.sio_inb = superio_wmi_inb;
1296c3963bc0SZev Weiss 			sio_data.sio_select = superio_wmi_select;
1297c3963bc0SZev Weiss 			sio_data.sio_enter = superio_wmi_enter;
1298c3963bc0SZev Weiss 			sio_data.sio_exit = superio_wmi_exit;
1299c3963bc0SZev Weiss 		}
1300c3963bc0SZev Weiss 
1301c3963bc0SZev Weiss 		pdev[i] = platform_device_alloc(DRVNAME, address);
1302c3963bc0SZev Weiss 		if (!pdev[i]) {
1303c3963bc0SZev Weiss 			err = -ENOMEM;
1304c3963bc0SZev Weiss 			goto exit_device_unregister;
1305c3963bc0SZev Weiss 		}
1306c3963bc0SZev Weiss 
1307c3963bc0SZev Weiss 		err = platform_device_add_data(pdev[i], &sio_data,
1308c3963bc0SZev Weiss 					       sizeof(struct nct6775_sio_data));
1309c3963bc0SZev Weiss 		if (err)
1310c3963bc0SZev Weiss 			goto exit_device_put;
1311c3963bc0SZev Weiss 
1312c3963bc0SZev Weiss 		if (sio_data.access == access_direct) {
1313c3963bc0SZev Weiss 			memset(&res, 0, sizeof(res));
1314c3963bc0SZev Weiss 			res.name = DRVNAME;
1315c3963bc0SZev Weiss 			res.start = address + IOREGION_OFFSET;
1316c3963bc0SZev Weiss 			res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
1317c3963bc0SZev Weiss 			res.flags = IORESOURCE_IO;
1318c3963bc0SZev Weiss 
1319c3963bc0SZev Weiss 			err = acpi_check_resource_conflict(&res);
1320c3963bc0SZev Weiss 			if (err) {
1321c3963bc0SZev Weiss 				platform_device_put(pdev[i]);
1322c3963bc0SZev Weiss 				pdev[i] = NULL;
1323c3963bc0SZev Weiss 				continue;
1324c3963bc0SZev Weiss 			}
1325c3963bc0SZev Weiss 
1326c3963bc0SZev Weiss 			err = platform_device_add_resources(pdev[i], &res, 1);
1327c3963bc0SZev Weiss 			if (err)
1328c3963bc0SZev Weiss 				goto exit_device_put;
1329c3963bc0SZev Weiss 		}
1330c3963bc0SZev Weiss 
1331c3963bc0SZev Weiss 		/* platform_device_add calls probe() */
1332c3963bc0SZev Weiss 		err = platform_device_add(pdev[i]);
1333c3963bc0SZev Weiss 		if (err)
1334c3963bc0SZev Weiss 			goto exit_device_put;
1335c3963bc0SZev Weiss 	}
1336c3963bc0SZev Weiss 	if (!found) {
1337c3963bc0SZev Weiss 		err = -ENODEV;
1338c3963bc0SZev Weiss 		goto exit_unregister;
1339c3963bc0SZev Weiss 	}
1340c3963bc0SZev Weiss 
1341c3963bc0SZev Weiss 	return 0;
1342c3963bc0SZev Weiss 
1343c3963bc0SZev Weiss exit_device_put:
1344c3963bc0SZev Weiss 	platform_device_put(pdev[i]);
1345c3963bc0SZev Weiss exit_device_unregister:
1346452d5e29SAndy Shevchenko 	while (i--)
1347c3963bc0SZev Weiss 		platform_device_unregister(pdev[i]);
1348c3963bc0SZev Weiss exit_unregister:
1349c3963bc0SZev Weiss 	platform_driver_unregister(&nct6775_driver);
1350c3963bc0SZev Weiss 	return err;
1351c3963bc0SZev Weiss }
1352c3963bc0SZev Weiss 
1353c3963bc0SZev Weiss static void __exit sensors_nct6775_platform_exit(void)
1354c3963bc0SZev Weiss {
1355c3963bc0SZev Weiss 	int i;
1356c3963bc0SZev Weiss 
1357452d5e29SAndy Shevchenko 	for (i = 0; i < ARRAY_SIZE(pdev); i++)
1358c3963bc0SZev Weiss 		platform_device_unregister(pdev[i]);
1359c3963bc0SZev Weiss 	platform_driver_unregister(&nct6775_driver);
1360c3963bc0SZev Weiss }
1361c3963bc0SZev Weiss 
1362c3963bc0SZev Weiss MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
1363c3963bc0SZev Weiss MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips");
1364c3963bc0SZev Weiss MODULE_LICENSE("GPL");
1365c3963bc0SZev Weiss MODULE_IMPORT_NS(HWMON_NCT6775);
1366c3963bc0SZev Weiss 
1367c3963bc0SZev Weiss module_init(sensors_nct6775_platform_init);
1368c3963bc0SZev Weiss module_exit(sensors_nct6775_platform_exit);
1369