?? it87.c
字號:
/* it87.c - Part of lm_sensors, Linux kernel modules for hardware monitoring. The IT8705F is an LPC-based Super I/O part that contains UARTs, a parallel port, an IR port, a MIDI port, a floppy controller, etc., in addition to an Environment Controller (Enhanced Hardware Monitor and Fan Controller) This driver supports only the Environment Controller in the IT8705F and similar parts. The other devices are supported by different drivers. Supports: IT8705F Super I/O chip w/LPC interface IT8712F Super I/O chip w/LPC interface IT8716F Super I/O chip w/LPC interface IT8718F Super I/O chip w/LPC interface IT8720F Super I/O chip w/LPC interface IT8726F Super I/O chip w/LPC interface IT8781F Super I/O chip w/LPC interface IT8782F Super I/O chip w/LPC interface Sis950 A clone of the IT8705F Copyright (C) 2001 Chris Gauthron Copyright (C) 2005-2007 Jean Delvare <khali@linux-fr.org> Copyright (C) 2008 Cark Li <cark.li@ite.com.tw> 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/sysfs.h>#include <asm/io.h>#define DRVNAME "it87"enum chips { it87, it8712, it8716, it8718 };static unsigned short force_id;module_param(force_id, ushort, 0);MODULE_PARM_DESC(force_id, "Override the detected device ID");static struct platform_device *pdev;#define REG 0x2e /* The register to read/write */#define DEV 0x07 /* Register: Logical device select */#define VAL 0x2f /* The value to read/write */#define PME 0x04 /* The device with the fan registers in it */#define GPIO 0x07 /* The device with the IT8718F VID value in it */#define DEVID 0x20 /* Register: Device ID */#define DEVREV 0x22 /* Register: Device Revision */static inline intsuperio_inb(int reg){ outb(reg, REG); return inb(VAL);}static int superio_inw(int reg){ int val; outb(reg++, REG); val = inb(VAL) << 8; outb(reg, REG); val |= inb(VAL); return val;}static inline voidsuperio_select(int ldn){ outb(DEV, REG); outb(ldn, VAL);}static inline voidsuperio_enter(void){ outb(0x87, REG); outb(0x01, REG); outb(0x55, REG); outb(0x55, REG);}static inline voidsuperio_exit(void){ outb(0x02, REG); outb(0x02, VAL);}/* Logical device 4 registers */#define IT8712F_DEVID 0x8712#define IT8705F_DEVID 0x8705#define IT8716F_DEVID 0x8716#define IT8718F_DEVID 0x8718#define IT8720F_DEVID 0x8720#define IT8726F_DEVID 0x8726#define IT8781F_DEVID 0x8781#define IT8782F_DEVID 0x8782#define IT87_ACT_REG 0x30#define IT87_BASE_REG 0x60/* Logical device 7 registers (IT8712F and later) */#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */#define IT87_SIO_VID_REG 0xfc /* VID value *//* Update battery voltage after every reading if true */static int update_vbat;/* Not all BIOSes properly configure the PWM registers */static int fix_pwm_polarity;/* Many IT87 constants specified below *//* Length of ISA address segment */#define IT87_EXTENT 8/* Length of ISA address segment for Environmental Controller */#define IT87_EC_EXTENT 2/* Offset of EC registers from ISA base address */#define IT87_EC_OFFSET 5/* Where are the ISA address/data registers relative to the EC base address */#define IT87_ADDR_REG_OFFSET 0#define IT87_DATA_REG_OFFSET 1/*----- The IT87 registers -----*/#define IT87_REG_CONFIG 0x00#define IT87_REG_ALARM1 0x01#define IT87_REG_ALARM2 0x02#define IT87_REG_ALARM3 0x03/* The IT8718F has the VID value in a different register, in Super-I/O configuration space. */#define IT87_REG_VID 0x0a/* Warning: register 0x0b is used for something completely different in new chips/revisions. I suspect only 16-bit tachometer mode will work for these. */#define IT87_REG_FAN_DIV 0x0b#define IT87_REG_FAN_16BIT 0x0c/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 };static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 };static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };#define IT87_REG_FAN_MAIN_CTRL 0x13#define IT87_REG_FAN_CTL 0x14#define IT87_REG_PWM(nr) (0x15 + (nr))#define IT87_REG_VIN(nr) (0x20 + (nr))#define IT87_REG_TEMP(nr) (0x29 + (nr))#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2)#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2)#define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2)#define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2)#define IT87_REG_VIN_ENABLE 0x50#define IT87_REG_TEMP_ENABLE 0x51#define IT87_REG_CHIPID 0x58#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255))#define IN_FROM_REG(val) ((val) * 16)static inline u8 FAN_TO_REG(long rpm, int div){ if (rpm == 0) return 255; rpm = SENSORS_LIMIT(rpm, 1, 1000000); return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);}static inline u16 FAN16_TO_REG(long rpm){ if (rpm == 0) return 0xffff; return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe);}#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))/* The divider is fixed to 2 in 16-bit mode */#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2))#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ ((val)+500)/1000),-128,127))#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000)#define PWM_TO_REG(val) ((val) >> 1)#define PWM_FROM_REG(val) (((val)&0x7f) << 1)static int DIV_TO_REG(int val){ int answer = 0; while (answer < 7 && (val >>= 1)) answer++; return answer;}#define DIV_FROM_REG(val) (1 << (val))static const unsigned int pwm_freq[8] = { 48000000 / 128, 24000000 / 128, 12000000 / 128, 8000000 / 128, 6000000 / 128, 3000000 / 128, 1500000 / 128, 750000 / 128,};struct it87_sio_data { enum chips type; /* Values read from Super-I/O config space */ u8 vid_value;};/* For each registered chip, we need to keep some data in memory. The structure is dynamically allocated. */struct it87_data { struct device *hwmon_dev; enum chips type; unsigned short addr; const char *name; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[9]; /* Register value */ u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ u8 has_fan; /* Bitfield, fans enabled */ u16 fan[5]; /* Register values, possibly combined */ u16 fan_min[5]; /* Register values, possibly combined */ u8 temp[3]; /* Register value */ u8 temp_high[3]; /* Register value */ u8 temp_low[3]; /* Register value */ u8 sensor; /* Register value */ u8 fan_div[3]; /* Register encoding, shifted right */ u8 vid; /* Register encoding, combined */ u8 vrm; u32 alarms; /* Register encoding, combined */ u8 fan_main_ctrl; /* Register value */ u8 fan_ctl; /* Register value */ u8 manual_pwm_ctl[3]; /* manual PWM value set by user */};static int it87_probe(struct platform_device *pdev);static int __devexit it87_remove(struct platform_device *pdev);static int it87_read_value(struct it87_data *data, u8 reg);static void it87_write_value(struct it87_data *data, u8 reg, u8 value);static struct it87_data *it87_update_device(struct device *dev);static int it87_check_pwm(struct device *dev);static void it87_init_device(struct platform_device *pdev);static struct platform_driver it87_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, }, .probe = it87_probe, .remove = __devexit_p(it87_remove),};static ssize_t show_in(struct device *dev, struct device_attribute *attr, char *buf){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));}static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, char *buf){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));}static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, char *buf){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));}static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val); it87_write_value(data, IT87_REG_VIN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); return count;}static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val); it87_write_value(data, IT87_REG_VIN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); return count;}#define show_in_offset(offset) \static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ show_in, NULL, offset);#define limit_in_offset(offset) \static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ show_in_min, set_in_min, offset); \static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ show_in_max, set_in_max, offset);show_in_offset(0);limit_in_offset(0);show_in_offset(1);limit_in_offset(1);show_in_offset(2);limit_in_offset(2);show_in_offset(3);limit_in_offset(3);show_in_offset(4);limit_in_offset(4);show_in_offset(5);limit_in_offset(5);show_in_offset(6);limit_in_offset(6);show_in_offset(7);limit_in_offset(7);show_in_offset(8);/* 3 temperatures */static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf){ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -