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