w83627ehf.c

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

C
1,579
字号
/*    w83627ehf - Driver for the hardware monitoring functionality of                the Winbond W83627EHF Super-I/O chip    Copyright (C) 2005  Jean Delvare <khali@linux-fr.org>    Copyright (C) 2006  Yuan Mu (Winbond),                        Rudolf Marek <r.marek@assembler.cz>                        David Hubbard <david.c.hubbard@gmail.com>    Shamelessly ripped from the w83627hf driver    Copyright (C) 2003  Mark Studebaker    Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help    in testing and debugging this driver.    This driver also supports the W83627EHG, which is the lead-free    version of the W83627EHF.    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 the following chips:    Chip        #vin    #fan    #pwm    #temp  chip IDs       man ID    w83627ehf   10      5       4       3      0x8850 0x88    0x5ca3                                               0x8860 0xa1    w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3*/#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 <asm/io.h>#include "lm75.h"enum kinds { w83627ehf, w83627dhg };/* used to set data->name = w83627ehf_device_names[data->sio_kind] */static const char * w83627ehf_device_names[] = {	"w83627ehf",	"w83627dhg",};#define DRVNAME "w83627ehf"/* * Super-I/O constants and functions */#define W83627EHF_LD_HWM	0x0b#define SIO_REG_LDSEL		0x07	/* Logical device select */#define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */#define SIO_REG_EN_VRM10	0x2C	/* GPIO3, GPIO4 selection */#define SIO_REG_ENABLE		0x30	/* Logical device enable */#define SIO_REG_ADDR		0x60	/* Logical device address (2 bytes) */#define SIO_REG_VID_CTRL	0xF0	/* VID control */#define SIO_REG_VID_DATA	0xF1	/* VID data */#define SIO_W83627EHF_ID	0x8850#define SIO_W83627EHG_ID	0x8860#define SIO_W83627DHG_ID	0xa020#define SIO_ID_MASK		0xFFF0static inline voidsuperio_outb(int ioreg, int reg, int val){	outb(reg, ioreg);	outb(val, ioreg + 1);}static inline intsuperio_inb(int ioreg, int reg){	outb(reg, ioreg);	return inb(ioreg + 1);}static inline voidsuperio_select(int ioreg, int ld){	outb(SIO_REG_LDSEL, ioreg);	outb(ld, ioreg + 1);}static inline voidsuperio_enter(int ioreg){	outb(0x87, ioreg);	outb(0x87, ioreg);}static inline voidsuperio_exit(int ioreg){	outb(0x02, ioreg);	outb(0x02, ioreg + 1);}/* * ISA constants */#define IOREGION_ALIGNMENT	~7#define IOREGION_OFFSET		5#define IOREGION_LENGTH		2#define ADDR_REG_OFFSET		0#define DATA_REG_OFFSET		1#define W83627EHF_REG_BANK		0x4E#define W83627EHF_REG_CONFIG		0x40/* Not currently used: * REG_MAN_ID has the value 0x5ca3 for all supported chips. * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. * REG_MAN_ID is at port 0x4f * REG_CHIP_ID is at port 0x58 */static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };/* The W83627EHF registers for nr=7,8,9 are in bank 5 */#define W83627EHF_REG_IN_MAX(nr)	((nr < 7) ? (0x2b + (nr) * 2) : \					 (0x554 + (((nr) - 7) * 2)))#define W83627EHF_REG_IN_MIN(nr)	((nr < 7) ? (0x2c + (nr) * 2) : \					 (0x555 + (((nr) - 7) * 2)))#define W83627EHF_REG_IN(nr)		((nr < 7) ? (0x20 + (nr)) : \					 (0x550 + (nr) - 7))#define W83627EHF_REG_TEMP1		0x27#define W83627EHF_REG_TEMP1_HYST	0x3a#define W83627EHF_REG_TEMP1_OVER	0x39static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };/* Fan clock dividers are spread over the following five registers */#define W83627EHF_REG_FANDIV1		0x47#define W83627EHF_REG_FANDIV2		0x4B#define W83627EHF_REG_VBAT		0x5D#define W83627EHF_REG_DIODE		0x59#define W83627EHF_REG_SMI_OVT		0x4C#define W83627EHF_REG_ALARM1		0x459#define W83627EHF_REG_ALARM2		0x45A#define W83627EHF_REG_ALARM3		0x45B/* SmartFan registers *//* DC or PWM output fan configuration */static const u8 W83627EHF_REG_PWM_ENABLE[] = {	0x04,			/* SYS FAN0 output mode and PWM mode */	0x04,			/* CPU FAN0 output mode and PWM mode */	0x12,			/* AUX FAN mode */	0x62,			/* CPU fan1 mode */};static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };/* FAN Duty Cycle, be used to control */static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };/* Advanced Fan control, some values are common for all fans */static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };/* * Conversions *//* 1 is PWM mode, output in ms */static inline unsigned int step_time_from_reg(u8 reg, u8 mode){	return mode ? 100 * reg : 400 * reg;}static inline u8 step_time_to_reg(unsigned int msec, u8 mode){	return SENSORS_LIMIT((mode ? (msec + 50) / 100 :						(msec + 200) / 400), 1, 255);}static inline unsigned intfan_from_reg(u8 reg, unsigned int div){	if (reg == 0 || reg == 255)		return 0;	return 1350000U / (reg * div);}static inline unsigned intdiv_from_reg(u8 reg){	return 1 << reg;}static inline inttemp1_from_reg(s8 reg){	return reg * 1000;}static inline s8temp1_to_reg(long temp, int min, int max){	if (temp <= min)		return min / 1000;	if (temp >= max)		return max / 1000;	if (temp < 0)		return (temp - 500) / 1000;	return (temp + 500) / 1000;}/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };static inline long in_from_reg(u8 reg, u8 nr){	return reg * scale_in[nr];}static inline u8 in_to_reg(u32 val, u8 nr){	return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255);}/* * Data structures and manipulation thereof */struct w83627ehf_data {	int addr;	/* IO base of hw monitor block */	const char *name;	struct device *hwmon_dev;	struct mutex lock;	struct mutex update_lock;	char valid;		/* !=0 if following fields are valid */	unsigned long last_updated;	/* In jiffies */	/* Register values */	u8 in_num;		/* number of in inputs we have */	u8 in[10];		/* Register value */	u8 in_max[10];		/* Register value */	u8 in_min[10];		/* Register value */	u8 fan[5];	u8 fan_min[5];	u8 fan_div[5];	u8 has_fan;		/* some fan inputs can be disabled */	u8 temp_type[3];	s8 temp1;	s8 temp1_max;	s8 temp1_max_hyst;	s16 temp[2];	s16 temp_max[2];	s16 temp_max_hyst[2];	u32 alarms;	u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */	u8 pwm_enable[4]; /* 1->manual			     2->thermal cruise (also called SmartFan I) */	u8 pwm[4];	u8 target_temp[4];	u8 tolerance[4];	u8 fan_min_output[4]; /* minimum fan speed */	u8 fan_stop_time[4];	u8 vid;	u8 vrm;};struct w83627ehf_sio_data {	int sioreg;	enum kinds kind;};static inline int is_word_sized(u16 reg){	return (((reg & 0xff00) == 0x100	      || (reg & 0xff00) == 0x200)	     && ((reg & 0x00ff) == 0x50	      || (reg & 0x00ff) == 0x53	      || (reg & 0x00ff) == 0x55));}/* Registers 0x50-0x5f are banked */static inline void w83627ehf_set_bank(struct w83627ehf_data *data, u16 reg){	if ((reg & 0x00f0) == 0x50) {		outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET);		outb_p(reg >> 8, data->addr + DATA_REG_OFFSET);	}}/* Not strictly necessary, but play it safe for now */static inline void w83627ehf_reset_bank(struct w83627ehf_data *data, u16 reg){	if (reg & 0xff00) {		outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET);		outb_p(0, data->addr + DATA_REG_OFFSET);	}}static u16 w83627ehf_read_value(struct w83627ehf_data *data, u16 reg){	int res, word_sized = is_word_sized(reg);	mutex_lock(&data->lock);	w83627ehf_set_bank(data, reg);	outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);	res = inb_p(data->addr + DATA_REG_OFFSET);	if (word_sized) {		outb_p((reg & 0xff) + 1,		       data->addr + ADDR_REG_OFFSET);		res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);	}	w83627ehf_reset_bank(data, reg);	mutex_unlock(&data->lock);	return res;}static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, u16 value){	int word_sized = is_word_sized(reg);	mutex_lock(&data->lock);	w83627ehf_set_bank(data, reg);	outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);	if (word_sized) {		outb_p(value >> 8, data->addr + DATA_REG_OFFSET);		outb_p((reg & 0xff) + 1,		       data->addr + ADDR_REG_OFFSET);	}	outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);	w83627ehf_reset_bank(data, reg);	mutex_unlock(&data->lock);	return 0;}/* This function assumes that the caller holds data->update_lock */static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr){	u8 reg;	switch (nr) {	case 0:		reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0xcf)		    | ((data->fan_div[0] & 0x03) << 4);		/* fan5 input control bit is write only, compute the value */		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;		w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);		reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xdf)		    | ((data->fan_div[0] & 0x04) << 3);		w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);		break;	case 1:		reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0x3f)		    | ((data->fan_div[1] & 0x03) << 6);		/* fan5 input control bit is write only, compute the value */		reg |= (data->has_fan & (1 << 4)) ? 1 : 0;		w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);		reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xbf)		    | ((data->fan_div[1] & 0x04) << 4);		w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);

⌨️ 快捷键说明

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