Skip to content

Commit

Permalink
hwmon: (nct6683) Add basic support for NCT6683 on Mitac boards
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Guenter Roeck committed Jan 9, 2016
1 parent 449278d commit 91918d1
Showing 1 changed file with 61 additions and 17 deletions.
78 changes: 61 additions & 17 deletions drivers/hwmon/nct6683.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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",
Expand Down Expand Up @@ -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))
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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))
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 91918d1

Please sign in to comment.