⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pc87360.c

📁 h内核
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  pc87360.c - Part of lm_sensors, Linux kernel modules *              for hardware monitoring *  Copyright (C) 2004 Jean Delvare <khali@linux-fr.org> * *  Copied from smsc47m1.c: *  Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> * *  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   devid *  PC87360     -       2       2       -       0xE1 *  PC87363     -       2       2       -       0xE8 *  PC87364     -       3       3       -       0xE4 *  PC87365     11      3       3       2       0xE5 *  PC87366     11      3       3       3-4     0xE9 * *  This driver assumes that no more than one chip is present, and one of *  the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/i2c-sensor.h>#include <linux/i2c-vid.h>#include <asm/io.h>static unsigned short normal_i2c[] = { I2C_CLIENT_END };static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END };static struct i2c_force_data forces[] = {{ NULL }};static u8 devid;static unsigned int extra_isa[3];static u8 confreg[4];enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 };static struct i2c_address_data addr_data = {	.normal_i2c		= normal_i2c,	.normal_isa		= normal_isa,	.forces			= forces,};static int init = 1;module_param(init, int, 0);MODULE_PARM_DESC(init, "Chip initialization level:\n" " 0: None\n" "*1: Forcibly enable internal voltage and temperature channels, except in9\n" " 2: Forcibly enable all voltage and temperature channels, except in9\n" " 3: Forcibly enable all voltage and temperature channels, including in9");/* * Super-I/O registers and operations */#define DEV	0x07	/* Register: Logical device select */#define DEVID	0x20	/* Register: Device ID */#define ACT	0x30	/* Register: Device activation */#define BASE	0x60	/* Register: Base address */#define FSCM	0x09	/* Logical device: fans */#define VLM	0x0d	/* Logical device: voltages */#define TMS	0x0e	/* Logical device: temperatures */static const u8 logdev[3] = { FSCM, VLM, TMS };#define LD_FAN		0#define LD_IN		1#define LD_TEMP		2static inline void superio_outb(int sioaddr, int reg, int val){	outb(reg, sioaddr);	outb(val, sioaddr+1);}static inline int superio_inb(int sioaddr, int reg){	outb(reg, sioaddr);	return inb(sioaddr+1);}static inline void superio_exit(int sioaddr){	outb(0x02, sioaddr);	outb(0x02, sioaddr+1);}/* * Logical devices */#define PC87360_EXTENT		0x10#define PC87365_REG_BANK	0x09#define NO_BANK			0xff/* * Fan registers and conversions *//* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */#define PC87360_REG_PRESCALE(nr)	(0x00 + 2 * (nr))#define PC87360_REG_PWM(nr)		(0x01 + 2 * (nr))#define PC87360_REG_FAN_MIN(nr)		(0x06 + 3 * (nr))#define PC87360_REG_FAN(nr)		(0x07 + 3 * (nr))#define PC87360_REG_FAN_STATUS(nr)	(0x08 + 3 * (nr))#define FAN_FROM_REG(val,div)		((val) == 0 ? 0: \					 480000 / ((val)*(div)))#define FAN_TO_REG(val,div)		((val) <= 100 ? 0 : \					 480000 / ((val)*(div)))#define FAN_DIV_FROM_REG(val)		(1 << ((val >> 5) & 0x03))#define FAN_STATUS_FROM_REG(val)	((val) & 0x07)#define FAN_CONFIG_MONITOR(val,nr)	(((val) >> (2 + nr * 3)) & 1)#define FAN_CONFIG_CONTROL(val,nr)	(((val) >> (3 + nr * 3)) & 1)#define FAN_CONFIG_INVERT(val,nr)	(((val) >> (4 + nr * 3)) & 1)#define PWM_FROM_REG(val,inv)		((inv) ? 255 - (val) : (val))static inline u8 PWM_TO_REG(int val, int inv){	if (inv)		val = 255 - val;	if (val < 0)		return 0;	if (val > 255)		return 255;	return val;}/* * Voltage registers and conversions */#define PC87365_REG_IN_CONVRATE		0x07#define PC87365_REG_IN_CONFIG		0x08#define PC87365_REG_IN			0x0B#define PC87365_REG_IN_MIN		0x0D#define PC87365_REG_IN_MAX		0x0C#define PC87365_REG_IN_STATUS		0x0A#define PC87365_REG_IN_ALARMS1		0x00#define PC87365_REG_IN_ALARMS2		0x01#define PC87365_REG_VID			0x06#define IN_FROM_REG(val,ref)		(((val) * (ref) + 128) / 256)#define IN_TO_REG(val,ref)		((val) < 0 ? 0 : \					 (val)*256 >= (ref)*255 ? 255: \					 ((val) * 256 + (ref)/2) / (ref))/* * Temperature registers and conversions */#define PC87365_REG_TEMP_CONFIG		0x08#define PC87365_REG_TEMP		0x0B#define PC87365_REG_TEMP_MIN		0x0D#define PC87365_REG_TEMP_MAX		0x0C#define PC87365_REG_TEMP_CRIT		0x0E#define PC87365_REG_TEMP_STATUS		0x0A#define PC87365_REG_TEMP_ALARMS		0x00#define TEMP_FROM_REG(val)		((val) * 1000)#define TEMP_TO_REG(val)		((val) < -55000 ? -55 : \					 (val) > 127000 ? 127 : \					 (val) < 0 ? ((val) - 500) / 1000 : \					 ((val) + 500) / 1000)/* * Client data (each client gets its own) */struct pc87360_data {	struct i2c_client client;	struct semaphore lock;	struct semaphore update_lock;	char valid;		/* !=0 if following fields are valid */	unsigned long last_updated;	/* In jiffies */	int address[3];	u8 fannr, innr, tempnr;	u8 fan[3];		/* Register value */	u8 fan_min[3];		/* Register value */	u8 fan_status[3];	/* Register value */	u8 pwm[3];		/* Register value */	u16 fan_conf;		/* Configuration register values, combined */	u16 in_vref;		/* 1 mV/bit */	u8 in[14];		/* Register value */	u8 in_min[14];		/* Register value */	u8 in_max[14];		/* Register value */	u8 in_crit[3];		/* Register value */	u8 in_status[14];	/* Register value */	u16 in_alarms;		/* Register values, combined, masked */	u8 vid_conf;		/* Configuration register value */	u8 vrm;	u8 vid;			/* Register value */	s8 temp[3];		/* Register value */	s8 temp_min[3];		/* Register value */	s8 temp_max[3];		/* Register value */	s8 temp_crit[3];	/* Register value */	u8 temp_status[3];	/* Register value */	u8 temp_alarms;		/* Register value, masked */};/* * Functions declaration */static int pc87360_attach_adapter(struct i2c_adapter *adapter);static int pc87360_detect(struct i2c_adapter *adapter, int address, int kind);static int pc87360_detach_client(struct i2c_client *client);static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,			      u8 reg);static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,				u8 reg, u8 value);static void pc87360_init_client(struct i2c_client *client, int use_thermistors);static struct pc87360_data *pc87360_update_device(struct device *dev);/* * Driver data (common to all clients) */static struct i2c_driver pc87360_driver = {	.owner		= THIS_MODULE,	.name		= "pc87360",	.flags		= I2C_DF_NOTIFY,	.attach_adapter	= pc87360_attach_adapter,	.detach_client	= pc87360_detach_client,};/* * Sysfs stuff */static ssize_t set_fan_min(struct device *dev, const char *buf,	size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct pc87360_data *data = i2c_get_clientdata(client);	long fan_min = simple_strtol(buf, NULL, 10);	fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[nr]));	/* If it wouldn't fit, change clock divisor */	while (fan_min > 255	    && (data->fan_status[nr] & 0x60) != 0x60) {		fan_min >>= 1;		data->fan[nr] >>= 1;		data->fan_status[nr] += 0x20;	}	data->fan_min[nr] = fan_min > 255 ? 255 : fan_min;	pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(nr),			    data->fan_min[nr]);	/* Write new divider, preserve alarm bits */	pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(nr),			    data->fan_status[nr] & 0xF9);	return count;}#define show_and_set_fan(offset) \static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[offset-1], \		       FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \} \static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[offset-1], \		       FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \} \static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", \		       FAN_DIV_FROM_REG(data->fan_status[offset-1])); \} \static ssize_t show_fan##offset##_status(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", \		       FAN_STATUS_FROM_REG(data->fan_status[offset-1])); \} \static ssize_t set_fan##offset##_min(struct device *dev, const char *buf, \	size_t count) \{ \	return set_fan_min(dev, buf, count, offset-1); \} \static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \	show_fan##offset##_input, NULL); \static DEVICE_ATTR(fan##offset##_min, S_IWUSR | S_IRUGO, \	show_fan##offset##_min, set_fan##offset##_min); \static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \	show_fan##offset##_div, NULL); \static DEVICE_ATTR(fan##offset##_status, S_IRUGO, \	show_fan##offset##_status, NULL);show_and_set_fan(1)show_and_set_fan(2)show_and_set_fan(3)#define show_and_set_pwm(offset) \static ssize_t show_pwm##offset(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", \		       PWM_FROM_REG(data->pwm[offset-1], \				    FAN_CONFIG_INVERT(data->fan_conf, \						      offset-1))); \} \static ssize_t set_pwm##offset(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct pc87360_data *data = i2c_get_clientdata(client); \	long val = simple_strtol(buf, NULL, 10); \	data->pwm[offset-1] = PWM_TO_REG(val, \			      FAN_CONFIG_INVERT(data->fan_conf, offset-1)); \	pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(offset-1), \			    data->pwm[offset-1]); \	return count; \} \static DEVICE_ATTR(pwm##offset, S_IWUSR | S_IRUGO, \	show_pwm##offset, set_pwm##offset);show_and_set_pwm(1)show_and_set_pwm(2)show_and_set_pwm(3)#define show_and_set_in(offset) \static ssize_t show_in##offset##_input(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \		       data->in_vref)); \} \static ssize_t show_in##offset##_min(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \		       data->in_vref)); \} \static ssize_t show_in##offset##_max(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \		       data->in_vref)); \} \static ssize_t show_in##offset##_status(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", data->in_status[offset]); \} \static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct pc87360_data *data = i2c_get_clientdata(client); \	long val = simple_strtol(buf, NULL, 10); \	data->in_min[offset] = IN_TO_REG(val, data->in_vref); \	pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MIN, \			    data->in_min[offset]); \	return count; \} \static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \	size_t count) \{ \	struct i2c_client *client = to_i2c_client(dev); \	struct pc87360_data *data = i2c_get_clientdata(client); \	long val = simple_strtol(buf, NULL, 10); \	data->in_max[offset] = IN_TO_REG(val, \			       data->in_vref); \	pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MAX, \			    data->in_max[offset]); \	return count; \} \static DEVICE_ATTR(in##offset##_input, S_IRUGO, \	show_in##offset##_input, NULL); \static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \	show_in##offset##_min, set_in##offset##_min); \static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \	show_in##offset##_max, set_in##offset##_max); \static DEVICE_ATTR(in##offset##_status, S_IRUGO, \	show_in##offset##_status, NULL);show_and_set_in(0)show_and_set_in(1)show_and_set_in(2)show_and_set_in(3)show_and_set_in(4)show_and_set_in(5)show_and_set_in(6)show_and_set_in(7)show_and_set_in(8)show_and_set_in(9)show_and_set_in(10)#define show_and_set_therm(offset) \static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset+7], \		       data->in_vref)); \} \static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset+7], \		       data->in_vref)); \} \static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset+7], \		       data->in_vref)); \} \static ssize_t show_temp##offset##_crit(struct device *dev, char *buf) \{ \	struct pc87360_data *data = pc87360_update_device(dev); \	return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[offset-4], \		       data->in_vref)); \} \

⌨️ 快捷键说明

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