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