/*
* f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated
* hardware monitoring features
* Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
*
* The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
* complete hardware monitoring features: voltage, fan and temperature
* sensors, and manual and automatic fan speed control.
*
* 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/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 <asm/io.h>
static struct platform_device *pdev;
#define DRVNAME "f71805f"
/*
* Super-I/O constants and functions
*/
#define F71805F_LD_HWM 0x04
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_DEVREV 0x22 /* Device revision */
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934
#define SIO_F71805F_ID 0x0406
static inline int
superio_inb(int base, int reg)
{
outb(reg, base);
return inb(base + 1);
}
static int
superio_inw(int base, int reg)
{
int val;
outb(reg++, base);
val = inb(base + 1) << 8;
outb(reg, base);
val |= inb(base + 1);
return val;
}
static inline void
superio_select(int base, int ld)
{
outb(SIO_REG_LDSEL, base);
outb(ld, base + 1);
}
static inline void
superio_enter(int base)
{
outb(0x87, base);
outb(0x87, base);
}
static inline void
superio_exit(int base)
{
outb(0xaa, base);
}
/*
* ISA constants
*/
#define REGION_LENGTH 2
#define ADDR_REG_OFFSET 0
#define DATA_REG_OFFSET 1
/*
* Registers
*/
/* in nr from 0 to 8 (8-bit values) */
#define F71805F_REG_IN(nr) (0x10 + (nr))
#define F71805F_REG_IN_HIGH(nr) (0x40 + 2 * (nr))
#define F71805F_REG_IN_LOW(nr) (0x41 + 2 * (nr))
/* fan nr from 0 to 2 (12-bit values, two registers) */
#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr))
/* temp nr from 0 to 2 (8-bit values) */
#define F71805F_REG_TEMP(nr) (0x1B + (nr))
#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
#define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr))
#define F71805F_REG_TEMP_MODE 0x01
#define F71805F_REG_START 0x00
/* status nr from 0 to 2 */
#define F71805F_REG_STATUS(nr) (0x36 + (nr))
/*
* Data structures and manipulation thereof
*/
struct f71805f_data {
unsigned short addr;
const char *name;
struct mutex lock;
struct class_device *class_dev;
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
unsigned long last_limits; /* In jiffies */
/* Register values */
u8 in[9];
u8 in_high[9];
u8 in_low[9];
u16 fan[3];
u16 fan_low[3];
u8 fan_enabled; /* Read once at init time */
u8 temp[3];
u8 temp_high[3];
u8 temp_hyst[3];
u8 temp_mode;
unsigned long alarms;
};
static inline long in_from_reg(u8 reg)
{
return (reg * 8);
}
/* The 2 least significant bits are not used */
static inline u8 in_to_reg(long val)
{
if (val <= 0)
return 0;
if (val >= 2016)
return 0xfc;
return (((val + 16) / 32) << 2);
}
/* in0 is downscaled by a factor 2 internally */
static inline long in0_from_reg(u8 reg)
{
return (reg * 16);
}
static inline u8 in0_to_reg(long val)
{
if (val <= 0)
return 0;
if (val >= 4032)
return 0xfc;
return (((val + 32) / 64) << 2);
}
/* The 4 most significant bits are not used */
static inline long fan_from_reg(u16 reg)
{
reg &= 0xfff;
if (!reg || reg == 0xfff)
return 0;
return (1500000 / reg);
}
static inline u16 fan_to_reg(long rpm)
{
/* If the low limit is set below what the chip can measure,
store the largest possible 12-bit value in the registers,
so that no alarm will ever trigger. */
if (rpm < 367)
return 0xfff;
return (1500000 / rpm);
}
static inline long temp_from_reg(u8 reg)
{
return (reg * 1000);
}
static inline u8 temp_to_reg(long val)
{
if (val < 0)
val<