From 91918d13eb17b8c11a9b6b76bfdd7cc0efab4f50 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 19 Feb 2015 09:21:29 -0800 Subject: [PATCH] hwmon: (nct6683) Add basic support for NCT6683 on Mitac boards Mitac microcode differs from Intel microcode. One key difference is that pwm values can be written. Detect vendor from customer ID field and no longer use DMI data to identify which microcode is running on the chip. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6683.c | 78 ++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 6cf392c22803..559c596b24f9 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -29,7 +29,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include +#include #include #include #include @@ -45,7 +45,7 @@ enum kinds { nct6683 }; static bool force; module_param(force, bool, 0); -MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards"); +MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors"); static const char * const nct6683_device_names[] = { "nct6683", @@ -141,6 +141,7 @@ superio_exit(int ioreg) #define NCT6683_REG_MON(x) (0x100 + (x) * 2) #define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2) #define NCT6683_REG_PWM(x) (0x160 + (x)) +#define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x)) #define NCT6683_REG_MON_STS(x) (0x174 + (x)) #define NCT6683_REG_IDLE(x) (0x178 + (x)) @@ -165,8 +166,13 @@ superio_exit(int ioreg) #define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */ +#define NCT6683_REG_FAN_CFG_CTRL 0xa01 +#define NCT6683_FAN_CFG_REQ 0x80 +#define NCT6683_FAN_CFG_DONE 0x40 + #define NCT6683_REG_CUSTOMER_ID 0x602 #define NCT6683_CUSTOMER_ID_INTEL 0x805 +#define NCT6683_CUSTOMER_ID_MITAC 0xa0e #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -560,6 +566,7 @@ static int get_temp_reg(struct nct6683_data *data, int nr, int index) break; } break; + case NCT6683_CUSTOMER_ID_MITAC: default: switch (nr) { default: @@ -919,7 +926,29 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%d\n", data->pwm[index]); } -SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0); +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6683_data *data = dev_get_drvdata(dev); + int index = sattr->index; + unsigned long val; + + if (kstrtoul(buf, 10, &val) || val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ); + usleep_range(1000, 2000); + nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val); + nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE); + mutex_unlock(&data->update_lock); + + return count; +} + +SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0); static umode_t nct6683_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) @@ -931,6 +960,10 @@ static umode_t nct6683_pwm_is_visible(struct kobject *kobj, if (!(data->have_pwm & (1 << pwm))) return 0; + /* Only update pwm values for Mitac boards */ + if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC) + return attr->mode | S_IWUSR; + return attr->mode; } @@ -1171,6 +1204,7 @@ static int nct6683_probe(struct platform_device *pdev) struct device *hwmon_dev; struct resource *res; int groups = 0; + char build[16]; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) @@ -1188,6 +1222,17 @@ static int nct6683_probe(struct platform_device *pdev) data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID); + /* By default only instantiate driver if the customer ID is known */ + switch (data->customer_id) { + case NCT6683_CUSTOMER_ID_INTEL: + break; + case NCT6683_CUSTOMER_ID_MITAC: + break; + default: + if (!force) + return -ENODEV; + } + nct6683_init_device(data); nct6683_setup_fans(data); nct6683_setup_sensors(data); @@ -1231,13 +1276,22 @@ static int nct6683_probe(struct platform_device *pdev) } data->groups[groups++] = &nct6683_group_other; - dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n", + if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL) + scnprintf(build, sizeof(build), "%02x/%02x/%02x", + nct6683_read(data, NCT6683_REG_BUILD_MONTH), + nct6683_read(data, NCT6683_REG_BUILD_DAY), + nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + else + scnprintf(build, sizeof(build), "%02d/%02d/%02d", + nct6683_read(data, NCT6683_REG_BUILD_MONTH), + nct6683_read(data, NCT6683_REG_BUILD_DAY), + nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + + dev_info(dev, "%s EC firmware version %d.%d build %s\n", nct6683_chip_names[data->kind], nct6683_read(data, NCT6683_REG_VERSION_HI), nct6683_read(data, NCT6683_REG_VERSION_LO), - nct6683_read(data, NCT6683_REG_BUILD_MONTH), - nct6683_read(data, NCT6683_REG_BUILD_DAY), - nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + build); hwmon_dev = devm_hwmon_device_register_with_groups(dev, nct6683_device_names[data->kind], data, data->groups); @@ -1293,20 +1347,10 @@ static struct platform_driver nct6683_driver = { static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) { - const char *board_vendor; int addr; u16 val; int err; - /* - * Only run on Intel boards unless the 'force' module parameter is set - */ - if (!force) { - board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - if (!board_vendor || strcmp(board_vendor, "Intel Corporation")) - return -ENODEV; - } - err = superio_enter(sioaddr); if (err) return err; -- 2.30.2