NetBSD/sys/dev/i2c/adt7463.c

406 lines
12 KiB
C

/* $NetBSD: adt7463.c,v 1.4 2006/04/30 18:54:20 xtraeme Exp $ */
/*
* Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Analog devices AD7463 remote thermal controller and voltage monitor
* Data sheet at:
* http://www.analog.com/UploadedFiles/Data_Sheets/272624927ADT7463_c.pdf
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: adt7463.c,v 1.4 2006/04/30 18:54:20 xtraeme Exp $");
/* Fan speed control added by Hanns Hartman */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/adt7463reg.h>
int adt7463c_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
static int adt7463c_send_1(struct adt7463c_softc *sc, u_int8_t val);
static int adt7463c_receive_1(struct adt7463c_softc *sc);
static int adt7463c_write_1(struct adt7463c_softc *sc, u_int8_t cmd, u_int8_t val);
static void adt7463c_setup_volt(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_setup_temp(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_setup_fan(struct adt7463c_softc *sc, int start, int tot);
static void adt7463c_refresh_volt(struct adt7463c_softc *sc);
static void adt7463c_refresh_temp(struct adt7463c_softc *sc);
static void adt7463c_refresh_fan(struct adt7463c_softc *sc);
static int adt7463c_verify(struct adt7463c_softc *sc);
static int adt7463c_match(struct device *, struct cfdata *, void *);
static void adt7463c_attach(struct device *, struct device *, void *);
CFATTACH_DECL(adt7463c, sizeof(struct adt7463c_softc),
adt7463c_match, adt7463c_attach, NULL, NULL);
static int
adt7463c_match(struct device *parent, struct cfdata *cf, void *aux)
{
struct i2c_attach_args *ia = aux;
struct adt7463c_softc sc;
sc.sc_tag = ia->ia_tag;
sc.sc_address = ia->ia_addr;
if(adt7463c_verify(&sc))
return (1);
return (0);
}
static void
adt7463c_attach(struct device *parent, struct device *self, void *aux)
{
struct adt7463c_softc *sc = device_private(self);
struct i2c_attach_args *ia = aux;
int i = 0;
printf("\n");
sc->sc_tag = ia->ia_tag;
sc->sc_address = ia->ia_addr;
/* start ADT7463 */
adt7463c_write_1(sc, ADT7463_CONFIG_REG1, ADT7463_START);
/* set config reg3 to enable fast TACH measurements */
adt7463c_write_1(sc, ADT7463_CONFIG_REG3, ADT7463_CONFIG_REG3_FAST);
/* begin fan speed control addition */
/* associate each fan with Temp zone 2 */
adt7463c_write_1(sc, FANZONEREG1, TEMPCHANNEL);
adt7463c_write_1(sc, FANZONEREG2, TEMPCHANNEL);
adt7463c_write_1(sc, FANZONEREG3, TEMPCHANNEL);
/* set Tmin */
adt7463c_write_1(sc, TMINREG, TMINTEMP);
/* set fans to always on when below Tmin */
adt7463c_write_1(sc, FANONREG, ALWAYSON);
/* set min fan speed */
adt7463c_write_1(sc, FANMINREG1, FANMINSPEED);
adt7463c_write_1(sc, FANMINREG2, FANMINSPEED);
adt7463c_write_1(sc, FANMINREG3, FANMINSPEED);
/* set Trange */
adt7463c_write_1(sc, TRANGEREG, TRANGEVAL);
/* set Tterm */
adt7463c_write_1(sc, TTERMREG, TTERMVAL);
/* set operating point */
adt7463c_write_1(sc, OPPTREG, OPPTTEMP);
/* set Tlow */
adt7463c_write_1(sc, TLOWREG, TLOW);
/* set Thigh */
adt7463c_write_1(sc, THIGHREG, THIGH);
/* turn on dynamic control */
adt7463c_write_1(sc, ENABLEDYNAMICREG, REMOTE2);
/* set a hyst value */
adt7463c_write_1(sc,THYSTREG, THYST);
/* done with fan speed control additions */
/* Initialize sensors */
adt7463c_setup_volt(sc, 0, ADT7463_VOLT_SENSORS_COUNT);
adt7463c_setup_temp(sc, ADT7463_VOLT_SENSORS_COUNT,
ADT7463_TEMP_SENSORS_COUNT);
adt7463c_setup_fan(sc, ADT7463_VOLT_SENSORS_COUNT+ADT7463_TEMP_SENSORS_COUNT,
ADT7463_FAN_SENSORS_COUNT);
for (i = 0; i < ADT7463_MAX_ENVSYS_RANGE; ++i) {
sc->sc_sensor[i].sensor = sc->sc_info[i].sensor = i;
sc->sc_sensor[i].validflags = (ENVSYS_FVALID|ENVSYS_FCURVALID);
sc->sc_info[i].validflags = ENVSYS_FVALID;
sc->sc_sensor[i].warnflags = ENVSYS_WARN_OK;
}
/* Hook into the System Monitor. */
sc->sc_sysmon.sme_ranges = adt7463c_ranges;
sc->sc_sysmon.sme_sensor_info = sc->sc_info;
sc->sc_sysmon.sme_sensor_data = sc->sc_sensor;
sc->sc_sysmon.sme_cookie = sc;
/* callback for envsys get data */
sc->sc_sysmon.sme_gtredata = adt7463c_gtredata;
sc->sc_sysmon.sme_nsensors = ADT7463_MAX_ENVSYS_RANGE;
sc->sc_sysmon.sme_envsys_version = 1000;
sc->sc_sysmon.sme_flags = 0;
if (sysmon_envsys_register(&sc->sc_sysmon))
printf("adt7463: unable to register with sysmon\n");
}
static int
adt7463c_verify(struct adt7463c_softc *sc)
{
/* verify this is an adt7463 */
int c_id, d_id;
adt7463c_send_1(sc, ADT7463_COMPANYID_REG);
c_id = adt7463c_receive_1(sc);
adt7463c_send_1(sc, ADT7463_DEVICEID_REG);
d_id = adt7463c_receive_1(sc);
if ( (c_id == ADT7463_COMPANYID) &&
(d_id == ADT7463_DEVICEID) ) {
return (1);
}
return (0);
}
static void
adt7463c_setup_volt(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start+0].units = sc->sc_info[start+0].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+0].desc, sizeof(sc->sc_info[start+0].desc), "2.5V");
sc->sc_info[start+0].rfact = 10000;
sc->sc_sensor[start+1].units = sc->sc_info[start+1].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+1].desc, sizeof(sc->sc_info[start+1].desc), "VCCP");
sc->sc_info[start+1].rfact = 10000;
sc->sc_sensor[start+2].units = sc->sc_info[start+2].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+2].desc, sizeof(sc->sc_info[start+2].desc), "VCC");
sc->sc_info[start+2].rfact = 10000;
sc->sc_sensor[start+3].units = sc->sc_info[start+3].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+3].desc, sizeof(sc->sc_info[start+3].desc), "5V");
sc->sc_info[start+3].rfact = 10000;
sc->sc_sensor[start+4].units = sc->sc_info[start+4].units = ENVSYS_SVOLTS_DC;
snprintf(sc->sc_info[start+4].desc, sizeof(sc->sc_info[start+4].desc), "12V");
sc->sc_info[start+4].rfact = 10000;
}
static void
adt7463c_setup_temp(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start+0].units = sc->sc_info[start+0].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 0].desc,
sizeof(sc->sc_info[start + 0].desc), "Temp-1");
sc->sc_sensor[start+1].units = sc->sc_info[start+1].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 1].desc,
sizeof(sc->sc_info[start + 1].desc), "Temp-2");
sc->sc_sensor[start+2].units = sc->sc_info[start+2].units = ENVSYS_STEMP;
snprintf(sc->sc_info[start + 2].desc,
sizeof(sc->sc_info[start + 2].desc), "Temp-3");
}
static void
adt7463c_setup_fan(struct adt7463c_softc *sc, int start, int tot)
{
sc->sc_sensor[start + 0].units = ENVSYS_SFANRPM;
sc->sc_info[start + 0].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 0].desc,
sizeof(sc->sc_info[start + 0].desc), "Fan-1");
sc->sc_sensor[start + 1].units = ENVSYS_SFANRPM;
sc->sc_info[start + 1].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 1].desc,
sizeof(sc->sc_info[start + 1].desc), "Fan-2");
sc->sc_sensor[start + 2].units = ENVSYS_SFANRPM;
sc->sc_info[start + 2].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 2].desc,
sizeof(sc->sc_info[start + 2].desc), "Fan-3");
sc->sc_sensor[start + 3].units = ENVSYS_SFANRPM;
sc->sc_info[start + 3].units = ENVSYS_SFANRPM;
snprintf(sc->sc_info[start + 3].desc,
sizeof(sc->sc_info[start + 3].desc), "Fan-4");
}
int
adt7463c_gtredata(sme, tred)
struct sysmon_envsys *sme;
struct envsys_tre_data *tred;
{
struct adt7463c_softc *sc = sme->sme_cookie;
adt7463c_refresh_volt(sc);
adt7463c_refresh_temp(sc);
adt7463c_refresh_fan(sc);
*tred = sc->sc_sensor[tred->sensor];
return (0);
}
void
adt7463c_refresh_volt(struct adt7463c_softc *sc)
{
int i;
u_int8_t reg;
int data;
float mult[] = {ADT7463_2_5V_CONST,
ADT7463_VCC_CONST,
ADT7463_3_3V_CONST,
ADT7463_5V_CONST,
ADT7463_12V_CONST};
reg = ADT7463_VOLT_REG_START;
for (i = 0; i < ADT7463_VOLT_SENSORS_COUNT; i++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
/* envstat assumes that voltage is in uVDC */
double val = (data * 1000000.0 * mult[i]);
if (data > 0)
sc->sc_sensor[i].cur.data_us = (u_int32_t)val;
else
sc->sc_sensor[i].cur.data_us = 0;
}
}
void
adt7463c_refresh_temp(struct adt7463c_softc *sc)
{
int i = 0;
u_int8_t reg;
int data;
reg = ADT7463_TEMP_REG_START;
for (i = 0; i < ADT7463_TEMP_SENSORS_COUNT; i++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
/* envstat assumes temperature is in micro kelvin */
if (data > 0)
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT].cur.data_us
= (data + ADT7463_CEL_TO_KELVIN)* 1000000;
else
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT].cur.data_us
= 0;
}
}
void
adt7463c_refresh_fan(struct adt7463c_softc *sc)
{
int i, j;
u_int8_t reg;
int data = 0;
u_int16_t val = 0;
u_char buf[2];
reg = ADT7463_FAN_REG_START;
for (i = 0; i < ADT7463_FAN_SENSORS_COUNT; i++) {
/* read LSB and then MSB */
for (j = 0; j < 2; j++) {
adt7463c_send_1(sc, reg++);
data = adt7463c_receive_1(sc);
if (data > 0)
buf[j] = data;
else
buf[j] = 0;
}
val = le16dec(buf);
#if _BYTE_ORDER == _BIG_ENDIAN
val = LE16TOH(val);
#endif
/* calculate RPM */
if (val > 0)
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT +
ADT7463_TEMP_SENSORS_COUNT].cur.data_us
= (ADT7463_RPM_CONST)/val;
else
sc->sc_sensor[i + ADT7463_VOLT_SENSORS_COUNT +
ADT7463_TEMP_SENSORS_COUNT].cur.data_us = 0;
}
}
int
adt7463c_receive_1(struct adt7463c_softc *sc)
{
u_int8_t val = 0;
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_READ,
sc->sc_address, NULL, 0, &val, 1, 0)) != 0) {
iic_release_bus(sc->sc_tag, 0);
return (error);
}
iic_release_bus(sc->sc_tag, 0);
return (val);
}
int
adt7463c_send_1(struct adt7463c_softc *sc, u_int8_t val)
{
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE,
sc->sc_address, NULL, 0, &val, 1, 0)) != 0) {
iic_release_bus(sc->sc_tag, 0);
return (error);
}
iic_release_bus(sc->sc_tag, 0);
return (0);
}
int
adt7463c_write_1(struct adt7463c_softc *sc, u_int8_t cmd, u_int8_t val)
{
int error = 0;
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
return (error);
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE,
sc->sc_address, &cmd, 1, &val, 1, 0)) != 0) {
iic_release_bus(sc->sc_tag, 0);
return (error);
}
iic_release_bus(sc->sc_tag, 0);
return (0);
}