tipmic.c revision 1.1
1/* $OpenBSD: tipmic.c,v 1.1 2018/05/20 19:30:21 kettenis Exp $ */ 2/* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/device.h> 21#include <sys/kernel.h> 22#include <sys/malloc.h> 23 24#include <dev/acpi/acpireg.h> 25#include <dev/acpi/acpivar.h> 26#include <dev/acpi/acpidev.h> 27#include <dev/acpi/amltypes.h> 28#include <dev/acpi/dsdt.h> 29 30#include <dev/i2c/i2cvar.h> 31 32#define TIPMIC_INTR_STAT 0x01 33#define TIPMIC_INTR_STAT_ADC (1 << 2) 34#define TIPMIC_INTR_MASK 0x02 35#define TIPMIC_INTR_MASK_ADC (1 << 2) 36#define TIPMIC_INTR_MASK_ALL 0xff 37#define TIPMIC_ADC_CTRL 0x50 38#define TIPMIC_ADC_CTRL_START (1 << 0) 39#define TIPMIC_ADC_CTRL_CH_MASK (3 << 1) 40#define TIPMIC_ADC_CTRL_CH_PMICTEMP (1 << 1) 41#define TIPMIC_ADC_CTRL_CH_BATTEMP (2 << 1) 42#define TIPMIC_ADC_CTRL_CH_SYSTEMP (3 << 1) 43#define TIPMIC_ADC_CTRL_EN (1 << 5) 44#define TIPMIC_PMICTEMP_HI 0x56 45#define TIPMIC_PMICTEMP_LO 0x57 46#define TIPMIC_BATTEMP_HI 0x58 47#define TIPMIC_BATTEMP_LO 0x59 48#define TIPMIC_SYSTEMP_HI 0x5a 49#define TIPMIC_SYSTEMP_LO 0x5b 50 51#define TIPMIC_REGIONSPACE_THERMAL 0x8c 52 53struct acpi_lpat { 54 int32_t temp; 55 int32_t raw; 56}; 57 58struct tipmic_softc { 59 struct device sc_dev; 60 struct acpi_softc *sc_acpi; 61 struct aml_node *sc_node; 62 i2c_tag_t sc_tag; 63 i2c_addr_t sc_addr; 64 65 void *sc_ih; 66 volatile int sc_stat_adc; 67 68 struct acpi_lpat *sc_lpat; 69 size_t sc_lpat_len; 70 71 struct acpi_gpio sc_gpio; 72}; 73 74int tipmic_match(struct device *, void *, void *); 75void tipmic_attach(struct device *, struct device *, void *); 76 77struct cfattach tipmic_ca = { 78 sizeof(struct tipmic_softc), tipmic_match, tipmic_attach 79}; 80 81struct cfdriver tipmic_cd = { 82 NULL, "tipmic", DV_DULL 83}; 84 85uint8_t tipmic_read_1(struct tipmic_softc *, uint8_t, int); 86void tipmic_write_1(struct tipmic_softc *, uint8_t, uint8_t, int); 87int tipmic_intr(void *); 88void tipmic_get_lpat(struct tipmic_softc *); 89int32_t tipmic_raw_to_temp(struct tipmic_softc *, int32_t); 90int tipmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *); 91int tipmic_read_pin(void *, int); 92void tipmic_write_pin(void *, int, int); 93 94int 95tipmic_match(struct device *parent, void *match, void *aux) 96{ 97 struct i2c_attach_args *ia = aux; 98 99 return (strcmp(ia->ia_name, "INT33F5") == 0); 100} 101 102void 103tipmic_attach(struct device *parent, struct device *self, void *aux) 104{ 105 struct tipmic_softc *sc = (struct tipmic_softc *)self; 106 struct i2c_attach_args *ia = aux; 107 108 sc->sc_tag = ia->ia_tag; 109 sc->sc_addr = ia->ia_addr; 110 sc->sc_acpi = acpi_softc; 111 sc->sc_node = ia->ia_cookie; 112 113 if (ia->ia_intr == NULL) { 114 printf(": no interrupt\n"); 115 return; 116 } 117 118 /* Mask all interrupts before we install our interrupt handler. */ 119 tipmic_write_1(sc, TIPMIC_INTR_MASK, TIPMIC_INTR_MASK_ALL, I2C_F_POLL); 120 121 printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); 122 sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, 123 IPL_BIO, tipmic_intr, sc, sc->sc_dev.dv_xname); 124 if (sc->sc_ih == NULL) { 125 printf(": can't establish interrupt\n"); 126 return; 127 } 128 129 printf("\n"); 130 131 tipmic_get_lpat(sc); 132 if (sc->sc_lpat == NULL) 133 return; 134 135 sc->sc_gpio.cookie = sc; 136 sc->sc_gpio.read_pin = tipmic_read_pin; 137 sc->sc_gpio.write_pin = tipmic_write_pin; 138 sc->sc_node->gpio = &sc->sc_gpio; 139 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 140 141 /* Register OEM defined address space. */ 142 aml_register_regionspace(sc->sc_node, TIPMIC_REGIONSPACE_THERMAL, 143 sc, tipmic_thermal_opreg_handler); 144} 145 146uint8_t 147tipmic_read_1(struct tipmic_softc *sc, uint8_t reg, int flags) 148{ 149 uint8_t val; 150 int error; 151 152 iic_acquire_bus(sc->sc_tag, flags); 153 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 154 ®, sizeof(reg), &val, sizeof(val), flags); 155 iic_release_bus(sc->sc_tag, flags); 156 157 if (error) { 158 printf("%s: can't read register 0x%02x\n", 159 sc->sc_dev.dv_xname, reg); 160 val = 0xff; 161 } 162 163 return val; 164} 165 166void 167tipmic_write_1(struct tipmic_softc *sc, uint8_t reg, uint8_t val, int flags) 168{ 169 int error; 170 171 iic_acquire_bus(sc->sc_tag, flags); 172 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 173 ®, sizeof(reg), &val, sizeof(val), flags); 174 iic_release_bus(sc->sc_tag, flags); 175 176 if (error) { 177 printf("%s: can't write register 0x%02x\n", 178 sc->sc_dev.dv_xname, reg); 179 } 180} 181 182int 183tipmic_intr(void *arg) 184{ 185 struct tipmic_softc *sc = arg; 186 int handled = 0; 187 uint8_t stat; 188 189 stat = tipmic_read_1(sc, TIPMIC_INTR_STAT, I2C_F_POLL); 190 tipmic_write_1(sc, TIPMIC_INTR_STAT, stat, I2C_F_POLL); 191 if (stat & TIPMIC_INTR_STAT_ADC) { 192 sc->sc_stat_adc = 1; 193 wakeup(&sc->sc_stat_adc); 194 handled = 1; 195 } 196 197 return handled; 198} 199 200void 201tipmic_get_lpat(struct tipmic_softc *sc) 202{ 203 struct aml_value res; 204 int i; 205 206 if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res)) 207 return; 208 if (res.type != AML_OBJTYPE_PACKAGE) 209 goto out; 210 if (res.length < 4 || (res.length % 2) != 0) 211 goto out; 212 213 sc->sc_lpat_len = res.length / 2; 214 sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat), 215 M_DEVBUF, M_WAITOK); 216 217 for (i = 0; i < sc->sc_lpat_len; i++) { 218 sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]); 219 sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]); 220 } 221 222out: 223 aml_freevalue(&res); 224} 225 226int32_t 227tipmic_raw_to_temp(struct tipmic_softc *sc, int32_t raw) 228{ 229 struct acpi_lpat *lpat = sc->sc_lpat; 230 int32_t raw0, delta_raw; 231 int32_t temp0, delta_temp; 232 int i; 233 234 for (i = 1; i < sc->sc_lpat_len; i++) { 235 /* Coefficient can be positive or negative. */ 236 if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw) 237 break; 238 if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw) 239 break; 240 } 241 if (i == sc->sc_lpat_len) 242 return -1; 243 244 raw0 = lpat[i - 1].raw; 245 temp0 = lpat[i - 1].temp; 246 delta_raw = lpat[i].raw - raw0; 247 delta_temp = lpat[i].temp - temp0; 248 249 return temp0 + (raw - raw0) * delta_temp / delta_raw; 250} 251 252struct tipmic_regmap { 253 uint8_t address; 254 uint8_t hi, lo; 255}; 256 257struct tipmic_regmap tipmic_thermal_regmap[] = { 258 { 0x18, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO } 259}; 260 261int 262tipmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address, 263 int size, uint64_t *value) 264{ 265 struct tipmic_softc *sc = cookie; 266 int32_t temp; 267 uint16_t raw; 268 uint8_t hi, lo; 269 uint8_t reg; 270 int i, s; 271 272 /* Only allow 32-bit read access. */ 273 if (size != 4 || iodir != ACPI_IOREAD) 274 return -1; 275 276 for (i = 0; i < nitems(tipmic_thermal_regmap); i++) { 277 if (address == tipmic_thermal_regmap[i].address) 278 break; 279 } 280 if (i == nitems(tipmic_thermal_regmap)) 281 return -1; 282 283 /* Turn ADC on and select the appropriate channel. */ 284 reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0); 285 reg |= TIPMIC_ADC_CTRL_EN; 286 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0); 287 switch (tipmic_thermal_regmap[i].hi) { 288 case TIPMIC_SYSTEMP_HI: 289 reg |= TIPMIC_ADC_CTRL_CH_SYSTEMP; 290 break; 291 default: 292 panic("%s: unsupported channel", sc->sc_dev.dv_xname); 293 } 294 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0); 295 296 /* Need to wait 50us before starting the conversion. */ 297 delay(50); 298 299 /* Start conversion. */ 300 sc->sc_stat_adc = 0; 301 reg |= TIPMIC_ADC_CTRL_START; 302 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0); 303 304 /* 305 * Block interrupts to prevent I2C access from the interrupt 306 * handler during the completion of the write that unmasks the 307 * ADC interrupt. 308 */ 309 s = splbio(); 310 reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL); 311 reg &= ~TIPMIC_INTR_MASK_ADC; 312 tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL); 313 splx(s); 314 315 while (sc->sc_stat_adc == 0) { 316 if (tsleep(&sc->sc_stat_adc, PRIBIO, "tipmic", hz)) { 317 printf("%s: ADC timeout\n", sc->sc_dev.dv_xname); 318 break; 319 } 320 } 321 322 /* Mask ADC interrupt again. */ 323 s = splbio(); 324 reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL); 325 reg |= TIPMIC_INTR_MASK_ADC; 326 tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL); 327 splx(s); 328 329 hi = tipmic_thermal_regmap[i].hi; 330 lo = tipmic_thermal_regmap[i].lo; 331 raw = (tipmic_read_1(sc, hi, 0) & 0x03) << 8; 332 raw |= tipmic_read_1(sc, lo, 0); 333 334 /* Turn ADC off. */ 335 reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0); 336 reg &= ~(TIPMIC_ADC_CTRL_EN | TIPMIC_ADC_CTRL_CH_MASK); 337 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0); 338 339 temp = tipmic_raw_to_temp(sc, raw); 340 if (temp < 0) 341 return -1; 342 343 *value = temp; 344 return 0; 345} 346 347/* 348 * Allegdly the GPIOs are virtual and only there to deal with a 349 * limitation of Microsoft Windows. 350 */ 351 352int 353tipmic_read_pin(void *cookie, int pin) 354{ 355 return 0; 356} 357 358void 359tipmic_write_pin(void *cookie, int pin, int value) 360{ 361} 362