|
@@ -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;
|