// SPDX-License-Identifier: GPL-2.0-only /* * w83793.c - Linux kernel driver for hardware monitoring * Copyright (C) 2006 Winbond Electronics Corp. * Yuan Mu * Rudolf Marek <r.marek@assembler.cz> * Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG. * Watchdog driver part * (Based partially on fschmd driver, * Copyright 2007-2008 by Hans de Goede)
*/
/* * Supports following chips: * * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA * w83793 10 12 8 6 0x7b 0x5ca3 yes no
*/
#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
struct w83793_data { struct device *hwmon_dev; struct mutex update_lock; bool valid; /* true if following fields are valid */ unsignedlong last_updated; /* In jiffies */ unsignedlong last_nonvolatile; /* In jiffies, last time we update the * nonvolatile registers
*/
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 has_temp;
u8 has_vid;
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 */
/* watchdog */ struct i2c_client *client; struct mutex watchdog_lock; struct list_head list; /* member of the watchdog_data_list */ struct kref kref; struct miscdevice watchdog_miscdev; unsignedlong watchdog_is_open; char watchdog_expect_close; char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ unsignedint watchdog_caused_reboot; int watchdog_timeout; /* watchdog timeout in minutes */
};
/* * Somewhat ugly :( global data pointer list with all devices, so that * we can find our device data as when using misc_register. There is no * other method to get to one's device data from the open file-op and * for usage in the reboot notifier callback.
*/ static LIST_HEAD(watchdog_data_list);
/* Note this lock not only protect list access, but also data.kref access */ static DEFINE_MUTEX(watchdog_data_mutex);
/* * Release our data struct when we're detached from the i2c client *and* all * references to our watchdog device are released
*/ staticvoid w83793_release_resources(struct kref *ref)
{ struct w83793_data *data = container_of(ref, struct w83793_data, kref);
kfree(data);
}
/* * TD1-TD4 * each has 4 mode:(2 bits) * 0: Stop monitor * 1: Use internal temp sensor(default) * 2: Reserved * 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
*/
/* * 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.
*/
/* * We get called from drivers/char/misc.c with misc_mtx hold, and we * call misc_register() from w83793_probe() with watchdog_data_mutex * hold, as misc_register() takes the misc_mtx lock, this is a possible * deadlock, so we use mutex_trylock here.
*/ if (!mutex_trylock(&watchdog_data_mutex)) return -ERESTARTSYS;
list_for_each_entry(pos, &watchdog_data_list, list) { if (pos->watchdog_miscdev.minor == iminor(inode)) {
data = pos; break;
}
}
/* Check, if device is already open */
watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
/* * Increase data reference counter (if not already done). * Note we can never not have found data, so we don't check for this
*/ if (!watchdog_is_open)
kref_get(&data->kref);
mutex_unlock(&watchdog_data_mutex);
/* Check, if device is already open and possibly issue error */ if (watchdog_is_open) return -EBUSY;
/* Enable Soft Watchdog */
watchdog_enable(data);
/* Store pointer to data into filp's private data */
filp->private_data = data;
int val, ret = 0; struct w83793_data *data = filp->private_data;
switch (cmd) { case WDIOC_GETSUPPORT: if (!nowayout)
ident.options |= WDIOF_MAGICCLOSE; if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
ret = -EFAULT; break;
case WDIOC_GETSTATUS:
val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0;
ret = put_user(val, (int __user *)arg); break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(0, (int __user *)arg); break;
case WDIOC_KEEPALIVE:
ret = watchdog_trigger(data); break;
case WDIOC_GETTIMEOUT:
val = watchdog_get_timeout(data);
ret = put_user(val, (int __user *)arg); break;
case WDIOC_SETTIMEOUT: if (get_user(val, (int __user *)arg)) {
ret = -EFAULT; break;
}
ret = watchdog_set_timeout(data, val); if (ret > 0)
ret = put_user(ret, (int __user *)arg); break;
case WDIOC_SETOPTIONS: if (get_user(val, (int __user *)arg)) {
ret = -EFAULT; break;
}
if (val & WDIOS_DISABLECARD)
ret = watchdog_disable(data); elseif (val & WDIOS_ENABLECARD)
ret = watchdog_enable(data); else
ret = -EINVAL;
/* * Store client pointer in our data struct for watchdog usage * (where the client is found through a data ptr instead of the * otherway around)
*/
data->client = client;
err = w83793_detect_subclients(client); if (err) goto free_mem;
/* Initialize the chip */
w83793_init_client(client);
/* * Only fan 1-5 has their own input pins, * Pwm 1-3 has their own pins
*/
data->has_fan = 0x1f;
data->has_pwm = 0x07;
tmp = w83793_read_value(client, W83793_REG_MFC);
val = w83793_read_value(client, W83793_REG_FANIN_CTRL);
/* check the function of pins 49-56 */ if (tmp & 0x80) {
data->has_vid |= 0x2; /* has VIDB */
} else {
data->has_pwm |= 0x18; /* pwm 4,5 */ if (val & 0x01) { /* fan 6 */
data->has_fan |= 0x20;
data->has_pwm |= 0x20;
} if (val & 0x02) { /* fan 7 */
data->has_fan |= 0x40;
data->has_pwm |= 0x40;
} if (!(tmp & 0x40) && (val & 0x04)) { /* fan 8 */
data->has_fan |= 0x80;
data->has_pwm |= 0x80;
}
}
/* check the function of pins 37-40 */ if (!(tmp & 0x29))
data->has_vid |= 0x1; /* has VIDA */ if (0x08 == (tmp & 0x0c)) { if (val & 0x08) /* fan 9 */
data->has_fan |= 0x100; if (val & 0x10) /* fan 10 */
data->has_fan |= 0x200;
} if (0x20 == (tmp & 0x30)) { if (val & 0x20) /* fan 11 */
data->has_fan |= 0x400; if (val & 0x40) /* fan 12 */
data->has_fan |= 0x800;
}
if ((tmp & 0x01) && (val & 0x04)) { /* fan 8, second location */
data->has_fan |= 0x80;
data->has_pwm |= 0x80;
}
tmp = w83793_read_value(client, W83793_REG_FANIN_SEL); if ((tmp & 0x01) && (val & 0x08)) { /* fan 9, second location */
data->has_fan |= 0x100;
} if ((tmp & 0x02) && (val & 0x10)) { /* fan 10, second location */
data->has_fan |= 0x200;
} if ((tmp & 0x04) && (val & 0x20)) { /* fan 11, second location */
data->has_fan |= 0x400;
} if ((tmp & 0x08) && (val & 0x40)) { /* fan 12, second location */
data->has_fan |= 0x800;
}
/* check the temp1-6 mode, ignore former AMDSI selected inputs */
tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[0]); if (tmp & 0x01)
data->has_temp |= 0x01; if (tmp & 0x04)
data->has_temp |= 0x02; if (tmp & 0x10)
data->has_temp |= 0x04; if (tmp & 0x40)
data->has_temp |= 0x08;
tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[1]); if (tmp & 0x01)
data->has_temp |= 0x10; if (tmp & 0x02)
data->has_temp |= 0x20;
/* Register sysfs hooks */ for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) {
err = device_create_file(dev,
&w83793_sensor_attr_2[i].dev_attr); if (err) goto exit_remove;
}
for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) { if (!(data->has_vid & (1 << i))) continue;
err = device_create_file(dev, &w83793_vid[i].dev_attr); if (err) goto exit_remove;
} if (data->has_vid) {
data->vrm = vid_which_vrm();
err = device_create_file(dev, &dev_attr_vrm); if (err) goto exit_remove;
}
for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) {
err = device_create_file(dev, &sda_single_files[i].dev_attr); if (err) goto exit_remove;
}
for (i = 0; i < 6; i++) { int j; if (!(data->has_temp & (1 << i))) continue; for (j = 0; j < files_temp; j++) {
err = device_create_file(dev,
&w83793_temp[(i) * files_temp
+ j].dev_attr); if (err) goto exit_remove;
}
}
for (i = 5; i < 12; i++) { int j; if (!(data->has_fan & (1 << i))) continue; for (j = 0; j < files_fan; j++) {
err = device_create_file(dev,
&w83793_left_fan[(i - 5) * files_fan
+ j].dev_attr); if (err) goto exit_remove;
}
}
for (i = 3; i < 8; i++) { int j; if (!(data->has_pwm & (1 << i))) continue; for (j = 0; j < files_pwm; j++) {
err = device_create_file(dev,
&w83793_left_pwm[(i - 3) * files_pwm
+ j].dev_attr); if (err) goto exit_remove;
}
}
/* * Enable Watchdog registers. * Set Configuration Register to Enable Watch Dog Registers * (Bit 2) = XXXX, X1XX.
*/
tmp = w83793_read_value(client, W83793_REG_CONFIG);
w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04);
/* Set the default watchdog timeout */
data->watchdog_timeout = timeout;
/* Check, if last reboot was caused by watchdog */
data->watchdog_caused_reboot =
w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01;
/* Disable Soft Watchdog during initialiation */
watchdog_disable(data);
/* * We take the data_mutex lock early so that watchdog_open() cannot * run when misc_register() has completed, but we've not yet added * our data to the watchdog_data_list (and set the default timeout)
*/
mutex_lock(&watchdog_data_mutex); for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { /* Register our watchdog part */
snprintf(data->watchdog_name, sizeof(data->watchdog_name), "watchdog%c", (i == 0) ? '\0' : ('0' + i));
data->watchdog_miscdev.name = data->watchdog_name;
data->watchdog_miscdev.fops = &watchdog_fops;
data->watchdog_miscdev.minor = watchdog_minors[i];
dev_info(&client->dev, "Registered watchdog chardev major 10, minor: %d\n",
watchdog_minors[i]); break;
} if (i == ARRAY_SIZE(watchdog_minors)) {
data->watchdog_miscdev.minor = 0;
dev_warn(&client->dev, "Couldn't register watchdog chardev (due to no free minor)\n");
}
mutex_unlock(&watchdog_data_mutex);
return 0;
/* Unregister hwmon device */
exit_devunreg:
hwmon_device_unregister(data->hwmon_dev);
/* Unregister sysfs hooks */
exit_remove: for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++)
device_remove_file(dev, &w83793_sensor_attr_2[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(sda_single_files); i++)
device_remove_file(dev, &sda_single_files[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(w83793_vid); i++)
device_remove_file(dev, &w83793_vid[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++)
device_remove_file(dev, &w83793_left_fan[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++)
device_remove_file(dev, &w83793_left_pwm[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
device_remove_file(dev, &w83793_temp[i].dev_attr);
free_mem:
kfree(data); exit: return err;
}
staticvoid w83793_update_nonvolatile(struct device *dev)
{ struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); int i, j; /* * They are somewhat "stable" registers, and to update them every time * takes so much time, it's just not worthy. Update them in a long * interval to avoid exception.
*/ if (!(time_after(jiffies, data->last_nonvolatile + HZ * 300)
|| !data->valid)) return; /* update voltage limits */ for (i = 1; i < 3; i++) { for (j = 0; j < ARRAY_SIZE(data->in); j++) {
data->in[j][i] =
w83793_read_value(client, W83793_REG_IN[j][i]);
}
data->in_low_bits[i] =
w83793_read_value(client, W83793_REG_IN_LOW_BITS[i]);
}
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { /* Update the Fan measured value and limits */ if (!(data->has_fan & (1 << i))) continue;
data->fan_min[i] =
w83793_read_value(client, W83793_REG_FAN_MIN(i)) << 8;
data->fan_min[i] |=
w83793_read_value(client, W83793_REG_FAN_MIN(i) + 1);
}
for (i = 0; i < ARRAY_SIZE(data->temp_fan_map); i++) { if (!(data->has_temp & (1 << i))) continue;
data->temp_fan_map[i] =
w83793_read_value(client, W83793_REG_TEMP_FAN_MAP(i)); for (j = 1; j < 5; j++) {
data->temp[i][j] =
w83793_read_value(client, W83793_REG_TEMP[i][j]);
}
data->temp_cruise[i] =
w83793_read_value(client, W83793_REG_TEMP_CRUISE(i)); for (j = 0; j < 7; j++) {
data->sf2_pwm[i][j] =
w83793_read_value(client, W83793_REG_SF2_PWM(i, j));
data->sf2_temp[i][j] =
w83793_read_value(client,
W83793_REG_SF2_TEMP(i, j));
}
}
for (i = 0; i < ARRAY_SIZE(data->temp_mode); i++)
data->temp_mode[i] =
w83793_read_value(client, W83793_REG_TEMP_MODE[i]);
for (i = 0; i < ARRAY_SIZE(data->tolerance); i++) {
data->tolerance[i] =
w83793_read_value(client, W83793_REG_TEMP_TOL(i));
}
for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { if (!(data->has_pwm & (1 << i))) continue;
data->pwm[i][PWM_NONSTOP] =
w83793_read_value(client, W83793_REG_PWM(i, PWM_NONSTOP));
data->pwm[i][PWM_START] =
w83793_read_value(client, W83793_REG_PWM(i, PWM_START));
data->pwm_stop_time[i] =
w83793_read_value(client, W83793_REG_PWM_STOP_TIME(i));
}
/* Update the voltages measured value and limits */ for (i = 0; i < ARRAY_SIZE(data->in); i++)
data->in[i][IN_READ] =
w83793_read_value(client, W83793_REG_IN[i][IN_READ]);
/* * Ignore the possibility that somebody change bank outside the driver * Must be called with data->update_lock held, except during initialization
*/ static u8 w83793_read_value(struct i2c_client *client, u16 reg)
{ struct w83793_data *data = i2c_get_clientdata(client);
u8 res;
u8 new_bank = reg >> 8;
new_bank |= data->bank & 0xfc; if (data->bank != new_bank) { if (i2c_smbus_write_byte_data
(client, W83793_REG_BANKSEL, new_bank) >= 0)
data->bank = new_bank; else {
dev_err(&client->dev, "set bank to %d failed, fall back " "to bank %d, read reg 0x%x error\n",
new_bank, data->bank, reg);
res = 0x0; /* read 0x0 from the chip */ goto END;
}
}
res = i2c_smbus_read_byte_data(client, reg & 0xff);
END: return res;
}
/* Must be called with data->update_lock held, except during initialization */ staticint w83793_write_value(struct i2c_client *client, u16 reg, u8 value)
{ struct w83793_data *data = i2c_get_clientdata(client); int res;
u8 new_bank = reg >> 8;
new_bank |= data->bank & 0xfc; if (data->bank != new_bank) {
res = i2c_smbus_write_byte_data(client, W83793_REG_BANKSEL,
new_bank); if (res < 0) {
dev_err(&client->dev, "set bank to %d failed, fall back " "to bank %d, write reg 0x%x error\n",
new_bank, data->bank, reg); goto END;
}
data->bank = new_bank;
}