xref: /openbsd/sys/dev/acpi/ccpmic.c (revision 471aeecf)
1 /*	$OpenBSD: ccpmic.c,v 1.3 2022/04/06 18:59:27 naddy 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 CCPMIC_GPIO0P0CTLO		0x2b
33 #define CCPMIC_GPIO0P0CTLI		0x33
34 #define CCPMIC_GPIO1P0CTLO		0x3b
35 #define CCPMIC_GPIO1P0CTLI		0x43
36 #define CCPMIC_GPIOPANELCTL		0x52
37 #define  CCPMIC_GPIOCTLO_RVAL_2KUP	(1 << 1)
38 #define  CCPMIC_GPIOCTLO_DRV_REN	(1 << 3)
39 #define  CCPMIC_GPIOCTLO_DIR_OUT	(1 << 5)
40 #define  CCPMIC_GPIOCTLI_VALUE		(1 << 0)
41 
42 #define CCPMIC_GPIOCTLO_INPUT \
43     (CCPMIC_GPIOCTLO_DRV_REN | CCPMIC_GPIOCTLO_RVAL_2KUP)
44 #define CCPMIC_GPIOCTLO_OUTPUT \
45     (CCPMIC_GPIOCTLO_INPUT | CCPMIC_GPIOCTLO_DIR_OUT)
46 
47 #define CCPMIC_V1P8SX			0x5d
48 #define CCPMIC_V1P2SX			0x61
49 #define CCPMIC_V2P85SX			0x66
50 #define  CCPMIC_PWR_ON			(1 << 0)
51 #define  CCPMIC_PWR_SEL			(1 << 1)
52 #define CCPMIC_SYS2_THRM_RSLT_H		0x78
53 #define CCPMIC_SYS2_THRM_RSLT_L		0x79
54 
55 #define CCPMIC_REGIONSPACE_THERMAL	0x8c
56 #define CCPMIC_REGIONSPACE_POWER	0x8d
57 
58 #define CCPMIC_NPINS			16
59 
60 struct acpi_lpat {
61 	int32_t temp;
62 	int32_t raw;
63 };
64 
65 struct ccpmic_softc {
66 	struct device	sc_dev;
67 	struct acpi_softc *sc_acpi;
68 	struct aml_node *sc_node;
69 	i2c_tag_t	sc_tag;
70 	i2c_addr_t	sc_addr;
71 
72 	struct acpi_lpat *sc_lpat;
73 	size_t		sc_lpat_len;
74 
75 	struct acpi_gpio sc_gpio;
76 };
77 
78 int	ccpmic_match(struct device *, void *, void *);
79 void	ccpmic_attach(struct device *, struct device *, void *);
80 
81 const struct cfattach ccpmic_ca = {
82 	sizeof(struct ccpmic_softc), ccpmic_match, ccpmic_attach
83 };
84 
85 struct cfdriver ccpmic_cd = {
86 	NULL, "ccpmic", DV_DULL
87 };
88 
89 uint8_t	ccpmic_read_1(struct ccpmic_softc *, uint8_t, int);
90 void	ccpmic_write_1(struct ccpmic_softc *, uint8_t, uint8_t, int);
91 void	ccpmic_get_lpat(struct ccpmic_softc *);
92 int32_t	ccpmic_raw_to_temp(struct ccpmic_softc *, int32_t);
93 int	ccpmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *);
94 int	ccpmic_power_opreg_handler(void *, int, uint64_t, int, uint64_t *);
95 int	ccpmic_read_pin(void *, int);
96 void	ccpmic_write_pin(void *, int, int);
97 
98 int
ccpmic_match(struct device * parent,void * match,void * aux)99 ccpmic_match(struct device *parent, void *match, void *aux)
100 {
101 	struct i2c_attach_args *ia = aux;
102 
103 	return (strcmp(ia->ia_name, "INT33FD") == 0);
104 }
105 
106 void
ccpmic_attach(struct device * parent,struct device * self,void * aux)107 ccpmic_attach(struct device *parent, struct device *self, void *aux)
108 {
109 	struct ccpmic_softc *sc = (struct ccpmic_softc *)self;
110 	struct i2c_attach_args *ia = aux;
111 
112 	sc->sc_tag = ia->ia_tag;
113 	sc->sc_addr = ia->ia_addr;
114 	sc->sc_acpi = acpi_softc;
115 	sc->sc_node = ia->ia_cookie;
116 
117 	printf("\n");
118 
119 	ccpmic_get_lpat(sc);
120 	if (sc->sc_lpat == NULL)
121 		return;
122 
123 	sc->sc_gpio.cookie = sc;
124 	sc->sc_gpio.read_pin = ccpmic_read_pin;
125 	sc->sc_gpio.write_pin = ccpmic_write_pin;
126 	sc->sc_node->gpio = &sc->sc_gpio;
127 	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
128 
129 	/* Register OEM defined address space. */
130 	aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_THERMAL,
131 	    sc, ccpmic_thermal_opreg_handler);
132 	aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_POWER,
133 	    sc, ccpmic_power_opreg_handler);
134 }
135 
136 uint8_t
ccpmic_read_1(struct ccpmic_softc * sc,uint8_t reg,int flags)137 ccpmic_read_1(struct ccpmic_softc *sc, uint8_t reg, int flags)
138 {
139 	uint8_t val;
140 	int error;
141 
142 	iic_acquire_bus(sc->sc_tag, flags);
143 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
144 	    &reg, sizeof(reg), &val, sizeof(val), flags);
145 	iic_release_bus(sc->sc_tag, flags);
146 
147 	if (error) {
148 		printf("%s: can't read register 0x%02x\n",
149 		    sc->sc_dev.dv_xname, reg);
150 		val = 0xff;
151 	}
152 
153 	return val;
154 }
155 
156 void
ccpmic_write_1(struct ccpmic_softc * sc,uint8_t reg,uint8_t val,int flags)157 ccpmic_write_1(struct ccpmic_softc *sc, uint8_t reg, uint8_t val, int flags)
158 {
159 	int error;
160 
161 	iic_acquire_bus(sc->sc_tag, flags);
162 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
163 	    &reg, sizeof(reg), &val, sizeof(val), flags);
164 	iic_release_bus(sc->sc_tag, flags);
165 
166 	if (error) {
167 		printf("%s: can't write register 0x%02x\n",
168 		    sc->sc_dev.dv_xname, reg);
169 	}
170 }
171 
172 void
ccpmic_get_lpat(struct ccpmic_softc * sc)173 ccpmic_get_lpat(struct ccpmic_softc *sc)
174 {
175 	struct aml_value res;
176 	int i;
177 
178 	if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res))
179 		return;
180 	if (res.type != AML_OBJTYPE_PACKAGE)
181 		goto out;
182 	if (res.length < 4 || (res.length % 2) != 0)
183 		goto out;
184 
185 	sc->sc_lpat_len = res.length / 2;
186 	sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat),
187 	    M_DEVBUF, M_WAITOK);
188 
189 	for (i = 0; i < sc->sc_lpat_len; i++) {
190 		sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]);
191 		sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]);
192 	}
193 
194 out:
195 	aml_freevalue(&res);
196 }
197 
198 int32_t
ccpmic_raw_to_temp(struct ccpmic_softc * sc,int32_t raw)199 ccpmic_raw_to_temp(struct ccpmic_softc *sc, int32_t raw)
200 {
201 	struct acpi_lpat *lpat = sc->sc_lpat;
202 	int32_t raw0, delta_raw;
203 	int32_t temp0, delta_temp;
204 	int i;
205 
206 	for (i = 1; i < sc->sc_lpat_len; i++) {
207 		/* Coefficient can be positive or negative. */
208 		if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw)
209 			break;
210 		if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw)
211 			break;
212 	}
213 	if (i == sc->sc_lpat_len)
214 		return -1;
215 
216 	raw0 = lpat[i - 1].raw;
217 	temp0 = lpat[i - 1].temp;
218 	delta_raw = lpat[i].raw - raw0;
219 	delta_temp = lpat[i].temp - temp0;
220 
221 	return temp0 + (raw - raw0) * delta_temp / delta_raw;
222 }
223 
224 struct ccpmic_regmap {
225 	uint8_t address;
226 	uint8_t hi, lo;
227 };
228 
229 struct ccpmic_regmap ccpmic_thermal_regmap[] = {
230 	{ 0x18, CCPMIC_SYS2_THRM_RSLT_H, CCPMIC_SYS2_THRM_RSLT_L }, /* TMP2 */
231 };
232 
233 int
ccpmic_thermal_opreg_handler(void * cookie,int iodir,uint64_t address,int size,uint64_t * value)234 ccpmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address,
235     int size, uint64_t *value)
236 {
237 	struct ccpmic_softc *sc = cookie;
238 	int32_t temp;
239 	uint16_t raw;
240 	uint8_t lo, hi;
241 	int i;
242 
243 	/* Only allow 32-bit read access. */
244 	if (size != 4 || iodir != ACPI_IOREAD)
245 		return -1;
246 
247 	for (i = 0; i < nitems(ccpmic_thermal_regmap); i++) {
248 		if (address == ccpmic_thermal_regmap[i].address)
249 			break;
250 	}
251 	if (i == nitems(ccpmic_thermal_regmap)) {
252 		printf("%s: addr 0x%02llx\n", __func__, address);
253 		return -1;
254 	}
255 
256 	lo = ccpmic_thermal_regmap[i].lo;
257 	hi = ccpmic_thermal_regmap[i].hi;
258 	raw = ccpmic_read_1(sc, lo, 0);
259 	raw |= (ccpmic_read_1(sc, hi, 0) & 0x03) << 8;
260 
261 	temp = ccpmic_raw_to_temp(sc, raw);
262 	if (temp < 0)
263 		return -1;
264 
265 	*value = temp;
266 	return 0;
267 }
268 
269 struct ccpmic_regmap ccpmic_power_regmap[] = {
270 	{ 0x24, CCPMIC_V2P85SX },	/* X285 */
271 	{ 0x48, CCPMIC_V1P8SX },	/* V18X */
272 	{ 0x50, CCPMIC_V1P2SX },	/* V12X */
273 };
274 
275 int
ccpmic_power_opreg_handler(void * cookie,int iodir,uint64_t address,int size,uint64_t * value)276 ccpmic_power_opreg_handler(void *cookie, int iodir, uint64_t address,
277     int size, uint64_t *value)
278 {
279 	struct ccpmic_softc *sc = cookie;
280 	uint8_t reg, val;
281 	int i;
282 
283 	/* Only allow 32-bit access. */
284 	if (size != 4)
285 		return -1;
286 
287 	for (i = 0; i < nitems(ccpmic_power_regmap); i++) {
288 		if (address == ccpmic_power_regmap[i].address)
289 			break;
290 	}
291 	if (i == nitems(ccpmic_power_regmap)) {
292 		printf("%s: addr 0x%02llx\n", __func__, address);
293 		return -1;
294 	}
295 
296 	reg = ccpmic_power_regmap[i].hi;
297 	val = ccpmic_read_1(sc, reg, 0);
298 	if (iodir == ACPI_IOREAD) {
299 		if ((val & CCPMIC_PWR_SEL) && (val & CCPMIC_PWR_ON))
300 			*value = 1;
301 		else
302 			*value = 0;
303 	} else {
304 		if (*value)
305 			val |= CCPMIC_PWR_ON;
306 		else
307 			val &= ~CCPMIC_PWR_ON;
308 		ccpmic_write_1(sc, reg, val | CCPMIC_PWR_SEL, 0);
309 	}
310 
311 	return 0;
312 }
313 
314 /*
315  * We have 16 real GPIOs and a bunch of virtual ones.  The virtual
316  * ones are mostly there to deal with a limitation of Microsoft
317  * Windows.  We only implement the "panel" control GPIO, which
318  * actually maps onto a real GPIO.
319  */
320 
321 int
ccpmic_read_pin(void * cookie,int pin)322 ccpmic_read_pin(void *cookie, int pin)
323 {
324 	struct ccpmic_softc *sc = cookie;
325 	uint8_t reg;
326 
327 	if (pin >= CCPMIC_NPINS)
328 		return 0;
329 
330 	reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
331 	ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_INPUT, 0);
332 	reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLI : CCPMIC_GPIO1P0CTLI) + pin % 8;
333 	return ccpmic_read_1(sc, reg, 0) & CCPMIC_GPIOCTLI_VALUE;
334 }
335 
336 void
ccpmic_write_pin(void * cookie,int pin,int value)337 ccpmic_write_pin(void *cookie, int pin, int value)
338 {
339 	struct ccpmic_softc *sc = cookie;
340 	uint8_t reg;
341 
342 	if (pin == 0x5e) {
343 		reg = CCPMIC_GPIOPANELCTL;
344 		ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
345 		return;
346 	}
347 
348 	if (pin >= CCPMIC_NPINS)
349 		return;
350 
351 	reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
352 	ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
353 }
354