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