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