pc87360.c
来自「linux 内核源代码」· C语言 代码 · 共 1,503 行 · 第 1/4 页
C
1,503 行
/* * pc87360.c - Part of lm_sensors, Linux kernel modules * for hardware monitoring * Copyright (C) 2004, 2007 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/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/platform_device.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#include <linux/hwmon-vid.h>#include <linux/err.h>#include <linux/mutex.h>#include <asm/io.h>static u8 devid;static struct platform_device *pdev;static unsigned short extra_isa[3];static u8 confreg[4];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)/* * Device data */struct pc87360_data { const char *name; struct device *hwmon_dev; struct mutex lock; struct mutex 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_probe(struct platform_device *pdev);static int __devexit pc87360_remove(struct platform_device *pdev);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_device(struct platform_device *pdev, int use_thermistors);static struct pc87360_data *pc87360_update_device(struct device *dev);/* * Driver data */static struct platform_driver pc87360_driver = { .driver = { .owner = THIS_MODULE, .name = "pc87360", }, .probe = pc87360_probe, .remove = __devexit_p(pc87360_remove),};/* * Sysfs stuff */static ssize_t show_fan_input(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index])));}static ssize_t show_fan_min(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index])));}static ssize_t show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_DIV_FROM_REG(data->fan_status[attr->index]));}static ssize_t show_fan_status(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_STATUS_FROM_REG(data->fan_status[attr->index]));}static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long fan_min = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[attr->index])); /* If it wouldn't fit, change clock divisor */ while (fan_min > 255 && (data->fan_status[attr->index] & 0x60) != 0x60) { fan_min >>= 1; data->fan[attr->index] >>= 1; data->fan_status[attr->index] += 0x20; } data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(attr->index), data->fan_min[attr->index]); /* Write new divider, preserve alarm bits */ pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(attr->index), data->fan_status[attr->index] & 0xF9); mutex_unlock(&data->update_lock); return count;}static struct sensor_device_attribute fan_input[] = { SENSOR_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0), SENSOR_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1), SENSOR_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2),};static struct sensor_device_attribute fan_status[] = { SENSOR_ATTR(fan1_status, S_IRUGO, show_fan_status, NULL, 0), SENSOR_ATTR(fan2_status, S_IRUGO, show_fan_status, NULL, 1), SENSOR_ATTR(fan3_status, S_IRUGO, show_fan_status, NULL, 2),};static struct sensor_device_attribute fan_div[] = { SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),};static struct sensor_device_attribute fan_min[] = { SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 0), SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 1), SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2),};#define FAN_UNIT_ATTRS(X) \ &fan_input[X].dev_attr.attr, \ &fan_status[X].dev_attr.attr, \ &fan_div[X].dev_attr.attr, \ &fan_min[X].dev_attr.attrstatic ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", PWM_FROM_REG(data->pwm[attr->index], FAN_CONFIG_INVERT(data->fan_conf, attr->index)));}static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->pwm[attr->index] = PWM_TO_REG(val, FAN_CONFIG_INVERT(data->fan_conf, attr->index)); pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), data->pwm[attr->index]); mutex_unlock(&data->update_lock); return count;}static struct sensor_device_attribute pwm[] = { SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0), SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1), SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2),};static struct attribute * pc8736x_fan_attr_array[] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(2), &pwm[0].dev_attr.attr, &pwm[1].dev_attr.attr, &pwm[2].dev_attr.attr, NULL};static const struct attribute_group pc8736x_fan_group = { .attrs = pc8736x_fan_attr_array,};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?