⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lm87.c

📁 h内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * lm87.c * * Copyright (C) 2000       Frodo Looijaard <frodol@dds.nl> *                          Philip Edelbrock <phil@netroedge.com> *                          Stephen Rousset <stephen.rousset@rocketlogix.com> *                          Dan Eaton <dan.eaton@rocketlogix.com> * Copyright (C) 2004       Jean Delvare <khali@linux-fr.org> * * Original port to Linux 2.6 by Jeff Oliver. * * The LM87 is a sensor chip made by National Semiconductor. It monitors up * to 8 voltages (including its own power source), up to three temperatures * (its own plus up to two external ones) and up to two fans. The default * configuration is 6 voltages, two temperatures and two fans (see below). * Voltages are scaled internally with ratios such that the nominal value of * each voltage correspond to a register value of 192 (which means a * resolution of about 0.5% of the nominal value). Temperature values are * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete * datasheet can be obtained from National's website at: *   http://www.national.com/pf/LM/LM87.html * * Some functions share pins, so not all functions are available at the same * time. Which are depends on the hardware setup. This driver assumes that * the BIOS configured the chip correctly. In that respect, it  differs from * the original driver (from lm_sensors for Linux 2.4), which would force the * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual * chipset wiring. * For reference, here is the list of exclusive functions: *  - in0+in5 (default) or temp3 *  - fan1 (default) or in6 *  - fan2 (default) or in7 *  - VID lines (default) or IRQ lines (not handled by this driver) * * The LM87 additionally features an analog output, supposedly usable to * control the speed of a fan. All new chips use pulse width modulation * instead. The LM87 is the only hardware monitoring chipset I know of * which uses amplitude modulation. Be careful when using this feature. * * 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/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/i2c-sensor.h>#include <linux/i2c-vid.h>/* * Addresses to scan * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e. */static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };/* * Insmod parameters */SENSORS_INSMOD_1(lm87);/* * The LM87 registers *//* nr in 0..5 */#define LM87_REG_IN(nr)			(0x20 + (nr))#define LM87_REG_IN_MAX(nr)		(0x2B + (nr) * 2)#define LM87_REG_IN_MIN(nr)		(0x2C + (nr) * 2)/* nr in 0..1 */#define LM87_REG_AIN(nr)		(0x28 + (nr))#define LM87_REG_AIN_MIN(nr)		(0x1A + (nr))#define LM87_REG_AIN_MAX(nr)		(0x3B + (nr))static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 };static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B };static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };#define LM87_REG_TEMP_HW_INT_LOCK	0x13#define LM87_REG_TEMP_HW_EXT_LOCK	0x14#define LM87_REG_TEMP_HW_INT		0x17#define LM87_REG_TEMP_HW_EXT		0x18/* nr in 0..1 */#define LM87_REG_FAN(nr)		(0x28 + (nr))#define LM87_REG_FAN_MIN(nr)		(0x3B + (nr))#define LM87_REG_AOUT			0x19#define LM87_REG_CONFIG			0x40#define LM87_REG_CHANNEL_MODE		0x16#define LM87_REG_VID_FAN_DIV		0x47#define LM87_REG_VID4			0x49#define LM87_REG_ALARMS1		0x41#define LM87_REG_ALARMS2		0x42#define LM87_REG_COMPANY_ID		0x3E#define LM87_REG_REVISION		0x3F/* * Conversions and various macros * The LM87 uses signed 8-bit values for temperatures. */#define IN_FROM_REG(reg,scale)	(((reg) * (scale) + 96) / 192)#define IN_TO_REG(val,scale)	((val) <= 0 ? 0 : \				 (val) * 192 >= (scale) * 255 ? 255 : \				 ((val) * 192 + (scale)/2) / (scale))#define TEMP_FROM_REG(reg)	((reg) * 1000)#define TEMP_TO_REG(val)	((val) <= -127500 ? -128 : \				 (val) >= 126500 ? 127 : \				 (((val) < 0 ? (val)-500 : (val)+500) / 1000))#define FAN_FROM_REG(reg,div)	((reg) == 255 || (reg) == 0 ? 0 : \				 1350000 + (reg)*(div) / 2) / ((reg)*(div))#define FAN_TO_REG(val,div)	((val)*(div) * 255 <= 1350000 ? 255 : \				 (1350000 + (val)*(div) / 2) / ((val)*(div)))#define FAN_DIV_FROM_REG(reg)	(1 << (reg))/* analog out is 9.80mV/LSB */#define AOUT_FROM_REG(reg)	(((reg) * 98 + 5) / 10)#define AOUT_TO_REG(val)	((val) <= 0 ? 0 : \				 (val) >= 2500 ? 255 : \				 ((val) * 10 + 49) / 98)/* nr in 0..1 */#define CHAN_NO_FAN(nr)		(1 << (nr))#define CHAN_TEMP3		(1 << 2)#define CHAN_VCC_5V		(1 << 3)#define CHAN_NO_VID		(1 << 8)/* * Functions declaration */static int lm87_attach_adapter(struct i2c_adapter *adapter);static int lm87_detect(struct i2c_adapter *adapter, int address, int kind);static void lm87_init_client(struct i2c_client *client);static int lm87_detach_client(struct i2c_client *client);static struct lm87_data *lm87_update_device(struct device *dev);/* * Driver data (common to all clients) */static struct i2c_driver lm87_driver = {	.owner		= THIS_MODULE,	.name		= "lm87",	.id		= I2C_DRIVERID_LM87,	.flags		= I2C_DF_NOTIFY,	.attach_adapter	= lm87_attach_adapter,	.detach_client	= lm87_detach_client,};/* * Client data (each client gets its own) */struct lm87_data {	struct i2c_client client;	struct semaphore update_lock;	char valid; /* zero until following fields are valid */	unsigned long last_updated; /* In jiffies */	u8 channel;		/* register value */	u8 in[8];		/* register value */	u8 in_max[8];		/* register value */	u8 in_min[8];		/* register value */	u16 in_scale[8];	s8 temp[3];		/* register value */	s8 temp_high[3];	/* register value */	s8 temp_low[3];		/* register value */	s8 temp_crit_int;	/* min of two register values */	s8 temp_crit_ext;	/* min of two register values */	u8 fan[2];		/* register value */	u8 fan_min[2];		/* register value */	u8 fan_div[2];		/* register value, shifted right */	u8 aout;		/* register value */	u16 alarms;		/* register values, combined */	u8 vid;			/* register values, combined */	u8 vrm;};/* * Internal variables */static int lm87_id;/* * Sysfs stuff */static inline int lm87_read_value(struct i2c_client *client, u8 reg){	return i2c_smbus_read_byte_data(client, reg);}static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value){	return i2c_smbus_write_byte_data(client, reg, value);}#define show_in(offset) \static ssize_t show_in##offset##_input(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \		       data->in_scale[offset])); \} \static ssize_t show_in##offset##_min(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \		       data->in_scale[offset])); \} \static ssize_t show_in##offset##_max(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \		       data->in_scale[offset])); \} \static DEVICE_ATTR(in##offset##_input, S_IRUGO, \		show_in##offset##_input, NULL);show_in(0);show_in(1);show_in(2);show_in(3);show_in(4);show_in(5);show_in(6);show_in(7);static void set_in_min(struct device *dev, const char *buf, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct lm87_data *data = i2c_get_clientdata(client);	long val = simple_strtol(buf, NULL, 10);	data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]);	lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) :			 LM87_REG_AIN_MIN(nr-6), data->in_min[nr]);}static void set_in_max(struct device *dev, const char *buf, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct lm87_data *data = i2c_get_clientdata(client);	long val = simple_strtol(buf, NULL, 10);	data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]);	lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) :			 LM87_REG_AIN_MAX(nr-6), data->in_max[nr]);}#define set_in(offset) \static ssize_t set_in##offset##_min(struct device *dev, \		const char *buf, size_t count) \{ \	set_in_min(dev, buf, offset); \	return count; \} \static ssize_t set_in##offset##_max(struct device *dev, \		const char *buf, size_t count) \{ \	set_in_max(dev, buf, offset); \	return count; \} \static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \		show_in##offset##_min, set_in##offset##_min); \static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \		show_in##offset##_max, set_in##offset##_max);set_in(0);set_in(1);set_in(2);set_in(3);set_in(4);set_in(5);set_in(6);set_in(7);#define show_temp(offset) \static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \} \static ssize_t show_temp##offset##_low(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \} \static ssize_t show_temp##offset##_high(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \}\static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \		show_temp##offset##_input, NULL);show_temp(1);show_temp(2);show_temp(3);static void set_temp_low(struct device *dev, const char *buf, int nr){    struct i2c_client *client = to_i2c_client(dev);    struct lm87_data *data = i2c_get_clientdata(client);    long val = simple_strtol(buf, NULL, 10);    data->temp_low[nr] = TEMP_TO_REG(val);    lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);}static void set_temp_high(struct device *dev, const char *buf, int nr){    struct i2c_client *client = to_i2c_client(dev);    struct lm87_data *data = i2c_get_clientdata(client);    long val = simple_strtol(buf, NULL, 10);    data->temp_high[nr] = TEMP_TO_REG(val);    lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);}#define set_temp(offset) \static ssize_t set_temp##offset##_low(struct device *dev, \		const char *buf, size_t count) \{ \	set_temp_low(dev, buf, offset-1); \	return count; \} \static ssize_t set_temp##offset##_high(struct device *dev, \		const char *buf, size_t count) \{ \	set_temp_high(dev, buf, offset-1); \	return count; \} \static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \		show_temp##offset##_high, set_temp##offset##_high); \static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \		show_temp##offset##_low, set_temp##offset##_low);set_temp(1);set_temp(2);set_temp(3);static ssize_t show_temp_crit_int(struct device *dev, char *buf){	struct lm87_data *data = lm87_update_device(dev);	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));}static ssize_t show_temp_crit_ext(struct device *dev, char *buf){	struct lm87_data *data = lm87_update_device(dev);	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));}static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL);static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL);static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL);#define show_fan(offset) \static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \		       FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \} \static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \		       FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \} \static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \{ \	struct lm87_data *data = lm87_update_device(dev); \	return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \} \static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \		show_fan##offset##_input, NULL);show_fan(1);show_fan(2);static void set_fan_min(struct device *dev, const char *buf, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct lm87_data *data = i2c_get_clientdata(client);	long val = simple_strtol(buf, NULL, 10);	data->fan_min[nr] = FAN_TO_REG(val,			    FAN_DIV_FROM_REG(data->fan_div[nr]));

⌨️ 快捷键说明

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