1 /* $OpenBSD: tipmic.c,v 1.8 2023/03/04 01:23:40 dlg 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_LDO1_CTRL 0x41
38 #define TIPMIC_LDO2_CTRL 0x42
39 #define TIPMIC_LDO3_CTRL 0x43
40 #define TIPMIC_LDO5_CTRL 0x45
41 #define TIPMIC_LDO6_CTRL 0x46
42 #define TIPMIC_LDO7_CTRL 0x47
43 #define TIPMIC_LDO8_CTRL 0x48
44 #define TIPMIC_LDO9_CTRL 0x49
45 #define TIPMIC_LDO10_CTRL 0x4a
46 #define TIPMIC_LDO11_CTRL 0x4b
47 #define TIPMIC_LDO12_CTRL 0x4c
48 #define TIPMIC_LDO13_CTRL 0x4d
49 #define TIPMIC_LDO14_CTRL 0x4e
50 #define TIPMIC_ADC_CTRL 0x50
51 #define TIPMIC_ADC_CTRL_START (1 << 0)
52 #define TIPMIC_ADC_CTRL_CH_MASK (3 << 1)
53 #define TIPMIC_ADC_CTRL_CH_PMICTEMP (1 << 1)
54 #define TIPMIC_ADC_CTRL_CH_BATTEMP (2 << 1)
55 #define TIPMIC_ADC_CTRL_CH_SYSTEMP (3 << 1)
56 #define TIPMIC_ADC_CTRL_EN (1 << 5)
57 #define TIPMIC_PMICTEMP_HI 0x56
58 #define TIPMIC_PMICTEMP_LO 0x57
59 #define TIPMIC_BATTEMP_HI 0x58
60 #define TIPMIC_BATTEMP_LO 0x59
61 #define TIPMIC_SYSTEMP_HI 0x5a
62 #define TIPMIC_SYSTEMP_LO 0x5b
63
64 #define TIPMIC_REGIONSPACE_THERMAL 0x8c
65 #define TIPMIC_REGIONSPACE_POWER 0x8d
66
67 struct acpi_lpat {
68 int32_t temp;
69 int32_t raw;
70 };
71
72 struct tipmic_softc {
73 struct device sc_dev;
74 struct acpi_softc *sc_acpi;
75 struct aml_node *sc_node;
76 i2c_tag_t sc_tag;
77 i2c_addr_t sc_addr;
78
79 void *sc_ih;
80 volatile int sc_stat_adc;
81
82 struct acpi_lpat *sc_lpat;
83 size_t sc_lpat_len;
84
85 struct acpi_gpio sc_gpio;
86 };
87
88 int tipmic_match(struct device *, void *, void *);
89 void tipmic_attach(struct device *, struct device *, void *);
90
91 const struct cfattach tipmic_ca = {
92 sizeof(struct tipmic_softc), tipmic_match, tipmic_attach
93 };
94
95 struct cfdriver tipmic_cd = {
96 NULL, "tipmic", DV_DULL
97 };
98
99 uint8_t tipmic_read_1(struct tipmic_softc *, uint8_t, int);
100 void tipmic_write_1(struct tipmic_softc *, uint8_t, uint8_t, int);
101 int tipmic_intr(void *);
102 void tipmic_get_lpat(struct tipmic_softc *);
103 int32_t tipmic_raw_to_temp(struct tipmic_softc *, int32_t);
104 int tipmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *);
105 int tipmic_power_opreg_handler(void *, int, uint64_t, int, uint64_t *);
106 int tipmic_read_pin(void *, int);
107 void tipmic_write_pin(void *, int, int);
108
109 int
tipmic_match(struct device * parent,void * match,void * aux)110 tipmic_match(struct device *parent, void *match, void *aux)
111 {
112 struct i2c_attach_args *ia = aux;
113
114 return (strcmp(ia->ia_name, "INT33F5") == 0);
115 }
116
117 void
tipmic_attach(struct device * parent,struct device * self,void * aux)118 tipmic_attach(struct device *parent, struct device *self, void *aux)
119 {
120 struct tipmic_softc *sc = (struct tipmic_softc *)self;
121 struct i2c_attach_args *ia = aux;
122
123 sc->sc_tag = ia->ia_tag;
124 sc->sc_addr = ia->ia_addr;
125 sc->sc_acpi = acpi_softc;
126 sc->sc_node = ia->ia_cookie;
127
128 if (ia->ia_intr == NULL) {
129 printf(": no interrupt\n");
130 return;
131 }
132
133 /* Mask all interrupts before we install our interrupt handler. */
134 tipmic_write_1(sc, TIPMIC_INTR_MASK, TIPMIC_INTR_MASK_ALL, I2C_F_POLL);
135
136 printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
137 sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
138 IPL_BIO, tipmic_intr, sc, sc->sc_dev.dv_xname);
139 if (sc->sc_ih == NULL) {
140 printf(": can't establish interrupt\n");
141 return;
142 }
143
144 printf("\n");
145
146 tipmic_get_lpat(sc);
147 if (sc->sc_lpat == NULL)
148 return;
149
150 sc->sc_gpio.cookie = sc;
151 sc->sc_gpio.read_pin = tipmic_read_pin;
152 sc->sc_gpio.write_pin = tipmic_write_pin;
153 sc->sc_node->gpio = &sc->sc_gpio;
154 acpi_register_gpio(sc->sc_acpi, sc->sc_node);
155
156 /* Register OEM defined address space. */
157 aml_register_regionspace(sc->sc_node, TIPMIC_REGIONSPACE_THERMAL,
158 sc, tipmic_thermal_opreg_handler);
159 aml_register_regionspace(sc->sc_node, TIPMIC_REGIONSPACE_POWER,
160 sc, tipmic_power_opreg_handler);
161 }
162
163 uint8_t
tipmic_read_1(struct tipmic_softc * sc,uint8_t reg,int flags)164 tipmic_read_1(struct tipmic_softc *sc, uint8_t reg, int flags)
165 {
166 uint8_t val;
167 int error;
168
169 iic_acquire_bus(sc->sc_tag, flags);
170 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
171 ®, sizeof(reg), &val, sizeof(val), flags);
172 iic_release_bus(sc->sc_tag, flags);
173
174 if (error) {
175 printf("%s: can't read register 0x%02x\n",
176 sc->sc_dev.dv_xname, reg);
177 val = 0xff;
178 }
179
180 return val;
181 }
182
183 void
tipmic_write_1(struct tipmic_softc * sc,uint8_t reg,uint8_t val,int flags)184 tipmic_write_1(struct tipmic_softc *sc, uint8_t reg, uint8_t val, int flags)
185 {
186 int error;
187
188 iic_acquire_bus(sc->sc_tag, flags);
189 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
190 ®, sizeof(reg), &val, sizeof(val), flags);
191 iic_release_bus(sc->sc_tag, flags);
192
193 if (error) {
194 printf("%s: can't write register 0x%02x\n",
195 sc->sc_dev.dv_xname, reg);
196 }
197 }
198
199 int
tipmic_intr(void * arg)200 tipmic_intr(void *arg)
201 {
202 struct tipmic_softc *sc = arg;
203 int handled = 0;
204 uint8_t stat;
205
206 stat = tipmic_read_1(sc, TIPMIC_INTR_STAT, I2C_F_POLL);
207 tipmic_write_1(sc, TIPMIC_INTR_STAT, stat, I2C_F_POLL);
208 if (stat & TIPMIC_INTR_STAT_ADC) {
209 sc->sc_stat_adc = 1;
210 wakeup(&sc->sc_stat_adc);
211 handled = 1;
212 }
213
214 return handled;
215 }
216
217 void
tipmic_get_lpat(struct tipmic_softc * sc)218 tipmic_get_lpat(struct tipmic_softc *sc)
219 {
220 struct aml_value res;
221 int i;
222
223 if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res))
224 return;
225 if (res.type != AML_OBJTYPE_PACKAGE)
226 goto out;
227 if (res.length < 4 || (res.length % 2) != 0)
228 goto out;
229
230 sc->sc_lpat_len = res.length / 2;
231 sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat),
232 M_DEVBUF, M_WAITOK);
233
234 for (i = 0; i < sc->sc_lpat_len; i++) {
235 sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]);
236 sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]);
237 }
238
239 out:
240 aml_freevalue(&res);
241 }
242
243 int32_t
tipmic_raw_to_temp(struct tipmic_softc * sc,int32_t raw)244 tipmic_raw_to_temp(struct tipmic_softc *sc, int32_t raw)
245 {
246 struct acpi_lpat *lpat = sc->sc_lpat;
247 int32_t raw0, delta_raw;
248 int32_t temp0, delta_temp;
249 int i;
250
251 for (i = 1; i < sc->sc_lpat_len; i++) {
252 /* Coefficient can be positive or negative. */
253 if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw)
254 break;
255 if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw)
256 break;
257 }
258 if (i == sc->sc_lpat_len)
259 return -1;
260
261 raw0 = lpat[i - 1].raw;
262 temp0 = lpat[i - 1].temp;
263 delta_raw = lpat[i].raw - raw0;
264 delta_temp = lpat[i].temp - temp0;
265
266 return temp0 + (raw - raw0) * delta_temp / delta_raw;
267 }
268
269 struct tipmic_regmap {
270 uint8_t address;
271 uint8_t hi, lo;
272 };
273
274 struct tipmic_regmap tipmic_thermal_regmap[] = {
275 { 0x00, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO },
276 { 0x18, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO }
277 };
278
279 static int
tipmic_wait_adc(struct tipmic_softc * sc)280 tipmic_wait_adc(struct tipmic_softc *sc)
281 {
282 int i;
283
284 if (!cold) {
285 return (tsleep_nsec(&sc->sc_stat_adc, PRIBIO, "tipmic",
286 SEC_TO_NSEC(1)));
287 }
288
289 for (i = 0; i < 1000; i++) {
290 delay(1000);
291 if (tipmic_intr(sc) == 1)
292 return (0);
293 }
294
295 return (EWOULDBLOCK);
296 }
297
298 int
tipmic_thermal_opreg_handler(void * cookie,int iodir,uint64_t address,int size,uint64_t * value)299 tipmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address,
300 int size, uint64_t *value)
301 {
302 struct tipmic_softc *sc = cookie;
303 int32_t temp;
304 uint16_t raw;
305 uint8_t hi, lo;
306 uint8_t reg;
307 int i, s;
308
309 /* Only allow 32-bit read access. */
310 if (size != 4 || iodir != ACPI_IOREAD)
311 return -1;
312
313 for (i = 0; i < nitems(tipmic_thermal_regmap); i++) {
314 if (address == tipmic_thermal_regmap[i].address)
315 break;
316 }
317 if (i == nitems(tipmic_thermal_regmap)) {
318 printf("%s: addr 0x%02llx\n", __func__, address);
319 return -1;
320 }
321
322 /* Turn ADC on and select the appropriate channel. */
323 reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0);
324 reg |= TIPMIC_ADC_CTRL_EN;
325 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
326 switch (tipmic_thermal_regmap[i].hi) {
327 case TIPMIC_SYSTEMP_HI:
328 reg |= TIPMIC_ADC_CTRL_CH_SYSTEMP;
329 break;
330 default:
331 panic("%s: unsupported channel", sc->sc_dev.dv_xname);
332 }
333 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
334
335 /* Need to wait 50us before starting the conversion. */
336 delay(50);
337
338 /* Start conversion. */
339 sc->sc_stat_adc = 0;
340 reg |= TIPMIC_ADC_CTRL_START;
341 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
342
343 /*
344 * Block interrupts to prevent I2C access from the interrupt
345 * handler during the completion of the write that unmasks the
346 * ADC interrupt.
347 */
348 s = splbio();
349 reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL);
350 reg &= ~TIPMIC_INTR_MASK_ADC;
351 tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL);
352 splx(s);
353
354 while (sc->sc_stat_adc == 0) {
355 if (tipmic_wait_adc(sc)) {
356 printf("%s: ADC timeout\n", sc->sc_dev.dv_xname);
357 break;
358 }
359 }
360
361 /* Mask ADC interrupt again. */
362 s = splbio();
363 reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL);
364 reg |= TIPMIC_INTR_MASK_ADC;
365 tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL);
366 splx(s);
367
368 hi = tipmic_thermal_regmap[i].hi;
369 lo = tipmic_thermal_regmap[i].lo;
370 raw = (tipmic_read_1(sc, hi, 0) & 0x03) << 8;
371 raw |= tipmic_read_1(sc, lo, 0);
372
373 /* Turn ADC off. */
374 reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0);
375 reg &= ~(TIPMIC_ADC_CTRL_EN | TIPMIC_ADC_CTRL_CH_MASK);
376 tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
377
378 temp = tipmic_raw_to_temp(sc, raw);
379 if (temp < 0)
380 return -1;
381
382 *value = temp;
383 return 0;
384 }
385
386 struct tipmic_regmap tipmic_power_regmap[] = {
387 { 0x00, TIPMIC_LDO1_CTRL },
388 { 0x04, TIPMIC_LDO2_CTRL },
389 { 0x08, TIPMIC_LDO3_CTRL },
390 { 0x0c, TIPMIC_LDO5_CTRL },
391 { 0x10, TIPMIC_LDO6_CTRL },
392 { 0x14, TIPMIC_LDO7_CTRL },
393 { 0x18, TIPMIC_LDO8_CTRL },
394 { 0x1c, TIPMIC_LDO9_CTRL },
395 { 0x20, TIPMIC_LDO10_CTRL },
396 { 0x24, TIPMIC_LDO11_CTRL },
397 { 0x28, TIPMIC_LDO12_CTRL },
398 { 0x2c, TIPMIC_LDO13_CTRL },
399 { 0x30, TIPMIC_LDO14_CTRL }
400 };
401
402 int
tipmic_power_opreg_handler(void * cookie,int iodir,uint64_t address,int size,uint64_t * value)403 tipmic_power_opreg_handler(void *cookie, int iodir, uint64_t address,
404 int size, uint64_t *value)
405 {
406 struct tipmic_softc *sc = cookie;
407 uint8_t reg, val;
408 int i;
409
410 /* Only allow 32-bit access. */
411 if (size != 4)
412 return -1;
413
414 for (i = 0; i < nitems(tipmic_power_regmap); i++) {
415 if (address == tipmic_power_regmap[i].address)
416 break;
417 }
418 if (i == nitems(tipmic_power_regmap)) {
419 printf("%s: addr 0x%02llx\n", __func__, address);
420 return -1;
421 }
422
423 reg = tipmic_power_regmap[i].hi;
424 val = tipmic_read_1(sc, reg, 0);
425 if (iodir == ACPI_IOREAD) {
426 *value = val & 0x1;
427 } else {
428 if (*value)
429 val |= 0x1;
430 else
431 val &= ~0x1;
432 tipmic_write_1(sc, reg, val, 0);
433 }
434
435 return 0;
436 }
437
438 /*
439 * Allegedly the GPIOs are virtual and only there to deal with a
440 * limitation of Microsoft Windows.
441 */
442
443 int
tipmic_read_pin(void * cookie,int pin)444 tipmic_read_pin(void *cookie, int pin)
445 {
446 return 0;
447 }
448
449 void
tipmic_write_pin(void * cookie,int pin,int value)450 tipmic_write_pin(void *cookie, int pin, int value)
451 {
452 }
453