/*
amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
Based on max6650.c:
Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
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/kernel.h> /* Needed for KERN_INFO */
#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>
/*
* Addresses to scan.
*/
static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
0x4c, 0x4d, 0x4e, I2C_CLIENT_END};
/*
* Insmod parameters
*/
static int pwminv = 0; /*Inverted PWM output. */
module_param(pwminv, int, S_IRUGO);
static int init = 1; /*Power-on initialization.*/
module_param(init, int, S_IRUGO);
enum chips { amc6821 };
#define AMC6821_REG_DEV_ID 0x3D
#define AMC6821_REG_COMP_ID 0x3E
#define AMC6821_REG_CONF1 0x00
#define AMC6821_REG_CONF2 0x01
#define AMC6821_REG_CONF3 0x3F
#define AMC6821_REG_CONF4 0x04
#define AMC6821_REG_STAT1 0x02
#define AMC6821_REG_STAT2 0x03
#define AMC6821_REG_TDATA_LOW 0x08
#define AMC6821_REG_TDATA_HI 0x09
#define AMC6821_REG_LTEMP_HI 0x0A
#define AMC6821_REG_RTEMP_HI 0x0B
#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
#define AMC6821_REG_LTEMP_CRIT 0x1B
#define AMC6821_REG_RTEMP_CRIT 0x1D
#define AMC6821_REG_PSV_TEMP 0x1C
#define AMC6821_REG_DCY 0x22
#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
#define AMC6821_REG_DCY_LOW_TEMP 0x21
#define AMC6821_REG_TACH_LLIMITL 0x10
#define AMC6821_REG_TACH_LLIMITH 0x11
#define AMC6821_REG_TACH_HLIMITL 0x12
#define AMC6821_REG_TACH_HLIMITH 0x13
#define AMC6821_CONF1_START 0x01
#define AMC6821_CONF1_FAN_INT_EN 0x02
#define AMC6821_CONF1_FANIE 0x04
#define AMC6821_CONF1_PWMINV 0x08
#define AMC6821_CONF1_FAN_FAULT_EN 0x10
#define AMC6821_CONF1_FDRC0 0x20
#define AMC6821_CONF1_FDRC1 0x40
#define AMC6821_CONF1_THERMOVIE 0x80
#define AMC6821_CONF2_PWM_EN 0x01
#define AMC6821_CONF2_TACH_MODE 0x02
#define AMC6821_CONF2_TACH_EN 0x04
#define AMC6821_CONF2_RTFIE 0x08
#define AMC6821_CONF2_LTOIE 0x10
#define AMC6821_CONF2_RTOIE 0x20
#define AMC6821_CONF2_PSVIE 0x40
#define AMC6821_CONF2_RST 0x80
#define AMC6821_CONF3_THERM_FAN_EN 0x80
#define AMC6821_CONF3_REV_MASK 0x0F
#define AMC6821_CONF4_OVREN 0x10
#define AMC6821_CONF4_TACH_FAST 0x20
#define AMC6821_CONF4_PSPR 0x40
#define AMC6821_CONF4_MODE 0x80
#define AMC6821_STAT1_RPM_ALARM 0x01
#define AMC6821_STAT1_FANS 0x02
#define AMC6821_STAT1_RTH 0x04
#define AMC6821_STAT1_RTL 0x08
#define AMC6821_STAT1_R_THERM 0x10
#define AMC6821_STAT1_RTF 0x20
#define AMC6821_STAT1_LTH 0x40
#define AMC6821_STAT1_LTL 0x80
#define AMC6821_STAT2_RTC 0x08
#define AMC6821_STAT2_LTC 0x10
#define AMC6821_STAT2_LPSV 0x20
#define AMC6821_STAT2_L_THERM 0x40
#define AMC6821_STAT2_THERM_IN 0x80
enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
TEMP_IDX_LEN, };
static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI,
AMC6821_REG_LTEMP_LIMIT_MIN,
AMC6821_REG_LTEMP_LIMIT_MAX,
AMC6821_REG_LTEMP_CRIT,
AMC6821_REG_RTEMP_HI,
AMC6821_REG_RTEMP_LIMIT_MIN,
AMC6821_REG_RTEMP_LIMIT_MAX,
AMC6821_REG_RTEMP_CRIT, };
enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX,
FAN1_IDX_LEN, };
static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
AMC6821_REG_TACH_LLIMITL,
AMC6821_REG_TACH_HLIMITL, };
static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
AMC6821_REG_TACH_LLIMITH,
AMC6821_REG_TACH_HLIMITH, };
static int amc6821_probe(
struct i2c_client *client,
const struct i2c_device_id *id);
static int amc6821_detect(
struct i2c_client *client,
struct i2c_board_info *info);
static int amc6821_init_client(struct i2c_client *client);
static int amc6821_remove(struct i2c_client *client);
static struct amc6821_data *amc6821_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id amc6821_id[] = {
{ "amc6821", amc6821 },
{ }
};
MODULE_DEVICE_TABLE(i2c, amc6821_id);
static struct i2c_driver amc6821_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "amc6821",
},
.probe = amc6821_probe,
.remove = amc6821_remove,
.id_table = amc6821_id,
.detect = amc6821_detect,
.address_list = normal_i2c,
};
/*
* Client data (each client gets its own)
*/
struct amc6821_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid;