1/* $NetBSD: adm1021.c,v 1.6 2011/06/19 11:44:03 martin Exp $ */ 2/* $OpenBSD: adm1021.c,v 1.27 2007/06/24 05:34:35 dlg Exp $ */ 3 4/* 5 * Copyright (c) 2005 Theo de Raadt 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/cdefs.h> 21__KERNEL_RCSID(0, "$NetBSD: adm1021.c,v 1.6 2011/06/19 11:44:03 martin Exp $"); 22 23#include <sys/param.h> 24#include <sys/systm.h> 25#include <sys/device.h> 26#include <dev/sysmon/sysmonvar.h> 27 28#include <dev/i2c/i2cvar.h> 29 30 31/* ADM 1021 registers */ 32#define ADM1021_INT_TEMP 0x00 33#define ADM1021_EXT_TEMP 0x01 34#define ADM1021_STATUS 0x02 35#define ADM1021_STATUS_INVAL 0x7f 36#define ADM1021_STATUS_NOEXT 0x40 37#define ADM1021_CONFIG_READ 0x03 38#define ADM1021_CONFIG_WRITE 0x09 39#define ADM1021_CONFIG_RUN 0x40 40#define ADM1021_COMPANY 0xfe /* contains 0x41 */ 41#define ADM1021_DIE_REVISION 0xff 42 43/* Sensors */ 44#define ADMTEMP_INT 0 45#define ADMTEMP_EXT 1 46#define ADMTEMP_NUM_SENSORS 2 47 48struct admtemp_softc { 49 struct device sc_dev; 50 i2c_tag_t sc_tag; 51 i2c_addr_t sc_addr; 52 53 int sc_noexternal; 54 struct sysmon_envsys *sc_sme; 55 envsys_data_t sc_sensor[ADMTEMP_NUM_SENSORS]; 56}; 57 58int admtemp_match(device_t, cfdata_t, void *); 59void admtemp_attach(device_t, device_t, void *); 60void admtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 61 62CFATTACH_DECL_NEW(admtemp, sizeof(struct admtemp_softc), 63 admtemp_match, admtemp_attach, NULL, NULL); 64 65static const char * admtemp_compats[] = { 66 "i2c-max1617", 67 NULL 68}; 69 70int 71admtemp_match(device_t parent, cfdata_t match, void *aux) 72{ 73 struct i2c_attach_args *ia = aux; 74 75 if (ia->ia_name == NULL) { 76 /* 77 * Indirect config - not much we can do! 78 * Check typical addresses. 79 */ 80 if (((ia->ia_addr >= 0x18) && (ia->ia_addr <= 0x1a)) || 81 ((ia->ia_addr >= 0x29) && (ia->ia_addr <= 0x2b)) || 82 ((ia->ia_addr >= 0x4c) && (ia->ia_addr <= 0x4e))) 83 return (1); 84 } else { 85 /* 86 * Direct config - match via the list of compatible 87 * hardware. 88 */ 89 if (iic_compat_match(ia, admtemp_compats)) 90 return 1; 91 } 92 93 return 0; 94} 95 96 97void 98admtemp_attach(device_t parent, device_t self, void *aux) 99{ 100 struct admtemp_softc *sc = device_private(self); 101 struct i2c_attach_args *ia = aux; 102 u_int8_t cmd, data, stat; 103 104 sc->sc_tag = ia->ia_tag; 105 sc->sc_addr = ia->ia_addr; 106 107 aprint_normal(": ADM1021 or compatible environmental sensor\n"); 108 aprint_naive(": Environmental sensor\n"); 109 110 iic_acquire_bus(sc->sc_tag, 0); 111 cmd = ADM1021_CONFIG_READ; 112 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 113 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 114 iic_release_bus(sc->sc_tag, 0); 115 aprint_error_dev(&sc->sc_dev, "cannot get control register\n"); 116 return; 117 } 118 if (data & ADM1021_CONFIG_RUN) { 119 cmd = ADM1021_STATUS; 120 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 121 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 0)) { 122 iic_release_bus(sc->sc_tag, 0); 123 aprint_error_dev(&sc->sc_dev, 124 "cannot read status register\n"); 125 return; 126 } 127 if ((stat & ADM1021_STATUS_INVAL) == ADM1021_STATUS_INVAL) { 128 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 129 sc->sc_addr, &cmd, sizeof cmd, &stat, sizeof stat, 130 0)) { 131 iic_release_bus(sc->sc_tag, 0); 132 aprint_error_dev(&sc->sc_dev, 133 "cannot read status register\n"); 134 return; 135 } 136 } 137 138 /* means external is dead */ 139 if ((stat & ADM1021_STATUS_INVAL) != ADM1021_STATUS_INVAL && 140 (stat & ADM1021_STATUS_NOEXT)) 141 sc->sc_noexternal = 1; 142 143 data &= ~ADM1021_CONFIG_RUN; 144 cmd = ADM1021_CONFIG_WRITE; 145 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 146 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 147 iic_release_bus(sc->sc_tag, 0); 148 aprint_error_dev(&sc->sc_dev, 149 "cannot set control register\n"); 150 return; 151 } 152 } 153 iic_release_bus(sc->sc_tag, 0); 154 155 /* Initialize sensor data. */ 156 sc->sc_sensor[ADMTEMP_INT].state = ENVSYS_SINVALID; 157 sc->sc_sensor[ADMTEMP_INT].units = ENVSYS_STEMP; 158 sc->sc_sensor[ADMTEMP_EXT].state = ENVSYS_SINVALID; 159 sc->sc_sensor[ADMTEMP_EXT].units = ENVSYS_STEMP; 160 sc->sc_sensor[ADMTEMP_INT].state = ENVSYS_SINVALID; 161 sc->sc_sensor[ADMTEMP_EXT].state = ENVSYS_SINVALID; 162 strlcpy(sc->sc_sensor[ADMTEMP_INT].desc, "internal",sizeof("internal")); 163 strlcpy(sc->sc_sensor[ADMTEMP_EXT].desc, "external",sizeof("external")); 164 sc->sc_sme = sysmon_envsys_create(); 165 if (sysmon_envsys_sensor_attach( 166 sc->sc_sme, &sc->sc_sensor[ADMTEMP_INT])) { 167 sysmon_envsys_destroy(sc->sc_sme); 168 aprint_error_dev(&sc->sc_dev, 169 "unable to attach internal at sysmon\n"); 170 return; 171 } 172 if (sc->sc_noexternal == 0 && 173 sysmon_envsys_sensor_attach( 174 sc->sc_sme, &sc->sc_sensor[ADMTEMP_EXT])) { 175 sysmon_envsys_destroy(sc->sc_sme); 176 aprint_error_dev(&sc->sc_dev, 177 "unable to attach external at sysmon\n"); 178 return; 179 } 180 sc->sc_sme->sme_name = device_xname(self); 181 sc->sc_sme->sme_cookie = sc; 182 sc->sc_sme->sme_refresh = admtemp_refresh; 183 if (sysmon_envsys_register(sc->sc_sme)) { 184 aprint_error_dev(&sc->sc_dev, 185 "unable to register with sysmon\n"); 186 sysmon_envsys_destroy(sc->sc_sme); 187 return; 188 } 189} 190 191 192void 193admtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 194{ 195 struct admtemp_softc *sc = sme->sme_cookie; 196 u_int8_t cmd; 197 int8_t sdata; 198 199 iic_acquire_bus(sc->sc_tag, 0); 200 201 if (edata->sensor == ADMTEMP_INT) 202 cmd = ADM1021_INT_TEMP; 203 else 204 cmd = ADM1021_EXT_TEMP; 205 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 206 &cmd, sizeof cmd, &sdata, sizeof sdata, 0) == 0) { 207 if (sdata == ADM1021_STATUS_INVAL) { 208 edata->state = ENVSYS_SINVALID; 209 } else { 210 edata->value_cur = 273150000 + 1000000 * sdata; 211 edata->state = ENVSYS_SVALID; 212 } 213 } 214 215 iic_release_bus(sc->sc_tag, 0); 216} 217