hwmon: (nct6683) Add basic support for NCT6683 on Mitac boards
authorGuenter Roeck <linux@roeck-us.net>
Thu, 19 Feb 2015 17:21:29 +0000 (09:21 -0800)
committerGuenter Roeck <linux@roeck-us.net>
Sat, 9 Jan 2016 15:31:58 +0000 (07:31 -0800)
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 <linux@roeck-us.net>
drivers/hwmon/nct6683.c

index 6cf392c22803b95ed965fa291ca9cd828efe6ca7..559c596b24f9b916d9f1fe6b85c7eabc8f1868d9 100644 (file)
@@ -29,7 +29,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/acpi.h>
-#include <linux/dmi.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -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;