pc87427.c
来自「linux 内核源代码」· C语言 代码 · 共 641 行 · 第 1/2 页
C
641 行
/* * pc87427.c - hardware monitoring driver for the * National Semiconductor PC87427 Super-I/O chip * Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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. * * Supports the following chips: * * Chip #vin #fan #pwm #temp devid * PC87427 - 8 - - 0xF2 * * This driver assumes that no more than one chip is present. * Only fan inputs are supported so far, although the chip can do much more. */#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/err.h>#include <linux/mutex.h>#include <linux/sysfs.h>#include <linux/ioport.h>#include <asm/io.h>static struct platform_device *pdev;#define DRVNAME "pc87427"/* The lock mutex protects both the I/O accesses (needed because the device is using banked registers) and the register cache (needed to keep the data in the registers and the cache in sync at any time). */struct pc87427_data { struct device *hwmon_dev; struct mutex lock; int address[2]; const char *name; unsigned long last_updated; /* in jiffies */ u8 fan_enabled; /* bit vector */ u16 fan[8]; /* register values */ u16 fan_min[8]; /* register values */ u8 fan_status[8]; /* register values */};/* * Super-I/O registers and operations */#define SIOREG_LDSEL 0x07 /* Logical device select */#define SIOREG_DEVID 0x20 /* Device ID */#define SIOREG_ACT 0x30 /* Device activation */#define SIOREG_MAP 0x50 /* I/O or memory mapping */#define SIOREG_IOBASE 0x60 /* I/O base address */static const u8 logdev[2] = { 0x09, 0x14 };static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" };#define LD_FAN 0#define LD_IN 1#define LD_TEMP 1static 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 REGION_LENGTH 32#define PC87427_REG_BANK 0x0f#define BANK_FM(nr) (nr)#define BANK_FT(nr) (0x08 + (nr))#define BANK_FC(nr) (0x10 + (nr) * 2)/* * I/O access functions *//* ldi is the logical device index */static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg){ return inb(data->address[ldi] + reg);}/* Must be called with data->lock held, except during init */static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi, u8 bank, u8 reg){ outb(bank, data->address[ldi] + PC87427_REG_BANK); return inb(data->address[ldi] + reg);}/* Must be called with data->lock held, except during init */static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi, u8 bank, u8 reg, u8 value){ outb(bank, data->address[ldi] + PC87427_REG_BANK); outb(value, data->address[ldi] + reg);}/* * Fan registers and conversions *//* fan data registers are 16-bit wide */#define PC87427_REG_FAN 0x12#define PC87427_REG_FAN_MIN 0x14#define PC87427_REG_FAN_STATUS 0x10#define FAN_STATUS_STALL (1 << 3)#define FAN_STATUS_LOSPD (1 << 1)#define FAN_STATUS_MONEN (1 << 0)/* Dedicated function to read all registers related to a given fan input. This saves us quite a few locks and bank selections. Must be called with data->lock held. nr is from 0 to 7 */static void pc87427_readall_fan(struct pc87427_data *data, u8 nr){ int iobase = data->address[LD_FAN]; outb(BANK_FM(nr), iobase + PC87427_REG_BANK); data->fan[nr] = inw(iobase + PC87427_REG_FAN); data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN); data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS); /* Clear fan alarm bits */ outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS);}/* The 2 LSB of fan speed registers are used for something different. The actual 2 LSB of the measurements are not available. */static inline unsigned long fan_from_reg(u16 reg){ reg &= 0xfffc; if (reg == 0x0000 || reg == 0xfffc) return 0; return 5400000UL / reg;}/* The 2 LSB of the fan speed limit registers are not significant. */static inline u16 fan_to_reg(unsigned long val){ if (val < 83UL) return 0xffff; if (val >= 1350000UL) return 0x0004; return ((1350000UL + val / 2) / val) << 2;}/* * Data interface */static struct pc87427_data *pc87427_update_device(struct device *dev){ struct pc87427_data *data = dev_get_drvdata(dev); int i; mutex_lock(&data->lock); if (!time_after(jiffies, data->last_updated + HZ) && data->last_updated) goto done; /* Fans */ for (i = 0; i < 8; i++) { if (!(data->fan_enabled & (1 << i))) continue; pc87427_readall_fan(data, i); } data->last_updated = jiffies;done: mutex_unlock(&data->lock); return data;}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 pc87427_data *data = pc87427_update_device(dev); int nr = attr->index; return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));}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 pc87427_data *data = pc87427_update_device(dev); int nr = attr->index; return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));}static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); int nr = attr->index; return sprintf(buf, "%d\n", !!(data->fan_status[nr] & FAN_STATUS_LOSPD));}static ssize_t show_fan_fault(struct device *dev, struct device_attribute *devattr, char *buf){ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); int nr = attr->index; return sprintf(buf, "%d\n", !!(data->fan_status[nr] & FAN_STATUS_STALL));}static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count){ struct pc87427_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; unsigned long val = simple_strtoul(buf, NULL, 10); int iobase = data->address[LD_FAN]; mutex_lock(&data->lock); outb(BANK_FM(nr), iobase + PC87427_REG_BANK); /* The low speed limit registers are read-only while monitoring is enabled, so we have to disable monitoring, then change the limit, and finally enable monitoring again. */ outb(0, iobase + PC87427_REG_FAN_STATUS); data->fan_min[nr] = fan_to_reg(val); outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN); outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS); mutex_unlock(&data->lock); return count;}static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0);static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1);static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2);static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_input, NULL, 3);static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_input, NULL, 4);static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_input, NULL, 5);static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_input, NULL, 6);static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_input, NULL, 7);static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 0);static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 1);static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2);static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 3);static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 4);static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 5);static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 6);static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 7);static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2);static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3);static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4);static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5);static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6);static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7);static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0);static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1);static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2);static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3);static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4);static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5);static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6);static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7);static struct attribute *pc87427_attributes_fan[8][5] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, &sensor_dev_attr_fan1_fault.dev_attr.attr, NULL }, { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?