xref: /openbmc/linux/drivers/hwmon/nct6683.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
241082d66SGuenter Roeck /*
341082d66SGuenter Roeck  * nct6683 - Driver for the hardware monitoring functionality of
4bfbbbe04SJiqi Li  *	     Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO
541082d66SGuenter Roeck  *
641082d66SGuenter Roeck  * Copyright (C) 2013  Guenter Roeck <linux@roeck-us.net>
741082d66SGuenter Roeck  *
841082d66SGuenter Roeck  * Derived from nct6775 driver
941082d66SGuenter Roeck  * Copyright (C) 2012, 2013  Guenter Roeck <linux@roeck-us.net>
1041082d66SGuenter Roeck  *
1141082d66SGuenter Roeck  * Supports the following chips:
1241082d66SGuenter Roeck  *
1341082d66SGuenter Roeck  * Chip        #vin    #fan    #pwm    #temp  chip ID
1441082d66SGuenter Roeck  * nct6683d     21(1)   16      8       32(1) 0xc730
15bfbbbe04SJiqi Li  * nct6686d     21(1)   16      8       32(1) 0xd440
16daf4feddSDavid Bartley  * nct6687d     21(1)   16      8       32(1) 0xd590
1741082d66SGuenter Roeck  *
1841082d66SGuenter Roeck  * Notes:
1941082d66SGuenter Roeck  *	(1) Total number of vin and temp inputs is 32.
2041082d66SGuenter Roeck  */
2141082d66SGuenter Roeck 
2241082d66SGuenter Roeck #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2341082d66SGuenter Roeck 
2441082d66SGuenter Roeck #include <linux/acpi.h>
2591918d13SGuenter Roeck #include <linux/delay.h>
2641082d66SGuenter Roeck #include <linux/err.h>
2741082d66SGuenter Roeck #include <linux/init.h>
2841082d66SGuenter Roeck #include <linux/io.h>
2941082d66SGuenter Roeck #include <linux/jiffies.h>
3041082d66SGuenter Roeck #include <linux/hwmon.h>
3141082d66SGuenter Roeck #include <linux/hwmon-sysfs.h>
3241082d66SGuenter Roeck #include <linux/module.h>
3341082d66SGuenter Roeck #include <linux/mutex.h>
3441082d66SGuenter Roeck #include <linux/platform_device.h>
3541082d66SGuenter Roeck #include <linux/slab.h>
3641082d66SGuenter Roeck 
37bfbbbe04SJiqi Li enum kinds { nct6683, nct6686, nct6687 };
3841082d66SGuenter Roeck 
3941082d66SGuenter Roeck static bool force;
4041082d66SGuenter Roeck module_param(force, bool, 0);
4191918d13SGuenter Roeck MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
4241082d66SGuenter Roeck 
4341082d66SGuenter Roeck static const char * const nct6683_device_names[] = {
4441082d66SGuenter Roeck 	"nct6683",
45bfbbbe04SJiqi Li 	"nct6686",
46daf4feddSDavid Bartley 	"nct6687",
4741082d66SGuenter Roeck };
4841082d66SGuenter Roeck 
4941082d66SGuenter Roeck static const char * const nct6683_chip_names[] = {
5041082d66SGuenter Roeck 	"NCT6683D",
51bfbbbe04SJiqi Li 	"NCT6686D",
52daf4feddSDavid Bartley 	"NCT6687D",
5341082d66SGuenter Roeck };
5441082d66SGuenter Roeck 
5541082d66SGuenter Roeck #define DRVNAME "nct6683"
5641082d66SGuenter Roeck 
5741082d66SGuenter Roeck /*
5841082d66SGuenter Roeck  * Super-I/O constants and functions
5941082d66SGuenter Roeck  */
6041082d66SGuenter Roeck 
6141082d66SGuenter Roeck #define NCT6683_LD_ACPI		0x0a
6241082d66SGuenter Roeck #define NCT6683_LD_HWM		0x0b
6341082d66SGuenter Roeck #define NCT6683_LD_VID		0x0d
6441082d66SGuenter Roeck 
6541082d66SGuenter Roeck #define SIO_REG_LDSEL		0x07	/* Logical device select */
6641082d66SGuenter Roeck #define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
6741082d66SGuenter Roeck #define SIO_REG_ENABLE		0x30	/* Logical device enable */
6841082d66SGuenter Roeck #define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */
6941082d66SGuenter Roeck 
7041082d66SGuenter Roeck #define SIO_NCT6681_ID		0xb270	/* for later */
7141082d66SGuenter Roeck #define SIO_NCT6683_ID		0xc730
72bfbbbe04SJiqi Li #define SIO_NCT6686_ID		0xd440
73daf4feddSDavid Bartley #define SIO_NCT6687_ID		0xd590
7441082d66SGuenter Roeck #define SIO_ID_MASK		0xFFF0
7541082d66SGuenter Roeck 
7641082d66SGuenter Roeck static inline void
superio_outb(int ioreg,int reg,int val)7741082d66SGuenter Roeck superio_outb(int ioreg, int reg, int val)
7841082d66SGuenter Roeck {
7941082d66SGuenter Roeck 	outb(reg, ioreg);
8041082d66SGuenter Roeck 	outb(val, ioreg + 1);
8141082d66SGuenter Roeck }
8241082d66SGuenter Roeck 
8341082d66SGuenter Roeck static inline int
superio_inb(int ioreg,int reg)8441082d66SGuenter Roeck superio_inb(int ioreg, int reg)
8541082d66SGuenter Roeck {
8641082d66SGuenter Roeck 	outb(reg, ioreg);
8741082d66SGuenter Roeck 	return inb(ioreg + 1);
8841082d66SGuenter Roeck }
8941082d66SGuenter Roeck 
9041082d66SGuenter Roeck static inline void
superio_select(int ioreg,int ld)9141082d66SGuenter Roeck superio_select(int ioreg, int ld)
9241082d66SGuenter Roeck {
9341082d66SGuenter Roeck 	outb(SIO_REG_LDSEL, ioreg);
9441082d66SGuenter Roeck 	outb(ld, ioreg + 1);
9541082d66SGuenter Roeck }
9641082d66SGuenter Roeck 
9741082d66SGuenter Roeck static inline int
superio_enter(int ioreg)9841082d66SGuenter Roeck superio_enter(int ioreg)
9941082d66SGuenter Roeck {
10041082d66SGuenter Roeck 	/*
10141082d66SGuenter Roeck 	 * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
10241082d66SGuenter Roeck 	 */
10341082d66SGuenter Roeck 	if (!request_muxed_region(ioreg, 2, DRVNAME))
10441082d66SGuenter Roeck 		return -EBUSY;
10541082d66SGuenter Roeck 
10641082d66SGuenter Roeck 	outb(0x87, ioreg);
10741082d66SGuenter Roeck 	outb(0x87, ioreg);
10841082d66SGuenter Roeck 
10941082d66SGuenter Roeck 	return 0;
11041082d66SGuenter Roeck }
11141082d66SGuenter Roeck 
11241082d66SGuenter Roeck static inline void
superio_exit(int ioreg)11341082d66SGuenter Roeck superio_exit(int ioreg)
11441082d66SGuenter Roeck {
11541082d66SGuenter Roeck 	outb(0xaa, ioreg);
11641082d66SGuenter Roeck 	outb(0x02, ioreg);
11741082d66SGuenter Roeck 	outb(0x02, ioreg + 1);
11841082d66SGuenter Roeck 	release_region(ioreg, 2);
11941082d66SGuenter Roeck }
12041082d66SGuenter Roeck 
12141082d66SGuenter Roeck /*
12241082d66SGuenter Roeck  * ISA constants
12341082d66SGuenter Roeck  */
12441082d66SGuenter Roeck 
12541082d66SGuenter Roeck #define IOREGION_ALIGNMENT	(~7)
12641082d66SGuenter Roeck #define IOREGION_OFFSET		4	/* Use EC port 1 */
12741082d66SGuenter Roeck #define IOREGION_LENGTH		4
12841082d66SGuenter Roeck 
12941082d66SGuenter Roeck #define EC_PAGE_REG		0
13041082d66SGuenter Roeck #define EC_INDEX_REG		1
13141082d66SGuenter Roeck #define EC_DATA_REG		2
13241082d66SGuenter Roeck #define EC_EVENT_REG		3
13341082d66SGuenter Roeck 
13441082d66SGuenter Roeck /* Common and NCT6683 specific data */
13541082d66SGuenter Roeck 
13641082d66SGuenter Roeck #define NCT6683_NUM_REG_MON		32
13741082d66SGuenter Roeck #define NCT6683_NUM_REG_FAN		16
13841082d66SGuenter Roeck #define NCT6683_NUM_REG_PWM		8
13941082d66SGuenter Roeck 
14041082d66SGuenter Roeck #define NCT6683_REG_MON(x)		(0x100 + (x) * 2)
14141082d66SGuenter Roeck #define NCT6683_REG_FAN_RPM(x)		(0x140 + (x) * 2)
14241082d66SGuenter Roeck #define NCT6683_REG_PWM(x)		(0x160 + (x))
14391918d13SGuenter Roeck #define NCT6683_REG_PWM_WRITE(x)	(0xa28 + (x))
14441082d66SGuenter Roeck 
14541082d66SGuenter Roeck #define NCT6683_REG_MON_STS(x)		(0x174 + (x))
14641082d66SGuenter Roeck #define NCT6683_REG_IDLE(x)		(0x178 + (x))
14741082d66SGuenter Roeck 
14841082d66SGuenter Roeck #define NCT6683_REG_FAN_STS(x)		(0x17c + (x))
14941082d66SGuenter Roeck #define NCT6683_REG_FAN_ERRSTS		0x17e
15041082d66SGuenter Roeck #define NCT6683_REG_FAN_INITSTS		0x17f
15141082d66SGuenter Roeck 
15241082d66SGuenter Roeck #define NCT6683_HWM_CFG			0x180
15341082d66SGuenter Roeck 
15441082d66SGuenter Roeck #define NCT6683_REG_MON_CFG(x)		(0x1a0 + (x))
15541082d66SGuenter Roeck #define NCT6683_REG_FANIN_CFG(x)	(0x1c0 + (x))
15641082d66SGuenter Roeck #define NCT6683_REG_FANOUT_CFG(x)	(0x1d0 + (x))
15741082d66SGuenter Roeck 
15841082d66SGuenter Roeck #define NCT6683_REG_INTEL_TEMP_MAX(x)	(0x901 + (x) * 16)
15941082d66SGuenter Roeck #define NCT6683_REG_INTEL_TEMP_CRIT(x)	(0x90d + (x) * 16)
16041082d66SGuenter Roeck 
16141082d66SGuenter Roeck #define NCT6683_REG_TEMP_HYST(x)	(0x330 + (x))		/* 8 bit */
16241082d66SGuenter Roeck #define NCT6683_REG_TEMP_MAX(x)		(0x350 + (x))		/* 8 bit */
16341082d66SGuenter Roeck #define NCT6683_REG_MON_HIGH(x)		(0x370 + (x) * 2)	/* 8 bit */
16441082d66SGuenter Roeck #define NCT6683_REG_MON_LOW(x)		(0x371 + (x) * 2)	/* 8 bit */
16541082d66SGuenter Roeck 
16641082d66SGuenter Roeck #define NCT6683_REG_FAN_MIN(x)		(0x3b8 + (x) * 2)	/* 16 bit */
16741082d66SGuenter Roeck 
16891918d13SGuenter Roeck #define NCT6683_REG_FAN_CFG_CTRL	0xa01
16991918d13SGuenter Roeck #define NCT6683_FAN_CFG_REQ		0x80
17091918d13SGuenter Roeck #define NCT6683_FAN_CFG_DONE		0x40
17191918d13SGuenter Roeck 
17241082d66SGuenter Roeck #define NCT6683_REG_CUSTOMER_ID		0x602
17341082d66SGuenter Roeck #define NCT6683_CUSTOMER_ID_INTEL	0x805
17491918d13SGuenter Roeck #define NCT6683_CUSTOMER_ID_MITAC	0xa0e
175daf4feddSDavid Bartley #define NCT6683_CUSTOMER_ID_MSI		0x201
176*7c415ed8SGopal Prasad #define NCT6683_CUSTOMER_ID_MSI2	0x200
177bd433537SBlaž Hrastnik #define NCT6683_CUSTOMER_ID_ASROCK		0xe2c
178d55532f7SDaniel Dawson #define NCT6683_CUSTOMER_ID_ASROCK2	0xe1b
17941082d66SGuenter Roeck 
18041082d66SGuenter Roeck #define NCT6683_REG_BUILD_YEAR		0x604
18141082d66SGuenter Roeck #define NCT6683_REG_BUILD_MONTH		0x605
18241082d66SGuenter Roeck #define NCT6683_REG_BUILD_DAY		0x606
18341082d66SGuenter Roeck #define NCT6683_REG_SERIAL		0x607
18441082d66SGuenter Roeck #define NCT6683_REG_VERSION_HI		0x608
18541082d66SGuenter Roeck #define NCT6683_REG_VERSION_LO		0x609
18641082d66SGuenter Roeck 
18741082d66SGuenter Roeck #define NCT6683_REG_CR_CASEOPEN		0xe8
18841082d66SGuenter Roeck #define NCT6683_CR_CASEOPEN_MASK	(1 << 7)
18941082d66SGuenter Roeck 
19041082d66SGuenter Roeck #define NCT6683_REG_CR_BEEP		0xe0
19141082d66SGuenter Roeck #define NCT6683_CR_BEEP_MASK		(1 << 6)
19241082d66SGuenter Roeck 
19341082d66SGuenter Roeck static const char *const nct6683_mon_label[] = {
19441082d66SGuenter Roeck 	NULL,	/* disabled */
19541082d66SGuenter Roeck 	"Local",
19641082d66SGuenter Roeck 	"Diode 0 (curr)",
19741082d66SGuenter Roeck 	"Diode 1 (curr)",
19841082d66SGuenter Roeck 	"Diode 2 (curr)",
19941082d66SGuenter Roeck 	"Diode 0 (volt)",
20041082d66SGuenter Roeck 	"Diode 1 (volt)",
20141082d66SGuenter Roeck 	"Diode 2 (volt)",
20241082d66SGuenter Roeck 	"Thermistor 14",
20341082d66SGuenter Roeck 	"Thermistor 15",
20441082d66SGuenter Roeck 	"Thermistor 16",
20541082d66SGuenter Roeck 	"Thermistor 0",
20641082d66SGuenter Roeck 	"Thermistor 1",
20741082d66SGuenter Roeck 	"Thermistor 2",
20841082d66SGuenter Roeck 	"Thermistor 3",
20941082d66SGuenter Roeck 	"Thermistor 4",
21041082d66SGuenter Roeck 	"Thermistor 5",		/* 0x10 */
21141082d66SGuenter Roeck 	"Thermistor 6",
21241082d66SGuenter Roeck 	"Thermistor 7",
21341082d66SGuenter Roeck 	"Thermistor 8",
21441082d66SGuenter Roeck 	"Thermistor 9",
21541082d66SGuenter Roeck 	"Thermistor 10",
21641082d66SGuenter Roeck 	"Thermistor 11",
21741082d66SGuenter Roeck 	"Thermistor 12",
21841082d66SGuenter Roeck 	"Thermistor 13",
21941082d66SGuenter Roeck 	NULL, NULL, NULL, NULL, NULL, NULL, NULL,
22041082d66SGuenter Roeck 	"PECI 0.0",		/* 0x20 */
22141082d66SGuenter Roeck 	"PECI 1.0",
22241082d66SGuenter Roeck 	"PECI 2.0",
22341082d66SGuenter Roeck 	"PECI 3.0",
22441082d66SGuenter Roeck 	"PECI 0.1",
22541082d66SGuenter Roeck 	"PECI 1.1",
22641082d66SGuenter Roeck 	"PECI 2.1",
22741082d66SGuenter Roeck 	"PECI 3.1",
22841082d66SGuenter Roeck 	"PECI DIMM 0",
22941082d66SGuenter Roeck 	"PECI DIMM 1",
23041082d66SGuenter Roeck 	"PECI DIMM 2",
23141082d66SGuenter Roeck 	"PECI DIMM 3",
23241082d66SGuenter Roeck 	NULL, NULL, NULL, NULL,
23341082d66SGuenter Roeck 	"PCH CPU",		/* 0x30 */
23441082d66SGuenter Roeck 	"PCH CHIP",
23541082d66SGuenter Roeck 	"PCH CHIP CPU MAX",
23641082d66SGuenter Roeck 	"PCH MCH",
23741082d66SGuenter Roeck 	"PCH DIMM 0",
23841082d66SGuenter Roeck 	"PCH DIMM 1",
23941082d66SGuenter Roeck 	"PCH DIMM 2",
24041082d66SGuenter Roeck 	"PCH DIMM 3",
24141082d66SGuenter Roeck 	"SMBus 0",
24241082d66SGuenter Roeck 	"SMBus 1",
24341082d66SGuenter Roeck 	"SMBus 2",
24441082d66SGuenter Roeck 	"SMBus 3",
24541082d66SGuenter Roeck 	"SMBus 4",
24641082d66SGuenter Roeck 	"SMBus 5",
24741082d66SGuenter Roeck 	"DIMM 0",
24841082d66SGuenter Roeck 	"DIMM 1",
24941082d66SGuenter Roeck 	"DIMM 2",		/* 0x40 */
25041082d66SGuenter Roeck 	"DIMM 3",
25141082d66SGuenter Roeck 	"AMD TSI Addr 90h",
25241082d66SGuenter Roeck 	"AMD TSI Addr 92h",
25341082d66SGuenter Roeck 	"AMD TSI Addr 94h",
25441082d66SGuenter Roeck 	"AMD TSI Addr 96h",
25541082d66SGuenter Roeck 	"AMD TSI Addr 98h",
25641082d66SGuenter Roeck 	"AMD TSI Addr 9ah",
25741082d66SGuenter Roeck 	"AMD TSI Addr 9ch",
25841082d66SGuenter Roeck 	"AMD TSI Addr 9dh",
25941082d66SGuenter Roeck 	NULL, NULL, NULL, NULL, NULL, NULL,
26041082d66SGuenter Roeck 	"Virtual 0",		/* 0x50 */
26141082d66SGuenter Roeck 	"Virtual 1",
26241082d66SGuenter Roeck 	"Virtual 2",
26341082d66SGuenter Roeck 	"Virtual 3",
26441082d66SGuenter Roeck 	"Virtual 4",
26541082d66SGuenter Roeck 	"Virtual 5",
26641082d66SGuenter Roeck 	"Virtual 6",
26741082d66SGuenter Roeck 	"Virtual 7",
26841082d66SGuenter Roeck 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
26941082d66SGuenter Roeck 	"VCC",			/* 0x60 voltage sensors */
27041082d66SGuenter Roeck 	"VSB",
27141082d66SGuenter Roeck 	"AVSB",
27241082d66SGuenter Roeck 	"VTT",
27341082d66SGuenter Roeck 	"VBAT",
27441082d66SGuenter Roeck 	"VREF",
27541082d66SGuenter Roeck 	"VIN0",
27641082d66SGuenter Roeck 	"VIN1",
27741082d66SGuenter Roeck 	"VIN2",
27841082d66SGuenter Roeck 	"VIN3",
27941082d66SGuenter Roeck 	"VIN4",
28041082d66SGuenter Roeck 	"VIN5",
28141082d66SGuenter Roeck 	"VIN6",
28241082d66SGuenter Roeck 	"VIN7",
28341082d66SGuenter Roeck 	"VIN8",
28441082d66SGuenter Roeck 	"VIN9",
28541082d66SGuenter Roeck 	"VIN10",
28641082d66SGuenter Roeck 	"VIN11",
28741082d66SGuenter Roeck 	"VIN12",
28841082d66SGuenter Roeck 	"VIN13",
28941082d66SGuenter Roeck 	"VIN14",
29041082d66SGuenter Roeck 	"VIN15",
29141082d66SGuenter Roeck 	"VIN16",
29241082d66SGuenter Roeck };
29341082d66SGuenter Roeck 
29441082d66SGuenter Roeck #define NUM_MON_LABELS		ARRAY_SIZE(nct6683_mon_label)
29541082d66SGuenter Roeck #define MON_VOLTAGE_START	0x60
29641082d66SGuenter Roeck 
29741082d66SGuenter Roeck /* ------------------------------------------------------- */
29841082d66SGuenter Roeck 
29941082d66SGuenter Roeck struct nct6683_data {
30041082d66SGuenter Roeck 	int addr;		/* IO base of EC space */
30141082d66SGuenter Roeck 	int sioreg;		/* SIO register */
30241082d66SGuenter Roeck 	enum kinds kind;
30341082d66SGuenter Roeck 	u16 customer_id;
30441082d66SGuenter Roeck 
30541082d66SGuenter Roeck 	struct device *hwmon_dev;
30641082d66SGuenter Roeck 	const struct attribute_group *groups[6];
30741082d66SGuenter Roeck 
30841082d66SGuenter Roeck 	int temp_num;			/* number of temperature attributes */
30941082d66SGuenter Roeck 	u8 temp_index[NCT6683_NUM_REG_MON];
31041082d66SGuenter Roeck 	u8 temp_src[NCT6683_NUM_REG_MON];
31141082d66SGuenter Roeck 
31241082d66SGuenter Roeck 	u8 in_num;			/* number of voltage attributes */
31341082d66SGuenter Roeck 	u8 in_index[NCT6683_NUM_REG_MON];
31441082d66SGuenter Roeck 	u8 in_src[NCT6683_NUM_REG_MON];
31541082d66SGuenter Roeck 
31641082d66SGuenter Roeck 	struct mutex update_lock;	/* used to protect sensor updates */
31741082d66SGuenter Roeck 	bool valid;			/* true if following fields are valid */
31841082d66SGuenter Roeck 	unsigned long last_updated;	/* In jiffies */
31941082d66SGuenter Roeck 
32041082d66SGuenter Roeck 	/* Voltage attribute values */
32141082d66SGuenter Roeck 	u8 in[3][NCT6683_NUM_REG_MON];	/* [0]=in, [1]=in_max, [2]=in_min */
32241082d66SGuenter Roeck 
32341082d66SGuenter Roeck 	/* Temperature attribute values */
32441082d66SGuenter Roeck 	s16 temp_in[NCT6683_NUM_REG_MON];
32541082d66SGuenter Roeck 	s8 temp[4][NCT6683_NUM_REG_MON];/* [0]=min, [1]=max, [2]=hyst,
32641082d66SGuenter Roeck 					 * [3]=crit
32741082d66SGuenter Roeck 					 */
32841082d66SGuenter Roeck 
32941082d66SGuenter Roeck 	/* Fan attribute values */
33041082d66SGuenter Roeck 	unsigned int rpm[NCT6683_NUM_REG_FAN];
33141082d66SGuenter Roeck 	u16 fan_min[NCT6683_NUM_REG_FAN];
33241082d66SGuenter Roeck 	u8 fanin_cfg[NCT6683_NUM_REG_FAN];
33341082d66SGuenter Roeck 	u8 fanout_cfg[NCT6683_NUM_REG_FAN];
33441082d66SGuenter Roeck 	u16 have_fan;			/* some fan inputs can be disabled */
33541082d66SGuenter Roeck 
33641082d66SGuenter Roeck 	u8 have_pwm;
33741082d66SGuenter Roeck 	u8 pwm[NCT6683_NUM_REG_PWM];
33841082d66SGuenter Roeck 
33941082d66SGuenter Roeck #ifdef CONFIG_PM
34041082d66SGuenter Roeck 	/* Remember extra register values over suspend/resume */
34141082d66SGuenter Roeck 	u8 hwm_cfg;
34241082d66SGuenter Roeck #endif
34341082d66SGuenter Roeck };
34441082d66SGuenter Roeck 
34541082d66SGuenter Roeck struct nct6683_sio_data {
34641082d66SGuenter Roeck 	int sioreg;
34741082d66SGuenter Roeck 	enum kinds kind;
34841082d66SGuenter Roeck };
34941082d66SGuenter Roeck 
35041082d66SGuenter Roeck struct sensor_device_template {
35141082d66SGuenter Roeck 	struct device_attribute dev_attr;
35241082d66SGuenter Roeck 	union {
35341082d66SGuenter Roeck 		struct {
35441082d66SGuenter Roeck 			u8 nr;
35541082d66SGuenter Roeck 			u8 index;
35641082d66SGuenter Roeck 		} s;
35741082d66SGuenter Roeck 		int index;
35841082d66SGuenter Roeck 	} u;
35941082d66SGuenter Roeck 	bool s2;	/* true if both index and nr are used */
36041082d66SGuenter Roeck };
36141082d66SGuenter Roeck 
36241082d66SGuenter Roeck struct sensor_device_attr_u {
36341082d66SGuenter Roeck 	union {
36441082d66SGuenter Roeck 		struct sensor_device_attribute a1;
36541082d66SGuenter Roeck 		struct sensor_device_attribute_2 a2;
36641082d66SGuenter Roeck 	} u;
36741082d66SGuenter Roeck 	char name[32];
36841082d66SGuenter Roeck };
36941082d66SGuenter Roeck 
37041082d66SGuenter Roeck #define __TEMPLATE_ATTR(_template, _mode, _show, _store) {	\
37141082d66SGuenter Roeck 	.attr = {.name = _template, .mode = _mode },		\
37241082d66SGuenter Roeck 	.show	= _show,					\
37341082d66SGuenter Roeck 	.store	= _store,					\
37441082d66SGuenter Roeck }
37541082d66SGuenter Roeck 
37641082d66SGuenter Roeck #define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index)	\
37741082d66SGuenter Roeck 	{ .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store),	\
37841082d66SGuenter Roeck 	  .u.index = _index,						\
37941082d66SGuenter Roeck 	  .s2 = false }
38041082d66SGuenter Roeck 
38141082d66SGuenter Roeck #define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store,	\
38241082d66SGuenter Roeck 				 _nr, _index)				\
38341082d66SGuenter Roeck 	{ .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store),	\
38441082d66SGuenter Roeck 	  .u.s.index = _index,						\
38541082d66SGuenter Roeck 	  .u.s.nr = _nr,						\
38641082d66SGuenter Roeck 	  .s2 = true }
38741082d66SGuenter Roeck 
38841082d66SGuenter Roeck #define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index)	\
38941082d66SGuenter Roeck static struct sensor_device_template sensor_dev_template_##_name	\
39041082d66SGuenter Roeck 	= SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store,	\
39141082d66SGuenter Roeck 				 _index)
39241082d66SGuenter Roeck 
39341082d66SGuenter Roeck #define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store,	\
39441082d66SGuenter Roeck 			  _nr, _index)					\
39541082d66SGuenter Roeck static struct sensor_device_template sensor_dev_template_##_name	\
39641082d66SGuenter Roeck 	= SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store,	\
39741082d66SGuenter Roeck 				 _nr, _index)
39841082d66SGuenter Roeck 
39941082d66SGuenter Roeck struct sensor_template_group {
40041082d66SGuenter Roeck 	struct sensor_device_template **templates;
40141082d66SGuenter Roeck 	umode_t (*is_visible)(struct kobject *, struct attribute *, int);
40241082d66SGuenter Roeck 	int base;
40341082d66SGuenter Roeck };
40441082d66SGuenter Roeck 
40541082d66SGuenter Roeck static struct attribute_group *
nct6683_create_attr_group(struct device * dev,const struct sensor_template_group * tg,int repeat)406c60fdf85SJulia Lawall nct6683_create_attr_group(struct device *dev,
407c60fdf85SJulia Lawall 			  const struct sensor_template_group *tg,
40841082d66SGuenter Roeck 			  int repeat)
40941082d66SGuenter Roeck {
41041082d66SGuenter Roeck 	struct sensor_device_attribute_2 *a2;
41141082d66SGuenter Roeck 	struct sensor_device_attribute *a;
41241082d66SGuenter Roeck 	struct sensor_device_template **t;
41341082d66SGuenter Roeck 	struct sensor_device_attr_u *su;
41441082d66SGuenter Roeck 	struct attribute_group *group;
41541082d66SGuenter Roeck 	struct attribute **attrs;
4161e4aa3e1SZeng Heng 	int i, count;
41741082d66SGuenter Roeck 
41841082d66SGuenter Roeck 	if (repeat <= 0)
41941082d66SGuenter Roeck 		return ERR_PTR(-EINVAL);
42041082d66SGuenter Roeck 
42141082d66SGuenter Roeck 	t = tg->templates;
42241082d66SGuenter Roeck 	for (count = 0; *t; t++, count++)
42341082d66SGuenter Roeck 		;
42441082d66SGuenter Roeck 
42541082d66SGuenter Roeck 	if (count == 0)
42641082d66SGuenter Roeck 		return ERR_PTR(-EINVAL);
42741082d66SGuenter Roeck 
42841082d66SGuenter Roeck 	group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
42941082d66SGuenter Roeck 	if (group == NULL)
43041082d66SGuenter Roeck 		return ERR_PTR(-ENOMEM);
43141082d66SGuenter Roeck 
432a86854d0SKees Cook 	attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs),
43341082d66SGuenter Roeck 			     GFP_KERNEL);
43441082d66SGuenter Roeck 	if (attrs == NULL)
43541082d66SGuenter Roeck 		return ERR_PTR(-ENOMEM);
43641082d66SGuenter Roeck 
437a86854d0SKees Cook 	su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)),
43841082d66SGuenter Roeck 			  GFP_KERNEL);
43941082d66SGuenter Roeck 	if (su == NULL)
44041082d66SGuenter Roeck 		return ERR_PTR(-ENOMEM);
44141082d66SGuenter Roeck 
44241082d66SGuenter Roeck 	group->attrs = attrs;
44341082d66SGuenter Roeck 	group->is_visible = tg->is_visible;
44441082d66SGuenter Roeck 
44541082d66SGuenter Roeck 	for (i = 0; i < repeat; i++) {
44641082d66SGuenter Roeck 		t = tg->templates;
4471e4aa3e1SZeng Heng 		while (*t) {
44841082d66SGuenter Roeck 			snprintf(su->name, sizeof(su->name),
44941082d66SGuenter Roeck 				 (*t)->dev_attr.attr.name, tg->base + i);
45041082d66SGuenter Roeck 			if ((*t)->s2) {
45141082d66SGuenter Roeck 				a2 = &su->u.a2;
452c7bd6dc3SGuenter Roeck 				sysfs_attr_init(&a2->dev_attr.attr);
45341082d66SGuenter Roeck 				a2->dev_attr.attr.name = su->name;
45441082d66SGuenter Roeck 				a2->nr = (*t)->u.s.nr + i;
45541082d66SGuenter Roeck 				a2->index = (*t)->u.s.index;
45641082d66SGuenter Roeck 				a2->dev_attr.attr.mode =
45741082d66SGuenter Roeck 				  (*t)->dev_attr.attr.mode;
45841082d66SGuenter Roeck 				a2->dev_attr.show = (*t)->dev_attr.show;
45941082d66SGuenter Roeck 				a2->dev_attr.store = (*t)->dev_attr.store;
46041082d66SGuenter Roeck 				*attrs = &a2->dev_attr.attr;
46141082d66SGuenter Roeck 			} else {
46241082d66SGuenter Roeck 				a = &su->u.a1;
463c7bd6dc3SGuenter Roeck 				sysfs_attr_init(&a->dev_attr.attr);
46441082d66SGuenter Roeck 				a->dev_attr.attr.name = su->name;
46541082d66SGuenter Roeck 				a->index = (*t)->u.index + i;
46641082d66SGuenter Roeck 				a->dev_attr.attr.mode =
46741082d66SGuenter Roeck 				  (*t)->dev_attr.attr.mode;
46841082d66SGuenter Roeck 				a->dev_attr.show = (*t)->dev_attr.show;
46941082d66SGuenter Roeck 				a->dev_attr.store = (*t)->dev_attr.store;
47041082d66SGuenter Roeck 				*attrs = &a->dev_attr.attr;
47141082d66SGuenter Roeck 			}
47241082d66SGuenter Roeck 			attrs++;
47341082d66SGuenter Roeck 			su++;
47441082d66SGuenter Roeck 			t++;
47541082d66SGuenter Roeck 		}
47641082d66SGuenter Roeck 	}
47741082d66SGuenter Roeck 
47841082d66SGuenter Roeck 	return group;
47941082d66SGuenter Roeck }
48041082d66SGuenter Roeck 
48141082d66SGuenter Roeck /* LSB is 16 mV, except for the following sources, where it is 32 mV */
48241082d66SGuenter Roeck #define MON_SRC_VCC	0x60
48341082d66SGuenter Roeck #define MON_SRC_VSB	0x61
48441082d66SGuenter Roeck #define MON_SRC_AVSB	0x62
48541082d66SGuenter Roeck #define MON_SRC_VBAT	0x64
48641082d66SGuenter Roeck 
in_from_reg(u16 reg,u8 src)48741082d66SGuenter Roeck static inline long in_from_reg(u16 reg, u8 src)
48841082d66SGuenter Roeck {
48941082d66SGuenter Roeck 	int scale = 16;
49041082d66SGuenter Roeck 
49141082d66SGuenter Roeck 	if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB ||
49241082d66SGuenter Roeck 	    src == MON_SRC_VBAT)
49341082d66SGuenter Roeck 		scale <<= 1;
49441082d66SGuenter Roeck 	return reg * scale;
49541082d66SGuenter Roeck }
49641082d66SGuenter Roeck 
nct6683_read(struct nct6683_data * data,u16 reg)49741082d66SGuenter Roeck static u16 nct6683_read(struct nct6683_data *data, u16 reg)
49841082d66SGuenter Roeck {
49941082d66SGuenter Roeck 	int res;
50041082d66SGuenter Roeck 
50141082d66SGuenter Roeck 	outb_p(0xff, data->addr + EC_PAGE_REG);		/* unlock */
50241082d66SGuenter Roeck 	outb_p(reg >> 8, data->addr + EC_PAGE_REG);
50341082d66SGuenter Roeck 	outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
50441082d66SGuenter Roeck 	res = inb_p(data->addr + EC_DATA_REG);
50541082d66SGuenter Roeck 	return res;
50641082d66SGuenter Roeck }
50741082d66SGuenter Roeck 
nct6683_read16(struct nct6683_data * data,u16 reg)50841082d66SGuenter Roeck static u16 nct6683_read16(struct nct6683_data *data, u16 reg)
50941082d66SGuenter Roeck {
51041082d66SGuenter Roeck 	return (nct6683_read(data, reg) << 8) | nct6683_read(data, reg + 1);
51141082d66SGuenter Roeck }
51241082d66SGuenter Roeck 
nct6683_write(struct nct6683_data * data,u16 reg,u16 value)51341082d66SGuenter Roeck static void nct6683_write(struct nct6683_data *data, u16 reg, u16 value)
51441082d66SGuenter Roeck {
51541082d66SGuenter Roeck 	outb_p(0xff, data->addr + EC_PAGE_REG);		/* unlock */
51641082d66SGuenter Roeck 	outb_p(reg >> 8, data->addr + EC_PAGE_REG);
51741082d66SGuenter Roeck 	outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
51841082d66SGuenter Roeck 	outb_p(value & 0xff, data->addr + EC_DATA_REG);
51941082d66SGuenter Roeck }
52041082d66SGuenter Roeck 
get_in_reg(struct nct6683_data * data,int nr,int index)52141082d66SGuenter Roeck static int get_in_reg(struct nct6683_data *data, int nr, int index)
52241082d66SGuenter Roeck {
52341082d66SGuenter Roeck 	int ch = data->in_index[index];
52441082d66SGuenter Roeck 	int reg = -EINVAL;
52541082d66SGuenter Roeck 
52641082d66SGuenter Roeck 	switch (nr) {
52741082d66SGuenter Roeck 	case 0:
52841082d66SGuenter Roeck 		reg = NCT6683_REG_MON(ch);
52941082d66SGuenter Roeck 		break;
53041082d66SGuenter Roeck 	case 1:
53141082d66SGuenter Roeck 		if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
53241082d66SGuenter Roeck 			reg = NCT6683_REG_MON_LOW(ch);
53341082d66SGuenter Roeck 		break;
53441082d66SGuenter Roeck 	case 2:
53541082d66SGuenter Roeck 		if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
53641082d66SGuenter Roeck 			reg = NCT6683_REG_MON_HIGH(ch);
53741082d66SGuenter Roeck 		break;
53841082d66SGuenter Roeck 	default:
53941082d66SGuenter Roeck 		break;
54041082d66SGuenter Roeck 	}
54141082d66SGuenter Roeck 	return reg;
54241082d66SGuenter Roeck }
54341082d66SGuenter Roeck 
get_temp_reg(struct nct6683_data * data,int nr,int index)54441082d66SGuenter Roeck static int get_temp_reg(struct nct6683_data *data, int nr, int index)
54541082d66SGuenter Roeck {
54641082d66SGuenter Roeck 	int ch = data->temp_index[index];
54741082d66SGuenter Roeck 	int reg = -EINVAL;
54841082d66SGuenter Roeck 
54941082d66SGuenter Roeck 	switch (data->customer_id) {
55041082d66SGuenter Roeck 	case NCT6683_CUSTOMER_ID_INTEL:
55141082d66SGuenter Roeck 		switch (nr) {
55241082d66SGuenter Roeck 		default:
55341082d66SGuenter Roeck 		case 1:	/* max */
55441082d66SGuenter Roeck 			reg = NCT6683_REG_INTEL_TEMP_MAX(ch);
55541082d66SGuenter Roeck 			break;
55641082d66SGuenter Roeck 		case 3:	/* crit */
55741082d66SGuenter Roeck 			reg = NCT6683_REG_INTEL_TEMP_CRIT(ch);
55841082d66SGuenter Roeck 			break;
55941082d66SGuenter Roeck 		}
56041082d66SGuenter Roeck 		break;
56191918d13SGuenter Roeck 	case NCT6683_CUSTOMER_ID_MITAC:
56241082d66SGuenter Roeck 	default:
56341082d66SGuenter Roeck 		switch (nr) {
56441082d66SGuenter Roeck 		default:
56541082d66SGuenter Roeck 		case 0:	/* min */
56641082d66SGuenter Roeck 			reg = NCT6683_REG_MON_LOW(ch);
56741082d66SGuenter Roeck 			break;
56841082d66SGuenter Roeck 		case 1:	/* max */
56941082d66SGuenter Roeck 			reg = NCT6683_REG_TEMP_MAX(ch);
57041082d66SGuenter Roeck 			break;
57141082d66SGuenter Roeck 		case 2:	/* hyst */
57241082d66SGuenter Roeck 			reg = NCT6683_REG_TEMP_HYST(ch);
57341082d66SGuenter Roeck 			break;
57441082d66SGuenter Roeck 		case 3:	/* crit */
57541082d66SGuenter Roeck 			reg = NCT6683_REG_MON_HIGH(ch);
57641082d66SGuenter Roeck 			break;
57741082d66SGuenter Roeck 		}
57841082d66SGuenter Roeck 		break;
57941082d66SGuenter Roeck 	}
58041082d66SGuenter Roeck 	return reg;
58141082d66SGuenter Roeck }
58241082d66SGuenter Roeck 
nct6683_update_pwm(struct device * dev)58341082d66SGuenter Roeck static void nct6683_update_pwm(struct device *dev)
58441082d66SGuenter Roeck {
58541082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
58641082d66SGuenter Roeck 	int i;
58741082d66SGuenter Roeck 
58841082d66SGuenter Roeck 	for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
58941082d66SGuenter Roeck 		if (!(data->have_pwm & (1 << i)))
59041082d66SGuenter Roeck 			continue;
59141082d66SGuenter Roeck 		data->pwm[i] = nct6683_read(data, NCT6683_REG_PWM(i));
59241082d66SGuenter Roeck 	}
59341082d66SGuenter Roeck }
59441082d66SGuenter Roeck 
nct6683_update_device(struct device * dev)59541082d66SGuenter Roeck static struct nct6683_data *nct6683_update_device(struct device *dev)
59641082d66SGuenter Roeck {
59741082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
59841082d66SGuenter Roeck 	int i, j;
59941082d66SGuenter Roeck 
60041082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
60141082d66SGuenter Roeck 
60241082d66SGuenter Roeck 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
60341082d66SGuenter Roeck 		/* Measured voltages and limits */
60441082d66SGuenter Roeck 		for (i = 0; i < data->in_num; i++) {
60541082d66SGuenter Roeck 			for (j = 0; j < 3; j++) {
60641082d66SGuenter Roeck 				int reg = get_in_reg(data, j, i);
60741082d66SGuenter Roeck 
60841082d66SGuenter Roeck 				if (reg >= 0)
60941082d66SGuenter Roeck 					data->in[j][i] =
61041082d66SGuenter Roeck 						nct6683_read(data, reg);
61141082d66SGuenter Roeck 			}
61241082d66SGuenter Roeck 		}
61341082d66SGuenter Roeck 
61441082d66SGuenter Roeck 		/* Measured temperatures and limits */
61541082d66SGuenter Roeck 		for (i = 0; i < data->temp_num; i++) {
61641082d66SGuenter Roeck 			u8 ch = data->temp_index[i];
61741082d66SGuenter Roeck 
61841082d66SGuenter Roeck 			data->temp_in[i] = nct6683_read16(data,
61941082d66SGuenter Roeck 							  NCT6683_REG_MON(ch));
62041082d66SGuenter Roeck 			for (j = 0; j < 4; j++) {
62141082d66SGuenter Roeck 				int reg = get_temp_reg(data, j, i);
62241082d66SGuenter Roeck 
62341082d66SGuenter Roeck 				if (reg >= 0)
62441082d66SGuenter Roeck 					data->temp[j][i] =
62541082d66SGuenter Roeck 						nct6683_read(data, reg);
62641082d66SGuenter Roeck 			}
62741082d66SGuenter Roeck 		}
62841082d66SGuenter Roeck 
62941082d66SGuenter Roeck 		/* Measured fan speeds and limits */
63041082d66SGuenter Roeck 		for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
63141082d66SGuenter Roeck 			if (!(data->have_fan & (1 << i)))
63241082d66SGuenter Roeck 				continue;
63341082d66SGuenter Roeck 
63441082d66SGuenter Roeck 			data->rpm[i] = nct6683_read16(data,
63541082d66SGuenter Roeck 						NCT6683_REG_FAN_RPM(i));
63641082d66SGuenter Roeck 			data->fan_min[i] = nct6683_read16(data,
63741082d66SGuenter Roeck 						NCT6683_REG_FAN_MIN(i));
63841082d66SGuenter Roeck 		}
63941082d66SGuenter Roeck 
64041082d66SGuenter Roeck 		nct6683_update_pwm(dev);
64141082d66SGuenter Roeck 
64241082d66SGuenter Roeck 		data->last_updated = jiffies;
64341082d66SGuenter Roeck 		data->valid = true;
64441082d66SGuenter Roeck 	}
64541082d66SGuenter Roeck 
64641082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
64741082d66SGuenter Roeck 	return data;
64841082d66SGuenter Roeck }
64941082d66SGuenter Roeck 
65041082d66SGuenter Roeck /*
65141082d66SGuenter Roeck  * Sysfs callback functions
65241082d66SGuenter Roeck  */
65341082d66SGuenter Roeck static ssize_t
show_in_label(struct device * dev,struct device_attribute * attr,char * buf)65441082d66SGuenter Roeck show_in_label(struct device *dev, struct device_attribute *attr, char *buf)
65541082d66SGuenter Roeck {
65641082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
65741082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
65841082d66SGuenter Roeck 	int nr = sattr->index;
65941082d66SGuenter Roeck 
66041082d66SGuenter Roeck 	return sprintf(buf, "%s\n", nct6683_mon_label[data->in_src[nr]]);
66141082d66SGuenter Roeck }
66241082d66SGuenter Roeck 
66341082d66SGuenter Roeck static ssize_t
show_in_reg(struct device * dev,struct device_attribute * attr,char * buf)66441082d66SGuenter Roeck show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
66541082d66SGuenter Roeck {
66641082d66SGuenter Roeck 	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
66741082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
66841082d66SGuenter Roeck 	int index = sattr->index;
66941082d66SGuenter Roeck 	int nr = sattr->nr;
67041082d66SGuenter Roeck 
67141082d66SGuenter Roeck 	return sprintf(buf, "%ld\n",
67241082d66SGuenter Roeck 		       in_from_reg(data->in[index][nr], data->in_index[index]));
67341082d66SGuenter Roeck }
67441082d66SGuenter Roeck 
nct6683_in_is_visible(struct kobject * kobj,struct attribute * attr,int index)67541082d66SGuenter Roeck static umode_t nct6683_in_is_visible(struct kobject *kobj,
67641082d66SGuenter Roeck 				     struct attribute *attr, int index)
67741082d66SGuenter Roeck {
67823fd63a4SWang Qing 	struct device *dev = kobj_to_dev(kobj);
67941082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
68041082d66SGuenter Roeck 	int nr = index % 4;	/* attribute */
68141082d66SGuenter Roeck 
68241082d66SGuenter Roeck 	/*
68341082d66SGuenter Roeck 	 * Voltage limits exist for Intel boards,
68441082d66SGuenter Roeck 	 * but register location and encoding is unknown
68541082d66SGuenter Roeck 	 */
68641082d66SGuenter Roeck 	if ((nr == 2 || nr == 3) &&
68741082d66SGuenter Roeck 	    data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
68841082d66SGuenter Roeck 		return 0;
68941082d66SGuenter Roeck 
69041082d66SGuenter Roeck 	return attr->mode;
69141082d66SGuenter Roeck }
69241082d66SGuenter Roeck 
69341082d66SGuenter Roeck SENSOR_TEMPLATE(in_label, "in%d_label", S_IRUGO, show_in_label, NULL, 0);
69441082d66SGuenter Roeck SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
69541082d66SGuenter Roeck SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IRUGO, show_in_reg, NULL, 0, 1);
69641082d66SGuenter Roeck SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IRUGO, show_in_reg, NULL, 0, 2);
69741082d66SGuenter Roeck 
69841082d66SGuenter Roeck static struct sensor_device_template *nct6683_attributes_in_template[] = {
69941082d66SGuenter Roeck 	&sensor_dev_template_in_label,
70041082d66SGuenter Roeck 	&sensor_dev_template_in_input,
70141082d66SGuenter Roeck 	&sensor_dev_template_in_min,
70241082d66SGuenter Roeck 	&sensor_dev_template_in_max,
70341082d66SGuenter Roeck 	NULL
70441082d66SGuenter Roeck };
70541082d66SGuenter Roeck 
706c60fdf85SJulia Lawall static const struct sensor_template_group nct6683_in_template_group = {
70741082d66SGuenter Roeck 	.templates = nct6683_attributes_in_template,
70841082d66SGuenter Roeck 	.is_visible = nct6683_in_is_visible,
70941082d66SGuenter Roeck };
71041082d66SGuenter Roeck 
71141082d66SGuenter Roeck static ssize_t
show_fan(struct device * dev,struct device_attribute * attr,char * buf)71241082d66SGuenter Roeck show_fan(struct device *dev, struct device_attribute *attr, char *buf)
71341082d66SGuenter Roeck {
71441082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
71541082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
71641082d66SGuenter Roeck 
71741082d66SGuenter Roeck 	return sprintf(buf, "%d\n", data->rpm[sattr->index]);
71841082d66SGuenter Roeck }
71941082d66SGuenter Roeck 
72041082d66SGuenter Roeck static ssize_t
show_fan_min(struct device * dev,struct device_attribute * attr,char * buf)72141082d66SGuenter Roeck show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
72241082d66SGuenter Roeck {
72341082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
72441082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
72541082d66SGuenter Roeck 	int nr = sattr->index;
72641082d66SGuenter Roeck 
72741082d66SGuenter Roeck 	return sprintf(buf, "%d\n", data->fan_min[nr]);
72841082d66SGuenter Roeck }
72941082d66SGuenter Roeck 
73041082d66SGuenter Roeck static ssize_t
show_fan_pulses(struct device * dev,struct device_attribute * attr,char * buf)73141082d66SGuenter Roeck show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
73241082d66SGuenter Roeck {
73341082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
73441082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
73541082d66SGuenter Roeck 
73641082d66SGuenter Roeck 	return sprintf(buf, "%d\n",
73741082d66SGuenter Roeck 		       ((data->fanin_cfg[sattr->index] >> 5) & 0x03) + 1);
73841082d66SGuenter Roeck }
73941082d66SGuenter Roeck 
nct6683_fan_is_visible(struct kobject * kobj,struct attribute * attr,int index)74041082d66SGuenter Roeck static umode_t nct6683_fan_is_visible(struct kobject *kobj,
74141082d66SGuenter Roeck 				      struct attribute *attr, int index)
74241082d66SGuenter Roeck {
74323fd63a4SWang Qing 	struct device *dev = kobj_to_dev(kobj);
74441082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
74541082d66SGuenter Roeck 	int fan = index / 3;	/* fan index */
74641082d66SGuenter Roeck 	int nr = index % 3;	/* attribute index */
74741082d66SGuenter Roeck 
74841082d66SGuenter Roeck 	if (!(data->have_fan & (1 << fan)))
74941082d66SGuenter Roeck 		return 0;
75041082d66SGuenter Roeck 
75141082d66SGuenter Roeck 	/*
75241082d66SGuenter Roeck 	 * Intel may have minimum fan speed limits,
75341082d66SGuenter Roeck 	 * but register location and encoding are unknown.
75441082d66SGuenter Roeck 	 */
75541082d66SGuenter Roeck 	if (nr == 2 && data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
75641082d66SGuenter Roeck 		return 0;
75741082d66SGuenter Roeck 
75841082d66SGuenter Roeck 	return attr->mode;
75941082d66SGuenter Roeck }
76041082d66SGuenter Roeck 
76141082d66SGuenter Roeck SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
76241082d66SGuenter Roeck SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IRUGO, show_fan_pulses, NULL, 0);
76341082d66SGuenter Roeck SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IRUGO, show_fan_min, NULL, 0);
76441082d66SGuenter Roeck 
76541082d66SGuenter Roeck /*
76641082d66SGuenter Roeck  * nct6683_fan_is_visible uses the index into the following array
76741082d66SGuenter Roeck  * to determine if attributes should be created or not.
76841082d66SGuenter Roeck  * Any change in order or content must be matched.
76941082d66SGuenter Roeck  */
77041082d66SGuenter Roeck static struct sensor_device_template *nct6683_attributes_fan_template[] = {
77141082d66SGuenter Roeck 	&sensor_dev_template_fan_input,
77241082d66SGuenter Roeck 	&sensor_dev_template_fan_pulses,
77341082d66SGuenter Roeck 	&sensor_dev_template_fan_min,
77441082d66SGuenter Roeck 	NULL
77541082d66SGuenter Roeck };
77641082d66SGuenter Roeck 
777c60fdf85SJulia Lawall static const struct sensor_template_group nct6683_fan_template_group = {
77841082d66SGuenter Roeck 	.templates = nct6683_attributes_fan_template,
77941082d66SGuenter Roeck 	.is_visible = nct6683_fan_is_visible,
78041082d66SGuenter Roeck 	.base = 1,
78141082d66SGuenter Roeck };
78241082d66SGuenter Roeck 
78341082d66SGuenter Roeck static ssize_t
show_temp_label(struct device * dev,struct device_attribute * attr,char * buf)78441082d66SGuenter Roeck show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
78541082d66SGuenter Roeck {
78641082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
78741082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
78841082d66SGuenter Roeck 	int nr = sattr->index;
78941082d66SGuenter Roeck 
79041082d66SGuenter Roeck 	return sprintf(buf, "%s\n", nct6683_mon_label[data->temp_src[nr]]);
79141082d66SGuenter Roeck }
79241082d66SGuenter Roeck 
79341082d66SGuenter Roeck static ssize_t
show_temp8(struct device * dev,struct device_attribute * attr,char * buf)79441082d66SGuenter Roeck show_temp8(struct device *dev, struct device_attribute *attr, char *buf)
79541082d66SGuenter Roeck {
79641082d66SGuenter Roeck 	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
79741082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
79841082d66SGuenter Roeck 	int index = sattr->index;
79941082d66SGuenter Roeck 	int nr = sattr->nr;
80041082d66SGuenter Roeck 
80141082d66SGuenter Roeck 	return sprintf(buf, "%d\n", data->temp[index][nr] * 1000);
80241082d66SGuenter Roeck }
80341082d66SGuenter Roeck 
80441082d66SGuenter Roeck static ssize_t
show_temp_hyst(struct device * dev,struct device_attribute * attr,char * buf)80541082d66SGuenter Roeck show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
80641082d66SGuenter Roeck {
80741082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
80841082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
80941082d66SGuenter Roeck 	int nr = sattr->index;
81041082d66SGuenter Roeck 	int temp = data->temp[1][nr] - data->temp[2][nr];
81141082d66SGuenter Roeck 
81241082d66SGuenter Roeck 	return sprintf(buf, "%d\n", temp * 1000);
81341082d66SGuenter Roeck }
81441082d66SGuenter Roeck 
81541082d66SGuenter Roeck static ssize_t
show_temp16(struct device * dev,struct device_attribute * attr,char * buf)81641082d66SGuenter Roeck show_temp16(struct device *dev, struct device_attribute *attr, char *buf)
81741082d66SGuenter Roeck {
81841082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
81941082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
82041082d66SGuenter Roeck 	int index = sattr->index;
82141082d66SGuenter Roeck 
82241082d66SGuenter Roeck 	return sprintf(buf, "%d\n", (data->temp_in[index] / 128) * 500);
82341082d66SGuenter Roeck }
82441082d66SGuenter Roeck 
82541082d66SGuenter Roeck /*
82641082d66SGuenter Roeck  * Temperature sensor type is determined by temperature source
82741082d66SGuenter Roeck  * and can not be modified.
82841082d66SGuenter Roeck  * 0x02..0x07: Thermal diode
82941082d66SGuenter Roeck  * 0x08..0x18: Thermistor
83041082d66SGuenter Roeck  * 0x20..0x2b: Intel PECI
83141082d66SGuenter Roeck  * 0x42..0x49: AMD TSI
83241082d66SGuenter Roeck  * Others are unspecified (not visible)
83341082d66SGuenter Roeck  */
83441082d66SGuenter Roeck 
get_temp_type(u8 src)83541082d66SGuenter Roeck static int get_temp_type(u8 src)
83641082d66SGuenter Roeck {
83741082d66SGuenter Roeck 	if (src >= 0x02 && src <= 0x07)
83841082d66SGuenter Roeck 		return 3;	/* thermal diode */
83941082d66SGuenter Roeck 	else if (src >= 0x08 && src <= 0x18)
84041082d66SGuenter Roeck 		return 4;	/* thermistor */
84141082d66SGuenter Roeck 	else if (src >= 0x20 && src <= 0x2b)
84241082d66SGuenter Roeck 		return 6;	/* PECI */
84341082d66SGuenter Roeck 	else if (src >= 0x42 && src <= 0x49)
84441082d66SGuenter Roeck 		return 5;
84541082d66SGuenter Roeck 
84641082d66SGuenter Roeck 	return 0;
84741082d66SGuenter Roeck }
84841082d66SGuenter Roeck 
84941082d66SGuenter Roeck static ssize_t
show_temp_type(struct device * dev,struct device_attribute * attr,char * buf)85041082d66SGuenter Roeck show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
85141082d66SGuenter Roeck {
85241082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
85341082d66SGuenter Roeck 	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
85441082d66SGuenter Roeck 	int nr = sattr->index;
85541082d66SGuenter Roeck 	return sprintf(buf, "%d\n", get_temp_type(data->temp_src[nr]));
85641082d66SGuenter Roeck }
85741082d66SGuenter Roeck 
nct6683_temp_is_visible(struct kobject * kobj,struct attribute * attr,int index)85841082d66SGuenter Roeck static umode_t nct6683_temp_is_visible(struct kobject *kobj,
85941082d66SGuenter Roeck 				       struct attribute *attr, int index)
86041082d66SGuenter Roeck {
86123fd63a4SWang Qing 	struct device *dev = kobj_to_dev(kobj);
86241082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
86341082d66SGuenter Roeck 	int temp = index / 7;	/* temp index */
86441082d66SGuenter Roeck 	int nr = index % 7;	/* attribute index */
86541082d66SGuenter Roeck 
86641082d66SGuenter Roeck 	/*
86741082d66SGuenter Roeck 	 * Intel does not have low temperature limits or temperature hysteresis
86841082d66SGuenter Roeck 	 * registers, or at least register location and encoding is unknown.
86941082d66SGuenter Roeck 	 */
87041082d66SGuenter Roeck 	if ((nr == 2 || nr == 4) &&
87141082d66SGuenter Roeck 	    data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
87241082d66SGuenter Roeck 		return 0;
87341082d66SGuenter Roeck 
87441082d66SGuenter Roeck 	if (nr == 6 && get_temp_type(data->temp_src[temp]) == 0)
87541082d66SGuenter Roeck 		return 0;				/* type */
87641082d66SGuenter Roeck 
87741082d66SGuenter Roeck 	return attr->mode;
87841082d66SGuenter Roeck }
87941082d66SGuenter Roeck 
88041082d66SGuenter Roeck SENSOR_TEMPLATE(temp_input, "temp%d_input", S_IRUGO, show_temp16, NULL, 0);
88141082d66SGuenter Roeck SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
88241082d66SGuenter Roeck SENSOR_TEMPLATE_2(temp_min, "temp%d_min", S_IRUGO, show_temp8, NULL, 0, 0);
88341082d66SGuenter Roeck SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO, show_temp8, NULL, 0, 1);
88441082d66SGuenter Roeck SENSOR_TEMPLATE(temp_max_hyst, "temp%d_max_hyst", S_IRUGO, show_temp_hyst, NULL,
88541082d66SGuenter Roeck 		0);
88641082d66SGuenter Roeck SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO, show_temp8, NULL, 0, 3);
88741082d66SGuenter Roeck SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO, show_temp_type, NULL, 0);
88841082d66SGuenter Roeck 
88941082d66SGuenter Roeck /*
89041082d66SGuenter Roeck  * nct6683_temp_is_visible uses the index into the following array
89141082d66SGuenter Roeck  * to determine if attributes should be created or not.
89241082d66SGuenter Roeck  * Any change in order or content must be matched.
89341082d66SGuenter Roeck  */
89441082d66SGuenter Roeck static struct sensor_device_template *nct6683_attributes_temp_template[] = {
89541082d66SGuenter Roeck 	&sensor_dev_template_temp_input,
89641082d66SGuenter Roeck 	&sensor_dev_template_temp_label,
89741082d66SGuenter Roeck 	&sensor_dev_template_temp_min,		/* 2 */
89841082d66SGuenter Roeck 	&sensor_dev_template_temp_max,		/* 3 */
89941082d66SGuenter Roeck 	&sensor_dev_template_temp_max_hyst,	/* 4 */
90041082d66SGuenter Roeck 	&sensor_dev_template_temp_crit,		/* 5 */
90141082d66SGuenter Roeck 	&sensor_dev_template_temp_type,		/* 6 */
90241082d66SGuenter Roeck 	NULL
90341082d66SGuenter Roeck };
90441082d66SGuenter Roeck 
905c60fdf85SJulia Lawall static const struct sensor_template_group nct6683_temp_template_group = {
90641082d66SGuenter Roeck 	.templates = nct6683_attributes_temp_template,
90741082d66SGuenter Roeck 	.is_visible = nct6683_temp_is_visible,
90841082d66SGuenter Roeck 	.base = 1,
90941082d66SGuenter Roeck };
91041082d66SGuenter Roeck 
91141082d66SGuenter Roeck static ssize_t
show_pwm(struct device * dev,struct device_attribute * attr,char * buf)91241082d66SGuenter Roeck show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
91341082d66SGuenter Roeck {
91441082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
91541082d66SGuenter Roeck 	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
91641082d66SGuenter Roeck 	int index = sattr->index;
91741082d66SGuenter Roeck 
91841082d66SGuenter Roeck 	return sprintf(buf, "%d\n", data->pwm[index]);
91941082d66SGuenter Roeck }
92041082d66SGuenter Roeck 
92191918d13SGuenter Roeck static ssize_t
store_pwm(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)92291918d13SGuenter Roeck store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
92391918d13SGuenter Roeck 	  size_t count)
92491918d13SGuenter Roeck {
92591918d13SGuenter Roeck 	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
92691918d13SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
92791918d13SGuenter Roeck 	int index = sattr->index;
92891918d13SGuenter Roeck 	unsigned long val;
92991918d13SGuenter Roeck 
93091918d13SGuenter Roeck 	if (kstrtoul(buf, 10, &val) || val > 255)
93191918d13SGuenter Roeck 		return -EINVAL;
93291918d13SGuenter Roeck 
93391918d13SGuenter Roeck 	mutex_lock(&data->update_lock);
93491918d13SGuenter Roeck 	nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ);
93591918d13SGuenter Roeck 	usleep_range(1000, 2000);
93691918d13SGuenter Roeck 	nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val);
93791918d13SGuenter Roeck 	nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE);
93891918d13SGuenter Roeck 	mutex_unlock(&data->update_lock);
93991918d13SGuenter Roeck 
94091918d13SGuenter Roeck 	return count;
94191918d13SGuenter Roeck }
94291918d13SGuenter Roeck 
94391918d13SGuenter Roeck SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0);
94441082d66SGuenter Roeck 
nct6683_pwm_is_visible(struct kobject * kobj,struct attribute * attr,int index)94541082d66SGuenter Roeck static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
94641082d66SGuenter Roeck 				      struct attribute *attr, int index)
94741082d66SGuenter Roeck {
94823fd63a4SWang Qing 	struct device *dev = kobj_to_dev(kobj);
94941082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
95041082d66SGuenter Roeck 	int pwm = index;	/* pwm index */
95141082d66SGuenter Roeck 
95241082d66SGuenter Roeck 	if (!(data->have_pwm & (1 << pwm)))
95341082d66SGuenter Roeck 		return 0;
95441082d66SGuenter Roeck 
95591918d13SGuenter Roeck 	/* Only update pwm values for Mitac boards */
95691918d13SGuenter Roeck 	if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC)
95791918d13SGuenter Roeck 		return attr->mode | S_IWUSR;
95891918d13SGuenter Roeck 
95941082d66SGuenter Roeck 	return attr->mode;
96041082d66SGuenter Roeck }
96141082d66SGuenter Roeck 
96241082d66SGuenter Roeck static struct sensor_device_template *nct6683_attributes_pwm_template[] = {
96341082d66SGuenter Roeck 	&sensor_dev_template_pwm,
96441082d66SGuenter Roeck 	NULL
96541082d66SGuenter Roeck };
96641082d66SGuenter Roeck 
967c60fdf85SJulia Lawall static const struct sensor_template_group nct6683_pwm_template_group = {
96841082d66SGuenter Roeck 	.templates = nct6683_attributes_pwm_template,
96941082d66SGuenter Roeck 	.is_visible = nct6683_pwm_is_visible,
97041082d66SGuenter Roeck 	.base = 1,
97141082d66SGuenter Roeck };
97241082d66SGuenter Roeck 
97341082d66SGuenter Roeck static ssize_t
beep_enable_show(struct device * dev,struct device_attribute * attr,char * buf)9741f856175SJulia Lawall beep_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
97541082d66SGuenter Roeck {
97641082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
97741082d66SGuenter Roeck 	int ret;
97841082d66SGuenter Roeck 	u8 reg;
97941082d66SGuenter Roeck 
98041082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
98141082d66SGuenter Roeck 
98241082d66SGuenter Roeck 	ret = superio_enter(data->sioreg);
98341082d66SGuenter Roeck 	if (ret)
98441082d66SGuenter Roeck 		goto error;
98541082d66SGuenter Roeck 	superio_select(data->sioreg, NCT6683_LD_HWM);
98641082d66SGuenter Roeck 	reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
98741082d66SGuenter Roeck 	superio_exit(data->sioreg);
98841082d66SGuenter Roeck 
98941082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
99041082d66SGuenter Roeck 
99141082d66SGuenter Roeck 	return sprintf(buf, "%u\n", !!(reg & NCT6683_CR_BEEP_MASK));
99241082d66SGuenter Roeck 
99341082d66SGuenter Roeck error:
99441082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
99541082d66SGuenter Roeck 	return ret;
99641082d66SGuenter Roeck }
99741082d66SGuenter Roeck 
99841082d66SGuenter Roeck static ssize_t
beep_enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)9991f856175SJulia Lawall beep_enable_store(struct device *dev, struct device_attribute *attr,
100041082d66SGuenter Roeck 		  const char *buf, size_t count)
100141082d66SGuenter Roeck {
100241082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
100341082d66SGuenter Roeck 	unsigned long val;
100441082d66SGuenter Roeck 	u8 reg;
100541082d66SGuenter Roeck 	int ret;
100641082d66SGuenter Roeck 
100741082d66SGuenter Roeck 	if (kstrtoul(buf, 10, &val) || (val != 0 && val != 1))
100841082d66SGuenter Roeck 		return -EINVAL;
100941082d66SGuenter Roeck 
101041082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
101141082d66SGuenter Roeck 
101241082d66SGuenter Roeck 	ret = superio_enter(data->sioreg);
101341082d66SGuenter Roeck 	if (ret) {
101441082d66SGuenter Roeck 		count = ret;
101541082d66SGuenter Roeck 		goto error;
101641082d66SGuenter Roeck 	}
101741082d66SGuenter Roeck 
101841082d66SGuenter Roeck 	superio_select(data->sioreg, NCT6683_LD_HWM);
101941082d66SGuenter Roeck 	reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
102041082d66SGuenter Roeck 	if (val)
102141082d66SGuenter Roeck 		reg |= NCT6683_CR_BEEP_MASK;
102241082d66SGuenter Roeck 	else
102341082d66SGuenter Roeck 		reg &= ~NCT6683_CR_BEEP_MASK;
102441082d66SGuenter Roeck 	superio_outb(data->sioreg, NCT6683_REG_CR_BEEP, reg);
102541082d66SGuenter Roeck 	superio_exit(data->sioreg);
102641082d66SGuenter Roeck error:
102741082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
102841082d66SGuenter Roeck 	return count;
102941082d66SGuenter Roeck }
103041082d66SGuenter Roeck 
103141082d66SGuenter Roeck /* Case open detection */
103241082d66SGuenter Roeck 
103341082d66SGuenter Roeck static ssize_t
intrusion0_alarm_show(struct device * dev,struct device_attribute * attr,char * buf)10341f856175SJulia Lawall intrusion0_alarm_show(struct device *dev, struct device_attribute *attr,
10351f856175SJulia Lawall 		      char *buf)
103641082d66SGuenter Roeck {
103741082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
103841082d66SGuenter Roeck 	int ret;
103941082d66SGuenter Roeck 	u8 reg;
104041082d66SGuenter Roeck 
104141082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
104241082d66SGuenter Roeck 
104341082d66SGuenter Roeck 	ret = superio_enter(data->sioreg);
104441082d66SGuenter Roeck 	if (ret)
104541082d66SGuenter Roeck 		goto error;
104641082d66SGuenter Roeck 	superio_select(data->sioreg, NCT6683_LD_ACPI);
104741082d66SGuenter Roeck 	reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
104841082d66SGuenter Roeck 	superio_exit(data->sioreg);
104941082d66SGuenter Roeck 
105041082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
105141082d66SGuenter Roeck 
105241082d66SGuenter Roeck 	return sprintf(buf, "%u\n", !(reg & NCT6683_CR_CASEOPEN_MASK));
105341082d66SGuenter Roeck 
105441082d66SGuenter Roeck error:
105541082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
105641082d66SGuenter Roeck 	return ret;
105741082d66SGuenter Roeck }
105841082d66SGuenter Roeck 
105941082d66SGuenter Roeck static ssize_t
intrusion0_alarm_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)10601f856175SJulia Lawall intrusion0_alarm_store(struct device *dev, struct device_attribute *attr,
106141082d66SGuenter Roeck 		       const char *buf, size_t count)
106241082d66SGuenter Roeck {
106341082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
106441082d66SGuenter Roeck 	unsigned long val;
106541082d66SGuenter Roeck 	u8 reg;
106641082d66SGuenter Roeck 	int ret;
106741082d66SGuenter Roeck 
106841082d66SGuenter Roeck 	if (kstrtoul(buf, 10, &val) || val != 0)
106941082d66SGuenter Roeck 		return -EINVAL;
107041082d66SGuenter Roeck 
107141082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
107241082d66SGuenter Roeck 
107341082d66SGuenter Roeck 	/*
107441082d66SGuenter Roeck 	 * Use CR registers to clear caseopen status.
107541082d66SGuenter Roeck 	 * Caseopen is activ low, clear by writing 1 into the register.
107641082d66SGuenter Roeck 	 */
107741082d66SGuenter Roeck 
107841082d66SGuenter Roeck 	ret = superio_enter(data->sioreg);
107941082d66SGuenter Roeck 	if (ret) {
108041082d66SGuenter Roeck 		count = ret;
108141082d66SGuenter Roeck 		goto error;
108241082d66SGuenter Roeck 	}
108341082d66SGuenter Roeck 
108441082d66SGuenter Roeck 	superio_select(data->sioreg, NCT6683_LD_ACPI);
108541082d66SGuenter Roeck 	reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
108641082d66SGuenter Roeck 	reg |= NCT6683_CR_CASEOPEN_MASK;
108741082d66SGuenter Roeck 	superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
108841082d66SGuenter Roeck 	reg &= ~NCT6683_CR_CASEOPEN_MASK;
108941082d66SGuenter Roeck 	superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
109041082d66SGuenter Roeck 	superio_exit(data->sioreg);
109141082d66SGuenter Roeck 
109241082d66SGuenter Roeck 	data->valid = false;	/* Force cache refresh */
109341082d66SGuenter Roeck error:
109441082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
109541082d66SGuenter Roeck 	return count;
109641082d66SGuenter Roeck }
109741082d66SGuenter Roeck 
10981f856175SJulia Lawall static DEVICE_ATTR_RW(intrusion0_alarm);
10991f856175SJulia Lawall static DEVICE_ATTR_RW(beep_enable);
110041082d66SGuenter Roeck 
110141082d66SGuenter Roeck static struct attribute *nct6683_attributes_other[] = {
110241082d66SGuenter Roeck 	&dev_attr_intrusion0_alarm.attr,
110341082d66SGuenter Roeck 	&dev_attr_beep_enable.attr,
110441082d66SGuenter Roeck 	NULL
110541082d66SGuenter Roeck };
110641082d66SGuenter Roeck 
110741082d66SGuenter Roeck static const struct attribute_group nct6683_group_other = {
110841082d66SGuenter Roeck 	.attrs = nct6683_attributes_other,
110941082d66SGuenter Roeck };
111041082d66SGuenter Roeck 
111141082d66SGuenter Roeck /* Get the monitoring functions started */
nct6683_init_device(struct nct6683_data * data)111241082d66SGuenter Roeck static inline void nct6683_init_device(struct nct6683_data *data)
111341082d66SGuenter Roeck {
111441082d66SGuenter Roeck 	u8 tmp;
111541082d66SGuenter Roeck 
111641082d66SGuenter Roeck 	/* Start hardware monitoring if needed */
111741082d66SGuenter Roeck 	tmp = nct6683_read(data, NCT6683_HWM_CFG);
111841082d66SGuenter Roeck 	if (!(tmp & 0x80))
111941082d66SGuenter Roeck 		nct6683_write(data, NCT6683_HWM_CFG, tmp | 0x80);
112041082d66SGuenter Roeck }
112141082d66SGuenter Roeck 
112241082d66SGuenter Roeck /*
112341082d66SGuenter Roeck  * There are a total of 24 fan inputs. Each can be configured as input
112441082d66SGuenter Roeck  * or as output. A maximum of 16 inputs and 8 outputs is configurable.
112541082d66SGuenter Roeck  */
112641082d66SGuenter Roeck static void
nct6683_setup_fans(struct nct6683_data * data)112741082d66SGuenter Roeck nct6683_setup_fans(struct nct6683_data *data)
112841082d66SGuenter Roeck {
112941082d66SGuenter Roeck 	int i;
113041082d66SGuenter Roeck 	u8 reg;
113141082d66SGuenter Roeck 
113241082d66SGuenter Roeck 	for (i = 0; i < NCT6683_NUM_REG_FAN; i++) {
113341082d66SGuenter Roeck 		reg = nct6683_read(data, NCT6683_REG_FANIN_CFG(i));
113441082d66SGuenter Roeck 		if (reg & 0x80)
113541082d66SGuenter Roeck 			data->have_fan |= 1 << i;
113641082d66SGuenter Roeck 		data->fanin_cfg[i] = reg;
113741082d66SGuenter Roeck 	}
113841082d66SGuenter Roeck 	for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
113941082d66SGuenter Roeck 		reg = nct6683_read(data, NCT6683_REG_FANOUT_CFG(i));
114041082d66SGuenter Roeck 		if (reg & 0x80)
114141082d66SGuenter Roeck 			data->have_pwm |= 1 << i;
114241082d66SGuenter Roeck 		data->fanout_cfg[i] = reg;
114341082d66SGuenter Roeck 	}
114441082d66SGuenter Roeck }
114541082d66SGuenter Roeck 
114641082d66SGuenter Roeck /*
114741082d66SGuenter Roeck  * Translation from monitoring register to temperature and voltage attributes
114841082d66SGuenter Roeck  * ==========================================================================
114941082d66SGuenter Roeck  *
115041082d66SGuenter Roeck  * There are a total of 32 monitoring registers. Each can be assigned to either
115141082d66SGuenter Roeck  * a temperature or voltage monitoring source.
115241082d66SGuenter Roeck  * NCT6683_REG_MON_CFG(x) defines assignment for each monitoring source.
115341082d66SGuenter Roeck  *
115441082d66SGuenter Roeck  * Temperature and voltage attribute mapping is determined by walking through
115541082d66SGuenter Roeck  * the NCT6683_REG_MON_CFG registers. If the assigned source is
115641082d66SGuenter Roeck  * a temperature, temp_index[n] is set to the monitor register index, and
115741082d66SGuenter Roeck  * temp_src[n] is set to the temperature source. If the assigned source is
115841082d66SGuenter Roeck  * a voltage, the respective values are stored in in_index[] and in_src[],
115941082d66SGuenter Roeck  * respectively.
116041082d66SGuenter Roeck  */
116141082d66SGuenter Roeck 
nct6683_setup_sensors(struct nct6683_data * data)116241082d66SGuenter Roeck static void nct6683_setup_sensors(struct nct6683_data *data)
116341082d66SGuenter Roeck {
116441082d66SGuenter Roeck 	u8 reg;
116541082d66SGuenter Roeck 	int i;
116641082d66SGuenter Roeck 
116741082d66SGuenter Roeck 	data->temp_num = 0;
116841082d66SGuenter Roeck 	data->in_num = 0;
116941082d66SGuenter Roeck 	for (i = 0; i < NCT6683_NUM_REG_MON; i++) {
117041082d66SGuenter Roeck 		reg = nct6683_read(data, NCT6683_REG_MON_CFG(i)) & 0x7f;
117141082d66SGuenter Roeck 		/* Ignore invalid assignments */
117241082d66SGuenter Roeck 		if (reg >= NUM_MON_LABELS)
117341082d66SGuenter Roeck 			continue;
117441082d66SGuenter Roeck 		/* Skip if disabled or reserved */
117541082d66SGuenter Roeck 		if (nct6683_mon_label[reg] == NULL)
117641082d66SGuenter Roeck 			continue;
117741082d66SGuenter Roeck 		if (reg < MON_VOLTAGE_START) {
117841082d66SGuenter Roeck 			data->temp_index[data->temp_num] = i;
117941082d66SGuenter Roeck 			data->temp_src[data->temp_num] = reg;
118041082d66SGuenter Roeck 			data->temp_num++;
118141082d66SGuenter Roeck 		} else {
118241082d66SGuenter Roeck 			data->in_index[data->in_num] = i;
118341082d66SGuenter Roeck 			data->in_src[data->in_num] = reg;
118441082d66SGuenter Roeck 			data->in_num++;
118541082d66SGuenter Roeck 		}
118641082d66SGuenter Roeck 	}
118741082d66SGuenter Roeck }
118841082d66SGuenter Roeck 
nct6683_probe(struct platform_device * pdev)118941082d66SGuenter Roeck static int nct6683_probe(struct platform_device *pdev)
119041082d66SGuenter Roeck {
119141082d66SGuenter Roeck 	struct device *dev = &pdev->dev;
119241082d66SGuenter Roeck 	struct nct6683_sio_data *sio_data = dev->platform_data;
119341082d66SGuenter Roeck 	struct attribute_group *group;
119441082d66SGuenter Roeck 	struct nct6683_data *data;
119541082d66SGuenter Roeck 	struct device *hwmon_dev;
119641082d66SGuenter Roeck 	struct resource *res;
119741082d66SGuenter Roeck 	int groups = 0;
119891918d13SGuenter Roeck 	char build[16];
119941082d66SGuenter Roeck 
120041082d66SGuenter Roeck 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
120141082d66SGuenter Roeck 	if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
120241082d66SGuenter Roeck 		return -EBUSY;
120341082d66SGuenter Roeck 
120441082d66SGuenter Roeck 	data = devm_kzalloc(dev, sizeof(struct nct6683_data), GFP_KERNEL);
120541082d66SGuenter Roeck 	if (!data)
120641082d66SGuenter Roeck 		return -ENOMEM;
120741082d66SGuenter Roeck 
120841082d66SGuenter Roeck 	data->kind = sio_data->kind;
120941082d66SGuenter Roeck 	data->sioreg = sio_data->sioreg;
121041082d66SGuenter Roeck 	data->addr = res->start;
121141082d66SGuenter Roeck 	mutex_init(&data->update_lock);
121241082d66SGuenter Roeck 	platform_set_drvdata(pdev, data);
121341082d66SGuenter Roeck 
121441082d66SGuenter Roeck 	data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID);
121541082d66SGuenter Roeck 
121691918d13SGuenter Roeck 	/* By default only instantiate driver if the customer ID is known */
121791918d13SGuenter Roeck 	switch (data->customer_id) {
121891918d13SGuenter Roeck 	case NCT6683_CUSTOMER_ID_INTEL:
121991918d13SGuenter Roeck 		break;
122091918d13SGuenter Roeck 	case NCT6683_CUSTOMER_ID_MITAC:
122191918d13SGuenter Roeck 		break;
1222daf4feddSDavid Bartley 	case NCT6683_CUSTOMER_ID_MSI:
1223daf4feddSDavid Bartley 		break;
1224*7c415ed8SGopal Prasad 	case NCT6683_CUSTOMER_ID_MSI2:
1225*7c415ed8SGopal Prasad 		break;
1226bd433537SBlaž Hrastnik 	case NCT6683_CUSTOMER_ID_ASROCK:
1227bd433537SBlaž Hrastnik 		break;
1228d55532f7SDaniel Dawson 	case NCT6683_CUSTOMER_ID_ASROCK2:
1229d55532f7SDaniel Dawson 		break;
123091918d13SGuenter Roeck 	default:
123191918d13SGuenter Roeck 		if (!force)
123291918d13SGuenter Roeck 			return -ENODEV;
123391918d13SGuenter Roeck 	}
123491918d13SGuenter Roeck 
123541082d66SGuenter Roeck 	nct6683_init_device(data);
123641082d66SGuenter Roeck 	nct6683_setup_fans(data);
123741082d66SGuenter Roeck 	nct6683_setup_sensors(data);
123841082d66SGuenter Roeck 
123941082d66SGuenter Roeck 	/* Register sysfs hooks */
124041082d66SGuenter Roeck 
124141082d66SGuenter Roeck 	if (data->have_pwm) {
124241082d66SGuenter Roeck 		group = nct6683_create_attr_group(dev,
124341082d66SGuenter Roeck 						  &nct6683_pwm_template_group,
124441082d66SGuenter Roeck 						  fls(data->have_pwm));
124541082d66SGuenter Roeck 		if (IS_ERR(group))
124641082d66SGuenter Roeck 			return PTR_ERR(group);
124741082d66SGuenter Roeck 		data->groups[groups++] = group;
124841082d66SGuenter Roeck 	}
124941082d66SGuenter Roeck 
125041082d66SGuenter Roeck 	if (data->in_num) {
125141082d66SGuenter Roeck 		group = nct6683_create_attr_group(dev,
125241082d66SGuenter Roeck 						  &nct6683_in_template_group,
125341082d66SGuenter Roeck 						  data->in_num);
125441082d66SGuenter Roeck 		if (IS_ERR(group))
125541082d66SGuenter Roeck 			return PTR_ERR(group);
125641082d66SGuenter Roeck 		data->groups[groups++] = group;
125741082d66SGuenter Roeck 	}
125841082d66SGuenter Roeck 
125941082d66SGuenter Roeck 	if (data->have_fan) {
126041082d66SGuenter Roeck 		group = nct6683_create_attr_group(dev,
126141082d66SGuenter Roeck 						  &nct6683_fan_template_group,
126241082d66SGuenter Roeck 						  fls(data->have_fan));
126341082d66SGuenter Roeck 		if (IS_ERR(group))
126441082d66SGuenter Roeck 			return PTR_ERR(group);
126541082d66SGuenter Roeck 		data->groups[groups++] = group;
126641082d66SGuenter Roeck 	}
126741082d66SGuenter Roeck 
126841082d66SGuenter Roeck 	if (data->temp_num) {
126941082d66SGuenter Roeck 		group = nct6683_create_attr_group(dev,
127041082d66SGuenter Roeck 						  &nct6683_temp_template_group,
127141082d66SGuenter Roeck 						  data->temp_num);
127241082d66SGuenter Roeck 		if (IS_ERR(group))
127341082d66SGuenter Roeck 			return PTR_ERR(group);
127441082d66SGuenter Roeck 		data->groups[groups++] = group;
127541082d66SGuenter Roeck 	}
127641082d66SGuenter Roeck 	data->groups[groups++] = &nct6683_group_other;
127741082d66SGuenter Roeck 
127891918d13SGuenter Roeck 	if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
127991918d13SGuenter Roeck 		scnprintf(build, sizeof(build), "%02x/%02x/%02x",
128041082d66SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_MONTH),
128141082d66SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_DAY),
128241082d66SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_YEAR));
128391918d13SGuenter Roeck 	else
128491918d13SGuenter Roeck 		scnprintf(build, sizeof(build), "%02d/%02d/%02d",
128591918d13SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_MONTH),
128691918d13SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_DAY),
128791918d13SGuenter Roeck 			  nct6683_read(data, NCT6683_REG_BUILD_YEAR));
128891918d13SGuenter Roeck 
128991918d13SGuenter Roeck 	dev_info(dev, "%s EC firmware version %d.%d build %s\n",
129091918d13SGuenter Roeck 		 nct6683_chip_names[data->kind],
129191918d13SGuenter Roeck 		 nct6683_read(data, NCT6683_REG_VERSION_HI),
129291918d13SGuenter Roeck 		 nct6683_read(data, NCT6683_REG_VERSION_LO),
129391918d13SGuenter Roeck 		 build);
129441082d66SGuenter Roeck 
129541082d66SGuenter Roeck 	hwmon_dev = devm_hwmon_device_register_with_groups(dev,
129641082d66SGuenter Roeck 			nct6683_device_names[data->kind], data, data->groups);
129741082d66SGuenter Roeck 	return PTR_ERR_OR_ZERO(hwmon_dev);
129841082d66SGuenter Roeck }
129941082d66SGuenter Roeck 
130041082d66SGuenter Roeck #ifdef CONFIG_PM
nct6683_suspend(struct device * dev)130141082d66SGuenter Roeck static int nct6683_suspend(struct device *dev)
130241082d66SGuenter Roeck {
130341082d66SGuenter Roeck 	struct nct6683_data *data = nct6683_update_device(dev);
130441082d66SGuenter Roeck 
130541082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
130641082d66SGuenter Roeck 	data->hwm_cfg = nct6683_read(data, NCT6683_HWM_CFG);
130741082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
130841082d66SGuenter Roeck 
130941082d66SGuenter Roeck 	return 0;
131041082d66SGuenter Roeck }
131141082d66SGuenter Roeck 
nct6683_resume(struct device * dev)131241082d66SGuenter Roeck static int nct6683_resume(struct device *dev)
131341082d66SGuenter Roeck {
131441082d66SGuenter Roeck 	struct nct6683_data *data = dev_get_drvdata(dev);
131541082d66SGuenter Roeck 
131641082d66SGuenter Roeck 	mutex_lock(&data->update_lock);
131741082d66SGuenter Roeck 
131841082d66SGuenter Roeck 	nct6683_write(data, NCT6683_HWM_CFG, data->hwm_cfg);
131941082d66SGuenter Roeck 
132041082d66SGuenter Roeck 	/* Force re-reading all values */
132141082d66SGuenter Roeck 	data->valid = false;
132241082d66SGuenter Roeck 	mutex_unlock(&data->update_lock);
132341082d66SGuenter Roeck 
132441082d66SGuenter Roeck 	return 0;
132541082d66SGuenter Roeck }
132641082d66SGuenter Roeck 
132741082d66SGuenter Roeck static const struct dev_pm_ops nct6683_dev_pm_ops = {
132841082d66SGuenter Roeck 	.suspend = nct6683_suspend,
132941082d66SGuenter Roeck 	.resume = nct6683_resume,
133041082d66SGuenter Roeck 	.freeze = nct6683_suspend,
133141082d66SGuenter Roeck 	.restore = nct6683_resume,
133241082d66SGuenter Roeck };
133341082d66SGuenter Roeck 
133441082d66SGuenter Roeck #define NCT6683_DEV_PM_OPS	(&nct6683_dev_pm_ops)
133541082d66SGuenter Roeck #else
133641082d66SGuenter Roeck #define NCT6683_DEV_PM_OPS	NULL
133741082d66SGuenter Roeck #endif /* CONFIG_PM */
133841082d66SGuenter Roeck 
133941082d66SGuenter Roeck static struct platform_driver nct6683_driver = {
134041082d66SGuenter Roeck 	.driver = {
134141082d66SGuenter Roeck 		.name	= DRVNAME,
134241082d66SGuenter Roeck 		.pm	= NCT6683_DEV_PM_OPS,
134341082d66SGuenter Roeck 	},
134441082d66SGuenter Roeck 	.probe		= nct6683_probe,
134541082d66SGuenter Roeck };
134641082d66SGuenter Roeck 
nct6683_find(int sioaddr,struct nct6683_sio_data * sio_data)134741082d66SGuenter Roeck static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
134841082d66SGuenter Roeck {
134941082d66SGuenter Roeck 	int addr;
135041082d66SGuenter Roeck 	u16 val;
135141082d66SGuenter Roeck 	int err;
135241082d66SGuenter Roeck 
135341082d66SGuenter Roeck 	err = superio_enter(sioaddr);
135441082d66SGuenter Roeck 	if (err)
135541082d66SGuenter Roeck 		return err;
135641082d66SGuenter Roeck 
135741082d66SGuenter Roeck 	val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
135841082d66SGuenter Roeck 	       | superio_inb(sioaddr, SIO_REG_DEVID + 1);
135941082d66SGuenter Roeck 
136041082d66SGuenter Roeck 	switch (val & SIO_ID_MASK) {
136141082d66SGuenter Roeck 	case SIO_NCT6683_ID:
136241082d66SGuenter Roeck 		sio_data->kind = nct6683;
136341082d66SGuenter Roeck 		break;
1364bfbbbe04SJiqi Li 	case SIO_NCT6686_ID:
1365bfbbbe04SJiqi Li 		sio_data->kind = nct6686;
1366bfbbbe04SJiqi Li 		break;
1367daf4feddSDavid Bartley 	case SIO_NCT6687_ID:
1368daf4feddSDavid Bartley 		sio_data->kind = nct6687;
1369daf4feddSDavid Bartley 		break;
137041082d66SGuenter Roeck 	default:
137141082d66SGuenter Roeck 		if (val != 0xffff)
137241082d66SGuenter Roeck 			pr_debug("unsupported chip ID: 0x%04x\n", val);
137341082d66SGuenter Roeck 		goto fail;
137441082d66SGuenter Roeck 	}
137541082d66SGuenter Roeck 
137641082d66SGuenter Roeck 	/* We have a known chip, find the HWM I/O address */
137741082d66SGuenter Roeck 	superio_select(sioaddr, NCT6683_LD_HWM);
137841082d66SGuenter Roeck 	val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8)
137941082d66SGuenter Roeck 	    | superio_inb(sioaddr, SIO_REG_ADDR + 1);
138041082d66SGuenter Roeck 	addr = val & IOREGION_ALIGNMENT;
138141082d66SGuenter Roeck 	if (addr == 0) {
138241082d66SGuenter Roeck 		pr_err("EC base I/O port unconfigured\n");
138341082d66SGuenter Roeck 		goto fail;
138441082d66SGuenter Roeck 	}
138541082d66SGuenter Roeck 
138641082d66SGuenter Roeck 	/* Activate logical device if needed */
138741082d66SGuenter Roeck 	val = superio_inb(sioaddr, SIO_REG_ENABLE);
138841082d66SGuenter Roeck 	if (!(val & 0x01)) {
1389dbac00f0SGuenter Roeck 		pr_warn("Forcibly enabling EC access. Data may be unusable.\n");
1390dbac00f0SGuenter Roeck 		superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
139141082d66SGuenter Roeck 	}
139241082d66SGuenter Roeck 
139341082d66SGuenter Roeck 	superio_exit(sioaddr);
139441082d66SGuenter Roeck 	pr_info("Found %s or compatible chip at %#x:%#x\n",
139541082d66SGuenter Roeck 		nct6683_chip_names[sio_data->kind], sioaddr, addr);
139641082d66SGuenter Roeck 	sio_data->sioreg = sioaddr;
139741082d66SGuenter Roeck 
139841082d66SGuenter Roeck 	return addr;
139941082d66SGuenter Roeck 
140041082d66SGuenter Roeck fail:
140141082d66SGuenter Roeck 	superio_exit(sioaddr);
140241082d66SGuenter Roeck 	return -ENODEV;
140341082d66SGuenter Roeck }
140441082d66SGuenter Roeck 
140541082d66SGuenter Roeck /*
140641082d66SGuenter Roeck  * when Super-I/O functions move to a separate file, the Super-I/O
140741082d66SGuenter Roeck  * bus will manage the lifetime of the device and this module will only keep
140841082d66SGuenter Roeck  * track of the nct6683 driver. But since we use platform_device_alloc(), we
140941082d66SGuenter Roeck  * must keep track of the device
141041082d66SGuenter Roeck  */
141141082d66SGuenter Roeck static struct platform_device *pdev[2];
141241082d66SGuenter Roeck 
sensors_nct6683_init(void)141341082d66SGuenter Roeck static int __init sensors_nct6683_init(void)
141441082d66SGuenter Roeck {
141541082d66SGuenter Roeck 	struct nct6683_sio_data sio_data;
141641082d66SGuenter Roeck 	int sioaddr[2] = { 0x2e, 0x4e };
141741082d66SGuenter Roeck 	struct resource res;
141841082d66SGuenter Roeck 	bool found = false;
141941082d66SGuenter Roeck 	int address;
142041082d66SGuenter Roeck 	int i, err;
142141082d66SGuenter Roeck 
142241082d66SGuenter Roeck 	err = platform_driver_register(&nct6683_driver);
142341082d66SGuenter Roeck 	if (err)
142441082d66SGuenter Roeck 		return err;
142541082d66SGuenter Roeck 
142641082d66SGuenter Roeck 	/*
142741082d66SGuenter Roeck 	 * initialize sio_data->kind and sio_data->sioreg.
142841082d66SGuenter Roeck 	 *
142941082d66SGuenter Roeck 	 * when Super-I/O functions move to a separate file, the Super-I/O
143041082d66SGuenter Roeck 	 * driver will probe 0x2e and 0x4e and auto-detect the presence of a
143141082d66SGuenter Roeck 	 * nct6683 hardware monitor, and call probe()
143241082d66SGuenter Roeck 	 */
143341082d66SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(pdev); i++) {
143441082d66SGuenter Roeck 		address = nct6683_find(sioaddr[i], &sio_data);
143541082d66SGuenter Roeck 		if (address <= 0)
143641082d66SGuenter Roeck 			continue;
143741082d66SGuenter Roeck 
143841082d66SGuenter Roeck 		found = true;
143941082d66SGuenter Roeck 
144041082d66SGuenter Roeck 		pdev[i] = platform_device_alloc(DRVNAME, address);
144141082d66SGuenter Roeck 		if (!pdev[i]) {
144241082d66SGuenter Roeck 			err = -ENOMEM;
144330190c3cSAxel Lin 			goto exit_device_unregister;
144441082d66SGuenter Roeck 		}
144541082d66SGuenter Roeck 
144641082d66SGuenter Roeck 		err = platform_device_add_data(pdev[i], &sio_data,
144741082d66SGuenter Roeck 					       sizeof(struct nct6683_sio_data));
144841082d66SGuenter Roeck 		if (err)
144941082d66SGuenter Roeck 			goto exit_device_put;
145041082d66SGuenter Roeck 
145141082d66SGuenter Roeck 		memset(&res, 0, sizeof(res));
145241082d66SGuenter Roeck 		res.name = DRVNAME;
145341082d66SGuenter Roeck 		res.start = address + IOREGION_OFFSET;
145441082d66SGuenter Roeck 		res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
145541082d66SGuenter Roeck 		res.flags = IORESOURCE_IO;
145641082d66SGuenter Roeck 
145741082d66SGuenter Roeck 		err = acpi_check_resource_conflict(&res);
145841082d66SGuenter Roeck 		if (err) {
145941082d66SGuenter Roeck 			platform_device_put(pdev[i]);
146041082d66SGuenter Roeck 			pdev[i] = NULL;
146141082d66SGuenter Roeck 			continue;
146241082d66SGuenter Roeck 		}
146341082d66SGuenter Roeck 
146441082d66SGuenter Roeck 		err = platform_device_add_resources(pdev[i], &res, 1);
146541082d66SGuenter Roeck 		if (err)
146641082d66SGuenter Roeck 			goto exit_device_put;
146741082d66SGuenter Roeck 
146841082d66SGuenter Roeck 		/* platform_device_add calls probe() */
146941082d66SGuenter Roeck 		err = platform_device_add(pdev[i]);
147041082d66SGuenter Roeck 		if (err)
147141082d66SGuenter Roeck 			goto exit_device_put;
147241082d66SGuenter Roeck 	}
147341082d66SGuenter Roeck 	if (!found) {
147441082d66SGuenter Roeck 		err = -ENODEV;
147541082d66SGuenter Roeck 		goto exit_unregister;
147641082d66SGuenter Roeck 	}
147741082d66SGuenter Roeck 
147841082d66SGuenter Roeck 	return 0;
147941082d66SGuenter Roeck 
148041082d66SGuenter Roeck exit_device_put:
148141082d66SGuenter Roeck 	platform_device_put(pdev[i]);
148230190c3cSAxel Lin exit_device_unregister:
148330190c3cSAxel Lin 	while (--i >= 0) {
148430190c3cSAxel Lin 		if (pdev[i])
148530190c3cSAxel Lin 			platform_device_unregister(pdev[i]);
148641082d66SGuenter Roeck 	}
148741082d66SGuenter Roeck exit_unregister:
148841082d66SGuenter Roeck 	platform_driver_unregister(&nct6683_driver);
148941082d66SGuenter Roeck 	return err;
149041082d66SGuenter Roeck }
149141082d66SGuenter Roeck 
sensors_nct6683_exit(void)149241082d66SGuenter Roeck static void __exit sensors_nct6683_exit(void)
149341082d66SGuenter Roeck {
149441082d66SGuenter Roeck 	int i;
149541082d66SGuenter Roeck 
149641082d66SGuenter Roeck 	for (i = 0; i < ARRAY_SIZE(pdev); i++) {
149741082d66SGuenter Roeck 		if (pdev[i])
149841082d66SGuenter Roeck 			platform_device_unregister(pdev[i]);
149941082d66SGuenter Roeck 	}
150041082d66SGuenter Roeck 	platform_driver_unregister(&nct6683_driver);
150141082d66SGuenter Roeck }
150241082d66SGuenter Roeck 
150341082d66SGuenter Roeck MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
150441082d66SGuenter Roeck MODULE_DESCRIPTION("NCT6683D driver");
150541082d66SGuenter Roeck MODULE_LICENSE("GPL");
150641082d66SGuenter Roeck 
150741082d66SGuenter Roeck module_init(sensors_nct6683_init);
150841082d66SGuenter Roeck module_exit(sensors_nct6683_exit);
1509