diff options
author | Rudolf Marek <r.marek@assembler.cz> | 2006-12-12 18:18:30 +0100 |
---|---|---|
committer | Jean Delvare <khali@arrakis.delvare> | 2006-12-12 18:18:30 +0100 |
commit | 6800c3d027b4458a6fb5ab78064c2e3b7da2f94f (patch) | |
tree | 3ce7ac734ea3bde22e1153372c4dbdfb6aad32a8 | |
parent | 7188cc66b4facf749e4fc0a44165b06716e1b621 (diff) |
hwmon: New Winbond W83793 hardware monitoring driver
Add support for the W83793 hardware monitoring chip. This driver
was originally contributed by Yuan Mu of Winbond Electronics Corp.
Signed-off-by: Rudolf Marek <r.marek@assembler.cz>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/w83793.c | 1609 |
3 files changed, 1620 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5a0763197e2..23b7eac563b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -488,6 +488,16 @@ config SENSORS_W83792D This driver can also be built as a module. If so, the module will be called w83792d. +config SENSORS_W83793 + tristate "Winbond W83793" + depends on HWMON && I2C && EXPERIMENTAL + help + If you say yes here you get support for the Winbond W83793 + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called w83793. + config SENSORS_W83L785TS tristate "Winbond W83L785TS-S" depends on HWMON && I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 95f3fc56118..8b0a8b54a36 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_HWMON_VID) += hwmon-vid.o obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o +obj-$(CONFIG_SENSORS_W83793) += w83793.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c new file mode 100644 index 00000000000..c12ac5abc2b --- /dev/null +++ b/drivers/hwmon/w83793.c @@ -0,0 +1,1609 @@ +/* + w83793.c - Linux kernel driver for hardware monitoring + Copyright (C) 2006 Winbond Electronics Corp. + Yuan Mu + Rudolf Marek <r.marek@assembler.cz> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation - version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + w83793 10 12 8 6 0x7b 0x5ca3 yes no +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-vid.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(w83793); +I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +static int reset; +module_param(reset, bool, 0); +MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); + +/* + Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved + as ID, Bank Select registers +*/ +#define W83793_REG_BANKSEL 0x00 +#define W83793_REG_VENDORID 0x0d +#define W83793_REG_CHIPID 0x0e +#define W83793_REG_DEVICEID 0x0f + +#define W83793_REG_CONFIG 0x40 +#define W83793_REG_MFC 0x58 +#define W83793_REG_FANIN_CTRL 0x5c +#define W83793_REG_FANIN_SEL 0x5d +#define W83793_REG_I2C_ADDR 0x0b +#define W83793_REG_I2C_SUBADDR 0x0c +#define W83793_REG_VID_INA 0x05 +#define W83793_REG_VID_INB 0x06 +#define W83793_REG_VID_LATCHA 0x07 +#define W83793_REG_VID_LATCHB 0x08 +#define W83793_REG_VID_CTRL 0x59 + +static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; + +#define TEMP_READ 0 +#define TEMP_CRIT 1 +#define TEMP_CRIT_HYST 2 +#define TEMP_WARN 3 +#define TEMP_WARN_HYST 4 +/* only crit and crit_hyst affect real-time alarm status + current crit crit_hyst warn warn_hyst */ +static u16 W83793_REG_TEMP[][5] = { + {0x1c, 0x78, 0x79, 0x7a, 0x7b}, + {0x1d, 0x7c, 0x7d, 0x7e, 0x7f}, + {0x1e, 0x80, 0x81, 0x82, 0x83}, + {0x1f, 0x84, 0x85, 0x86, 0x87}, + {0x20, 0x88, 0x89, 0x8a, 0x8b}, + {0x21, 0x8c, 0x8d, 0x8e, 0x8f}, +}; + +#define W83793_REG_TEMP_LOW_BITS 0x22 + +#define W83793_REG_BEEP(index) (0x53 + (index)) +#define W83793_REG_ALARM(index) (0x4b + (index)) + +#define W83793_REG_CLR_CHASSIS 0x4a /* SMI MASK4 */ +#define W83793_REG_IRQ_CTRL 0x50 +#define W83793_REG_OVT_CTRL 0x51 +#define W83793_REG_OVT_BEEP 0x52 + +#define IN_READ 0 +#define IN_MAX 1 +#define IN_LOW 2 +static const u16 W83793_REG_IN[][3] = { + /* Current, High, Low */ + {0x10, 0x60, 0x61}, /* Vcore A */ + {0x11, 0x62, 0x63}, /* Vcore B */ + {0x12, 0x64, 0x65}, /* Vtt */ + {0x14, 0x6a, 0x6b}, /* VSEN1 */ + {0x15, 0x6c, 0x6d}, /* VSEN2 */ + {0x16, 0x6e, 0x6f}, /* +3VSEN */ + {0x17, 0x70, 0x71}, /* +12VSEN */ + {0x18, 0x72, 0x73}, /* 5VDD */ + {0x19, 0x74, 0x75}, /* 5VSB */ + {0x1a, 0x76, 0x77}, /* VBAT */ +}; + +/* Low Bits of Vcore A/B Vtt Read/High/Low */ +static const u16 W83793_REG_IN_LOW_BITS[] = { 0x1b, 0x68, 0x69 }; +static u8 scale_in[] = { 2, 2, 2, 16, 16, 16, 8, 24, 24, 16 }; + +#define W83793_REG_FAN(index) (0x23 + 2 * (index)) /* High byte */ +#define W83793_REG_FAN_MIN(index) (0x90 + 2 * (index)) /* High byte */ + +#define W83793_REG_PWM_DEFAULT 0xb2 +#define W83793_REG_PWM_ENABLE 0x207 +#define W83793_REG_PWM_UPTIME 0xc3 /* Unit in 0.1 second */ +#define W83793_REG_PWM_DOWNTIME 0xc4 /* Unit in 0.1 second */ +#define W83793_REG_TEMP_CRITICAL 0xc5 + +#define PWM_DUTY 0 +#define PWM_START 1 +#define PWM_NONSTOP 2 +#define W83793_REG_PWM(index, nr) (((nr) == 0 ? 0xb3 : \ + (nr) == 1 ? 0x220 : 0x218) + (index)) + +/* bit field, fan1 is bit0, fan2 is bit1 ... */ +#define W83793_REG_TEMP_FAN_MAP(index) (0x201 + (index)) +#define W83793_REG_TEMP_TOL(index) (0x208 + (index)) +#define W83793_REG_TEMP_CRUISE(index) (0x210 + (index)) +#define W83793_REG_PWM_STOP_TIME(index) (0x228 + (index)) +#define W83793_REG_SF2_TEMP(index, nr) (0x230 + ((index) << 4) + (nr)) +#define W83793_REG_SF2_PWM(index, nr) (0x238 + ((index) << 4) + (nr)) + +static inline unsigned long FAN_FROM_REG(u16 val) +{ + if ((val >= 0xfff) || (val == 0)) + return 0; + return (1350000UL / val); +} + +static inline u16 FAN_TO_REG(long rpm) +{ + if (rpm <= 0) + return 0x0fff; + return SENSORS_LIMIT((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); +} + +static inline unsigned long TIME_FROM_REG(u8 reg) +{ + return (reg * 100); +} + +static inline u8 TIME_TO_REG(unsigned long val) +{ + return SENSORS_LIMIT((val + 50) / 100, 0, 0xff); +} + +static inline long TEMP_FROM_REG(s8 reg) +{ + return (reg * 1000); +} + +static inline s8 TEMP_TO_REG(long val, s8 min, s8 max) +{ + return SENSORS_LIMIT((val + (val < 0 ? -500 : 500)) / 1000, min, max); +} + +struct w83793_data { + struct i2c_client client; + struct i2c_client *lm75[2]; + struct class_device *class_dev; + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_nonvolatile; /* In jiffies, last time we update the + nonvolatile registers */ + + u8 bank; + u8 vrm; + u8 vid[2]; + u8 in[10][3]; /* Register value, read/high/low */ + u8 in_low_bits[3]; /* Additional resolution for VCore A/B Vtt */ + + u16 has_fan; /* Only fan1- fan5 has own pins */ + u16 fan[12]; /* Register value combine */ + u16 fan_min[12]; /* Register value combine */ + + s8 temp[6][5]; /* current, crit, crit_hyst,warn, warn_hyst */ + u8 temp_low_bits; /* Additional resolution TD1-TD4 */ + u8 temp_mode[2]; /* byte 0: Temp D1-D4 mode each has 2 bits + byte 1: Temp R1,R2 mode, each has 1 bit */ + u8 temp_critical; /* If reached all fan will be at full speed */ + u8 temp_fan_map[6]; /* Temp controls which pwm fan, bit field */ + + u8 has_pwm; + u8 pwm_enable; /* Register value, each Temp has 1 bit */ + u8 pwm_uptime; /* Register value */ + u8 pwm_downtime; /* Register value */ + u8 pwm_default; /* All fan default pwm, next poweron valid */ + u8 pwm[8][3]; /* Register value */ + u8 pwm_stop_time[8]; + u8 temp_cruise[6]; + + u8 alarms[5]; /* realtime status registers */ + u8 beeps[5]; + u8 beep_enable; + u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */ + u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */ + u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */ +}; + +static u8 w83793_read_value(struct i2c_client *client, u16 reg); +static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); +static int w83793_attach_adapter(struct i2c_adapter *adapter); +static int w83793_detect(struct i2c_adapter *adapter, int address, int kind); +static int w83793_detach_client(struct i2c_client *client); +static void w83793_init_client(struct i2c_client *client); +static void w83793_update_nonvolatile(struct device *dev); +static struct w83793_data *w83793_update_device(struct device *dev); + +static struct i2c_driver w83793_driver = { + .driver = { + .name = "w83793", + }, + .attach_adapter = w83793_attach_adapter, + .detach_client = w83793_detach_client, +}; + +static ssize_t +show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", data->vrm); +} + +static ssize_t +show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83793_data *data = w83793_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + + return sprintf(buf, "%d\n", vid_from_reg(data->vid[index], data->vrm)); +} + +static ssize_t +store_vrm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} + +#define ALARM_STATUS 0 +#define BEEP_ENABLE 1 +static ssize_t +show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83793_data *data = w83793_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index >> 3; + int bit = sensor_attr->index & 0x07; + u8 val; + + if (ALARM_STATUS == nr) { + val = (data->alarms[index] >> (bit)) & 1; + } else { /* BEEP_ENABLE */ + val = (data->beeps[index] >> (bit)) & 1; + } + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index >> 3; + int shift = sensor_attr->index & 0x07; + u8 beep_bit = 1 << shift; + u8 val; + + val = simple_strtoul(buf, NULL, 10); + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beeps[index] = w83793_read_value(client, W83793_REG_BEEP(index)); + data->beeps[index] &= ~beep_bit; + data->beeps[index] |= val << shift; + w83793_write_value(client, W83793_REG_BEEP(index), data->beeps[index]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_beep_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83793_data *data = w83793_update_device(dev); + return sprintf(buf, "%u\n", (data->beep_enable >> 1) & 0x01); +} + +static ssize_t +store_beep_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + u8 val = simple_strtoul(buf, NULL, 10); + + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP) + & 0xfd; + data->beep_enable |= val << 1; + w83793_write_value(client, W83793_REG_OVT_BEEP, data->beep_enable); + mutex_unlock(&data->update_lock); + + return count; +} + +/* Write any value to clear chassis alarm */ +static ssize_t +store_chassis_clear(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + u8 val; + + mutex_lock(&data->update_lock); + val = w83793_read_value(client, W83793_REG_CLR_CHASSIS); + val |= 0x80; + w83793_write_value(client, W83793_REG_CLR_CHASSIS, val); + mutex_unlock(&data->update_lock); + return count; +} + +#define FAN_INPUT 0 +#define FAN_MIN 1 +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + u16 val; + + if (FAN_INPUT == nr) { + val = data->fan[index] & 0x0fff; + } else { + val = data->fan_min[index] & 0x0fff; + } + + return sprintf(buf, "%lu\n", FAN_FROM_REG(val)); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + u16 val = FAN_TO_REG(simple_strtoul(buf, NULL, 10)); + + mutex_lock(&data->update_lock); + data->fan_min[index] = val; + w83793_write_value(client, W83793_REG_FAN_MIN(index), + (val >> 8) & 0xff); + w83793_write_value(client, W83793_REG_FAN_MIN(index) + 1, val & 0xff); + mutex_unlock(&data->update_lock); + + return count; +} + +#define PWM_DUTY 0 +#define PWM_START 1 +#define PWM_NONSTOP 2 +#define PWM_STOP_TIME 3 +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + struct w83793_data *data = w83793_update_device(dev); + u16 val; + int nr = sensor_attr->nr; + int index = sensor_attr->index; + + if (PWM_STOP_TIME == nr) + val = TIME_FROM_REG(data->pwm_stop_time[index]); + else + val = (data->pwm[index][nr] & 0x3f) << 2; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u8 val; + + mutex_lock(&data->update_lock); + if (PWM_STOP_TIME == nr) { + val = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + data->pwm_stop_time[index] = val; + w83793_write_value(client, W83793_REG_PWM_STOP_TIME(index), + val); + } else { + val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) + >> 2; + data->pwm[index][nr] = + w83793_read_value(client, W83793_REG_PWM(index, nr)) & 0xc0; + data->pwm[index][nr] |= val; + w83793_write_value(client, W83793_REG_PWM(index, nr), + data->pwm[index][nr]); + } + + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + long temp = TEMP_FROM_REG(data->temp[index][nr]); + + if (TEMP_READ == nr && index < 4) { /* Only TD1-TD4 have low bits */ + int low = ((data->temp_low_bits >> (index * 2)) & 0x03) * 250; + temp += temp > 0 ? low : -low; + } + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + long tmp = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->temp[index][nr] = TEMP_TO_REG(tmp, -128, 127); + w83793_write_value(client, W83793_REG_TEMP[index][nr], + data->temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +/* + TD1-TD4 + each has 4 mode:(2 bits) + 0: Stop monitor + 1: Use internal temp sensor(default) + 2: Use sensor in AMD CPU and get result by AMDSI + 3: Use sensor in Intel CPU and get result by PECI + + TR1-TR2 + each has 2 mode:(1 bit) + 0: Disable temp sensor monitor + 1: To enable temp sensors monitor +*/ + +/* 0 disable, 5 AMDSI, 6 PECI */ +static u8 TO_TEMP_MODE[] = { 0, 0, 5, 6 }; + +static ssize_t +show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83793_data *data = w83793_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + u8 mask = (index < 4) ? 0x03 : 0x01; + u8 shift = (index < 4) ? (2 * index) : (index - 4); + u8 tmp; + index = (index < 4) ? 0 : 1; + + tmp = (data->temp_mode[index] >> shift) & mask; + + /* for the internal sensor, found out if diode or thermistor */ + if (tmp == 1) { + tmp = index == 0 ? 3 : 4; + } else { + tmp = TO_TEMP_MODE[tmp]; + } + + return sprintf(buf, "%d\n", tmp); +} + +static ssize_t +store_temp_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + u8 mask = (index < 4) ? 0x03 : 0x01; + u8 shift = (index < 4) ? (2 * index) : (index - 4); + u8 val = simple_strtoul(buf, NULL, 10); + + /* transform the sysfs interface values into table above */ + if ((val == 5 || val == 6) && (index < 4)) { + val -= 3; + } else if ((val == 3 && index < 4) + || (val == 4 && index >= 4) + || val == 0) { + /* transform diode or thermistor into internal enable */ + val = !!val; + } else { + return -EINVAL; + } + + index = (index < 4) ? 0 : 1; + mutex_lock(&data->update_lock); + data->temp_mode[index] = + w83793_read_value(client, W83793_REG_TEMP_MODE[index]); + data->temp_mode[index] &= ~(mask << shift); + data->temp_mode[index] |= val << shift; + w83793_write_value(client, W83793_REG_TEMP_MODE[index], + data->temp_mode[index]); + mutex_unlock(&data->update_lock); + + return count; +} + +#define SETUP_PWM_DEFAULT 0 +#define SETUP_PWM_UPTIME 1 /* Unit in 0.1s */ +#define SETUP_PWM_DOWNTIME 2 /* Unit in 0.1s */ +#define SETUP_TEMP_CRITICAL 3 +static ssize_t +show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct w83793_data *data = w83793_update_device(dev); + u32 val = 0; + + if (SETUP_PWM_DEFAULT == nr) { + val = (data->pwm_default & 0x3f) << 2; + } else if (SETUP_PWM_UPTIME == nr) { + val = TIME_FROM_REG(data->pwm_uptime); + } else if (SETUP_PWM_DOWNTIME == nr) { + val = TIME_FROM_REG(data->pwm_downtime); + } else if (SETUP_TEMP_CRITICAL == nr) { + val = TEMP_FROM_REG(data->temp_critical & 0x7f); + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_sf_setup(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + if (SETUP_PWM_DEFAULT == nr) { + data->pwm_default = + w83793_read_value(client, W83793_REG_PWM_DEFAULT) & 0xc0; + data->pwm_default |= SENSORS_LIMIT(simple_strtoul(buf, NULL, + 10), + 0, 0xff) >> 2; + w83793_write_value(client, W83793_REG_PWM_DEFAULT, + data->pwm_default); + } else if (SETUP_PWM_UPTIME == nr) { + data->pwm_uptime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + data->pwm_uptime += data->pwm_uptime == 0 ? 1 : 0; + w83793_write_value(client, W83793_REG_PWM_UPTIME, + data->pwm_uptime); + } else if (SETUP_PWM_DOWNTIME == nr) { + data->pwm_downtime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + data->pwm_downtime += data->pwm_downtime == 0 ? 1 : 0; + w83793_write_value(client, W83793_REG_PWM_DOWNTIME, + data->pwm_downtime); + } else { /* SETUP_TEMP_CRITICAL */ + data->temp_critical = + w83793_read_value(client, W83793_REG_TEMP_CRITICAL) & 0x80; + data->temp_critical |= TEMP_TO_REG(simple_strtol(buf, NULL, 10), + 0, 0x7f); + w83793_write_value(client, W83793_REG_TEMP_CRITICAL, + data->temp_critical); + } + + mutex_unlock(&data->update_lock); + return count; +} + +/* + Temp SmartFan control + TEMP_FAN_MAP + Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1... + It's possible two or more temp channels control the same fan, w83793 + always prefers to pick the most critical request and applies it to + the related Fan. + It's possible one fan is not in any mapping of 6 temp channels, this + means the fan is manual mode + + TEMP_PWM_ENABLE + Each temp channel has its own SmartFan mode, and temp channel + control fans that are set by TEMP_FAN_MAP + 0: SmartFanII mode + 1: Thermal Cruise Mode + + TEMP_CRUISE + Target temperature in thermal cruise mode, w83793 will try to turn + fan speed to keep the temperature of target device around this + temperature. + + TEMP_TOLERANCE + If Temp higher or lower than target with this tolerance, w83793 + will take actions to speed up or slow down the fan to keep the + temperature within the tolerance range. +*/ + +#define TEMP_FAN_MAP 0 +#define TEMP_PWM_ENABLE 1 +#define TEMP_CRUISE 2 +#define TEMP_TOLERANCE 3 +static ssize_t +show_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + u32 val; + + if (TEMP_FAN_MAP == nr) { + val = data->temp_fan_map[index]; + } else if (TEMP_PWM_ENABLE == nr) { + /* +2 to transfrom into 2 and 3 to conform with sysfs intf */ + val = ((data->pwm_enable >> index) & 0x01) + 2; + } else if (TEMP_CRUISE == nr) { + val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f); + } else { /* TEMP_TOLERANCE */ + val = data->tolerance[index >> 1] >> ((index & 0x01) ? 4 : 0); + val = TEMP_FROM_REG(val & 0x0f); + } + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_sf_ctrl(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + u32 val; + + mutex_lock(&data->update_lock); + if (TEMP_FAN_MAP == nr) { + val = simple_strtoul(buf, NULL, 10) & 0xff; + w83793_write_value(client, W83793_REG_TEMP_FAN_MAP(index), val); + data->temp_fan_map[index] = val; + } else if (TEMP_PWM_ENABLE == nr) { + val = simple_strtoul(buf, NULL, 10); + if (2 == val || 3 == val) { + data->pwm_enable = + w83793_read_value(client, W83793_REG_PWM_ENABLE); + if (val - 2) + data->pwm_enable |= 1 << index; + else + data->pwm_enable &= ~(1 << index); + w83793_write_value(client, W83793_REG_PWM_ENABLE, + data->pwm_enable); + } else { + mutex_unlock(&data->update_lock); + return -EINVAL; + } + } else if (TEMP_CRUISE == nr) { + data->temp_cruise[index] = + w83793_read_value(client, W83793_REG_TEMP_CRUISE(index)); + val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); + data->temp_cruise[index] &= 0x80; + data->temp_cruise[index] |= val; + + w83793_write_value(client, W83793_REG_TEMP_CRUISE(index), + data->temp_cruise[index]); + } else { /* TEMP_TOLERANCE */ + int i = index >> 1; + u8 shift = (index & 0x01) ? 4 : 0; + data->tolerance[i] = + w83793_read_value(client, W83793_REG_TEMP_TOL(i)); + + val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x0f); + data->tolerance[i] &= ~(0x0f << shift); + data->tolerance[i] |= val << shift; + w83793_write_value(client, W83793_REG_TEMP_TOL(i), + data->tolerance[i]); + } + + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_sf2_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + + return sprintf(buf, "%d\n", (data->sf2_pwm[index][nr] & 0x3f) << 2); +} + +static ssize_t +store_sf2_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) >> 2; + + mutex_lock(&data->update_lock); + data->sf2_pwm[index][nr] = + w83793_read_value(client, W83793_REG_SF2_PWM(index, nr)) & 0xc0; + data->sf2_pwm[index][nr] |= val; + w83793_write_value(client, W83793_REG_SF2_PWM(index, nr), + data->sf2_pwm[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_sf2_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + + return sprintf(buf, "%ld\n", + TEMP_FROM_REG(data->sf2_temp[index][nr] & 0x7f)); +} + +static ssize_t +store_sf2_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u8 val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); + + mutex_lock(&data->update_lock); + data->sf2_temp[index][nr] = + w83793_read_value(client, W83793_REG_SF2_TEMP(index, nr)) & 0x80; + data->sf2_temp[index][nr] |= val; + w83793_write_value(client, W83793_REG_SF2_TEMP(index, nr), + data->sf2_temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +/* only Vcore A/B and Vtt have additional 2 bits precision */ +static ssize_t +show_in(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83793_data *data = w83793_update_device(dev); + u16 val = data->in[index][nr]; + + if (index < 3) { + val <<= 2; + val += (data->in_low_bits[nr] >> (index * 2)) & 0x3; + } + return sprintf(buf, "%d\n", val * scale_in[index]); +} + +static ssize_t +store_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83793_data *data = i2c_get_clientdata(client); + u32 val; + + val = + (simple_strtoul(buf, NULL, 10) + + scale_in[index] / 2) / scale_in[index]; + mutex_lock(&data->update_lock); + if (index > 2) { + val = SENSORS_LIMIT(val, 0, 255); + } else { + val = SENSORS_LIMIT(val, 0, 0x3FF); + data->in_low_bits[nr] = + w83793_read_value(client, W83793_REG_IN_LOW_BITS[nr]); + data->in_low_bits[nr] &= ~(0x03 << (2 * index)); + data->in_low_bits[nr] |= (val & 0x03) << (2 * index); + w83793_write_value(client, W83793_REG_IN_LOW_BITS[nr], + data->in_low_bits[nr]); + val >>= 2; + } + data->in[index][nr] = val; + w83793_write_value(client, W83793_REG_IN[index][nr], + data->in[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +#define NOT_USED -1 + +#define SENSOR_ATTR_IN(index) \ + SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ + IN_READ, index), \ + SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_MAX, index), \ + SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_LOW, index), \ + SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + ((index > 2) ? 1 : 0)), \ + SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, \ + index + ((index > 2) ? 1 : 0)) + +#define SENSOR_ATTR_FAN(index) \ + SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + 17), \ + SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 17), \ + SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ + NULL, FAN_INPUT, index - 1), \ + SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ + show_fan, store_fan_min, FAN_MIN, index - 1) + +#define SENSOR_ATTR_PWM(index) \ + SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ + store_pwm, PWM_DUTY, index - 1), \ + SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ + SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_START, index - 1), \ + SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_STOP_TIME, index - 1) + +#define SENSOR_ATTR_TEMP(index) \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \ + show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ + SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ + NULL, TEMP_READ, index - 1), \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_CRIT, index - 1), \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_WARN, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ + show_alarm_beep, NULL, ALARM_STATUS, index + 11), \ + SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 11), \ + SENSOR_ATTR_2(temp##index##_auto_channels_pwm, \ + S_IRUGO | S_IWUSR, show_sf_ctrl, store_sf_ctrl, \ + TEMP_FAN_MAP, index - 1), \ + SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \ + show_sf_ctrl, store_sf_ctrl, TEMP_PWM_ENABLE, \ + index - 1), \ + SENSOR_ATTR_2(thermal_cruise##index, S_IRUGO | S_IWUSR, \ + show_sf_ctrl, store_sf_ctrl, TEMP_CRUISE, index - 1), \ + SENSOR_ATTR_2(tolerance##index, S_IRUGO | S_IWUSR, show_sf_ctrl,\ + store_sf_ctrl, TEMP_TOLERANCE, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \ + show_sf2_pwm, store_sf2_pwm, 6, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ + show_sf2_temp, store_sf2_temp, 6, index - 1) + +static struct sensor_device_attribute_2 w83793_sensor_attr_2[] = { + SENSOR_ATTR_IN(0), + SENSOR_ATTR_IN(1), + SENSOR_ATTR_IN(2), + SENSOR_ATTR_IN(3), + SENSOR_ATTR_IN(4), + SENSOR_ATTR_IN(5), + SENSOR_ATTR_IN(6), + SENSOR_ATTR_IN(7), + SENSOR_ATTR_IN(8), + SENSOR_ATTR_IN(9), + SENSOR_ATTR_TEMP(1), + SENSOR_ATTR_TEMP(2), + SENSOR_ATTR_TEMP(3), + SENSOR_ATTR_TEMP(4), + SENSOR_ATTR_TEMP(5), + SENSOR_ATTR_TEMP(6), + SENSOR_ATTR_FAN(1), + SENSOR_ATTR_FAN(2), + SENSOR_ATTR_FAN(3), + SENSOR_ATTR_FAN(4), + SENSOR_ATTR_FAN(5), + SENSOR_ATTR_PWM(1), + SENSOR_ATTR_PWM(2), + SENSOR_ATTR_PWM(3), +}; + +/* Fan6-Fan12 */ +static struct sensor_device_attribute_2 w83793_left_fan[] = { + SENSOR_ATTR_FAN(6), + SENSOR_ATTR_FAN(7), |