adm1031.c

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

C
1,016
字号
/*  adm1031.c - Part of lm_sensors, Linux kernel modules for hardware  monitoring  Based on lm75.c and lm85.c  Supports adm1030 / adm1031  Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>  Reworked by 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.*/#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/hwmon.h>#include <linux/err.h>#include <linux/mutex.h>/* Following macros takes channel parameter starting from 0 to 2 */#define ADM1031_REG_FAN_SPEED(nr)	(0x08 + (nr))#define ADM1031_REG_FAN_DIV(nr)		(0x20  + (nr))#define ADM1031_REG_PWM			(0x22)#define ADM1031_REG_FAN_MIN(nr)		(0x10 + (nr))#define ADM1031_REG_TEMP_MAX(nr)	(0x14  + 4*(nr))#define ADM1031_REG_TEMP_MIN(nr)	(0x15  + 4*(nr))#define ADM1031_REG_TEMP_CRIT(nr)	(0x16  + 4*(nr))#define ADM1031_REG_TEMP(nr)		(0xa + (nr))#define ADM1031_REG_AUTO_TEMP(nr)	(0x24 + (nr))#define ADM1031_REG_STATUS(nr)		(0x2 + (nr))#define ADM1031_REG_CONF1		0x0#define ADM1031_REG_CONF2		0x1#define ADM1031_REG_EXT_TEMP		0x6#define ADM1031_CONF1_MONITOR_ENABLE	0x01	/* Monitoring enable */#define ADM1031_CONF1_PWM_INVERT	0x08	/* PWM Invert */#define ADM1031_CONF1_AUTO_MODE		0x80	/* Auto FAN */#define ADM1031_CONF2_PWM1_ENABLE	0x01#define ADM1031_CONF2_PWM2_ENABLE	0x02#define ADM1031_CONF2_TACH1_ENABLE	0x04#define ADM1031_CONF2_TACH2_ENABLE	0x08#define ADM1031_CONF2_TEMP_ENABLE(chan)	(0x10 << (chan))/* Addresses to scan */static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };/* Insmod parameters */I2C_CLIENT_INSMOD_2(adm1030, adm1031);typedef u8 auto_chan_table_t[8][2];/* Each client has this additional data */struct adm1031_data {	struct i2c_client client;	struct device *hwmon_dev;	struct mutex update_lock;	int chip_type;	char valid;		/* !=0 if following fields are valid */	unsigned long last_updated;	/* In jiffies */	/* The chan_select_table contains the possible configurations for	 * auto fan control.	 */	auto_chan_table_t *chan_select_table;	u16 alarm;	u8 conf1;	u8 conf2;	u8 fan[2];	u8 fan_div[2];	u8 fan_min[2];	u8 pwm[2];	u8 old_pwm[2];	s8 temp[3];	u8 ext_temp[3];	u8 auto_temp[3];	u8 auto_temp_min[3];	u8 auto_temp_off[3];	u8 auto_temp_max[3];	s8 temp_min[3];	s8 temp_max[3];	s8 temp_crit[3];};static int adm1031_attach_adapter(struct i2c_adapter *adapter);static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);static void adm1031_init_client(struct i2c_client *client);static int adm1031_detach_client(struct i2c_client *client);static struct adm1031_data *adm1031_update_device(struct device *dev);/* This is the driver that will be inserted */static struct i2c_driver adm1031_driver = {	.driver = {		.name = "adm1031",	},	.attach_adapter = adm1031_attach_adapter,	.detach_client = adm1031_detach_client,};static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg){	return i2c_smbus_read_byte_data(client, reg);}static inline intadm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value){	return i2c_smbus_write_byte_data(client, reg, value);}#define TEMP_TO_REG(val)		(((val) < 0 ? ((val - 500) / 1000) : \					((val + 500) / 1000)))#define TEMP_FROM_REG(val)		((val) * 1000)#define TEMP_FROM_REG_EXT(val, ext)	(TEMP_FROM_REG(val) + (ext) * 125)#define FAN_FROM_REG(reg, div)		((reg) ? (11250 * 60) / ((reg) * (div)) : 0)static int FAN_TO_REG(int reg, int div){	int tmp;	tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div);	return tmp > 255 ? 255 : tmp;}#define FAN_DIV_FROM_REG(reg)		(1<<(((reg)&0xc0)>>6))#define PWM_TO_REG(val)			(SENSORS_LIMIT((val), 0, 255) >> 4)#define PWM_FROM_REG(val)		((val) << 4)#define FAN_CHAN_FROM_REG(reg)		(((reg) >> 5) & 7)#define FAN_CHAN_TO_REG(val, reg)	\	(((reg) & 0x1F) | (((val) << 5) & 0xe0))#define AUTO_TEMP_MIN_TO_REG(val, reg)	\	((((val)/500) & 0xf8)|((reg) & 0x7))#define AUTO_TEMP_RANGE_FROM_REG(reg)	(5000 * (1<< ((reg)&0x7)))#define AUTO_TEMP_MIN_FROM_REG(reg)	(1000 * ((((reg) >> 3) & 0x1f) << 2))#define AUTO_TEMP_MIN_FROM_REG_DEG(reg)	((((reg) >> 3) & 0x1f) << 2)#define AUTO_TEMP_OFF_FROM_REG(reg)		\	(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)#define AUTO_TEMP_MAX_FROM_REG(reg)		\	(AUTO_TEMP_RANGE_FROM_REG(reg) +	\	AUTO_TEMP_MIN_FROM_REG(reg))static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm){	int ret;	int range = val - AUTO_TEMP_MIN_FROM_REG(reg);	range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm);	ret = ((reg & 0xf8) |	       (range < 10000 ? 0 :		range < 20000 ? 1 :		range < 40000 ? 2 : range < 80000 ? 3 : 4));	return ret;}/* FAN auto control */#define GET_FAN_AUTO_BITFIELD(data, idx)	\	(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]/* The tables below contains the possible values for the auto fan  * control bitfields. the index in the table is the register value. * MSb is the auto fan control enable bit, so the four first entries * in the table disables auto fan control when both bitfields are zero. */static auto_chan_table_t auto_channel_select_table_adm1031 = {	{0, 0}, {0, 0}, {0, 0}, {0, 0},	{2 /*0b010 */ , 4 /*0b100 */ },	{2 /*0b010 */ , 2 /*0b010 */ },	{4 /*0b100 */ , 4 /*0b100 */ },	{7 /*0b111 */ , 7 /*0b111 */ },};static auto_chan_table_t auto_channel_select_table_adm1030 = {	{0, 0}, {0, 0}, {0, 0}, {0, 0},	{2 /*0b10 */		, 0},	{0xff /*invalid */	, 0},	{0xff /*invalid */	, 0},	{3 /*0b11 */		, 0},};/* That function checks if a bitfield is valid and returns the other bitfield * nearest match if no exact match where found. */static intget_fan_auto_nearest(struct adm1031_data *data,		     int chan, u8 val, u8 reg, u8 * new_reg){	int i;	int first_match = -1, exact_match = -1;	u8 other_reg_val =	    (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];	if (val == 0) {		*new_reg = 0;		return 0;	}	for (i = 0; i < 8; i++) {		if ((val == (*data->chan_select_table)[i][chan]) &&		    ((*data->chan_select_table)[i][chan ? 0 : 1] ==		     other_reg_val)) {			/* We found an exact match */			exact_match = i;			break;		} else if (val == (*data->chan_select_table)[i][chan] &&			   first_match == -1) {			/* Save the first match in case of an exact match has not been			 * found 			 */			first_match = i;		}	}	if (exact_match >= 0) {		*new_reg = exact_match;	} else if (first_match >= 0) {		*new_reg = first_match;	} else {		return -EINVAL;	}	return 0;}static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr){	struct adm1031_data *data = adm1031_update_device(dev);	return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));}static ssize_tset_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct adm1031_data *data = i2c_get_clientdata(client);	int val = simple_strtol(buf, NULL, 10);	u8 reg;	int ret;	u8 old_fan_mode;	old_fan_mode = data->conf1;	mutex_lock(&data->update_lock);		if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, &reg))) {		mutex_unlock(&data->update_lock);		return ret;	}	if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^ 	    (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {		if (data->conf1 & ADM1031_CONF1_AUTO_MODE){			/* Switch to Auto Fan Mode 			 * Save PWM registers 			 * Set PWM registers to 33% Both */			data->old_pwm[0] = data->pwm[0];			data->old_pwm[1] = data->pwm[1];			adm1031_write_value(client, ADM1031_REG_PWM, 0x55);		} else {			/* Switch to Manual Mode */			data->pwm[0] = data->old_pwm[0];			data->pwm[1] = data->old_pwm[1];			/* Restore PWM registers */			adm1031_write_value(client, ADM1031_REG_PWM, 					    data->pwm[0] | (data->pwm[1] << 4));		}	}	data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);	adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);	mutex_unlock(&data->update_lock);	return count;}#define fan_auto_channel_offset(offset)						\static ssize_t show_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, char *buf)	\{										\	return show_fan_auto_channel(dev, buf, offset - 1);			\}										\static ssize_t set_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr,		\	const char *buf, size_t count)						\{										\	return set_fan_auto_channel(dev, buf, count, offset - 1);		\}										\static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR,		\		   show_fan_auto_channel_##offset,				\		   set_fan_auto_channel_##offset)fan_auto_channel_offset(1);fan_auto_channel_offset(2);/* Auto Temps */static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr){	struct adm1031_data *data = adm1031_update_device(dev);	return sprintf(buf, "%d\n", 		       AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));}static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr){	struct adm1031_data *data = adm1031_update_device(dev);	return sprintf(buf, "%d\n",		       AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));}static ssize_tset_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct adm1031_data *data = i2c_get_clientdata(client);	int val = simple_strtol(buf, NULL, 10);	mutex_lock(&data->update_lock);	data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);	adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),			    data->auto_temp[nr]);	mutex_unlock(&data->update_lock);	return count;}

⌨️ 快捷键说明

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