lm93.c
来自「linux 内核源代码」· C语言 代码 · 共 2,063 行 · 第 1/5 页
C
2,063 行
/* lm93.c - Part of lm_sensors, Linux kernel modules for hardware monitoring Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> Copyright (c) 2004 Utilitek Systems, Inc. derived in part from lm78.c: Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> derived in part from lm85.c: Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> derived in part from w83l785ts.c: Copyright (c) 2003-2004 Jean Delvare <khali@linux-fr.org> Ported to Linux 2.6 by Eric J. Bowersox <ericb@aspsys.com> Copyright (c) 2005 Aspen Systems, Inc. Adapted to 2.6.20 by Carsten Emde <cbe@osadl.org> Copyright (c) 2006 Carsten Emde, Open Source Automation Development Lab Modified for mainline integration by Hans J. Koch <hjk@linutronix.de> Copyright (c) 2007 Hans J. Koch, Linutronix GmbH 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/i2c.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/hwmon-vid.h>#include <linux/err.h>#include <linux/delay.h>/* LM93 REGISTER ADDRESSES *//* miscellaneous */#define LM93_REG_MFR_ID 0x3e#define LM93_REG_VER 0x3f#define LM93_REG_STATUS_CONTROL 0xe2#define LM93_REG_CONFIG 0xe3#define LM93_REG_SLEEP_CONTROL 0xe4/* alarm values start here */#define LM93_REG_HOST_ERROR_1 0x48/* voltage inputs: in1-in16 (nr => 0-15) */#define LM93_REG_IN(nr) (0x56 + (nr))#define LM93_REG_IN_MIN(nr) (0x90 + (nr) * 2)#define LM93_REG_IN_MAX(nr) (0x91 + (nr) * 2)/* temperature inputs: temp1-temp4 (nr => 0-3) */#define LM93_REG_TEMP(nr) (0x50 + (nr))#define LM93_REG_TEMP_MIN(nr) (0x78 + (nr) * 2)#define LM93_REG_TEMP_MAX(nr) (0x79 + (nr) * 2)/* temp[1-4]_auto_boost (nr => 0-3) */#define LM93_REG_BOOST(nr) (0x80 + (nr))/* #PROCHOT inputs: prochot1-prochot2 (nr => 0-1) */#define LM93_REG_PROCHOT_CUR(nr) (0x67 + (nr) * 2)#define LM93_REG_PROCHOT_AVG(nr) (0x68 + (nr) * 2)#define LM93_REG_PROCHOT_MAX(nr) (0xb0 + (nr))/* fan tach inputs: fan1-fan4 (nr => 0-3) */#define LM93_REG_FAN(nr) (0x6e + (nr) * 2)#define LM93_REG_FAN_MIN(nr) (0xb4 + (nr) * 2)/* pwm outputs: pwm1-pwm2 (nr => 0-1, reg => 0-3) */#define LM93_REG_PWM_CTL(nr,reg) (0xc8 + (reg) + (nr) * 4)#define LM93_PWM_CTL1 0x0#define LM93_PWM_CTL2 0x1#define LM93_PWM_CTL3 0x2#define LM93_PWM_CTL4 0x3/* GPIO input state */#define LM93_REG_GPI 0x6b/* vid inputs: vid1-vid2 (nr => 0-1) */#define LM93_REG_VID(nr) (0x6c + (nr))/* vccp1 & vccp2: VID relative inputs (nr => 0-1) */#define LM93_REG_VCCP_LIMIT_OFF(nr) (0xb2 + (nr))/* temp[1-4]_auto_boost_hyst */#define LM93_REG_BOOST_HYST_12 0xc0#define LM93_REG_BOOST_HYST_34 0xc1#define LM93_REG_BOOST_HYST(nr) (0xc0 + (nr)/2)/* temp[1-4]_auto_pwm_[min|hyst] */#define LM93_REG_PWM_MIN_HYST_12 0xc3#define LM93_REG_PWM_MIN_HYST_34 0xc4#define LM93_REG_PWM_MIN_HYST(nr) (0xc3 + (nr)/2)/* prochot_override & prochot_interval */#define LM93_REG_PROCHOT_OVERRIDE 0xc6#define LM93_REG_PROCHOT_INTERVAL 0xc7/* temp[1-4]_auto_base (nr => 0-3) */#define LM93_REG_TEMP_BASE(nr) (0xd0 + (nr))/* temp[1-4]_auto_offsets (step => 0-11) */#define LM93_REG_TEMP_OFFSET(step) (0xd4 + (step))/* #PROCHOT & #VRDHOT PWM ramp control */#define LM93_REG_PWM_RAMP_CTL 0xbf/* miscellaneous */#define LM93_REG_SFC1 0xbc#define LM93_REG_SFC2 0xbd#define LM93_REG_GPI_VID_CTL 0xbe#define LM93_REG_SF_TACH_TO_PWM 0xe0/* error masks */#define LM93_REG_GPI_ERR_MASK 0xec#define LM93_REG_MISC_ERR_MASK 0xed/* LM93 REGISTER VALUES */#define LM93_MFR_ID 0x73#define LM93_MFR_ID_PROTOTYPE 0x72/* SMBus capabilities */#define LM93_SMBUS_FUNC_FULL (I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)#define LM93_SMBUS_FUNC_MIN (I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA)/* Addresses to scan */static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };/* Insmod parameters */I2C_CLIENT_INSMOD_1(lm93);static int disable_block;module_param(disable_block, bool, 0);MODULE_PARM_DESC(disable_block, "Set to non-zero to disable SMBus block data transactions.");static int init;module_param(init, bool, 0);MODULE_PARM_DESC(init, "Set to non-zero to force chip initialization.");static int vccp_limit_type[2] = {0,0};module_param_array(vccp_limit_type, int, NULL, 0);MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes.");static int vid_agtl;module_param(vid_agtl, int, 0);MODULE_PARM_DESC(vid_agtl, "Configures VID pin input thresholds.");/* Driver data */static struct i2c_driver lm93_driver;/* LM93 BLOCK READ COMMANDS */static const struct { u8 cmd; u8 len; } lm93_block_read_cmds[12] = { { 0xf2, 8 }, { 0xf3, 8 }, { 0xf4, 6 }, { 0xf5, 16 }, { 0xf6, 4 }, { 0xf7, 8 }, { 0xf8, 12 }, { 0xf9, 32 }, { 0xfa, 8 }, { 0xfb, 8 }, { 0xfc, 16 }, { 0xfd, 9 },};/* ALARMS: SYSCTL format described further below REG: 64 bits in 8 registers, as immediately below */struct block1_t { u8 host_status_1; u8 host_status_2; u8 host_status_3; u8 host_status_4; u8 p1_prochot_status; u8 p2_prochot_status; u8 gpi_status; u8 fan_status;};/* * Client-specific data */struct lm93_data { struct i2c_client client; struct device *hwmon_dev; struct mutex update_lock; unsigned long last_updated; /* In jiffies */ /* client update function */ void (*update)(struct lm93_data *, struct i2c_client *); char valid; /* !=0 if following fields are valid */ /* register values, arranged by block read groups */ struct block1_t block1; /* temp1 - temp4: unfiltered readings temp1 - temp2: filtered readings */ u8 block2[6]; /* vin1 - vin16: readings */ u8 block3[16]; /* prochot1 - prochot2: readings */ struct { u8 cur; u8 avg; } block4[2]; /* fan counts 1-4 => 14-bits, LE, *left* justified */ u16 block5[4]; /* block6 has a lot of data we don't need */ struct { u8 min; u8 max; } temp_lim[4]; /* vin1 - vin16: low and high limits */ struct { u8 min; u8 max; } block7[16]; /* fan count limits 1-4 => same format as block5 */ u16 block8[4]; /* pwm control registers (2 pwms, 4 regs) */ u8 block9[2][4]; /* auto/pwm base temp and offset temp registers */ struct { u8 base[4]; u8 offset[12]; } block10; /* master config register */ u8 config; /* VID1 & VID2 => register format, 6-bits, right justified */ u8 vid[2]; /* prochot1 - prochot2: limits */ u8 prochot_max[2]; /* vccp1 & vccp2 (in7 & in8): VID relative limits (register format) */ u8 vccp_limits[2]; /* GPIO input state (register format, i.e. inverted) */ u8 gpi; /* #PROCHOT override (register format) */ u8 prochot_override; /* #PROCHOT intervals (register format) */ u8 prochot_interval; /* Fan Boost Temperatures (register format) */ u8 boost[4]; /* Fan Boost Hysteresis (register format) */ u8 boost_hyst[2]; /* Temperature Zone Min. PWM & Hysteresis (register format) */ u8 auto_pwm_min_hyst[2]; /* #PROCHOT & #VRDHOT PWM Ramp Control */ u8 pwm_ramp_ctl; /* miscellaneous setup regs */ u8 sfc1; u8 sfc2; u8 sf_tach_to_pwm; /* The two PWM CTL2 registers can read something other than what was last written for the OVR_DC field (duty cycle override). So, we save the user-commanded value here. */ u8 pwm_override[2];};/* VID: mV REG: 6-bits, right justified, *always* using Intel VRM/VRD 10 */static int LM93_VID_FROM_REG(u8 reg){ return vid_from_reg((reg & 0x3f), 100);}/* min, max, and nominal register values, per channel (u8) */static const u8 lm93_vin_reg_min[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae,};static const u8 lm93_vin_reg_max[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1,};/* Values from the datasheet. They're here for documentation only.static const u8 lm93_vin_reg_nom[16] = { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0,};*//* min, max, and nominal voltage readings, per channel (mV)*/static const unsigned long lm93_vin_val_min[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3000,};static const unsigned long lm93_vin_val_max[16] = { 1236, 1236, 1236, 1600, 2000, 2000, 1600, 1600, 4400, 6500, 3333, 2625, 1312, 1312, 1236, 3600,};/* Values from the datasheet. They're here for documentation only.static const unsigned long lm93_vin_val_nom[16] = { 927, 927, 927, 1200, 1500, 1500, 1200, 1200, 3300, 5000, 2500, 1969, 984, 984, 309, 3300,};*/static unsigned LM93_IN_FROM_REG(int nr, u8 reg){ const long uV_max = lm93_vin_val_max[nr] * 1000; const long uV_min = lm93_vin_val_min[nr] * 1000; const long slope = (uV_max - uV_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; return (slope * reg + intercept + 500) / 1000;}/* IN: mV, limits determined by channel nr REG: scaling determined by channel nr */static u8 LM93_IN_TO_REG(int nr, unsigned val){ /* range limit */ const long mV = SENSORS_LIMIT(val, lm93_vin_val_min[nr], lm93_vin_val_max[nr]); /* try not to lose too much precision here */ const long uV = mV * 1000; const long uV_max = lm93_vin_val_max[nr] * 1000; const long uV_min = lm93_vin_val_min[nr] * 1000; /* convert */ const long slope = (uV_max - uV_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; u8 result = ((uV - intercept + (slope/2)) / slope); result = SENSORS_LIMIT(result, lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]); return result;}/* vid in mV, upper == 0 indicates low limit, otherwise upper limit */static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid){ const long uV_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) : (((reg >> 0 & 0x0f) + 1) * -25000); const long uV_vid = vid * 1000; return (uV_vid + uV_offset + 5000) / 10000;}#define LM93_IN_MIN_FROM_REG(reg,vid) LM93_IN_REL_FROM_REG(reg,0,vid)#define LM93_IN_MAX_FROM_REG(reg,vid) LM93_IN_REL_FROM_REG(reg,1,vid)/* vid in mV , upper == 0 indicates low limit, otherwise upper limit upper also determines which nibble of the register is returned (the other nibble will be 0x0) */static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid){ long uV_offset = vid * 1000 - val * 10000; if (upper) { uV_offset = SENSORS_LIMIT(uV_offset, 12500, 200000); return (u8)((uV_offset / 12500 - 1) << 4); } else { uV_offset = SENSORS_LIMIT(uV_offset, -400000, -25000); return (u8)((uV_offset / -25000 - 1) << 0); }}/* TEMP: 1/1000 degrees C (-128C to +127C) REG: 1C/bit, two's complement */static int LM93_TEMP_FROM_REG(u8 reg){ return (s8)reg * 1000;}#define LM93_TEMP_MIN (-128000)#define LM93_TEMP_MAX ( 127000)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?