fscpos.c
来自「linux 内核源代码」· C语言 代码 · 共 668 行 · 第 1/2 页
C
668 行
/* fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch> 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.*//* fujitsu siemens poseidon chip, module based on the old fscpos module by Hermann Jung <hej@odn.de> and the fscher module by Reinhard Nissl <rnissl@gmx.de> original module based on lm80.c Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com> Thanks to Jean Delvare for reviewing my code and suggesting a lot of improvements.*/#include <linux/module.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/init.h>#include <linux/hwmon.h>#include <linux/err.h>#include <linux/mutex.h>#include <linux/sysfs.h>/* * Addresses to scan */static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };/* * Insmod parameters */I2C_CLIENT_INSMOD_1(fscpos);/* * The FSCPOS registers *//* chip identification */#define FSCPOS_REG_IDENT_0 0x00#define FSCPOS_REG_IDENT_1 0x01#define FSCPOS_REG_IDENT_2 0x02#define FSCPOS_REG_REVISION 0x03/* global control and status */#define FSCPOS_REG_EVENT_STATE 0x04#define FSCPOS_REG_CONTROL 0x05/* watchdog */#define FSCPOS_REG_WDOG_PRESET 0x28#define FSCPOS_REG_WDOG_STATE 0x23#define FSCPOS_REG_WDOG_CONTROL 0x21/* voltages */#define FSCPOS_REG_VOLT_12 0x45#define FSCPOS_REG_VOLT_5 0x42#define FSCPOS_REG_VOLT_BATT 0x48/* fans - the chip does not support minimum speed for fan2 */static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 };static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab };static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 };static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf };/* temperatures */static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 };static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 };/* * Functions declaration */static int fscpos_attach_adapter(struct i2c_adapter *adapter);static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind);static int fscpos_detach_client(struct i2c_client *client);static int fscpos_read_value(struct i2c_client *client, u8 reg);static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value);static struct fscpos_data *fscpos_update_device(struct device *dev);static void fscpos_init_client(struct i2c_client *client);static void reset_fan_alarm(struct i2c_client *client, int nr);/* * Driver data (common to all clients) */static struct i2c_driver fscpos_driver = { .driver = { .name = "fscpos", }, .id = I2C_DRIVERID_FSCPOS, .attach_adapter = fscpos_attach_adapter, .detach_client = fscpos_detach_client,};/* * Client data (each client gets its own) */struct fscpos_data { struct i2c_client client; struct device *hwmon_dev; struct mutex update_lock; char valid; /* 0 until following fields are valid */ unsigned long last_updated; /* In jiffies */ /* register values */ u8 revision; /* revision of chip */ u8 global_event; /* global event status */ u8 global_control; /* global control register */ u8 wdog_control; /* watchdog control */ u8 wdog_state; /* watchdog status */ u8 wdog_preset; /* watchdog preset */ u8 volt[3]; /* 12, 5, battery current */ u8 temp_act[3]; /* temperature */ u8 temp_status[3]; /* status of sensor */ u8 fan_act[3]; /* fans revolutions per second */ u8 fan_status[3]; /* fan status */ u8 pwm[2]; /* fan min value for rps */ u8 fan_ripple[3]; /* divider for rps */};/* Temperature */#define TEMP_FROM_REG(val) (((val) - 128) * 1000)static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr){ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1]));}static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr){ /* bits 2..7 reserved => mask with 0x03 */ return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03);}static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr){ return sprintf(buf, "1\n");}static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int nr, int reg){ unsigned long v = simple_strtoul(buf, NULL, 10); if (v != 1) { dev_err(&client->dev, "temp_reset value %ld not supported. " "Use 1 to reset the alarm!\n", v); return -EINVAL; } dev_info(&client->dev, "You used the temp_reset feature which has not " "been proplerly tested. Please report your " "experience to the module author.\n"); /* Supported value: 2 (clears the status) */ fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr - 1], 2); return count;}/* Fans */#define RPM_FROM_REG(val) ((val) * 60)static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr){ /* bits 0..1, 3..7 reserved => mask with 0x04 */ return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04);}static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr){ return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1]));}static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr){ /* bits 2..7 reserved => mask with 0x03 */ return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03);}static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int nr, int reg){ /* supported values: 2, 4, 8 */ unsigned long v = simple_strtoul(buf, NULL, 10); switch (v) { case 2: v = 1; break; case 4: v = 2; break; case 8: v = 3; break; default: dev_err(&client->dev, "fan_ripple value %ld not supported. " "Must be one of 2, 4 or 8!\n", v); return -EINVAL; } mutex_lock(&data->update_lock); /* bits 2..7 reserved => mask with 0x03 */ data->fan_ripple[nr - 1] &= ~0x03; data->fan_ripple[nr - 1] |= v; fscpos_write_value(client, reg, data->fan_ripple[nr - 1]); mutex_unlock(&data->update_lock); return count;}static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr){ return sprintf(buf, "%u\n", data->pwm[nr - 1]);}static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int nr, int reg){ unsigned long v = simple_strtoul(buf, NULL, 10); /* Range: 0..255 */ if (v < 0) v = 0; if (v > 255) v = 255; mutex_lock(&data->update_lock); data->pwm[nr - 1] = v; fscpos_write_value(client, reg, data->pwm[nr - 1]); mutex_unlock(&data->update_lock); return count;}static void reset_fan_alarm(struct i2c_client *client, int nr){ fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4);}/* Volts */#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255)static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf){ struct fscpos_data *data = fscpos_update_device(dev); return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200));}static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf){ struct fscpos_data *data = fscpos_update_device(dev); return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600));}static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf){ struct fscpos_data *data = fscpos_update_device(dev); return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300));}/* Watchdog */static ssize_t show_wdog_control(struct fscpos_data *data, char *buf){ /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */ return sprintf(buf, "%u\n", data->wdog_control & 0xb0);}static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int reg){ /* bits 0..3 reserved => mask with 0xf0 */ unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; mutex_lock(&data->update_lock); data->wdog_control &= ~0xf0; data->wdog_control |= v; fscpos_write_value(client, reg, data->wdog_control); mutex_unlock(&data->update_lock); return count;}static ssize_t show_wdog_state(struct fscpos_data *data, char *buf){ /* bits 0, 2..7 reserved => mask with 0x02 */ return sprintf(buf, "%u\n", data->wdog_state & 0x02);}static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int reg){ unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; /* Valid values: 2 (clear) */ if (v != 2) { dev_err(&client->dev, "wdog_state value %ld not supported. " "Must be 2 to clear the state!\n", v); return -EINVAL; } mutex_lock(&data->update_lock); data->wdog_state &= ~v; fscpos_write_value(client, reg, v); mutex_unlock(&data->update_lock); return count;}static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf){ return sprintf(buf, "%u\n", data->wdog_preset);}static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data *data, const char *buf, size_t count, int reg){ unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; mutex_lock(&data->update_lock); data->wdog_preset = v; fscpos_write_value(client, reg, data->wdog_preset); mutex_unlock(&data->update_lock); return count;}/* Event */static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?