// SPDX-License-Identifier: GPL-2.0-or-later
/*
* nct6775 - Platform driver for the hardware monitoring
* functionality of Nuvoton NCT677x Super-I/O chips
*
* Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon-vid.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "nct6775.h"
enum sensor_access { access_direct, access_asuswmi };
static const char * const nct6775_sio_names[] __initconst = {
[nct6106] = "NCT6106D",
[nct6116] = "NCT6116D",
[nct6775] = "NCT6775F",
[nct6776] = "NCT6776D/F",
[nct6779] = "NCT6779D",
[nct6791] = "NCT6791D",
[nct6792] = "NCT6792D",
[nct6793] = "NCT6793D",
[nct6795] = "NCT6795D",
[nct6796] = "NCT6796D",
[nct6797] = "NCT6797D",
[nct6798] = "NCT6798D",
[nct6799] = "NCT6796D-S/NCT6799D-R",
};
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
static unsigned short fan_debounce;
module_param(fan_debounce, ushort, 0);
MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define DRVNAME "nct6775"
#define NCT6775_PORT_CHIPID 0x58
/*
* ISA constants
*/
#define IOREGION_ALIGNMENT (~7)
#define IOREGION_OFFSET 5
#define IOREGION_LENGTH 2
#define ADDR_REG_OFFSET 0
#define DATA_REG_OFFSET 1
/*
* Super-I/O constants and functions
*/
#define NCT6775_LD_ACPI 0x0a
#define NCT6775_LD_HWM 0x0b
#define NCT6775_LD_VID 0x0d
#define NCT6775_LD_12 0x12
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_NCT6106_ID 0xc450
#define SIO_NCT6116_ID 0xd280
#define SIO_NCT6775_ID 0xb470
#define SIO_NCT6776_ID 0xc330
#define SIO_NCT6779_ID 0xc560
#define SIO_NCT6791_ID 0xc800
#define SIO_NCT6792_ID 0xc910
#define SIO_NCT6793_ID 0xd120
#define SIO_NCT6795_ID 0xd350
#define SIO_NCT6796_ID 0xd420
#define SIO_NCT6797_ID 0xd450
#define SIO_NCT6798_ID 0xd428
#define SIO_NCT6799_ID 0xd800
#define SIO_ID_MASK 0xFFF8
/*
* Control registers
*/
#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
struct nct6775_sio_data {
int sioreg;
int ld;
enum kinds kind;
enum sensor_access access;
/* superio_() callbacks */
void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
int (*sio_enter)(struct nct6775_sio_data *sio_data);
void (*sio_exit)(struct nct6775_sio_data *sio_data);
};
#define ASUSWMI_METHOD "WMBD"
#define ASUSWMI_METHODID_RSIO 0x5253494F
#define ASUSWMI_METHODID_WSIO 0x5753494F
#define ASUSWMI_METHODID_RHWM 0x5248574D
#define ASUSWMI_METHODID_WHWM 0x5748574D
#define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE
#define ASUSWMI_DEVICE_HID "PNP0C14"
#define ASUSWMI_DEVICE_UID "ASUSWMI"
#define ASUSMSI_DEVICE_UID "AsusMbSwInterface"
#if IS_ENABLED(CONFIG_ACPI)
/*
* ASUS boards have only one device with WMI "WMBD" method and have provided
* access to only one SuperIO chip at 0x0290.
*/
static struct acpi_device *asus_acpi_dev;
#endif
static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
{
#if IS_ENABLED(CONFIG_ACPI)
acpi_handle handle = acpi_device_handle(asus_acpi_dev);
u32 args = bank | (reg << 8) | (val << 16);
struct acpi_object_list input;
union acpi_object params[3];
unsigned long long result;
acpi_status status;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = 0;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = method_id;
params[2].type = ACPI_TYPE_BUFFER;
params[2].buffer.length = sizeof(args);
params[2].buffer.pointer = (void *)&args;
input.count = 3;
input.pointer = params;
status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, &input, &result);
if (ACPI_FAILURE(status))
retur
|