/* temp status register bitmasks */ #define FSCHMD_TEMP_WORKING 0x01 #define FSCHMD_TEMP_ALERT 0x02 #define FSCHMD_TEMP_DISABLED 0x80 /* there only really is an alarm if the sensor is working and alert == 1 */ #define FSCHMD_TEMP_ALARM_MASK \
(FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT)
struct fschmd_data { struct i2c_client *client; struct device *hwmon_dev; struct mutex update_lock; struct mutex watchdog_lock; struct list_head list; /* member of the watchdog_data_list */ struct kref kref; struct miscdevice watchdog_miscdev; enum chips kind; unsignedlong watchdog_is_open; char watchdog_expect_close; char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ bool valid; /* false until following fields are valid */ unsignedlong last_updated; /* in jiffies */
/* register values */
u8 revision; /* chip revision */
u8 global_control; /* global control register */
u8 watchdog_control; /* watchdog control register */
u8 watchdog_state; /* watchdog status register */
u8 watchdog_preset; /* watchdog counter preset on trigger val */
u8 volt[6]; /* voltage */
u8 temp_act[11]; /* temperature */
u8 temp_status[11]; /* status of sensor */
u8 temp_max[11]; /* high temp limit, notice: undocumented! */
u8 fan_act[7]; /* fans revolutions per second */
u8 fan_status[7]; /* fan status */
u8 fan_min[7]; /* fan min value for rps */
u8 fan_ripple[7]; /* divider for rps */
};
/* *Somewhatugly:(globaldatapointerlistwithallfschmddevices,sothat *wecanfindourdevicedataaswhenusingmisc_registerthereisnoother *methodtogettoonesdevicedatafromtheopenfop.
*/ 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);
static ssize_t temp_fault_show(struct device *dev, struct device_attribute *devattr, char *buf)
{ int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev);
/* bit 0 set means sensor working ok, so no fault! */ if (data->temp_status[index] & FSCHMD_TEMP_WORKING) return sprintf(buf, "0\n"); else return sprintf(buf, "1\n");
}
static ssize_t temp_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf)
{ int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev);
switch (v) { case2:
v = 1; break; case4:
v = 2; break; case8:
v = 3; break; default:
dev_err(dev, "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n",
v); return -EINVAL;
}
static ssize_t pwm_auto_point1_pwm_show(struct device *dev, struct device_attribute *devattr, char *buf)
{ int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); int val = data->fan_min[index];
/* 0 = allow turning off (except on the syl), 1-255 = 50-100% */ if (val || data->kind == fscsyl)
val = val / 2 + 128;
return sprintf(buf, "%d\n", val);
}
static ssize_t pwm_auto_point1_pwm_store(struct device *dev, struct device_attribute *devattr, constchar *buf, size_t count)
{ int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = dev_get_drvdata(dev); unsignedlong v; int err;
err = kstrtoul(buf, 10, &v); if (err) return err;
/* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */ if (v || data->kind == fscsyl) {
v = clamp_val(v, 128, 255);
v = (v - 128) * 2 + 1;
}
/* Write new timeout value */
i2c_smbus_write_byte_data(data->client,
FSCHMD_REG_WDOG_PRESET[data->kind], data->watchdog_preset); /* Write new control register, do not trigger! */
i2c_smbus_write_byte_data(data->client,
FSCHMD_REG_WDOG_CONTROL[data->kind],
data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER);
/* *Wegetcalledfromdrivers/char/misc.cwithmisc_mtxhold,andwe *callmisc_register()fromfschmd_probe()withwatchdog_data_mutex *hold,asmisc_register()takesthemisc_mtxlock,thisisapossible *deadlock,soweusemutex_trylockhere.
*/ 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;
}
} /* Note we can never not have found data, so we don't check for this */
watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); if (!watchdog_is_open)
kref_get(&data->kref);
mutex_unlock(&watchdog_data_mutex);
if (watchdog_is_open) return -EBUSY;
/* Start the watchdog */
watchdog_trigger(data);
filp->private_data = data;
switch (cmd) { case WDIOC_GETSUPPORT:
ident.firmware_version = data->revision; if (!nowayout)
ident.options |= WDIOF_MAGICCLOSE; if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
ret = -EFAULT; break;
case WDIOC_GETSTATUS:
ret = put_user(0, (int __user *)arg); break;
case WDIOC_GETBOOTSTATUS: if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET)
ret = put_user(WDIOF_CARDRESET, (int __user *)arg); else
ret = put_user(0, (int __user *)arg); break;
case WDIOC_KEEPALIVE:
ret = watchdog_trigger(data); break;
case WDIOC_GETTIMEOUT:
i = watchdog_get_timeout(data);
ret = put_user(i, (int __user *)arg); break;
case WDIOC_SETTIMEOUT: if (get_user(i, (int __user *)arg)) {
ret = -EFAULT; break;
}
ret = watchdog_set_timeout(data, i); if (ret > 0)
ret = put_user(ret, (int __user *)arg); break;
case WDIOC_SETOPTIONS: if (get_user(i, (int __user *)arg)) {
ret = -EFAULT; break;
}
if (i & WDIOS_DISABLECARD)
ret = watchdog_stop(data); elseif (i & WDIOS_ENABLECARD)
ret = watchdog_trigger(data); else
ret = -EINVAL;
/* *Afterthesubtypecomes1unknownbyteandthenblocksof5bytes, *consistingofwhatSiemenscallsan"Entity"number,followedby *216-bitwordsinLSBfirstorder
*/ for (i = 6; (i + 4) < header->length; i += 5) { /* entity 1 - 3: voltage multiplier and offset */ if (dmi_data[i] >= 1 && dmi_data[i] <= 3) { /* Our in sensors order and the DMI order differ */ constint shuffle[3] = { 1, 0, 2 }; int in = shuffle[dmi_data[i] - 1];
/* Check for twice the same entity */ if (found & (1 << in)) return;
/* Read the special DMI table for fscher and newer chips */ if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) {
dmi_walk(fschmd_dmi_decode, NULL); if (dmi_vref == -1) {
dev_warn(&client->dev, "Couldn't get voltage scaling factors from " "BIOS DMI table, using builtin defaults\n");
dmi_vref = 33;
}
}
/* Read in some never changing registers */
data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
data->global_control = i2c_smbus_read_byte_data(client,
FSCHMD_REG_CONTROL);
data->watchdog_control = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_CONTROL[data->kind]);
data->watchdog_state = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_STATE[data->kind]);
data->watchdog_preset = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_PRESET[data->kind]);
err = device_create_file(&client->dev, &dev_attr_alert_led); if (err) goto exit_detach;
for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) {
err = device_create_file(&client->dev,
&fschmd_attr[i].dev_attr); if (err) goto exit_detach;
}
for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) { /* Poseidon doesn't have TEMP_LIMIT registers */ if (kind == fscpos && fschmd_temp_attr[i].dev_attr.show ==
temp_max_show) continue;
if (kind == fscsyl) { if (i % 4 == 0)
data->temp_status[i / 4] =
i2c_smbus_read_byte_data(client,
FSCHMD_REG_TEMP_STATE
[data->kind][i / 4]); if (data->temp_status[i / 4] & FSCHMD_TEMP_DISABLED) continue;
}
err = device_create_file(&client->dev,
&fschmd_temp_attr[i].dev_attr); if (err) goto exit_detach;
}
for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) { /* Poseidon doesn't have a FAN_MIN register for its 3rd fan */ if (kind == fscpos &&
!strcmp(fschmd_fan_attr[i].dev_attr.attr.name, "pwm3_auto_point1_pwm")) continue;
if (kind == fscsyl) { if (i % 5 == 0)
data->fan_status[i / 5] =
i2c_smbus_read_byte_data(client,
FSCHMD_REG_FAN_STATE
[data->kind][i / 5]); if (data->fan_status[i / 5] & FSCHMD_FAN_DISABLED) continue;
}
err = device_create_file(&client->dev,
&fschmd_fan_attr[i].dev_attr); if (err) goto exit_detach;
}
/* Unregister the watchdog (if registered) */ if (data->watchdog_miscdev.minor) {
misc_deregister(&data->watchdog_miscdev); if (data->watchdog_is_open) {
dev_warn(&client->dev, "i2c client detached with watchdog open! " "Stopping watchdog.\n");
watchdog_stop(data);
}
mutex_lock(&watchdog_data_mutex);
list_del(&data->list);
mutex_unlock(&watchdog_data_mutex); /* Tell the watchdog code the client is gone */
mutex_lock(&data->watchdog_lock);
data->client = NULL;
mutex_unlock(&data->watchdog_lock);
}
/* *Checkifregisteredincasewe'recalledfromfschmd_detect *tocleanupafteranerror
*/ if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
device_remove_file(&client->dev, &dev_attr_alert_led); for (i = 0; i < (FSCHMD_NO_VOLT_SENSORS[data->kind]); i++)
device_remove_file(&client->dev, &fschmd_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++)
device_remove_file(&client->dev,
&fschmd_temp_attr[i].dev_attr); for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++)
device_remove_file(&client->dev,
&fschmd_fan_attr[i].dev_attr);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) {
data->temp_act[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_TEMP_ACT[data->kind][i]);
data->temp_status[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_TEMP_STATE[data->kind][i]);
/* The fscpos doesn't have TEMP_LIMIT registers */ if (FSCHMD_REG_TEMP_LIMIT[data->kind][i])
data->temp_max[i] = i2c_smbus_read_byte_data(
client,
FSCHMD_REG_TEMP_LIMIT[data->kind][i]);
for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
data->fan_act[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_FAN_ACT[data->kind][i]);
data->fan_status[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_FAN_STATE[data->kind][i]);
data->fan_ripple[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_FAN_RIPPLE[data->kind][i]);
/* The fscpos third fan doesn't have a fan_min */ if (FSCHMD_REG_FAN_MIN[data->kind][i])
data->fan_min[i] = i2c_smbus_read_byte_data(
client,
FSCHMD_REG_FAN_MIN[data->kind][i]);
/* reset fan status if speed is back to > 0 */ if ((data->fan_status[i] & FSCHMD_FAN_ALARM) &&
data->fan_act[i])
i2c_smbus_write_byte_data(client,
FSCHMD_REG_FAN_STATE[data->kind][i],
data->fan_status[i]);
}
for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++)
data->volt[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_VOLT[data->kind][i]);