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