[prev in list] [next in list] [prev in thread] [next in thread]
List: lm-sensors
Subject: [lm-sensors] PATCH:
From: Hans de Goede <j.w.r.degoede () hhs ! nl>
Date: 2008-06-30 14:53:43
Message-ID: 4868F377.20309 () hhs ! nl
[Download RAW message or body]
Hi All,
As promised, this patch adds support for the watchdog part found in _all_
supported FSC sensor chips. Once this patch is in we can deprecate the old
fscpos and fscher drivers.
signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Regards,
Hans
["fschmd-watchdog.patch" (text/plain)]
This patch adds support for the watchdog part found in _all_ supported FSC
sensor chips.
signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl>
diff -up vanilla-2.6.26-rc8/drivers/hwmon/Kconfig.orig \
vanilla-2.6.26-rc8/drivers/hwmon/Kconfig
--- vanilla-2.6.26-rc8/drivers/hwmon/Kconfig.orig 2008-06-30 16:37:17.000000000 +0200
+++ vanilla-2.6.26-rc8/drivers/hwmon/Kconfig 2008-06-30 16:38:34.000000000 +0200
@@ -292,10 +292,11 @@ config SENSORS_FSCHMD
depends on X86 && I2C && EXPERIMENTAL
help
If you say yes here you get support for various Fujitsu Siemens
- Computers sensor chips.
+ Computers sensor chips, including support for the integrated
+ watchdog.
This is a new merged driver for FSC sensor chips which is intended
- as a replacment for the fscpos, fscscy and fscher drivers and adds
+ as a replacement for the fscpos, fscscy and fscher drivers and adds
support for several other FCS sensor chips.
This driver can also be built as a module. If so, the module
diff -up vanilla-2.6.26-rc8/drivers/hwmon/fschmd.c.orig \
vanilla-2.6.26-rc8/drivers/hwmon/fschmd.c
--- vanilla-2.6.26-rc8/drivers/hwmon/fschmd.c.orig 2008-06-30 16:36:32.000000000 \
+0200
+++ vanilla-2.6.26-rc8/drivers/hwmon/fschmd.c 2008-06-30 16:51:34.000000000 +0200
@@ -1,6 +1,6 @@
/* fschmd.c
*
- * Copyright (C) 2007 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2007,2008 Hans de Goede <j.w.r.degoede@hhs.nl>
*
* 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
@@ -42,13 +42,21 @@
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/dmi.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
/* Insmod parameters */
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" \
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, \
fschrc, fschmd);
+
/*
* The FSCHMD registers and other defines
*/
@@ -65,11 +73,19 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscs
#define FSCHMD_CONTROL_ALERT_LED_MASK 0x01
-/* watchdog (support to be implemented) */
+/* watchdog */
#define FSCHMD_REG_WDOG_PRESET 0x28
#define FSCHMD_REG_WDOG_STATE 0x23
#define FSCHMD_REG_WDOG_CONTROL 0x21
+#define FSCHMD_WDOG_CONTROL_TRIGGER_MASK 0x10
+#define FSCHMD_WDOG_CONTROL_STARTED_MASK 0x10 /* the same as trigger */
+#define FSCHMD_WDOG_CONTROL_STOP_MASK 0x20
+#define FSCHMD_WDOG_CONTROL_RESOLUTION_MASK 0x40
+
+#define FSCHMD_WDOG_STATE_CARDRESET_MASK 0x02
+
+
/* voltages, weird order is to keep the same order as the old drivers */
static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 };
@@ -167,6 +183,22 @@ static const int FSCHMD_NO_TEMP_SENSORS[
/* our driver name */
#define FSCHMD_NAME "fschmd"
+/* Watchdog filp->private_data pointer use utility macros. We store our minor
+ (to find our device data) in the private_data pointer, since the
+ private_data pointer can hold atleast an int, we use the 'major" space
+ of that int to store an per open expect_close flag. */
+#define WATCHDOG_INIT_FILP(filp, minor) \
+ (filp)->private_data = (void *)(long)(minor)
+#define WATCHDOG_GET_MINOR(filp) \
+ MINOR((long)(filp)->private_data)
+#define WATCHDOG_CLEAR_EXPECT_CLOSE(filp) \
+ (filp)->private_data = (void *)(long)WATCHDOG_GET_MINOR(filp)
+#define WATCHDOG_SET_EXPECT_CLOSE(filp) \
+ (filp)->private_data = (void *)(long)MKDEV(1, WATCHDOG_GET_MINOR(filp))
+#define WATCHDOG_GET_EXPECT_CLOSE(filp) \
+ MAJOR((long)(filp)->private_data)
+
+
/*
* Functions declarations
*/
@@ -195,12 +227,20 @@ struct fschmd_data {
struct i2c_client client;
struct device *hwmon_dev;
struct mutex update_lock;
+ struct list_head list;
+ struct miscdevice watchdog_miscdev;
int kind;
+ int watchdog_open_count;
+ char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
+ u8 revision; /* chip revision */
u8 global_control; /* global control register */
+ u8 watchdog_control; /* watchdog control register */
+ u8 watchdog_status; /* watchdog status register */
+ u8 watchdog_preset; /* watchdog counter preset on trigger val */
u8 volt[3]; /* 12, 5, battery voltage */
u8 temp_act[5]; /* temperature */
u8 temp_status[5]; /* status of sensor */
@@ -212,11 +252,20 @@ struct fschmd_data {
};
/* Global variables to hold information read from special DMI tables, which are
- available on FSC machines with an fscher or later chip. */
+ available on FSC machines with an fscher or later chip. There is no need to
+ protect these with a lock as they are only modified from our attach function
+ which always gets called with the i2c-core lock held and never accessed
+ before the attach function is done with them. */
static int dmi_mult[3] = { 490, 200, 100 };
static int dmi_offset[3] = { 0, 0, 0 };
static int dmi_vref = -1;
+/* Somewhat ugly :( global data pointer list with all fschmd devices, so that
+ we can find our device data as when using misc_register there is no other
+ method to get to ones device data from the open fop. */
+static LIST_HEAD(watchdog_data_list);
+static DEFINE_MUTEX(watchdog_data_mutex);
+
/*
* Sysfs attr show / store functions
@@ -535,7 +584,286 @@ static struct sensor_device_attribute fs
/*
- * Real code
+ * Watchdog routines
+ */
+static struct fschmd_data *watchdog_get_data_unlocked(int minor)
+{
+ struct fschmd_data *data = NULL, *pos;
+
+ list_for_each_entry(pos, &watchdog_data_list, list) {
+ if (pos->watchdog_miscdev.minor == minor) {
+ data = pos;
+ break;
+ }
+ }
+
+ return data;
+}
+
+static int watchdog_set_timeout_unlocked(struct fschmd_data *data, int timeout)
+{
+ int resolution;
+ int kind = data->kind + 1; /* 0-x array index -> 1-x module param */
+
+ /* 2 second or 60 second resolution? */
+ if (timeout <= 510 || kind == fscpos || kind == fscscy) {
+ data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION_MASK;
+ resolution = 2;
+ } else {
+ data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION_MASK;
+ resolution = 60;
+ }
+
+ if (timeout > (resolution * 255))
+ data->watchdog_preset = 255;
+ else
+ data->watchdog_preset = (timeout + (resolution - 1)) /
+ resolution;
+
+ /* Write new timeout value */
+ i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_WDOG_PRESET,
+ data->watchdog_preset);
+ /* Write new control register, do not trigger! */
+ i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_WDOG_CONTROL,
+ data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER_MASK);
+
+ return data->watchdog_preset * resolution;
+}
+
+static int watchdog_get_timeout(struct fschmd_data *data)
+{
+ int timeout;
+
+ mutex_lock(&data->update_lock);
+ if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION_MASK)
+ timeout = data->watchdog_preset * 60;
+ else
+ timeout = data->watchdog_preset * 2;
+ mutex_unlock(&data->update_lock);
+
+ return timeout;
+}
+
+static void watchdog_trigger(struct fschmd_data *data)
+{
+ mutex_lock(&data->update_lock);
+ data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER_MASK;
+ i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_WDOG_CONTROL,
+ data->watchdog_control);
+ mutex_unlock(&data->update_lock);
+}
+
+static void watchdog_stop(struct fschmd_data *data)
+{
+ mutex_lock(&data->update_lock);
+ data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED_MASK;
+ /* Don't store the stop flag in our watchdog control register copy, as
+ its a write only bit (read always returns 0) */
+ i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_WDOG_CONTROL,
+ data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP_MASK);
+ mutex_unlock(&data->update_lock);
+}
+
+
+static int watchdog_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+ struct fschmd_data *data;
+
+ mutex_lock(&watchdog_data_mutex);
+ data = watchdog_get_data_unlocked(iminor(inode));
+ /* Check our i2c client didn't get removed from underneath us */
+ if (!data) {
+ ret = -ENODEV;
+ goto unlock_mutex;
+ }
+
+ /* Set a default timeout if necessary */
+ mutex_lock(&data->update_lock);
+ if (data->watchdog_preset == 0)
+ watchdog_set_timeout_unlocked(data, 60);
+ mutex_unlock(&data->update_lock);
+
+ /* Start the watchdog */
+ watchdog_trigger(data);
+
+ data->watchdog_open_count++;
+ WATCHDOG_INIT_FILP(filp, data->watchdog_miscdev.minor);
+
+ ret = nonseekable_open(inode, filp);
+
+unlock_mutex:
+ mutex_unlock(&watchdog_data_mutex);
+
+ return ret;
+}
+
+static int watchdog_release(struct inode *inode, struct file *filp)
+{
+ struct fschmd_data *data;
+
+ mutex_lock(&watchdog_data_mutex);
+ data = watchdog_get_data_unlocked(WATCHDOG_GET_MINOR(filp));
+ /* Check our i2c client didn't get removed from underneath us */
+ if (!data)
+ goto unlock_mutex;
+
+ data->watchdog_open_count--;
+
+ if (WATCHDOG_GET_EXPECT_CLOSE(filp)) {
+ if (data->watchdog_open_count)
+ printk(KERN_WARNING FSCHMD_NAME
+ ": watchdog still opened %d time(s), "
+ "not stopping\n", data->watchdog_open_count);
+ else
+ watchdog_stop(data);
+ } else {
+ watchdog_trigger(data);
+ printk(KERN_CRIT FSCHMD_NAME
+ ": unexpected close, not stopping watchdog!\n");
+ }
+
+unlock_mutex:
+ mutex_unlock(&watchdog_data_mutex);
+
+ return 0;
+}
+
+static ssize_t watchdog_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ ssize_t ret = count;
+ struct fschmd_data *data;
+
+ mutex_lock(&watchdog_data_mutex);
+ data = watchdog_get_data_unlocked(WATCHDOG_GET_MINOR(filp));
+ /* Check our i2c client didn't get removed from underneath us */
+ if (!data) {
+ ret = -ENODEV;
+ goto unlock_mutex;
+ }
+
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* Clear it in case it was set with a previous write */
+ WATCHDOG_CLEAR_EXPECT_CLOSE(filp);
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i)) {
+ ret = -EFAULT;
+ goto unlock_mutex;
+ }
+ if (c == 'V')
+ WATCHDOG_SET_EXPECT_CLOSE(filp);
+ }
+ }
+ watchdog_trigger(data);
+ }
+
+unlock_mutex:
+ mutex_unlock(&watchdog_data_mutex);
+
+ return ret;
+}
+
+static int watchdog_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_CARDRESET,
+ .identity = "FSC watchdog"
+ };
+ int i, ret = 0;
+ struct fschmd_data *data;
+
+ mutex_lock(&watchdog_data_mutex);
+ data = watchdog_get_data_unlocked(WATCHDOG_GET_MINOR(filp));
+ /* Check our i2c client didn't get removed from underneath us */
+ if (!data) {
+ ret = -ENODEV;
+ goto unlock_mutex;
+ }
+
+ 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 *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ if (data->watchdog_status & FSCHMD_WDOG_STATE_CARDRESET_MASK)
+ ret = put_user(WDIOF_CARDRESET, (int *)arg);
+ else
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ watchdog_trigger(data);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ i = watchdog_get_timeout(data);
+ ret = put_user(i, (int *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(i, (int *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ mutex_lock(&data->update_lock);
+ i = watchdog_set_timeout_unlocked(data, i);
+ mutex_unlock(&data->update_lock);
+ ret = put_user(i, (int *)arg);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(i, (int *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (i & WDIOS_DISABLECARD)
+ watchdog_stop(data);
+ else if (i & WDIOS_ENABLECARD)
+ watchdog_trigger(data);
+ else
+ ret = -EINVAL;
+
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+unlock_mutex:
+ mutex_unlock(&watchdog_data_mutex);
+
+ return ret;
+}
+
+static struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_release,
+ .write = watchdog_write,
+ .ioctl = watchdog_ioctl,
+};
+
+
+/*
+ * Detect, register, unregister and update device functions
*/
/* DMI decode routine to read voltage scaling factors from special DMI tables,
@@ -604,11 +932,11 @@ static int fschmd_detect(struct i2c_adap
{
struct i2c_client *client;
struct fschmd_data *data;
- u8 revision;
const char * const names[5] = { "Poseidon", "Hermes", "Scylla",
"Heracles", "Heimdall" };
const char * const client_names[5] = { "fscpos", "fscher", "fscscy",
"fschrc", "fschmd" };
+ const int watchdog_minors[] = { 130, 212, 213, 214, 215 };
int i, err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -626,6 +954,7 @@ static int fschmd_detect(struct i2c_adap
client->adapter = adapter;
client->driver = &fschmd_driver;
mutex_init(&data->update_lock);
+ INIT_LIST_HEAD(&data->list);
/* Detect & Identify the chip */
if (kind <= 0) {
@@ -662,7 +991,7 @@ static int fschmd_detect(struct i2c_adap
}
/* Read the special DMI table for fscher and newer chips */
- if (kind == fscher || kind >= fschrc) {
+ if ((kind == fscher || kind >= fschrc) && dmi_vref == -1){
dmi_walk(fschmd_dmi_decode);
if (dmi_vref == -1) {
printk(KERN_WARNING FSCHMD_NAME
@@ -672,6 +1001,17 @@ static int fschmd_detect(struct i2c_adap
}
}
+ /* 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->watchdog_status = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_WDOG_STATE);
+ data->watchdog_preset = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_WDOG_PRESET);
+
/* i2c kind goes from 1-5, we want from 0-4 to address arrays */
data->kind = kind - 1;
strlcpy(client->name, client_names[data->kind], I2C_NAME_SIZE);
@@ -719,9 +1059,36 @@ static int fschmd_detect(struct i2c_adap
goto exit_detach;
}
- revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
+ /* Register our watchdog part */
+ for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
+ 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];
+ err = misc_register(&data->watchdog_miscdev);
+ if (err == -EBUSY)
+ continue;
+ if (err)
+ goto exit_detach;
+
+ mutex_lock(&watchdog_data_mutex);
+ list_add(&data->list, &watchdog_data_list);
+ mutex_unlock(&watchdog_data_mutex);
+ printk(KERN_INFO FSCHMD_NAME
+ ": Registered watchdog chardev major 10, minor: %d\n",
+ watchdog_minors[i]);
+ break;
+ }
+ if (i == ARRAY_SIZE(watchdog_minors)) {
+ data->watchdog_miscdev.minor = 0;
+ printk(KERN_WARNING FSCHMD_NAME
+ ": Couldn't register watchdog chardev "
+ "(due to no free minor)\n");
+ }
+
printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n",
- names[data->kind], (int) revision);
+ names[data->kind], (int) data->revision);
return 0;
@@ -746,6 +1113,20 @@ static int fschmd_detach_client(struct i
struct fschmd_data *data = i2c_get_clientdata(client);
int i, err;
+ /* Unregister the watchdog (if registered) */
+ if (data->watchdog_miscdev.minor) {
+ mutex_lock(&watchdog_data_mutex);
+ if (data->watchdog_open_count) {
+ printk(KERN_WARNING FSCHMD_NAME
+ ": i2c client detached with watchdog open! "
+ "Stopping watchdog.\n");
+ watchdog_stop(data);
+ }
+ misc_deregister(&data->watchdog_miscdev);
+ list_del(&data->list);
+ mutex_unlock(&watchdog_data_mutex);
+ }
+
/* Check if registered in case we're called from fschmd_detect
to cleanup after an error */
if (data->hwmon_dev)
@@ -825,17 +1206,6 @@ static struct fschmd_data *fschmd_update
data->volt[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_VOLT[i]);
- data->global_control = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_CONTROL);
-
- /* To be implemented in the future
- data->watchdog[0] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_WDOG_PRESET);
- data->watchdog[1] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_WDOG_STATE);
- data->watchdog[2] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_WDOG_CONTROL); */
-
data->last_updated = jiffies;
data->valid = 1;
}
@@ -859,6 +1229,7 @@ MODULE_AUTHOR("Hans de Goede <j.w.r.dego
MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and "
"Heimdall driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_GET_MINOR);
module_init(fschmd_init);
module_exit(fschmd_exit);
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic