359 lines
9.9 KiB
C
359 lines
9.9 KiB
C
/* $NetBSD: adt7463.c,v 1.12 2007/07/25 11:18:15 njoly 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.12 2007/07/25 11:18:15 njoly 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>
|
|
|
|
static int adt7463c_gtredata(struct sysmon_envsys *, envsys_data_t *);
|
|
|
|
static int adt7463c_send_1(struct adt7463c_softc *, uint8_t);
|
|
static int adt7463c_receive_1(struct adt7463c_softc *);
|
|
static int adt7463c_write_1(struct adt7463c_softc *, uint8_t, uint8_t);
|
|
|
|
static void adt7463c_setup_sensors(struct adt7463c_softc *);
|
|
static void adt7463c_refresh_volt(struct adt7463c_softc *, envsys_data_t *);
|
|
static void adt7463c_refresh_temp(struct adt7463c_softc *, envsys_data_t *);
|
|
static void adt7463c_refresh_fan(struct adt7463c_softc *, envsys_data_t *);
|
|
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;
|
|
size_t i;
|
|
|
|
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_sensors(sc);
|
|
|
|
for (i = 0; i < ADT7463_MAX_ENVSYS_RANGE; ++i) {
|
|
sc->sc_sensor[i].sensor = i;
|
|
sc->sc_sensor[i].state = ENVSYS_SVALID;
|
|
}
|
|
|
|
/* Hook into the System Monitor. */
|
|
sc->sc_sysmon.sme_name = sc->sc_dev.dv_xname;
|
|
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;
|
|
|
|
if (sysmon_envsys_register(&sc->sc_sysmon))
|
|
aprint_error("%s:: unable to register with sysmon\n",
|
|
sc->sc_dev.dv_xname);
|
|
}
|
|
|
|
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_sensors(struct adt7463c_softc *sc)
|
|
{
|
|
#define COPYDESCR(x, y) \
|
|
do { \
|
|
strlcpy((x), (y), sizeof(x)); \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
int i, start;
|
|
|
|
start = ADT7463_VOLT_SENSORS_START;
|
|
for (i = start; i < start + ADT7463_VOLT_SENSORS_COUNT; i++)
|
|
sc->sc_sensor[i].units = ENVSYS_SVOLTS_DC;
|
|
|
|
COPYDESCR(sc->sc_sensor[0].desc, "2.5V");
|
|
COPYDESCR(sc->sc_sensor[1].desc, "VCCP");
|
|
COPYDESCR(sc->sc_sensor[2].desc, "VCC");
|
|
COPYDESCR(sc->sc_sensor[3].desc, "5V");
|
|
COPYDESCR(sc->sc_sensor[4].desc, "12V");
|
|
|
|
start = ADT7463_TEMP_SENSORS_START;
|
|
for (i = start; i < start + ADT7463_TEMP_SENSORS_COUNT; i++)
|
|
sc->sc_sensor[i].units = ENVSYS_STEMP;
|
|
|
|
COPYDESCR(sc->sc_sensor[5].desc, "Temp-1");
|
|
COPYDESCR(sc->sc_sensor[6].desc, "Temp-2");
|
|
COPYDESCR(sc->sc_sensor[7].desc, "Temp-3");
|
|
|
|
start = ADT7463_FAN_SENSORS_START;
|
|
for (i = start; i < start + ADT7463_FAN_SENSORS_COUNT; i++)
|
|
sc->sc_sensor[i].units = ENVSYS_SFANRPM;
|
|
|
|
COPYDESCR(sc->sc_sensor[8].desc, "Fan-1");
|
|
COPYDESCR(sc->sc_sensor[9].desc, "Fan-2");
|
|
COPYDESCR(sc->sc_sensor[10].desc, "Fan-3");
|
|
COPYDESCR(sc->sc_sensor[11].desc, "Fan-4");
|
|
}
|
|
|
|
static int
|
|
adt7463c_gtredata(struct sysmon_envsys *sme, envsys_data_t *edata)
|
|
{
|
|
struct adt7463c_softc *sc = sme->sme_cookie;
|
|
|
|
if (edata->sensor < ADT7463_VOLT_SENSORS_COUNT)
|
|
adt7463c_refresh_volt(sc, edata);
|
|
else if (edata->sensor >= ADT7463_VOLT_SENSORS_COUNT &&
|
|
edata->sensor < (ADT7463_VOLT_SENSORS_COUNT +
|
|
ADT7463_TEMP_SENSORS_COUNT))
|
|
adt7463c_refresh_temp(sc, edata);
|
|
else
|
|
adt7463c_refresh_fan(sc, edata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
adt7463c_refresh_volt(struct adt7463c_softc *sc, envsys_data_t *edata)
|
|
{
|
|
size_t i = edata->sensor;
|
|
uint8_t reg;
|
|
int data;
|
|
|
|
static const uint32_t mult[] = {
|
|
ADT7463_2_5V_CONST,
|
|
ADT7463_VCC_CONST,
|
|
ADT7463_3_3V_CONST,
|
|
ADT7463_5V_CONST,
|
|
ADT7463_12V_CONST
|
|
};
|
|
|
|
reg = ADT7463_VOLT_REG_START + (i - ADT7463_VOLT_SENSORS_START);
|
|
adt7463c_send_1(sc, reg);
|
|
data = adt7463c_receive_1(sc);
|
|
|
|
/* envstat assumes that voltage is in uVDC */
|
|
if (data > 0)
|
|
edata->value_cur = data * 100 * mult[i];
|
|
else
|
|
edata->state = ENVSYS_SINVALID;
|
|
}
|
|
|
|
static void
|
|
adt7463c_refresh_temp(struct adt7463c_softc *sc, envsys_data_t *edata)
|
|
{
|
|
size_t i = edata->sensor;
|
|
uint8_t reg;
|
|
int data;
|
|
|
|
reg = ADT7463_TEMP_REG_START + (i - ADT7463_TEMP_SENSORS_START);
|
|
adt7463c_send_1(sc, reg);
|
|
data = adt7463c_receive_1(sc);
|
|
|
|
/* envstat assumes temperature is in micro kelvin */
|
|
if (data > 0)
|
|
edata->value_cur = (data * 100 + ADT7463_CEL_TO_KELVIN) * 10000;
|
|
else
|
|
edata->state = ENVSYS_SINVALID;
|
|
}
|
|
|
|
static void
|
|
adt7463c_refresh_fan(struct adt7463c_softc *sc, envsys_data_t *edata)
|
|
{
|
|
size_t i, j;
|
|
uint8_t reg;
|
|
int data = 0;
|
|
uint16_t val = 0;
|
|
u_char buf[2];
|
|
|
|
i = edata->sensor;
|
|
reg = ADT7463_FAN_REG_START + 2 * (i - ADT7463_FAN_SENSORS_START);
|
|
|
|
/* 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 && val != 0xffff)
|
|
edata->value_cur = (ADT7463_RPM_CONST) / val;
|
|
else
|
|
edata->state = ENVSYS_SINVALID;
|
|
}
|
|
|
|
static int
|
|
adt7463c_receive_1(struct adt7463c_softc *sc)
|
|
{
|
|
uint8_t val = 0;
|
|
int error;
|
|
|
|
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
|
|
return error;
|
|
|
|
if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
|
|
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;
|
|
}
|
|
|
|
static int
|
|
adt7463c_send_1(struct adt7463c_softc *sc, u_int8_t val)
|
|
{
|
|
int error;
|
|
|
|
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
|
|
return error;
|
|
|
|
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
|
|
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;
|
|
}
|
|
|
|
static int
|
|
adt7463c_write_1(struct adt7463c_softc *sc, u_int8_t cmd, u_int8_t val)
|
|
{
|
|
int error;
|
|
|
|
if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
|
|
return error;
|
|
|
|
if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
|
|
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;
|
|
}
|