[PATCH] driver for SMSC SCH311x

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Marcin,

Are you still interested in support for the SMSC sch311x family? I
discovered that the dme1737, which I is supported, is pretty
identical. It wouldn't take a lot to modify the dme1737 driver to
support the sch311x. Let me know if you're still interested and if you
have a system to test and I'll give it a shot. Or you can, if you want
to.

...juerg


> Hello,
>
> Below you find my SMSC SCH311x chips driver. Currently, it supports voltage, temperature inputs and alarms.
> There is not implemented support for fans and pwm control (I couldn't test it on my hardware).
>
> Thanks for your feedback,
> Marcin
>
> Signed-off-by: Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>
>
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/Kconfig linux-2.6.18-rc2-sch311x/drivers/hwmon/Kconfig
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/Kconfig	2006-07-15 23:53:08.000000000 +0200
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/Kconfig	2006-07-20 17:57:01.000000000 +0200
> @@ -396,6 +396,17 @@ config SENSORS_SMSC47B397
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called smsc47b397.
>
> +config SENSORS_SCH311X
> +	tristate "SMSC SCH311x family"
> +	depends on HWMON && I2C && EXPERIMENTAL
> +	select I2C_ISA
> +	help
> +	  If you say yes here you get support for the SMSC SCH311x family
> +	  sensor chip.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called sch311x.
> +
>  config SENSORS_VIA686A
>  	tristate "VIA686A"
>  	depends on HWMON && I2C && PCI
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/Makefile linux-2.6.18-rc2-sch311x/drivers/hwmon/Makefile
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/Makefile	2006-07-15 23:53:08.000000000 +0200
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/Makefile	2006-07-20 17:57:41.000000000 +0200
> @@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595
>  obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
>  obj-$(CONFIG_SENSORS_SMSC47M1)	+= smsc47m1.o
>  obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
> +obj-$(CONFIG_SENSORS_SCH311X)	+= sch311x.o
>  obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
>  obj-$(CONFIG_SENSORS_VT8231)	+= vt8231.o
>  obj-$(CONFIG_SENSORS_W83627EHF)	+= w83627ehf.o
> diff -uprN -X ./linux-2.6.18-rc2-vanilla/Documentation/dontdiff linux-2.6.18-rc2-vanilla/drivers/hwmon/sch311x.c linux-2.6.18-rc2-sch311x/drivers/hwmon/sch311x.c
> --- linux-2.6.18-rc2-vanilla/drivers/hwmon/sch311x.c	1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.18-rc2-sch311x/drivers/hwmon/sch311x.c	2006-07-24 12:36:02.000000000 +0200
> @@ -0,0 +1,629 @@
> +/*
> +    sch311x.c - Part of lm_sensors, Linux kernel modules
> +			for hardware monitoring
> +
> +    Supports the SMSC SCH311x Super-I/O chips.
> +
> +    Author: Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>
> +	Copyright (C) 2006 Kontron Modular Computers
> +
> +    derived in part from smsc47m1.c and smsc47b397:
> +	Copyright (C) 2002 Mark D. Studebaker <mdsxyz123 at yahoo.com>
> +	Copyright (C) 2004 Jean Delvare <khali at linux-fr.org>
> +	Copyright (C) 2004 Mark M. Hoffman <mhoffman at lightlink.com>
> +
> +    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; either version 2 of the License, or
> +    (at your option) any later version.
> +
> +    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., 675 Mass Ave, Cambridge, MA 02139, USA.
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-isa.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/mutex.h>
> +#include <asm/io.h>
> +
> +/* Address is autodetected, there is no default value */
> +static unsigned short address;
> +
> +/* Miscellaneous defines */
> +
> +#define SUPERIO_REG_DEVID		0x20
> +#define SUPERIO_REG_DEVREV		0x21
> +#define SUPERIO_REG_BASE_MSB		0x60
> +#define SUPERIO_REG_BASE_LSB		0x61
> +#define SUPERIO_REG_LD			0x0A
> +
> +#define SMSC_EXTENT			0x02	/* hw mon registers size */
> +
> +#define SCH311X_REG_TEMP(nr)		(0x25 + nr)
> +#define SCH311X_REG_TEMP_HIGH(nr)	(0x4f + (nr*2))
> +#define SCH311X_REG_TEMP_LOW(nr)	(0x4e + (nr*2))
> +#define SCH311X_REG_FANTACH_LSB(nr)	(0x28 + (nr*2))
> +#define SCH311X_REG_FANTACH_MSB(nr)	(0x29 + (nr*2))
> +#define SCH311X_REG_FANTACH_MIN_LSB(nr)	(0x54 + (nr*2))
> +#define SCH311X_REG_FANTACH_MIN_MSB(nr)	(0x55 + (nr*2))
> +
> +static const u8 sch311x_volt_regs[7] = {
> +	0x20	/*	2.5 V */,
> +	0x21	/* Vccp	1.5 V */,
> +	0x22	/* VCC	3.3 V */,
> +	0x23	/*	  5 V */,
> +	0x24	/*	 12 V */,
> +	0x99	/* VTR	3.3 V */,
> +	0x9a	/* Vbat	3.0 V */
> +};
> +
> +/* max volt values multiplied by 1000 */
> +static const u16 sch311x_volt_max_vals[7] = {
> +	6640	/*	2.5 V */,
> +	3000	/* Vccp	1.5 V */,
> +	4380	/* VCC	3.3 V */,
> +	6640	/*	  5 V */,
> +	16000	/*	 12 V */,
> +	4380	/* VTR	3.3 V */,
> +	4380	/* Vbat	3.0 V */
> +};
> +
> +#define SCH311X_REG_IN(nr)	(sch311x_volt_regs[nr])
> +
> +static const u8 sch311x_volt_lo_regs[7] = {
> +	0x44, 0x46, 0x48, 0x4a, 0x4c, 0x9b, 0x9d
> +};
> +
> +#define SCH311X_REG_MIN(nr)	(sch311x_volt_lo_regs[nr])
> +
> +static const u8 sch311x_volt_hi_regs[7] = {
> +	0x45, 0x47, 0x49, 0x4b, 0x4d, 0x9c, 0x9e
> +};
> +
> +#define SCH311X_REG_MAX(nr)	(sch311x_volt_hi_regs[nr])
> +
> +/* Interrupt enable voltages */
> +#define SCH311X_REG_INT_VOLT_EN	(0x7e)
> +
> +#define VOLT_EN_2_5V		(1<<2)
> +#define VOLT_EN_VCPP		(1<<3)
> +#define VOLT_EN_VCC		(1<<7)
> +#define VOLT_EN_5V		(1<<5)
> +#define VOLT_EN_12V		(1<<6)
> +#define VOLT_EN_VRT		(1<<4)
> +#define VOLT_EN_VBAT		(1<<1)
> +
> +/* Interrupt status registers */
> +#define SCH311X_REG_ISR1	(0x41)
> +#define SCH311X_REG_ISR2	(0x42)
> +#define SCH311X_REG_ISR3	(0x83)
> +
> +/* Alarm bits meaning */
> +#define ISR_TEMP3		(6)
> +#define ISR_TEMP2		(5)
> +#define ISR_TEMP1		(4)
> +#define ISR_5V			(3)
> +#define ISR_VCC			(2)
> +#define ISR_Vccp		(1)
> +#define ISR_2_5V		(0)
> +
> +#define ISR_ERR3		(15)
> +#define ISR_ERR1		(14)
> +#define ISR_FAN_3		(12)
> +#define ISR_FAN_2		(11)
> +#define ISR_FAN_1		(10)
> +#define ISR_12V			(8)
> +
> +#define ISR_VRT			(16)
> +#define ISR_Vbat		(17)
> +
> +/* logical device for hardware monitoring is 0x0A */
> +#define superio_select() superio_outb(0x07, SUPERIO_REG_LD)
> +
> +/* Super-I/0 registers and commands */
> +#define	REG	0x2e	/* The register to read/write */
> +#define	VAL	0x2f	/* The value to read/write */
> +
> +static inline void
> +superio_outb(int reg, int val)
> +{
> +	outb(reg, REG);
> +	outb(val, VAL);
> +}
> +
> +static inline int
> +superio_inb(int reg)
> +{
> +	outb(reg, REG);
> +	return inb(VAL);
> +}
> +
> +static inline void
> +superio_enter(void)
> +{
> +	outb(0x55, REG);
> +}
> +
> +static inline void
> +superio_exit(void)
> +{
> +	outb(0xAA, REG);
> +}
> +
> +struct sch311x_data {
> +	struct i2c_client client;
> +	struct class_device *class_dev;
> +	struct mutex lock;
> +
> +	struct mutex update_lock;
> +	unsigned long last_updated;	/* In jiffies */
> +	int valid;
> +
> +	u32 alarms;		/* Register value of interrupt status */
> +
> +	s8 temp[3];		/* register value */
> +	s8 temp_high[3];	/* register value */
> +	s8 temp_low[3];		/* register value */
> +
> +	u8 in[7];		/* register value */
> +	u8 in_max[7];		/* register value */
> +	u8 in_min[7];		/* register value */
> +};
> +
> +static int sch311x_detect(struct i2c_adapter *adapter);
> +static int sch311x_detach_client(struct i2c_client *client);
> +
> +static struct i2c_driver sch311x_driver = {
> +	.driver = {
> +		.name	= "sch311x",
> +	},
> +	.attach_adapter	= sch311x_detect,
> +	.detach_client	= sch311x_detach_client,
> +};
> +
> +static int sch311x_read_value(struct i2c_client *client, u8 reg)
> +{
> +	struct sch311x_data *data = i2c_get_clientdata(client);
> +	int res;
> +
> +	mutex_lock(&data->lock);
> +
> +	outb(reg, client->addr);
> +	res = inb_p(client->addr + 1);
> +
> +	mutex_unlock(&data->lock);
> +	return res;
> +}
> +
> +static void sch311x_write_value(struct i2c_client *client, u8 reg, u8 value)
> +{
> +	struct sch311x_data *data = i2c_get_clientdata(client);
> +
> +	mutex_lock(&data->lock);
> +
> +	outb(reg, client->addr);
> +	outb(value, client->addr + 1);
> +
> +	mutex_unlock(&data->lock);
> +}
> +
> +static struct sch311x_data *sch311x_update_device(struct device *dev)
> +{
> + 	struct i2c_client *client = to_i2c_client(dev);
> +	struct sch311x_data *data = i2c_get_clientdata(client);
> +	int i;
> +
> +	mutex_lock(&data->update_lock);
> +
> +	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
> +		dev_dbg(&client->dev, "starting device update...\n");
> +
> +		/* read alarms */
> +		data->alarms = sch311x_read_value(client,
> +				SCH311X_REG_ISR1);
> +		data->alarms|= sch311x_read_value(client,
> +				SCH311X_REG_ISR2) << 8;
> +		data->alarms|= sch311x_read_value(client,
> +				SCH311X_REG_ISR3) << 16;
> +
> +		/* 3 temperature inputs */
> +		for (i = 0; i < 3; i++) {
> +			data->temp[i] = sch311x_read_value(client,
> +					SCH311X_REG_TEMP(i));
> +			data->temp_high[i] = sch311x_read_value(client,
> +					SCH311X_REG_TEMP_HIGH(i));
> +			data->temp_low[i] = sch311x_read_value(client,
> +					SCH311X_REG_TEMP_LOW(i));
> +		}
> +		/* 7 voltage inputs */
> +		for (i = 0; i < 7; i++) {
> +			data->in[i] = sch311x_read_value(client,
> +					SCH311X_REG_IN(i));
> +			data->in_max[i] = sch311x_read_value(client,
> +					SCH311X_REG_MAX(i));
> +			data->in_min[i] = sch311x_read_value(client,
> +					SCH311X_REG_MIN(i));
> +		}
> +
> +		/* clear alarms */
> +		sch311x_write_value(client, SCH311X_REG_ISR1, 0xff);
> +		sch311x_write_value(client, SCH311X_REG_ISR2, 0xff);
> +
> +		data->last_updated = jiffies;
> +		data->valid = 1;
> +
> +		dev_dbg(&client->dev, "... device update complete\n");
> +	}
> +
> +	mutex_unlock(&data->update_lock);
> +
> +	return data;
> +}
> +
> +static int temp_from_reg(u8 reg)
> +{
> +	return (s8)reg * 100;
> +}
> +
> +/* 0 <= nr <= 3 */
> +static ssize_t show_temp(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
> +}
> +
> +static ssize_t show_temp_high(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[nr]));
> +}
> +
> +static ssize_t show_temp_low(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", temp_from_reg(data->temp_low[nr]));
> +}
> +
> +#define sysfs_temp(num) \
> +static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_temp(dev, buf, num-1); \
> +} \
> +static ssize_t show_temp_high##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_temp_high(dev, buf, num-1); \
> +} \
> +static ssize_t show_temp_low##num(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_temp_low(dev, buf, num-1); \
> +} \
> +static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
> +
> +sysfs_temp(1);
> +sysfs_temp(2);
> +sysfs_temp(3);
> +
> +static void set_temp_high(struct device *dev, const char *buf, int nr)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	s32 val;
> +
> +	val = simple_strtol(buf, NULL, 10);
> +	sch311x_write_value(client, SCH311X_REG_TEMP_HIGH(nr), val/100);
> +}
> +
> +static void set_temp_low(struct device *dev, const char *buf, int nr)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	s32 val;
> +
> +	val = simple_strtol(buf, NULL, 10);
> +	sch311x_write_value(client, SCH311X_REG_TEMP_LOW(nr), val/100);
> +}
> +
> +#define sysfs_set_temp(num) \
> +static ssize_t set_temp_high##num(struct device *dev, struct device_attribute *attr, \
> +		const char *buf, size_t count) \
> +{ \
> +	set_temp_high(dev, buf, num - 1); \
> +	return count; \
> +} \
> +static ssize_t set_temp_low##num(struct device *dev, struct device_attribute *attr, \
> +		const char *buf, size_t count) \
> +{ \
> +	set_temp_low(dev, buf, num - 1); \
> +	return count; \
> +} \
> +static DEVICE_ATTR(temp##num##_low, S_IRUGO | S_IWUSR, \
> +		show_temp_low##num, set_temp_low##num); \
> +static DEVICE_ATTR(temp##num##_high, S_IRUGO | S_IWUSR, \
> +		show_temp_high##num, set_temp_high##num)
> +
> +sysfs_set_temp(1);
> +sysfs_set_temp(2);
> +sysfs_set_temp(3);
> +
> +#define device_create_file_temp(client, num) \
> +	device_create_file(&client->dev, &dev_attr_temp##num##_input); \
> +	device_create_file(&client->dev, &dev_attr_temp##num##_high); \
> +	device_create_file(&client->dev, &dev_attr_temp##num##_low)
> +
> +static int in_from_reg(u8 reg, u16 max_val)
> +{
> +	return (int) reg * max_val / 255;
> +}
> +
> +/* 0 <= nr <= 7 */
> +static ssize_t show_in(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", in_from_reg(data->in[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +static ssize_t show_max(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", in_from_reg(data->in_max[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +static ssize_t show_min(struct device *dev, char *buf, int nr)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	return sprintf(buf, "%d\n", in_from_reg(data->in_min[nr], sch311x_volt_max_vals[nr]));
> +}
> +
> +#define sysfs_in(num) \
> +static ssize_t show_in##num##_input(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_in(dev, buf, num); \
> +} \
> +static ssize_t show_in##num##_max(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_max(dev, buf, num); \
> +} \
> +static ssize_t show_in##num##_min(struct device *dev, struct device_attribute *attr, char *buf) \
> +{ \
> +	return show_min(dev, buf, num); \
> +} \
> +static DEVICE_ATTR(in##num##_input, S_IRUGO, show_in##num##_input, NULL)
> +
> +sysfs_in(0);
> +sysfs_in(1);
> +sysfs_in(2);
> +sysfs_in(3);
> +sysfs_in(4);
> +sysfs_in(5);
> +sysfs_in(6);
> +
> +static void set_min(struct device *dev, const char *buf, int nr)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	u32 val;
> +
> +	val = simple_strtoul(buf, NULL, 10) * 255 / sch311x_volt_max_vals[nr];
> +	sch311x_write_value(client, SCH311X_REG_MIN(nr), val);
> +}
> +
> +static void set_max(struct device *dev, const char *buf, int nr)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	u32 val;
> +
> +	val = simple_strtoul(buf, NULL, 10) * 255 / sch311x_volt_max_vals[nr];
> +	sch311x_write_value(client, SCH311X_REG_MAX(nr), val);
> +}
> +
> +#define sysfs_set_in(num) \
> +static ssize_t set_in##num##_min(struct device *dev, struct device_attribute *attr, \
> +		const char *buf, size_t count) \
> +{ \
> +	set_min(dev, buf, num); \
> +	return count; \
> +} \
> +static ssize_t set_in##num##_max(struct device *dev, struct device_attribute *attr, \
> +		const char *buf, size_t count) \
> +{ \
> +	set_max(dev, buf, num); \
> +	return count; \
> +} \
> +static DEVICE_ATTR(in##num##_min, S_IRUGO | S_IWUSR, \
> +		show_in##num##_min, set_in##num##_min); \
> +static DEVICE_ATTR(in##num##_max, S_IRUGO | S_IWUSR, \
> +		show_in##num##_max, set_in##num##_max)
> +
> +#define device_create_file_in(client, num) \
> +	device_create_file(&client->dev, &dev_attr_in##num##_input); \
> +	device_create_file(&client->dev, &dev_attr_in##num##_max); \
> +	device_create_file(&client->dev, &dev_attr_in##num##_min); \
> +	device_create_file(&client->dev, &sensor_dev_attr_in##num##_alarm.dev_attr)
> +
> +sysfs_set_in(0);
> +sysfs_set_in(1);
> +sysfs_set_in(2);
> +sysfs_set_in(3);
> +sysfs_set_in(4);
> +sysfs_set_in(5);
> +sysfs_set_in(6);
> +
> +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct sch311x_data *data = sch311x_update_device(dev);
> +	struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> +	int nr = sensor_attr->index;
> +	return sprintf(buf, "%u\n", (data->alarms & (1<<nr)) ? 1 : 0);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP1);
> +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP2);
> +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, ISR_TEMP3);
> +static SENSOR_DEVICE_ATTR(temp1_error, S_IRUGO, show_alarm, NULL, ISR_ERR1);
> +static SENSOR_DEVICE_ATTR(temp3_error, S_IRUGO, show_alarm, NULL, ISR_ERR3);
> +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, ISR_2_5V);
> +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, ISR_Vccp);
> +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, ISR_VCC);
> +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, ISR_5V);
> +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, ISR_12V);
> +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, ISR_VRT);
> +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, ISR_Vbat);
> +
> +static int sch311x_detach_client(struct i2c_client *client)
> +{
> +	struct sch311x_data *data = i2c_get_clientdata(client);
> +	int err;
> +
> +	hwmon_device_unregister(data->class_dev);
> +
> +	if ((err = i2c_detach_client(client)))
> +		return err;
> +
> +	release_region(client->addr, SMSC_EXTENT);
> +	kfree(data);
> +
> +	return 0;
> +}
> +
> +static int sch311x_detect(struct i2c_adapter *adapter)
> +{
> +	struct i2c_client *new_client;
> +	struct sch311x_data *data;
> +	int err = 0;
> +	u8 mon_volt;
> +
> +	if (!request_region(address, SMSC_EXTENT,
> +			    sch311x_driver.driver.name)) {
> +		dev_err(&adapter->dev, "Region 0x%x already in use!\n",
> +			address);
> +		return -EBUSY;
> +	}
> +
> +	if (!(data = kzalloc(sizeof(struct sch311x_data), GFP_KERNEL))) {
> +		err = -ENOMEM;
> +		goto error_release;
> +	}
> +
> +	new_client = &data->client;
> +	i2c_set_clientdata(new_client, data);
> +	new_client->addr = address;
> +	mutex_init(&data->lock);
> +	new_client->adapter = adapter;
> +	new_client->driver = &sch311x_driver;
> +	new_client->flags = 0;
> +
> +	strlcpy(new_client->name, "sch311x", I2C_NAME_SIZE);
> +
> +	mutex_init(&data->update_lock);
> +
> +	if ((err = i2c_attach_client(new_client)))
> +		goto error_free;
> +
> +	mon_volt = superio_inb(SCH311X_REG_INT_VOLT_EN);
> +
> +	data->class_dev = hwmon_device_register(&new_client->dev);
> +	if (IS_ERR(data->class_dev)) {
> +		err = PTR_ERR(data->class_dev);
> +		goto error_detach;
> +	}
> +
> +	/* temp values */
> +	device_create_file_temp(new_client, 1);
> +	device_create_file(&new_client->dev, &sensor_dev_attr_temp1_alarm.dev_attr);
> +	device_create_file(&new_client->dev, &sensor_dev_attr_temp1_error.dev_attr);
> +	device_create_file_temp(new_client, 2);
> +	device_create_file(&new_client->dev, &sensor_dev_attr_temp2_alarm.dev_attr);
> +	device_create_file_temp(new_client, 3);
> +	device_create_file(&new_client->dev, &sensor_dev_attr_temp3_alarm.dev_attr);
> +	device_create_file(&new_client->dev, &sensor_dev_attr_temp3_error.dev_attr);
> +
> +	/* voltage values */
> +	if (mon_volt & VOLT_EN_2_5V) device_create_file_in(new_client, 0);
> +	if (mon_volt & VOLT_EN_VCPP) device_create_file_in(new_client, 1);
> +	if (mon_volt & VOLT_EN_VCC) device_create_file_in(new_client, 2);
> +	if (mon_volt & VOLT_EN_5V) device_create_file_in(new_client, 3);
> +	if (mon_volt & VOLT_EN_12V) device_create_file_in(new_client, 4);
> +	if (mon_volt & VOLT_EN_VRT) device_create_file_in(new_client, 5);
> +	if (mon_volt & VOLT_EN_VBAT) device_create_file_in(new_client, 6);
> +
> +	return 0;
> +
> +error_detach:
> +	i2c_detach_client(new_client);
> +error_free:
> +	kfree(data);
> +error_release:
> +	release_region(address, SMSC_EXTENT);
> +	return err;
> +}
> +
> +static int __init sch311x_find(unsigned short *addr)
> +{
> +	u8 id, rev;
> +
> +	superio_enter();
> +	id = superio_inb(SUPERIO_REG_DEVID);
> +
> +	if ((id & 0xfc) != 0x7c) {
> +		superio_exit();
> +		return -ENODEV;
> +	}
> +
> +	rev = superio_inb(SUPERIO_REG_DEVREV);
> +
> +	superio_select();
> +
> +	/*
> +	 * base address to "Runtime Registers"
> +	 * plus offset to HWM Index/Data
> +	 * registers
> +	 */
> +	*addr = ((superio_inb(SUPERIO_REG_BASE_MSB) << 8)
> +		 |  superio_inb(SUPERIO_REG_BASE_LSB))
> +		 + 0x70;
> +
> +	printk(KERN_INFO "sch311x: found SMSC %s "
> +		"(base address 0x%04x, revision %u)\n",
> +		id == 0x7c ? "SCH3112" :
> +		id == 0x7d ? "SCH3114" :
> +		id == 0x7f ? "SCH3116" :
> +		"SCH311X id=0x7e <Reserved>"
> +		, *addr, rev);
> +
> +	superio_exit();
> +
> +	return 0;
> +}
> +
> +static int __init sch311x_init(void)
> +{
> +	int ret;
> +
> +	if ((ret = sch311x_find(&address)))
> +		return ret;
> +
> +	return i2c_isa_add_driver(&sch311x_driver);
> +}
> +
> +static void __exit sch311x_exit(void)
> +{
> +	i2c_isa_del_driver(&sch311x_driver);
> +}
> +
> +MODULE_AUTHOR("Marcin Dawidowicz <Marcin.Dawidowicz at kontron.pl>");
> +MODULE_DESCRIPTION("SMSC SCH311x driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(sch311x_init);
> +module_exit(sch311x_exit);




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux