1
0
Fork 0

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 <linux@roeck-us.net>
hifive-unleashed-5.1
Guenter Roeck 2015-02-19 09:21:29 -08:00
parent 449278d924
commit 91918d13eb
1 changed files with 61 additions and 17 deletions

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