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

📄 smsc47m1.c

📁 h内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    smsc47m1.c - Part of lm_sensors, Linux kernel modules                 for hardware monitoring    Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x    Super-I/O chips.    Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>    Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>    Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>                        and Jean Delvare    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/slab.h>#include <linux/ioport.h>#include <linux/i2c.h>#include <linux/i2c-sensor.h>#include <linux/init.h>#include <asm/io.h>static unsigned short normal_i2c[] = { I2C_CLIENT_END };/* Address is autodetected, there is no default value */static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };static struct i2c_force_data forces[] = {{NULL}};enum chips { any_chip, smsc47m1 };static struct i2c_address_data addr_data = {	.normal_i2c		= normal_i2c,	.normal_isa		= normal_isa,	.forces			= forces,};/* Super-I/0 registers and commands */#define	REG	0x2e	/* The register to read/write */#define	VAL	0x2f	/* The value to read/write */static inline voidsuperio_outb(int reg, int val){	outb(reg, REG);	outb(val, VAL);}static inline intsuperio_inb(int reg){	outb(reg, REG);	return inb(VAL);}/* logical device for fans is 0x0A */#define superio_select() superio_outb(0x07, 0x0A)static inline voidsuperio_enter(void){	outb(0x55, REG);}static inline voidsuperio_exit(void){	outb(0xAA, REG);}#define SUPERIO_REG_ACT		0x30#define SUPERIO_REG_BASE	0x60#define SUPERIO_REG_DEVID	0x20/* Logical device registers */#define SMSC_EXTENT		0x80/* nr is 0 or 1 in the macros below */#define SMSC47M1_REG_ALARM		0x04#define SMSC47M1_REG_TPIN(nr)		(0x34 - (nr))#define SMSC47M1_REG_PPIN(nr)		(0x36 - (nr))#define SMSC47M1_REG_PWM(nr)		(0x56 + (nr))#define SMSC47M1_REG_FANDIV		0x58#define SMSC47M1_REG_FAN(nr)		(0x59 + (nr))#define SMSC47M1_REG_FAN_PRELOAD(nr)	(0x5B + (nr))#define MIN_FROM_REG(reg,div)		((reg)>=192 ? 0 : \					 983040/((192-(reg))*(div)))#define FAN_FROM_REG(reg,div,preload)	((reg)<=(preload) || (reg)==255 ? 0 : \					 983040/(((reg)-(preload))*(div)))#define DIV_FROM_REG(reg)		(1 << (reg))#define PWM_FROM_REG(reg)		(((reg) & 0x7E) << 1)#define PWM_EN_FROM_REG(reg)		((~(reg)) & 0x01)#define PWM_TO_REG(reg)			(((reg) >> 1) & 0x7E)struct smsc47m1_data {	struct i2c_client client;	struct semaphore lock;	int sysctl_id;	struct semaphore update_lock;	unsigned long last_updated;	/* In jiffies */	u8 fan[2];		/* Register value */	u8 fan_preload[2];	/* Register value */	u8 fan_div[2];		/* Register encoding, shifted right */	u8 alarms;		/* Register encoding */	u8 pwm[2];		/* Register value (bit 7 is enable) */};static int smsc47m1_attach_adapter(struct i2c_adapter *adapter);static int smsc47m1_find(int *address);static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind);static int smsc47m1_detach_client(struct i2c_client *client);static int smsc47m1_read_value(struct i2c_client *client, u8 reg);static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,		int init);static int smsc47m1_id;static struct i2c_driver smsc47m1_driver = {	.owner		= THIS_MODULE,	.name		= "smsc47m1",	.id		= I2C_DRIVERID_SMSC47M1,	.flags		= I2C_DF_NOTIFY,	.attach_adapter	= smsc47m1_attach_adapter,	.detach_client	= smsc47m1_detach_client,};/* nr is 0 or 1 in the callback functions below */static ssize_t get_fan(struct device *dev, char *buf, int nr){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	/* This chip (stupidly) stops monitoring fan speed if PWM is	   enabled and duty cycle is 0%. This is fine if the monitoring	   and control concern the same fan, but troublesome if they are	   not (which could as well happen). */	int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :		  FAN_FROM_REG(data->fan[nr],			       DIV_FROM_REG(data->fan_div[nr]),			       data->fan_preload[nr]);	return sprintf(buf, "%d\n", rpm);}static ssize_t get_fan_min(struct device *dev, char *buf, int nr){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	int rpm = MIN_FROM_REG(data->fan_preload[nr],			       DIV_FROM_REG(data->fan_div[nr]));	return sprintf(buf, "%d\n", rpm);}static ssize_t get_fan_div(struct device *dev, char *buf, int nr){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));}static ssize_t get_pwm(struct device *dev, char *buf, int nr){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));}static ssize_t get_pwm_en(struct device *dev, char *buf, int nr){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));}static ssize_t get_alarms(struct device *dev, char *buf){	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);	return sprintf(buf, "%d\n", data->alarms);}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 smsc47m1_data *data = i2c_get_clientdata(client);	long rpmdiv = simple_strtol(buf, NULL, 10)		    * DIV_FROM_REG(data->fan_div[nr]);	if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040)		return -EINVAL;	data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);	smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),			     data->fan_preload[nr]);	return count;}/* Note: we save and restore the fan minimum here, because its value is   determined in part by the fan clock divider.  This follows the principle   of least suprise; the user doesn't expect the fan minimum to change just   because the divider changed. */static ssize_t set_fan_div(struct device *dev, const char *buf,		size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct smsc47m1_data *data = i2c_get_clientdata(client);	long new_div = simple_strtol(buf, NULL, 10), tmp;	u8 old_div = DIV_FROM_REG(data->fan_div[nr]);	if (new_div == old_div) /* No change */		return count;	switch (new_div) {	case 1: data->fan_div[nr] = 0; break;	case 2: data->fan_div[nr] = 1; break;	case 4: data->fan_div[nr] = 2; break;	case 8: data->fan_div[nr] = 3; break;	default: return -EINVAL;	}	tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;	tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);	smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);	/* Preserve fan min */	tmp = 192 - (old_div * (192 - data->fan_preload[nr])		     + new_div / 2) / new_div;	data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);	smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),			     data->fan_preload[nr]);	return count;}static ssize_t set_pwm(struct device *dev, const char *buf,		size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct smsc47m1_data *data = i2c_get_clientdata(client);	long val = simple_strtol(buf, NULL, 10);	if (val < 0 || val > 255)		return -EINVAL;	data->pwm[nr] &= 0x81; /* Preserve additional bits */	data->pwm[nr] |= PWM_TO_REG(val);	smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),			     data->pwm[nr]);	return count;}static ssize_t set_pwm_en(struct device *dev, const char *buf,		size_t count, int nr){	struct i2c_client *client = to_i2c_client(dev);	struct smsc47m1_data *data = i2c_get_clientdata(client);	long val = simple_strtol(buf, NULL, 10);		if (val != 0 && val != 1)		return -EINVAL;	data->pwm[nr] &= 0xFE; /* preserve the other bits */	data->pwm[nr] |= !val;	smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),			     data->pwm[nr]);	return count;}#define fan_present(offset)						\static ssize_t get_fan##offset (struct device *dev, char *buf)		\{									\	return get_fan(dev, buf, offset - 1);				\

⌨️ 快捷键说明

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