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 + -
显示快捷键?