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
67struct acpi_lpat {
68	int32_t temp;
69	int32_t raw;
70};
71
72struct 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
88int	tipmic_match(struct device *, void *, void *);
89void	tipmic_attach(struct device *, struct device *, void *);
90
91const struct cfattach tipmic_ca = {
92	sizeof(struct tipmic_softc), tipmic_match, tipmic_attach
93};
94
95struct cfdriver tipmic_cd = {
96	NULL, "tipmic", DV_DULL
97};
98
99uint8_t	tipmic_read_1(struct tipmic_softc *, uint8_t, int);
100void	tipmic_write_1(struct tipmic_softc *, uint8_t, uint8_t, int);
101int	tipmic_intr(void *);
102void	tipmic_get_lpat(struct tipmic_softc *);
103int32_t	tipmic_raw_to_temp(struct tipmic_softc *, int32_t);
104int	tipmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *);
105int	tipmic_power_opreg_handler(void *, int, uint64_t, int, uint64_t *);
106int	tipmic_read_pin(void *, int);
107void	tipmic_write_pin(void *, int, int);
108
109int
110tipmic_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
117void
118tipmic_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
163uint8_t
164tipmic_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	    &reg, 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
183void
184tipmic_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	    &reg, 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
199int
200tipmic_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
217void
218tipmic_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
239out:
240	aml_freevalue(&res);
241}
242
243int32_t
244tipmic_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
269struct tipmic_regmap {
270	uint8_t address;
271	uint8_t hi, lo;
272};
273
274struct tipmic_regmap tipmic_thermal_regmap[] = {
275	{ 0x00, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO },
276	{ 0x18, TIPMIC_SYSTEMP_HI, TIPMIC_SYSTEMP_LO }
277};
278
279static int
280tipmic_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
298int
299tipmic_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
386struct 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
402int
403tipmic_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
443int
444tipmic_read_pin(void *cookie, int pin)
445{
446	return 0;
447}
448
449void
450tipmic_write_pin(void *cookie, int pin, int value)
451{
452}
453