vt1211.c

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

C
1,371
字号
/* * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware *            monitoring features * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com> * * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker * and its port to kernel 2.6 by Lars Ekman. * * 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/platform_device.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/hwmon-vid.h>#include <linux/err.h>#include <linux/mutex.h>#include <linux/ioport.h>#include <asm/io.h>static int uch_config = -1;module_param(uch_config, int, 0);MODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration");static int int_mode = -1;module_param(int_mode, int, 0);MODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode");static struct platform_device *pdev;#define DRVNAME "vt1211"/* --------------------------------------------------------------------- * Registers * * The sensors are defined as follows. * * Sensor          Voltage Mode   Temp Mode   Notes (from the datasheet) * --------        ------------   ---------   -------------------------- * Reading 1                      temp1       Intel thermal diode * Reading 3                      temp2       Internal thermal diode * UCH1/Reading2   in0            temp3       NTC type thermistor * UCH2            in1            temp4       +2.5V * UCH3            in2            temp5       VccP * UCH4            in3            temp6       +5V * UCH5            in4            temp7       +12V * 3.3V            in5                        Internal VDD (+3.3V) * * --------------------------------------------------------------------- *//* Voltages (in) numbered 0-5 (ix) */#define VT1211_REG_IN(ix)		(0x21 + (ix))#define VT1211_REG_IN_MIN(ix)		((ix) == 0 ? 0x3e : 0x2a + 2 * (ix))#define VT1211_REG_IN_MAX(ix)		((ix) == 0 ? 0x3d : 0x29 + 2 * (ix))/* Temperatures (temp) numbered 0-6 (ix) */static u8 regtemp[]	= {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25};static u8 regtempmax[]	= {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31};static u8 regtemphyst[]	= {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32};/* Fans numbered 0-1 (ix) */#define VT1211_REG_FAN(ix)		(0x29 + (ix))#define VT1211_REG_FAN_MIN(ix)		(0x3b + (ix))#define VT1211_REG_FAN_DIV		 0x47/* PWMs numbered 0-1 (ix) *//* Auto points numbered 0-3 (ap) */#define VT1211_REG_PWM(ix)		(0x60 + (ix))#define VT1211_REG_PWM_CLK		 0x50#define VT1211_REG_PWM_CTL		 0x51#define VT1211_REG_PWM_AUTO_TEMP(ap)	(0x55 - (ap))#define VT1211_REG_PWM_AUTO_PWM(ix, ap)	(0x58 + 2 * (ix) - (ap))/* Miscellaneous registers */#define VT1211_REG_CONFIG		0x40#define VT1211_REG_ALARM1		0x41#define VT1211_REG_ALARM2		0x42#define VT1211_REG_VID			0x45#define VT1211_REG_UCH_CONFIG		0x4a#define VT1211_REG_TEMP1_CONFIG		0x4b#define VT1211_REG_TEMP2_CONFIG		0x4c/* In, temp & fan alarm bits */static const u8 bitalarmin[]	= {11, 0, 1, 3, 8, 2, 9};static const u8 bitalarmtemp[]	= {4, 15, 11, 0, 1, 3, 8};static const u8 bitalarmfan[]	= {6, 7};/* --------------------------------------------------------------------- * Data structures and manipulation thereof * --------------------------------------------------------------------- */struct vt1211_data {	unsigned short addr;	const char *name;	struct device *hwmon_dev;	struct mutex update_lock;	char valid;			/* !=0 if following fields are valid */	unsigned long last_updated;	/* In jiffies */	/* Register values */	u8  in[6];	u8  in_max[6];	u8  in_min[6];	u8  temp[7];	u8  temp_max[7];	u8  temp_hyst[7];	u8  fan[2];	u8  fan_min[2];	u8  fan_div[2];	u8  fan_ctl;	u8  pwm[2];	u8  pwm_ctl[2];	u8  pwm_clk;	u8  pwm_auto_temp[4];	u8  pwm_auto_pwm[2][4];	u8  vid;		/* Read once at init time */	u8  vrm;	u8  uch_config;		/* Read once at init time */	u16 alarms;};/* ix = [0-5] */#define ISVOLT(ix, uch_config)	((ix) > 4 ? 1 : \				 !(((uch_config) >> ((ix) + 2)) & 1))/* ix = [0-6] */#define ISTEMP(ix, uch_config)	((ix) < 2 ? 1 : \				 ((uch_config) >> (ix)) & 1)/* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the   driver according to the VT1211 BIOS porting guide */#define IN_FROM_REG(ix, reg)	((reg) < 3 ? 0 : (ix) == 5 ? \				 (((reg) - 3) * 15882 + 479) / 958 : \				 (((reg) - 3) * 10000 + 479) / 958)#define IN_TO_REG(ix, val)	(SENSORS_LIMIT((ix) == 5 ? \				 ((val) * 958 + 7941) / 15882 + 3 : \				 ((val) * 958 + 5000) / 10000 + 3, 0, 255))/* temp1 (ix = 0) is an intel thermal diode which is scaled in user space.   temp2 (ix = 1) is the internal temp diode so it's scaled in the driver   according to some measurements that I took on an EPIA M10000.   temp3-7 are thermistor based so the driver returns the voltage measured at   the pin (range 0V - 2.2V). */#define TEMP_FROM_REG(ix, reg)	((ix) == 0 ? (reg) * 1000 : \				 (ix) == 1 ? (reg) < 51 ? 0 : \				 ((reg) - 51) * 1000 : \				 ((253 - (reg)) * 2200 + 105) / 210)#define TEMP_TO_REG(ix, val)	SENSORS_LIMIT( \				 ((ix) == 0 ? ((val) + 500) / 1000 : \				  (ix) == 1 ? ((val) + 500) / 1000 + 51 : \				  253 - ((val) * 210 + 1100) / 2200), 0, 255)#define DIV_FROM_REG(reg)	(1 << (reg))#define RPM_FROM_REG(reg, div)	(((reg) == 0) || ((reg) == 255) ? 0 : \				 1310720 / (reg) / DIV_FROM_REG(div))#define RPM_TO_REG(val, div)	((val) == 0 ? 255 : \				 SENSORS_LIMIT((1310720 / (val) / \				 DIV_FROM_REG(div)), 1, 254))/* --------------------------------------------------------------------- * Super-I/O constants and functions * --------------------------------------------------------------------- *//* Configuration index port registers * The vt1211 can live at 2 different addresses so we need to probe both */#define SIO_REG_CIP1		0x2e#define SIO_REG_CIP2		0x4e/* Configuration registers */#define SIO_VT1211_LDN		0x07	/* logical device number */#define SIO_VT1211_DEVID	0x20	/* device ID */#define SIO_VT1211_DEVREV	0x21	/* device revision */#define SIO_VT1211_ACTIVE	0x30	/* HW monitor active */#define SIO_VT1211_BADDR	0x60	/* base I/O address */#define SIO_VT1211_ID		0x3c	/* VT1211 device ID *//* VT1211 logical device numbers */#define SIO_VT1211_LDN_HWMON	0x0b	/* HW monitor */static inline void superio_outb(int sio_cip, int reg, int val){	outb(reg, sio_cip);	outb(val, sio_cip + 1);}static inline int superio_inb(int sio_cip, int reg){	outb(reg, sio_cip);	return inb(sio_cip + 1);}static inline void superio_select(int sio_cip, int ldn){	outb(SIO_VT1211_LDN, sio_cip);	outb(ldn, sio_cip + 1);}static inline void superio_enter(int sio_cip){	outb(0x87, sio_cip);	outb(0x87, sio_cip);}static inline void superio_exit(int sio_cip){	outb(0xaa, sio_cip);}/* --------------------------------------------------------------------- * Device I/O access * --------------------------------------------------------------------- */static inline u8 vt1211_read8(struct vt1211_data *data, u8 reg){	return inb(data->addr + reg);}static inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val){	outb(val, data->addr + reg);}static struct vt1211_data *vt1211_update_device(struct device *dev){	struct vt1211_data *data = dev_get_drvdata(dev);	int ix, val;	mutex_lock(&data->update_lock);	/* registers cache is refreshed after 1 second */	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {		/* read VID */		data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f;		/* voltage (in) registers */		for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {			if (ISVOLT(ix, data->uch_config)) {				data->in[ix] = vt1211_read8(data,						VT1211_REG_IN(ix));				data->in_min[ix] = vt1211_read8(data,						VT1211_REG_IN_MIN(ix));				data->in_max[ix] = vt1211_read8(data,						VT1211_REG_IN_MAX(ix));			}		}		/* temp registers */		for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {			if (ISTEMP(ix, data->uch_config)) {				data->temp[ix] = vt1211_read8(data,						regtemp[ix]);				data->temp_max[ix] = vt1211_read8(data,						regtempmax[ix]);				data->temp_hyst[ix] = vt1211_read8(data,						regtemphyst[ix]);			}		}		/* fan & pwm registers */		for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {			data->fan[ix] = vt1211_read8(data,						VT1211_REG_FAN(ix));			data->fan_min[ix] = vt1211_read8(data,						VT1211_REG_FAN_MIN(ix));			data->pwm[ix] = vt1211_read8(data,						VT1211_REG_PWM(ix));		}		val = vt1211_read8(data, VT1211_REG_FAN_DIV);		data->fan_div[0] = (val >> 4) & 3;		data->fan_div[1] = (val >> 6) & 3;		data->fan_ctl = val & 0xf;		val = vt1211_read8(data, VT1211_REG_PWM_CTL);		data->pwm_ctl[0] = val & 0xf;		data->pwm_ctl[1] = (val >> 4) & 0xf;		data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK);		/* pwm & temp auto point registers */		data->pwm_auto_pwm[0][1] = vt1211_read8(data,						VT1211_REG_PWM_AUTO_PWM(0, 1));		data->pwm_auto_pwm[0][2] = vt1211_read8(data,						VT1211_REG_PWM_AUTO_PWM(0, 2));		data->pwm_auto_pwm[1][1] = vt1211_read8(data,						VT1211_REG_PWM_AUTO_PWM(1, 1));		data->pwm_auto_pwm[1][2] = vt1211_read8(data,						VT1211_REG_PWM_AUTO_PWM(1, 2));		for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) {			data->pwm_auto_temp[ix] = vt1211_read8(data,						VT1211_REG_PWM_AUTO_TEMP(ix));		}		/* alarm registers */		data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) |				vt1211_read8(data, VT1211_REG_ALARM1);		data->last_updated = jiffies;		data->valid = 1;	}	mutex_unlock(&data->update_lock);	return data;}/* --------------------------------------------------------------------- * Voltage sysfs interfaces * ix = [0-5] * --------------------------------------------------------------------- */#define SHOW_IN_INPUT	0#define SHOW_SET_IN_MIN	1#define SHOW_SET_IN_MAX	2#define SHOW_IN_ALARM	3static ssize_t show_in(struct device *dev, struct device_attribute *attr,		       char *buf){	struct vt1211_data *data = vt1211_update_device(dev);	struct sensor_device_attribute_2 *sensor_attr_2 =						to_sensor_dev_attr_2(attr);	int ix = sensor_attr_2->index;	int fn = sensor_attr_2->nr;	int res;	switch (fn) {	case SHOW_IN_INPUT:		res = IN_FROM_REG(ix, data->in[ix]);		break;	case SHOW_SET_IN_MIN:		res = IN_FROM_REG(ix, data->in_min[ix]);		break;	case SHOW_SET_IN_MAX:		res = IN_FROM_REG(ix, data->in_max[ix]);		break;	case SHOW_IN_ALARM:		res = (data->alarms >> bitalarmin[ix]) & 1;		break;	default:		res = 0;		dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);	}	return sprintf(buf, "%d\n", res);}static ssize_t set_in(struct device *dev, struct device_attribute *attr,		      const char *buf, size_t count){	struct vt1211_data *data = dev_get_drvdata(dev);	struct sensor_device_attribute_2 *sensor_attr_2 =						to_sensor_dev_attr_2(attr);	int ix = sensor_attr_2->index;	int fn = sensor_attr_2->nr;	long val = simple_strtol(buf, NULL, 10);	mutex_lock(&data->update_lock);	switch (fn) {	case SHOW_SET_IN_MIN:		data->in_min[ix] = IN_TO_REG(ix, val);		vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]);		break;	case SHOW_SET_IN_MAX:		data->in_max[ix] = IN_TO_REG(ix, val);		vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]);		break;	default:		dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);	}	mutex_unlock(&data->update_lock);	return count;}/* --------------------------------------------------------------------- * Temperature sysfs interfaces * ix = [0-6] * --------------------------------------------------------------------- */#define SHOW_TEMP_INPUT		0#define SHOW_SET_TEMP_MAX	1#define SHOW_SET_TEMP_MAX_HYST	2#define SHOW_TEMP_ALARM		3static ssize_t show_temp(struct device *dev, struct device_attribute *attr,			 char *buf){	struct vt1211_data *data = vt1211_update_device(dev);	struct sensor_device_attribute_2 *sensor_attr_2 =						to_sensor_dev_attr_2(attr);	int ix = sensor_attr_2->index;	int fn = sensor_attr_2->nr;	int res;	switch (fn) {	case SHOW_TEMP_INPUT:		res = TEMP_FROM_REG(ix, data->temp[ix]);		break;	case SHOW_SET_TEMP_MAX:		res = TEMP_FROM_REG(ix, data->temp_max[ix]);		break;	case SHOW_SET_TEMP_MAX_HYST:		res = TEMP_FROM_REG(ix, data->temp_hyst[ix]);		break;	case SHOW_TEMP_ALARM:		res = (data->alarms >> bitalarmtemp[ix]) & 1;		break;	default:		res = 0;		dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);	}	return sprintf(buf, "%d\n", res);}static ssize_t set_temp(struct device *dev, struct device_attribute *attr,			const char *buf, size_t count){	struct vt1211_data *data = dev_get_drvdata(dev);	struct sensor_device_attribute_2 *sensor_attr_2 =						to_sensor_dev_attr_2(attr);	int ix = sensor_attr_2->index;	int fn = sensor_attr_2->nr;	long val = simple_strtol(buf, NULL, 10);	mutex_lock(&data->update_lock);	switch (fn) {	case SHOW_SET_TEMP_MAX:		data->temp_max[ix] = TEMP_TO_REG(ix, val);		vt1211_write8(data, regtempmax[ix],			      data->temp_max[ix]);		break;	case SHOW_SET_TEMP_MAX_HYST:		data->temp_hyst[ix] = TEMP_TO_REG(ix, val);		vt1211_write8(data, regtemphyst[ix],			      data->temp_hyst[ix]);		break;	default:		dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);

⌨️ 快捷键说明

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