/*
adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Based on lm75.c and lm85.c
Supports adm1030 / adm1031
Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
Reworked by 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 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/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
/* Following macros takes channel parameter starting from 0 to 2 */
#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
#define ADM1031_REG_PWM (0x22)
#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
#define ADM1031_REG_FAN_FILTER (0x23)
#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr))
#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr))
#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr))
#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr))
#define ADM1031_REG_TEMP(nr) (0x0a + (nr))
#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
#define ADM1031_REG_STATUS(nr) (0x2 + (nr))
#define ADM1031_REG_CONF1 0x00
#define ADM1031_REG_CONF2 0x01
#define ADM1031_REG_EXT_TEMP 0x06
#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */
#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */
#define ADM1031_CONF2_PWM1_ENABLE 0x01
#define ADM1031_CONF2_PWM2_ENABLE 0x02
#define ADM1031_CONF2_TACH1_ENABLE 0x04
#define ADM1031_CONF2_TACH2_ENABLE 0x08
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
#define ADM1031_UPDATE_RATE_MASK 0x1c
#define ADM1031_UPDATE_RATE_SHIFT 2
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
enum chips { adm1030, adm1031 };
typedef u8 auto_chan_table_t[8][2];
/* Each client has this additional data */
struct adm1031_data {
struct device *hwmon_dev;
struct mutex update_lock;
int chip_type;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
unsigned int update_interval; /* In milliseconds */
/* The chan_select_table contains the possible configurations for
* auto fan control.
*/
const auto_chan_table_t *chan_select_table;
u16 alarm;
u8 conf1;
u8 conf2;
u8 fan[2];
u8 fan_div[2];
u8 fan_min[2];
u8 pwm[2];
u8 old_pwm[2];
s8 temp[3];
u8 ext_temp[3];
u8 auto_temp[3];
u8 auto_temp_min[3];
u8 auto_temp_off[3];
u8 auto_temp_max[3];
s8 temp_offset[3];
s8 temp_min[3];
s8 temp_max[3];
s8 temp_crit[3];
};
static int adm1031_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int adm1031_detect(struct i2c_client *client,
struct i2c_board_info *info);
static void adm1031_init_client(struct i2c_client *client);
static int adm1031_remove(struct i2c_client *client);
static struct adm1031_data *adm1031_update_device(struct device *dev);
static const struct i2c_device_id adm1031_id[] = {
{ "adm1030", adm1030 },
{ "adm1031", adm1031 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1031_id);
/* This is the driver that will be inserted */
static struct i2c_driver adm1031_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "adm1031",
},
.probe = adm1031_probe,
.remove = adm1031_remove,
.id_table = adm1031_id,
.detect = adm1031_detect,
.address_list = normal_i2c,
};
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int
adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \
((val + 500) / 1000)))
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)
#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f)
#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \
(val) | 0x70 : (val))
#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
static int FAN_TO_REG(int reg, int div)
{
int tmp;
tmp = FAN_FROM_REG