w83781d.c

来自「linux 内核源代码」· C语言 代码 · 共 1,992 行 · 第 1/4 页

C
1,992
字号
/*    w83781d.c - Part of lm_sensors, Linux kernel modules for hardware                monitoring    Copyright (c) 1998 - 2001  Frodo Looijaard <frodol@dds.nl>,                               Philip Edelbrock <phil@netroedge.com>,                               and Mark Studebaker <mdsxyz123@yahoo.com>    Copyright (c) 2007         Jean Delvare <khali@linux-fr.org>    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.*//*    Supports following chips:    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA    as99127f	7	3	0	3	0x31	0x12c3	yes	no    as99127f rev.2 (type_name = as99127f)	0x31	0x5ca3	yes	no    w83781d	7	3	0	3	0x10-1	0x5ca3	yes	yes    w83627hf	9	3	2	3	0x21	0x5ca3	yes	yes(LPC)    w83782d	9	3	2-4	3	0x30	0x5ca3	yes	yes    w83783s	5-6	3	2	1-2	0x40	0x5ca3	yes	no*/#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/platform_device.h>#include <linux/ioport.h>#include <linux/hwmon.h>#include <linux/hwmon-vid.h>#include <linux/hwmon-sysfs.h>#include <linux/sysfs.h>#include <linux/err.h>#include <linux/mutex.h>#include <asm/io.h>#include "lm75.h"/* ISA device, if found */static struct platform_device *pdev;/* Addresses to scan */static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,					0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,					0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };static unsigned short isa_address = 0x290;/* Insmod parameters */I2C_CLIENT_INSMOD_5(w83781d, w83782d, w83783s, w83627hf, as99127f);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 one to reset chip on load");static int init = 1;module_param(init, bool, 0);MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");/* Constants specified below *//* Length of ISA address segment */#define W83781D_EXTENT			8/* Where are the ISA address/data registers relative to the base address */#define W83781D_ADDR_REG_OFFSET		5#define W83781D_DATA_REG_OFFSET		6/* The device registers *//* in nr from 0 to 8 */#define W83781D_REG_IN_MAX(nr)		((nr < 7) ? (0x2b + (nr) * 2) : \						    (0x554 + (((nr) - 7) * 2)))#define W83781D_REG_IN_MIN(nr)		((nr < 7) ? (0x2c + (nr) * 2) : \						    (0x555 + (((nr) - 7) * 2)))#define W83781D_REG_IN(nr)		((nr < 7) ? (0x20 + (nr)) : \						    (0x550 + (nr) - 7))/* fan nr from 0 to 2 */#define W83781D_REG_FAN_MIN(nr)		(0x3b + (nr))#define W83781D_REG_FAN(nr)		(0x28 + (nr))#define W83781D_REG_BANK		0x4E#define W83781D_REG_TEMP2_CONFIG	0x152#define W83781D_REG_TEMP3_CONFIG	0x252/* temp nr from 1 to 3 */#define W83781D_REG_TEMP(nr)		((nr == 3) ? (0x0250) : \					((nr == 2) ? (0x0150) : \						     (0x27)))#define W83781D_REG_TEMP_HYST(nr)	((nr == 3) ? (0x253) : \					((nr == 2) ? (0x153) : \						     (0x3A)))#define W83781D_REG_TEMP_OVER(nr)	((nr == 3) ? (0x255) : \					((nr == 2) ? (0x155) : \						     (0x39)))#define W83781D_REG_CONFIG		0x40/* Interrupt status (W83781D, AS99127F) */#define W83781D_REG_ALARM1		0x41#define W83781D_REG_ALARM2		0x42/* Real-time status (W83782D, W83783S, W83627HF) */#define W83782D_REG_ALARM1		0x459#define W83782D_REG_ALARM2		0x45A#define W83782D_REG_ALARM3		0x45B#define W83781D_REG_BEEP_CONFIG		0x4D#define W83781D_REG_BEEP_INTS1		0x56#define W83781D_REG_BEEP_INTS2		0x57#define W83781D_REG_BEEP_INTS3		0x453	/* not on W83781D */#define W83781D_REG_VID_FANDIV		0x47#define W83781D_REG_CHIPID		0x49#define W83781D_REG_WCHIPID		0x58#define W83781D_REG_CHIPMAN		0x4F#define W83781D_REG_PIN			0x4B/* 782D/783S only */#define W83781D_REG_VBAT		0x5D/* PWM 782D (1-4) and 783S (1-2) only */static const u8 W83781D_REG_PWM[] = { 0x5B, 0x5A, 0x5E, 0x5F };#define W83781D_REG_PWMCLK12		0x5C#define W83781D_REG_PWMCLK34		0x45C#define W83781D_REG_I2C_ADDR		0x48#define W83781D_REG_I2C_SUBADDR		0x4A/* The following are undocumented in the data sheets however we   received the information in an email from Winbond tech support *//* Sensor selection - not on 781d */#define W83781D_REG_SCFG1		0x5Dstatic const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };#define W83781D_REG_SCFG2		0x59static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };#define W83781D_DEFAULT_BETA		3435/* RT Table registers */#define W83781D_REG_RT_IDX		0x50#define W83781D_REG_RT_VAL		0x51/* Conversions */#define IN_TO_REG(val)			SENSORS_LIMIT(((val) + 8) / 16, 0, 255)#define IN_FROM_REG(val)		((val) * 16)static inline u8FAN_TO_REG(long rpm, int div){	if (rpm == 0)		return 255;	rpm = SENSORS_LIMIT(rpm, 1, 1000000);	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);}static inline longFAN_FROM_REG(u8 val, int div){	if (val == 0)		return -1;	if (val == 255)		return 0;	return 1350000 / (val * div);}#define TEMP_TO_REG(val)		SENSORS_LIMIT((val) / 1000, -127, 128)#define TEMP_FROM_REG(val)		((val) * 1000)#define BEEP_MASK_FROM_REG(val,type)	((type) == as99127f ? \					 (val) ^ 0x7fff : (val))#define BEEP_MASK_TO_REG(val,type)	((type) == as99127f ? \					 (~(val)) & 0x7fff : (val) & 0xffffff)#define DIV_FROM_REG(val)		(1 << (val))static inline u8DIV_TO_REG(long val, enum chips type){	int i;	val = SENSORS_LIMIT(val, 1,			    ((type == w83781d			      || type == as99127f) ? 8 : 128)) >> 1;	for (i = 0; i < 7; i++) {		if (val == 0)			break;		val >>= 1;	}	return i;}/* There are some complications in a module like this. First off, W83781D chips   may be both present on the SMBus and the ISA bus, and we have to handle   those cases separately at some places. Second, there might be several   W83781D chips available (well, actually, that is probably never done; but   it is a clean illustration of how to handle a case like that). Finally,   a specific chip may be attached to *both* ISA and SMBus, and we would   not like to detect it double. Fortunately, in the case of the W83781D at   least, a register tells us what SMBus address we are on, so that helps   a bit - except if there could be more than one SMBus. Groan. No solution   for this yet. *//* For ISA chips, we abuse the i2c_client addr and name fields. We also use   the driver field to differentiate between I2C and ISA chips. */struct w83781d_data {	struct i2c_client client;	struct device *hwmon_dev;	struct mutex lock;	enum chips type;	struct mutex update_lock;	char valid;		/* !=0 if following fields are valid */	unsigned long last_updated;	/* In jiffies */	struct i2c_client *lm75[2];	/* for secondary I2C addresses */	/* array of 2 pointers to subclients */	u8 in[9];		/* Register value - 8 & 9 for 782D only */	u8 in_max[9];		/* Register value - 8 & 9 for 782D only */	u8 in_min[9];		/* Register value - 8 & 9 for 782D only */	u8 fan[3];		/* Register value */	u8 fan_min[3];		/* Register value */	s8 temp;		/* Register value */	s8 temp_max;		/* Register value */	s8 temp_max_hyst;	/* Register value */	u16 temp_add[2];	/* Register value */	u16 temp_max_add[2];	/* Register value */	u16 temp_max_hyst_add[2];	/* Register value */	u8 fan_div[3];		/* Register encoding, shifted right */	u8 vid;			/* Register encoding, combined */	u32 alarms;		/* Register encoding, combined */	u32 beep_mask;		/* Register encoding, combined */	u8 beep_enable;		/* Boolean */	u8 pwm[4];		/* Register value */	u8 pwm2_enable;		/* Boolean */	u16 sens[3];		/* 782D/783S only.				   1 = pentium diode; 2 = 3904 diode;				   4 = thermistor */	u8 vrm;};static int w83781d_attach_adapter(struct i2c_adapter *adapter);static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind);static int w83781d_detach_client(struct i2c_client *client);static int __devinit w83781d_isa_probe(struct platform_device *pdev);static int __devexit w83781d_isa_remove(struct platform_device *pdev);static int w83781d_read_value(struct w83781d_data *data, u16 reg);static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value);static struct w83781d_data *w83781d_update_device(struct device *dev);static void w83781d_init_device(struct device *dev);static struct i2c_driver w83781d_driver = {	.driver = {		.name = "w83781d",	},	.id = I2C_DRIVERID_W83781D,	.attach_adapter = w83781d_attach_adapter,	.detach_client = w83781d_detach_client,};static struct platform_driver w83781d_isa_driver = {	.driver = {		.owner = THIS_MODULE,		.name = "w83781d",	},	.probe = w83781d_isa_probe,	.remove = w83781d_isa_remove,};/* following are the sysfs callback functions */#define show_in_reg(reg) \static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \		char *buf) \{ \	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \	struct w83781d_data *data = w83781d_update_device(dev); \	return sprintf(buf, "%ld\n", \		       (long)IN_FROM_REG(data->reg[attr->index])); \}show_in_reg(in);show_in_reg(in_min);show_in_reg(in_max);#define store_in_reg(REG, reg) \static ssize_t store_in_##reg (struct device *dev, struct device_attribute \		*da, const char *buf, size_t count) \{ \	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \	struct w83781d_data *data = dev_get_drvdata(dev); \	int nr = attr->index; \	u32 val; \	 \	val = simple_strtoul(buf, NULL, 10); \	 \	mutex_lock(&data->update_lock); \	data->in_##reg[nr] = IN_TO_REG(val); \	w83781d_write_value(data, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \	 \	mutex_unlock(&data->update_lock); \	return count; \}store_in_reg(MIN, min);store_in_reg(MAX, max);#define sysfs_in_offsets(offset) \static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \		show_in, NULL, offset); \static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \		show_in_min, store_in_min, offset); \static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \		show_in_max, store_in_max, offset)sysfs_in_offsets(0);sysfs_in_offsets(1);sysfs_in_offsets(2);sysfs_in_offsets(3);sysfs_in_offsets(4);sysfs_in_offsets(5);sysfs_in_offsets(6);sysfs_in_offsets(7);sysfs_in_offsets(8);#define show_fan_reg(reg) \static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \		char *buf) \{ \	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \	struct w83781d_data *data = w83781d_update_device(dev); \	return sprintf(buf,"%ld\n", \		FAN_FROM_REG(data->reg[attr->index], \			DIV_FROM_REG(data->fan_div[attr->index]))); \}show_fan_reg(fan);show_fan_reg(fan_min);static ssize_tstore_fan_min(struct device *dev, struct device_attribute *da,		const char *buf, size_t count){	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);	struct w83781d_data *data = dev_get_drvdata(dev);	int nr = attr->index;	u32 val;	val = simple_strtoul(buf, NULL, 10);	mutex_lock(&data->update_lock);	data->fan_min[nr] =	    FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));	w83781d_write_value(data, W83781D_REG_FAN_MIN(nr),			    data->fan_min[nr]);	mutex_unlock(&data->update_lock);	return count;}static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,		show_fan_min, store_fan_min, 0);static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR,		show_fan_min, store_fan_min, 1);static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR,		show_fan_min, store_fan_min, 2);#define show_temp_reg(reg) \static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \		char *buf) \{ \	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \	struct w83781d_data *data = w83781d_update_device(dev); \	int nr = attr->index; \	if (nr >= 2) {	/* TEMP2 and TEMP3 */ \		return sprintf(buf,"%d\n", \			LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \	} else {	/* TEMP1 */ \		return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \	} \}show_temp_reg(temp);show_temp_reg(temp_max);show_temp_reg(temp_max_hyst);#define store_temp_reg(REG, reg) \static ssize_t store_temp_##reg (struct device *dev, \		struct device_attribute *da, const char *buf, size_t count) \{ \	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \	struct w83781d_data *data = dev_get_drvdata(dev); \	int nr = attr->index; \	long val; \	 \	val = simple_strtol(buf, NULL, 10); \	 \	mutex_lock(&data->update_lock); \	 \	if (nr >= 2) {	/* TEMP2 and TEMP3 */ \		data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \		w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \				data->temp_##reg##_add[nr-2]); \	} else {	/* TEMP1 */ \		data->temp_##reg = TEMP_TO_REG(val); \		w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \			data->temp_##reg); \	} \	 \	mutex_unlock(&data->update_lock); \	return count; \}store_temp_reg(OVER, max);store_temp_reg(HYST, max_hyst);#define sysfs_temp_offsets(offset) \static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \		show_temp, NULL, offset); \static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \		show_temp_max, store_temp_max, offset); \static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \		show_temp_max_hyst, store_temp_max_hyst, offset);sysfs_temp_offsets(1);sysfs_temp_offsets(2);sysfs_temp_offsets(3);static ssize_tshow_vid_reg(struct device *dev, struct device_attribute *attr, char *buf){	struct w83781d_data *data = w83781d_update_device(dev);	return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));}static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);static ssize_tshow_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf){	struct w83781d_data *data = dev_get_drvdata(dev);	return sprintf(buf, "%ld\n", (long) data->vrm);}static ssize_tstore_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){	struct w83781d_data *data = dev_get_drvdata(dev);	u32 val;	val = simple_strtoul(buf, NULL, 10);	data->vrm = val;	return count;}static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);static ssize_tshow_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf){	struct w83781d_data *data = w83781d_update_device(dev);	return sprintf(buf, "%u\n", data->alarms);}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,		char *buf){	struct w83781d_data *data = w83781d_update_device(dev);	int bitnr = to_sensor_dev_attr(attr)->index;	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);}/* The W83781D has a single alarm bit for temp2 and temp3 */static ssize_t show_temp3_alarm(struct device *dev,		struct device_attribute *attr, char *buf){	struct w83781d_data *data = w83781d_update_device(dev);	int bitnr = (data->type == w83781d) ? 5 : 13;	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?