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