f75375s.c

来自「linux 内核源代码」· C语言 代码 · 共 790 行 · 第 1/2 页

C
790
字号
/* * f75375s.c - driver for the Fintek F75375/SP and F75373 *             hardware monitoring features * Copyright (C) 2006-2007  Riku Voipio <riku.voipio@movial.fi> * * Datasheets available at: * * f75375: * http://www.fintek.com.tw/files/productfiles/2005111152950.pdf * * f75373: * http://www.fintek.com.tw/files/productfiles/2005111153128.pdf * * 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/jiffies.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/i2c.h>#include <linux/err.h>#include <linux/mutex.h>#include <linux/f75375s.h>/* Addresses to scan */static unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END };/* Insmod parameters */I2C_CLIENT_INSMOD_2(f75373, f75375);/* Fintek F75375 registers  */#define F75375_REG_CONFIG0		0x0#define F75375_REG_CONFIG1		0x1#define F75375_REG_CONFIG2		0x2#define F75375_REG_CONFIG3		0x3#define F75375_REG_ADDR			0x4#define F75375_REG_INTR			0x31#define F75375_CHIP_ID			0x5A#define F75375_REG_VERSION		0x5C#define F75375_REG_VENDOR		0x5D#define F75375_REG_FAN_TIMER		0x60#define F75375_REG_VOLT(nr)		(0x10 + (nr))#define F75375_REG_VOLT_HIGH(nr)	(0x20 + (nr) * 2)#define F75375_REG_VOLT_LOW(nr)		(0x21 + (nr) * 2)#define F75375_REG_TEMP(nr)		(0x14 + (nr))#define F75375_REG_TEMP_HIGH(nr)	(0x28 + (nr) * 2)#define F75375_REG_TEMP_HYST(nr)	(0x29 + (nr) * 2)#define F75375_REG_FAN(nr)		(0x16 + (nr) * 2)#define F75375_REG_FAN_MIN(nr)		(0x2C + (nr) * 2)#define F75375_REG_FAN_FULL(nr)		(0x70 + (nr) * 0x10)#define F75375_REG_FAN_PWM_DUTY(nr)	(0x76 + (nr) * 0x10)#define F75375_REG_FAN_PWM_CLOCK(nr)	(0x7D + (nr) * 0x10)#define F75375_REG_FAN_EXP(nr)		(0x74 + (nr) * 0x10)#define F75375_REG_FAN_B_TEMP(nr, step)	((0xA0 + (nr) * 0x10) + (step))#define F75375_REG_FAN_B_SPEED(nr, step) \	((0xA5 + (nr) * 0x10) + (step) * 2)#define F75375_REG_PWM1_RAISE_DUTY	0x69#define F75375_REG_PWM2_RAISE_DUTY	0x6A#define F75375_REG_PWM1_DROP_DUTY	0x6B#define F75375_REG_PWM2_DROP_DUTY	0x6C#define FAN_CTRL_LINEAR(nr)		(4 + nr)#define FAN_CTRL_MODE(nr)		(5 + ((nr) * 2))/* * Data structures and manipulation thereof */struct f75375_data {	unsigned short addr;	struct i2c_client *client;	struct device *hwmon_dev;	const char *name;	int kind;	struct mutex update_lock; /* protect register access */	char valid;	unsigned long last_updated;	/* In jiffies */	unsigned long last_limits;	/* In jiffies */	/* Register values */	u8 in[4];	u8 in_max[4];	u8 in_min[4];	u16 fan[2];	u16 fan_min[2];	u16 fan_full[2];	u16 fan_exp[2];	u8 fan_timer;	u8 pwm[2];	u8 pwm_mode[2];	u8 pwm_enable[2];	s8 temp[2];	s8 temp_high[2];	s8 temp_max_hyst[2];};static int f75375_attach_adapter(struct i2c_adapter *adapter);static int f75375_detect(struct i2c_adapter *adapter, int address, int kind);static int f75375_detach_client(struct i2c_client *client);static int f75375_probe(struct i2c_client *client);static int f75375_remove(struct i2c_client *client);static struct i2c_driver f75375_legacy_driver = {	.driver = {		.name = "f75375_legacy",	},	.attach_adapter = f75375_attach_adapter,	.detach_client = f75375_detach_client,};static struct i2c_driver f75375_driver = {	.driver = {		.name = "f75375",	},	.probe = f75375_probe,	.remove = f75375_remove,};static inline int f75375_read8(struct i2c_client *client, u8 reg){	return i2c_smbus_read_byte_data(client, reg);}/* in most cases, should be called while holding update_lock */static inline u16 f75375_read16(struct i2c_client *client, u8 reg){	return ((i2c_smbus_read_byte_data(client, reg) << 8)		| i2c_smbus_read_byte_data(client, reg + 1));}static inline void f75375_write8(struct i2c_client *client, u8 reg,		u8 value){	i2c_smbus_write_byte_data(client, reg, value);}static inline void f75375_write16(struct i2c_client *client, u8 reg,		u16 value){	int err = i2c_smbus_write_byte_data(client, reg, (value << 8));	if (err)		return;	i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF));}static struct f75375_data *f75375_update_device(struct device *dev){	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int nr;	mutex_lock(&data->update_lock);	/* Limit registers cache is refreshed after 60 seconds */	if (time_after(jiffies, data->last_limits + 60 * HZ)		|| !data->valid) {		for (nr = 0; nr < 2; nr++) {			data->temp_high[nr] =				f75375_read8(client, F75375_REG_TEMP_HIGH(nr));			data->temp_max_hyst[nr] =				f75375_read8(client, F75375_REG_TEMP_HYST(nr));			data->fan_full[nr] =				f75375_read16(client, F75375_REG_FAN_FULL(nr));			data->fan_min[nr] =				f75375_read16(client, F75375_REG_FAN_MIN(nr));			data->fan_exp[nr] =				f75375_read16(client, F75375_REG_FAN_EXP(nr));			data->pwm[nr] =	f75375_read8(client,				F75375_REG_FAN_PWM_DUTY(nr));		}		for (nr = 0; nr < 4; nr++) {			data->in_max[nr] =				f75375_read8(client, F75375_REG_VOLT_HIGH(nr));			data->in_min[nr] =				f75375_read8(client, F75375_REG_VOLT_LOW(nr));		}		data->fan_timer = f75375_read8(client, F75375_REG_FAN_TIMER);		data->last_limits = jiffies;	}	/* Measurement registers cache is refreshed after 2 second */	if (time_after(jiffies, data->last_updated + 2 * HZ)		|| !data->valid) {		for (nr = 0; nr < 2; nr++) {			data->temp[nr] =				f75375_read8(client, F75375_REG_TEMP(nr));			data->fan[nr] =				f75375_read16(client, F75375_REG_FAN(nr));		}		for (nr = 0; nr < 4; nr++)			data->in[nr] =				f75375_read8(client, F75375_REG_VOLT(nr));		data->last_updated = jiffies;		data->valid = 1;	}	mutex_unlock(&data->update_lock);	return data;}static inline u16 rpm_from_reg(u16 reg){	if (reg == 0 || reg == 0xffff)		return 0;	return (1500000 / reg);}static inline u16 rpm_to_reg(int rpm){	if (rpm < 367 || rpm > 0xffff)		return 0xffff;	return (1500000 / rpm);}static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	int nr = to_sensor_dev_attr(attr)->index;	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int val = simple_strtoul(buf, NULL, 10);	mutex_lock(&data->update_lock);	data->fan_min[nr] = rpm_to_reg(val);	f75375_write16(client, F75375_REG_FAN_MIN(nr), data->fan_min[nr]);	mutex_unlock(&data->update_lock);	return count;}static ssize_t set_fan_exp(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	int nr = to_sensor_dev_attr(attr)->index;	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int val = simple_strtoul(buf, NULL, 10);	mutex_lock(&data->update_lock);	data->fan_exp[nr] = rpm_to_reg(val);	f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_exp[nr]);	mutex_unlock(&data->update_lock);	return count;}static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	int nr = to_sensor_dev_attr(attr)->index;	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int val = simple_strtoul(buf, NULL, 10);	mutex_lock(&data->update_lock);	data->pwm[nr] = SENSORS_LIMIT(val, 0, 255);	f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), data->pwm[nr]);	mutex_unlock(&data->update_lock);	return count;}static ssize_t show_pwm_enable(struct device *dev, struct device_attribute		*attr, char *buf){	int nr = to_sensor_dev_attr(attr)->index;	struct f75375_data *data = f75375_update_device(dev);	return sprintf(buf, "%d\n", data->pwm_enable[nr]);}static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val){	struct f75375_data *data = i2c_get_clientdata(client);	u8 fanmode;	if (val < 0 || val > 4)		return -EINVAL;	fanmode = f75375_read8(client, F75375_REG_FAN_TIMER);	fanmode = ~(3 << FAN_CTRL_MODE(nr));	switch (val) {	case 0: /* Full speed */		fanmode  |= (3 << FAN_CTRL_MODE(nr));		data->pwm[nr] = 255;		f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr),				data->pwm[nr]);		break;	case 1: /* PWM */		fanmode  |= (3 << FAN_CTRL_MODE(nr));		break;	case 2: /* AUTOMATIC*/		fanmode  |= (2 << FAN_CTRL_MODE(nr));		break;	case 3: /* fan speed */		break;	}	f75375_write8(client, F75375_REG_FAN_TIMER, fanmode);	data->pwm_enable[nr] = val;	return 0;}static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	int nr = to_sensor_dev_attr(attr)->index;	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int val = simple_strtoul(buf, NULL, 10);	int err = 0;	mutex_lock(&data->update_lock);	err = set_pwm_enable_direct(client, nr, val);	mutex_unlock(&data->update_lock);	return err ? err : count;}static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	int nr = to_sensor_dev_attr(attr)->index;	struct i2c_client *client = to_i2c_client(dev);	struct f75375_data *data = i2c_get_clientdata(client);	int val = simple_strtoul(buf, NULL, 10);	u8 conf = 0;	if (!(val == 0 || val == 1))		return -EINVAL;	mutex_lock(&data->update_lock);	conf = f75375_read8(client, F75375_REG_CONFIG1);	conf = ~(1 << FAN_CTRL_LINEAR(nr));	if (val == 0)		conf |= (1 << FAN_CTRL_LINEAR(nr)) ;	f75375_write8(client, F75375_REG_CONFIG1, conf);	data->pwm_mode[nr] = val;	mutex_unlock(&data->update_lock);	return count;}static ssize_t show_pwm(struct device *dev, struct device_attribute		*attr, char *buf){	int nr = to_sensor_dev_attr(attr)->index;	struct f75375_data *data = f75375_update_device(dev);	return sprintf(buf, "%d\n", data->pwm[nr]);}static ssize_t show_pwm_mode(struct device *dev, struct device_attribute		*attr, char *buf){	int nr = to_sensor_dev_attr(attr)->index;	struct f75375_data *data = f75375_update_device(dev);	return sprintf(buf, "%d\n", data->pwm_mode[nr]);}#define VOLT_FROM_REG(val) ((val) * 8)#define VOLT_TO_REG(val) ((val) / 8)static ssize_t show_in(struct device *dev, struct device_attribute *attr,		char *buf){	int nr = to_sensor_dev_attr(attr)->index;	struct f75375_data *data = f75375_update_device(dev);	return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in[nr]));}static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,		char *buf){	int nr = to_sensor_dev_attr(attr)->index;	struct f75375_data *data = f75375_update_device(dev);	return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_max[nr]));}

⌨️ 快捷键说明

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