Line data Source code
1 : /* $OpenBSD: tipmic.c,v 1.3 2018/05/23 23:08: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_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 : 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
110 0 : tipmic_match(struct device *parent, void *match, void *aux)
111 : {
112 0 : struct i2c_attach_args *ia = aux;
113 :
114 0 : return (strcmp(ia->ia_name, "INT33F5") == 0);
115 : }
116 :
117 : void
118 0 : tipmic_attach(struct device *parent, struct device *self, void *aux)
119 : {
120 0 : struct tipmic_softc *sc = (struct tipmic_softc *)self;
121 0 : struct i2c_attach_args *ia = aux;
122 :
123 0 : sc->sc_tag = ia->ia_tag;
124 0 : sc->sc_addr = ia->ia_addr;
125 0 : sc->sc_acpi = acpi_softc;
126 0 : sc->sc_node = ia->ia_cookie;
127 :
128 0 : if (ia->ia_intr == NULL) {
129 0 : printf(": no interrupt\n");
130 0 : return;
131 : }
132 :
133 : /* Mask all interrupts before we install our interrupt handler. */
134 0 : tipmic_write_1(sc, TIPMIC_INTR_MASK, TIPMIC_INTR_MASK_ALL, I2C_F_POLL);
135 :
136 0 : printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
137 0 : sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
138 : IPL_BIO, tipmic_intr, sc, sc->sc_dev.dv_xname);
139 0 : if (sc->sc_ih == NULL) {
140 0 : printf(": can't establish interrupt\n");
141 0 : return;
142 : }
143 :
144 0 : printf("\n");
145 :
146 0 : tipmic_get_lpat(sc);
147 0 : if (sc->sc_lpat == NULL)
148 0 : return;
149 :
150 0 : sc->sc_gpio.cookie = sc;
151 0 : sc->sc_gpio.read_pin = tipmic_read_pin;
152 0 : sc->sc_gpio.write_pin = tipmic_write_pin;
153 0 : sc->sc_node->gpio = &sc->sc_gpio;
154 0 : acpi_register_gpio(sc->sc_acpi, sc->sc_node);
155 :
156 : /* Register OEM defined address space. */
157 0 : aml_register_regionspace(sc->sc_node, TIPMIC_REGIONSPACE_THERMAL,
158 : sc, tipmic_thermal_opreg_handler);
159 0 : aml_register_regionspace(sc->sc_node, TIPMIC_REGIONSPACE_POWER,
160 : sc, tipmic_power_opreg_handler);
161 0 : }
162 :
163 : uint8_t
164 0 : tipmic_read_1(struct tipmic_softc *sc, uint8_t reg, int flags)
165 : {
166 0 : uint8_t val;
167 : int error;
168 :
169 0 : iic_acquire_bus(sc->sc_tag, flags);
170 0 : error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
171 : ®, sizeof(reg), &val, sizeof(val), flags);
172 0 : iic_release_bus(sc->sc_tag, flags);
173 :
174 0 : if (error) {
175 0 : printf("%s: can't read register 0x%02x\n",
176 0 : sc->sc_dev.dv_xname, reg);
177 0 : val = 0xff;
178 0 : }
179 :
180 0 : return val;
181 0 : }
182 :
183 : void
184 0 : tipmic_write_1(struct tipmic_softc *sc, uint8_t reg, uint8_t val, int flags)
185 : {
186 : int error;
187 :
188 0 : iic_acquire_bus(sc->sc_tag, flags);
189 0 : error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
190 : ®, sizeof(reg), &val, sizeof(val), flags);
191 0 : iic_release_bus(sc->sc_tag, flags);
192 :
193 0 : if (error) {
194 0 : printf("%s: can't write register 0x%02x\n",
195 0 : sc->sc_dev.dv_xname, reg);
196 0 : }
197 0 : }
198 :
199 : int
200 0 : tipmic_intr(void *arg)
201 : {
202 0 : struct tipmic_softc *sc = arg;
203 : int handled = 0;
204 : uint8_t stat;
205 :
206 0 : stat = tipmic_read_1(sc, TIPMIC_INTR_STAT, I2C_F_POLL);
207 0 : tipmic_write_1(sc, TIPMIC_INTR_STAT, stat, I2C_F_POLL);
208 0 : if (stat & TIPMIC_INTR_STAT_ADC) {
209 0 : sc->sc_stat_adc = 1;
210 0 : wakeup(&sc->sc_stat_adc);
211 : handled = 1;
212 0 : }
213 :
214 0 : return handled;
215 : }
216 :
217 : void
218 0 : tipmic_get_lpat(struct tipmic_softc *sc)
219 : {
220 0 : struct aml_value res;
221 : int i;
222 :
223 0 : if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res))
224 0 : return;
225 0 : if (res.type != AML_OBJTYPE_PACKAGE)
226 : goto out;
227 0 : if (res.length < 4 || (res.length % 2) != 0)
228 : goto out;
229 :
230 0 : sc->sc_lpat_len = res.length / 2;
231 0 : sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat),
232 : M_DEVBUF, M_WAITOK);
233 :
234 0 : for (i = 0; i < sc->sc_lpat_len; i++) {
235 0 : sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]);
236 0 : sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]);
237 : }
238 :
239 : out:
240 0 : aml_freevalue(&res);
241 0 : }
242 :
243 : int32_t
244 0 : tipmic_raw_to_temp(struct tipmic_softc *sc, int32_t raw)
245 : {
246 0 : struct acpi_lpat *lpat = sc->sc_lpat;
247 : int32_t raw0, delta_raw;
248 : int32_t temp0, delta_temp;
249 : int i;
250 :
251 0 : for (i = 1; i < sc->sc_lpat_len; i++) {
252 : /* Coefficient can be positive or negative. */
253 0 : if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw)
254 : break;
255 0 : if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw)
256 : break;
257 : }
258 0 : if (i == sc->sc_lpat_len)
259 0 : return -1;
260 :
261 0 : raw0 = lpat[i - 1].raw;
262 0 : temp0 = lpat[i - 1].temp;
263 0 : delta_raw = lpat[i].raw - raw0;
264 0 : delta_temp = lpat[i].temp - temp0;
265 :
266 0 : return temp0 + (raw - raw0) * delta_temp / delta_raw;
267 0 : }
268 :
269 : struct tipmic_regmap {
270 : uint8_t address;
271 : uint8_t hi, lo;
272 : };
273 :
274 : struct tipmic_regmap tipmic_thermal_regmap[] = {
275 : { 0x18, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO }
276 : };
277 :
278 : int
279 0 : tipmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address,
280 : int size, uint64_t *value)
281 : {
282 0 : struct tipmic_softc *sc = cookie;
283 : int32_t temp;
284 : uint16_t raw;
285 : uint8_t hi, lo;
286 : uint8_t reg;
287 : int i, s;
288 :
289 : /* Only allow 32-bit read access. */
290 0 : if (size != 4 || iodir != ACPI_IOREAD)
291 0 : return -1;
292 :
293 0 : for (i = 0; i < nitems(tipmic_thermal_regmap); i++) {
294 0 : if (address == tipmic_thermal_regmap[i].address)
295 : break;
296 : }
297 0 : if (i == nitems(tipmic_thermal_regmap)) {
298 0 : printf("%s: addr 0x%02llx\n", __func__, address);
299 0 : return -1;
300 : }
301 :
302 : /* Turn ADC on and select the appropriate channel. */
303 0 : reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0);
304 0 : reg |= TIPMIC_ADC_CTRL_EN;
305 0 : tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
306 0 : switch (tipmic_thermal_regmap[i].hi) {
307 : case TIPMIC_SYSTEMP_HI:
308 0 : reg |= TIPMIC_ADC_CTRL_CH_SYSTEMP;
309 : break;
310 : default:
311 0 : panic("%s: unsupported channel", sc->sc_dev.dv_xname);
312 : }
313 0 : tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
314 :
315 : /* Need to wait 50us before starting the conversion. */
316 0 : delay(50);
317 :
318 : /* Start conversion. */
319 0 : sc->sc_stat_adc = 0;
320 0 : reg |= TIPMIC_ADC_CTRL_START;
321 0 : tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
322 :
323 : /*
324 : * Block interrupts to prevent I2C access from the interrupt
325 : * handler during the completion of the write that unmasks the
326 : * ADC interrupt.
327 : */
328 0 : s = splbio();
329 0 : reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL);
330 0 : reg &= ~TIPMIC_INTR_MASK_ADC;
331 0 : tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL);
332 0 : splx(s);
333 :
334 0 : while (sc->sc_stat_adc == 0) {
335 0 : if (tsleep(&sc->sc_stat_adc, PRIBIO, "tipmic", hz)) {
336 0 : printf("%s: ADC timeout\n", sc->sc_dev.dv_xname);
337 0 : break;
338 : }
339 : }
340 :
341 : /* Mask ADC interrupt again. */
342 0 : s = splbio();
343 0 : reg = tipmic_read_1(sc, TIPMIC_INTR_MASK, I2C_F_POLL);
344 0 : reg |= TIPMIC_INTR_MASK_ADC;
345 0 : tipmic_write_1(sc, TIPMIC_INTR_MASK, reg, I2C_F_POLL);
346 0 : splx(s);
347 :
348 0 : hi = tipmic_thermal_regmap[i].hi;
349 0 : lo = tipmic_thermal_regmap[i].lo;
350 0 : raw = (tipmic_read_1(sc, hi, 0) & 0x03) << 8;
351 0 : raw |= tipmic_read_1(sc, lo, 0);
352 :
353 : /* Turn ADC off. */
354 0 : reg = tipmic_read_1(sc, TIPMIC_ADC_CTRL, 0);
355 0 : reg &= ~(TIPMIC_ADC_CTRL_EN | TIPMIC_ADC_CTRL_CH_MASK);
356 0 : tipmic_write_1(sc, TIPMIC_ADC_CTRL, reg, 0);
357 :
358 0 : temp = tipmic_raw_to_temp(sc, raw);
359 0 : if (temp < 0)
360 0 : return -1;
361 :
362 0 : *value = temp;
363 0 : return 0;
364 0 : }
365 :
366 : struct tipmic_regmap tipmic_power_regmap[] = {
367 : { 0x00, TIPMIC_LDO1_CTRL },
368 : { 0x04, TIPMIC_LDO2_CTRL },
369 : { 0x08, TIPMIC_LDO3_CTRL },
370 : { 0x0c, TIPMIC_LDO5_CTRL },
371 : { 0x10, TIPMIC_LDO6_CTRL },
372 : { 0x14, TIPMIC_LDO7_CTRL },
373 : { 0x18, TIPMIC_LDO8_CTRL },
374 : { 0x1c, TIPMIC_LDO9_CTRL },
375 : { 0x20, TIPMIC_LDO10_CTRL },
376 : { 0x24, TIPMIC_LDO11_CTRL },
377 : { 0x28, TIPMIC_LDO12_CTRL },
378 : { 0x2c, TIPMIC_LDO13_CTRL },
379 : { 0x30, TIPMIC_LDO14_CTRL }
380 : };
381 :
382 : int
383 0 : tipmic_power_opreg_handler(void *cookie, int iodir, uint64_t address,
384 : int size, uint64_t *value)
385 : {
386 0 : struct tipmic_softc *sc = cookie;
387 : uint8_t reg, val;
388 : int i;
389 :
390 : /* Only allow 32-bit access. */
391 0 : if (size != 4)
392 0 : return -1;
393 :
394 0 : for (i = 0; i < nitems(tipmic_power_regmap); i++) {
395 0 : if (address == tipmic_power_regmap[i].address)
396 : break;
397 : }
398 0 : if (i == nitems(tipmic_power_regmap)) {
399 0 : printf("%s: addr 0x%02llx\n", __func__, address);
400 0 : return -1;
401 : }
402 :
403 0 : reg = tipmic_power_regmap[i].hi;
404 0 : val = tipmic_read_1(sc, reg, 0);
405 0 : if (iodir == ACPI_IOREAD) {
406 0 : *value = val & 0x1;
407 0 : } else {
408 0 : if (*value)
409 0 : val |= 0x1;
410 : else
411 0 : val &= ~0x1;
412 0 : tipmic_write_1(sc, reg, val, 0);
413 : }
414 :
415 0 : return 0;
416 0 : }
417 :
418 : /*
419 : * Allegdly the GPIOs are virtual and only there to deal with a
420 : * limitation of Microsoft Windows.
421 : */
422 :
423 : int
424 0 : tipmic_read_pin(void *cookie, int pin)
425 : {
426 0 : return 0;
427 : }
428 :
429 : void
430 0 : tipmic_write_pin(void *cookie, int pin, int value)
431 : {
432 0 : }
|