xref: /openbsd-src/sys/dev/acpi/dwiic_acpi.c (revision 593d792ceb3cca540ce75d4e9938d62d34459e55)
1*593d792cSjcs /* $OpenBSD: dwiic_acpi.c,v 1.22 2023/07/08 02:43:02 jcs Exp $ */
2c6df0db7Sjcs /*
3c6df0db7Sjcs  * Synopsys DesignWare I2C controller
4c6df0db7Sjcs  *
5c6df0db7Sjcs  * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
6c6df0db7Sjcs  *
7c6df0db7Sjcs  * Permission to use, copy, modify, and/or distribute this software for any
8c6df0db7Sjcs  * purpose with or without fee is hereby granted, provided that the above
9c6df0db7Sjcs  * copyright notice and this permission notice appear in all copies.
10c6df0db7Sjcs  *
11c6df0db7Sjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12c6df0db7Sjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13c6df0db7Sjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14c6df0db7Sjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15c6df0db7Sjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16c6df0db7Sjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17c6df0db7Sjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18c6df0db7Sjcs  */
19c6df0db7Sjcs 
2075318f5bSdlg #include "iosf.h"
2175318f5bSdlg 
22c6df0db7Sjcs #include <sys/param.h>
23c6df0db7Sjcs #include <sys/systm.h>
24c6df0db7Sjcs #include <sys/kernel.h>
25c6df0db7Sjcs 
26c6df0db7Sjcs #include <dev/acpi/acpireg.h>
27c6df0db7Sjcs #include <dev/acpi/acpivar.h>
28c6df0db7Sjcs #include <dev/acpi/acpidev.h>
29c6df0db7Sjcs #include <dev/acpi/amltypes.h>
30c6df0db7Sjcs #include <dev/acpi/dsdt.h>
31c6df0db7Sjcs 
32c6df0db7Sjcs #include <dev/ic/dwiicvar.h>
3375318f5bSdlg #include <dev/ic/iosfvar.h>
34c6df0db7Sjcs 
35c6df0db7Sjcs struct dwiic_crs {
36c6df0db7Sjcs 	int irq_int;
37c6df0db7Sjcs 	uint8_t irq_flags;
38c6df0db7Sjcs 	uint16_t i2c_addr;
39c6df0db7Sjcs 	struct aml_node *devnode;
40c6df0db7Sjcs 	struct aml_node *gpio_int_node;
41c6df0db7Sjcs 	uint16_t gpio_int_pin;
42c6df0db7Sjcs 	uint16_t gpio_int_flags;
43c6df0db7Sjcs };
44c6df0db7Sjcs 
45c6df0db7Sjcs int		dwiic_acpi_match(struct device *, void *, void *);
46c6df0db7Sjcs void		dwiic_acpi_attach(struct device *, struct device *, void *);
47c6df0db7Sjcs 
48c6df0db7Sjcs int		dwiic_acpi_parse_crs(int, union acpi_resource *, void *);
49c6df0db7Sjcs int		dwiic_acpi_found_ihidev(struct dwiic_softc *,
50c6df0db7Sjcs 		    struct aml_node *, char *, struct dwiic_crs);
51c6df0db7Sjcs int		dwiic_acpi_found_iatp(struct dwiic_softc *, struct aml_node *,
52c6df0db7Sjcs 		    char *, struct dwiic_crs);
53*593d792cSjcs int		dwiic_acpi_found_ietp(struct dwiic_softc *, struct aml_node *,
54*593d792cSjcs 		    char *, struct dwiic_crs);
55c6df0db7Sjcs void		dwiic_acpi_get_params(struct dwiic_softc *, char *, uint16_t *,
56c6df0db7Sjcs 		    uint16_t *, uint32_t *);
57c6df0db7Sjcs void		dwiic_acpi_power(struct dwiic_softc *, int);
58c6df0db7Sjcs void		dwiic_acpi_bus_scan(struct device *,
59c6df0db7Sjcs 		    struct i2cbus_attach_args *, void *);
60c6df0db7Sjcs 
6175318f5bSdlg #if NIOSF > 0
6275318f5bSdlg int		dwiic_acpi_acquire_bus(void *, int);
6375318f5bSdlg void		dwiic_acpi_release_bus(void *, int);
6475318f5bSdlg #endif
6575318f5bSdlg 
66471aeecfSnaddy const struct cfattach dwiic_acpi_ca = {
67c6df0db7Sjcs 	sizeof(struct dwiic_softc),
68c6df0db7Sjcs 	dwiic_acpi_match,
69c6df0db7Sjcs 	dwiic_acpi_attach,
70c6df0db7Sjcs 	NULL,
71c6df0db7Sjcs 	dwiic_activate
72c6df0db7Sjcs };
73c6df0db7Sjcs 
74c6df0db7Sjcs const char *dwiic_hids[] = {
756d00698eSjsg 	"AMDI0010",
76fa04405aSkettenis 	"APMC0D0F",
77c6df0db7Sjcs 	"INT33C2",
78c6df0db7Sjcs 	"INT33C3",
79c6df0db7Sjcs 	"INT3432",
80c6df0db7Sjcs 	"INT3433",
81c6df0db7Sjcs 	"80860F41",
82c6df0db7Sjcs 	"808622C1",
83c6df0db7Sjcs 	NULL
84c6df0db7Sjcs };
85c6df0db7Sjcs 
86c6df0db7Sjcs const char *ihidev_hids[] = {
87c6df0db7Sjcs 	"PNP0C50",
88c6df0db7Sjcs 	"ACPI0C50",
89c6df0db7Sjcs 	NULL
90c6df0db7Sjcs };
91c6df0db7Sjcs 
92*593d792cSjcs const char *ietp_hids[] = {
93*593d792cSjcs 	"ELAN0000",
94*593d792cSjcs 	"ELAN0100",
95*593d792cSjcs 	"ELAN0600",
96*593d792cSjcs 	"ELAN0601",
97*593d792cSjcs 	"ELAN0602",
98*593d792cSjcs 	"ELAN0603",
99*593d792cSjcs 	"ELAN0604",
100*593d792cSjcs 	"ELAN0605",
101*593d792cSjcs 	"ELAN0606",
102*593d792cSjcs 	"ELAN0607",
103*593d792cSjcs 	"ELAN0608",
104*593d792cSjcs 	"ELAN0609",
105*593d792cSjcs 	"ELAN060B",
106*593d792cSjcs 	"ELAN060C",
107*593d792cSjcs 	"ELAN060F",
108*593d792cSjcs 	"ELAN0610",
109*593d792cSjcs 	"ELAN0611",
110*593d792cSjcs 	"ELAN0612",
111*593d792cSjcs 	"ELAN0615",
112*593d792cSjcs 	"ELAN0616",
113*593d792cSjcs 	"ELAN0617",
114*593d792cSjcs 	"ELAN0618",
115*593d792cSjcs 	"ELAN0619",
116*593d792cSjcs 	"ELAN061A",
117*593d792cSjcs 	"ELAN061B",
118*593d792cSjcs 	"ELAN061C",
119*593d792cSjcs 	"ELAN061D",
120*593d792cSjcs 	"ELAN061E",
121*593d792cSjcs 	"ELAN061F",
122*593d792cSjcs 	"ELAN0620",
123*593d792cSjcs 	"ELAN0621",
124*593d792cSjcs 	"ELAN0622",
125*593d792cSjcs 	"ELAN0623",
126*593d792cSjcs 	"ELAN0624",
127*593d792cSjcs 	"ELAN0625",
128*593d792cSjcs 	"ELAN0626",
129*593d792cSjcs 	"ELAN0627",
130*593d792cSjcs 	"ELAN0628",
131*593d792cSjcs 	"ELAN0629",
132*593d792cSjcs 	"ELAN062A",
133*593d792cSjcs 	"ELAN062B",
134*593d792cSjcs 	"ELAN062C",
135*593d792cSjcs 	"ELAN062D",
136*593d792cSjcs 	"ELAN062E",	/* Lenovo V340 Whiskey Lake U */
137*593d792cSjcs 	"ELAN062F",	/* Lenovo V340 Comet Lake U */
138*593d792cSjcs 	"ELAN0631",
139*593d792cSjcs 	"ELAN0632",
140*593d792cSjcs 	"ELAN0633",	/* Lenovo S145 */
141*593d792cSjcs 	"ELAN0634",	/* Lenovo V340 Ice lake */
142*593d792cSjcs 	"ELAN0635",	/* Lenovo V1415-IIL */
143*593d792cSjcs 	"ELAN0636",	/* Lenovo V1415-Dali */
144*593d792cSjcs 	"ELAN0637",	/* Lenovo V1415-IGLR */
145*593d792cSjcs 	"ELAN1000",
146*593d792cSjcs 	NULL
147*593d792cSjcs };
148*593d792cSjcs 
149c6df0db7Sjcs const char *iatp_hids[] = {
150c6df0db7Sjcs 	"ATML0000",
151c6df0db7Sjcs 	"ATML0001",
152c6df0db7Sjcs 	NULL
153c6df0db7Sjcs };
154c6df0db7Sjcs 
155c6df0db7Sjcs int
dwiic_acpi_match(struct device * parent,void * match,void * aux)156c6df0db7Sjcs dwiic_acpi_match(struct device *parent, void *match, void *aux)
157c6df0db7Sjcs {
158c6df0db7Sjcs 	struct acpi_attach_args *aaa = aux;
159c6df0db7Sjcs 	struct cfdata *cf = match;
160c6df0db7Sjcs 
16157ec0946Skettenis 	if (aaa->aaa_naddr < 1)
16257ec0946Skettenis 		return 0;
163c6df0db7Sjcs 	return acpi_matchhids(aaa, dwiic_hids, cf->cf_driver->cd_name);
164c6df0db7Sjcs }
165c6df0db7Sjcs 
166c6df0db7Sjcs void
dwiic_acpi_attach(struct device * parent,struct device * self,void * aux)167c6df0db7Sjcs dwiic_acpi_attach(struct device *parent, struct device *self, void *aux)
168c6df0db7Sjcs {
169c6df0db7Sjcs 	struct dwiic_softc *sc = (struct dwiic_softc *)self;
170f09f3dbfSkettenis 	struct acpi_attach_args *aaa = aux;
171c6df0db7Sjcs 	struct aml_value res;
172c6df0db7Sjcs 	struct dwiic_crs crs;
17375318f5bSdlg 	uint64_t sem;
174c6df0db7Sjcs 
175c6df0db7Sjcs 	sc->sc_acpi = (struct acpi_softc *)parent;
176f09f3dbfSkettenis 	sc->sc_devnode = aaa->aaa_node;
177f09f3dbfSkettenis 	memcpy(&sc->sc_hid, aaa->aaa_dev, sizeof(sc->sc_hid));
178c6df0db7Sjcs 
179158ca4d3Skettenis 	printf(" %s", sc->sc_devnode->name);
180c6df0db7Sjcs 
181c6df0db7Sjcs 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) {
182c6df0db7Sjcs 		printf(", no _CRS method\n");
183c6df0db7Sjcs 		return;
184c6df0db7Sjcs 	}
185c6df0db7Sjcs 	if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
186c6df0db7Sjcs 		printf(", invalid _CRS object (type %d len %d)\n",
187c6df0db7Sjcs 		    res.type, res.length);
188c6df0db7Sjcs 		aml_freevalue(&res);
189c6df0db7Sjcs 		return;
190c6df0db7Sjcs 	}
191c6df0db7Sjcs 	memset(&crs, 0, sizeof(crs));
192c6df0db7Sjcs 	crs.devnode = sc->sc_devnode;
193c6df0db7Sjcs 	aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs);
194c6df0db7Sjcs 	aml_freevalue(&res);
195c6df0db7Sjcs 
196f09f3dbfSkettenis 	printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
197c6df0db7Sjcs 
198f09f3dbfSkettenis 	sc->sc_iot = aaa->aaa_bst[0];
199f09f3dbfSkettenis 	if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0],
200f09f3dbfSkettenis 	    0, &sc->sc_ioh)) {
201f09f3dbfSkettenis 		printf(": can't map registers\n");
202c6df0db7Sjcs 		return;
203c6df0db7Sjcs 	}
204c6df0db7Sjcs 
205c6df0db7Sjcs 	/* power up the controller */
206c6df0db7Sjcs 	dwiic_acpi_power(sc, 1);
207c6df0db7Sjcs 
208c6df0db7Sjcs 	/* fetch timing parameters */
209c6df0db7Sjcs 	dwiic_acpi_get_params(sc, "SSCN", &sc->ss_hcnt, &sc->ss_lcnt, NULL);
210c6df0db7Sjcs 	dwiic_acpi_get_params(sc, "FMCN", &sc->fs_hcnt, &sc->fs_lcnt,
211c6df0db7Sjcs 	    &sc->sda_hold_time);
212c6df0db7Sjcs 
213c6df0db7Sjcs 	if (dwiic_init(sc)) {
214c6df0db7Sjcs 		printf(", failed initializing\n");
215f09f3dbfSkettenis 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, aaa->aaa_size[0]);
216c6df0db7Sjcs 		return;
217c6df0db7Sjcs 	}
218c6df0db7Sjcs 
219c6df0db7Sjcs 	/* leave the controller disabled */
220c6df0db7Sjcs 	dwiic_write(sc, DW_IC_INTR_MASK, 0);
221c6df0db7Sjcs 	dwiic_enable(sc, 0);
222c6df0db7Sjcs 	dwiic_read(sc, DW_IC_CLR_INTR);
223c6df0db7Sjcs 
224c6df0db7Sjcs 	/* try to register interrupt with apic, but not fatal without it */
225f09f3dbfSkettenis 	if (aaa->aaa_nirq > 0) {
226f09f3dbfSkettenis 		printf(" irq %d", aaa->aaa_irq[0]);
227c6df0db7Sjcs 
228f09f3dbfSkettenis 		sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
229c6df0db7Sjcs 		    IPL_BIO, dwiic_intr, sc, sc->sc_dev.dv_xname);
230c6df0db7Sjcs 		if (sc->sc_ih == NULL)
231f09f3dbfSkettenis 			printf(": can't establish interrupt");
232c6df0db7Sjcs 	}
233c6df0db7Sjcs 
23475318f5bSdlg 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
23575318f5bSdlg 	    "_SEM", 0, NULL, &sem))
23675318f5bSdlg 		sem = 0;
23775318f5bSdlg 
23875318f5bSdlg 	if (sem)
23975318f5bSdlg 		printf(", sem");
24075318f5bSdlg 
241c6df0db7Sjcs 	printf("\n");
242c6df0db7Sjcs 
243c6df0db7Sjcs 	rw_init(&sc->sc_i2c_lock, "iiclk");
244c6df0db7Sjcs 
245c6df0db7Sjcs 	/* setup and attach iic bus */
246c6df0db7Sjcs 	sc->sc_i2c_tag.ic_cookie = sc;
247c6df0db7Sjcs 	sc->sc_i2c_tag.ic_acquire_bus = dwiic_i2c_acquire_bus;
248c6df0db7Sjcs 	sc->sc_i2c_tag.ic_release_bus = dwiic_i2c_release_bus;
249c6df0db7Sjcs 	sc->sc_i2c_tag.ic_exec = dwiic_i2c_exec;
250c6df0db7Sjcs 	sc->sc_i2c_tag.ic_intr_establish = dwiic_i2c_intr_establish;
251d3689130Skettenis 	sc->sc_i2c_tag.ic_intr_disestablish = dwiic_i2c_intr_disestablish;
252c6df0db7Sjcs 	sc->sc_i2c_tag.ic_intr_string = dwiic_i2c_intr_string;
253c6df0db7Sjcs 
25475318f5bSdlg #if NIOSF > 0
25575318f5bSdlg 	if (sem) {
25675318f5bSdlg 		sc->sc_i2c_tag.ic_acquire_bus = dwiic_acpi_acquire_bus;
25775318f5bSdlg 		sc->sc_i2c_tag.ic_release_bus = dwiic_acpi_release_bus;
25875318f5bSdlg 	}
25975318f5bSdlg #endif
26075318f5bSdlg 
261c6df0db7Sjcs 	bzero(&sc->sc_iba, sizeof(sc->sc_iba));
262c6df0db7Sjcs 	sc->sc_iba.iba_name = "iic";
263c6df0db7Sjcs 	sc->sc_iba.iba_tag = &sc->sc_i2c_tag;
264c6df0db7Sjcs 	sc->sc_iba.iba_bus_scan = dwiic_acpi_bus_scan;
265c6df0db7Sjcs 	sc->sc_iba.iba_bus_scan_arg = sc;
266c6df0db7Sjcs 
267c6df0db7Sjcs 	config_found((struct device *)sc, &sc->sc_iba, iicbus_print);
268c6df0db7Sjcs 
26993d5c15fSkettenis #ifndef SMALL_KERNEL
27011d486e4Skettenis 	sc->sc_devnode->i2c = &sc->sc_i2c_tag;
27111d486e4Skettenis 	acpi_register_gsb(sc->sc_acpi, sc->sc_devnode);
27293d5c15fSkettenis #endif
273c6df0db7Sjcs }
274c6df0db7Sjcs 
275c6df0db7Sjcs int
dwiic_acpi_parse_crs(int crsidx,union acpi_resource * crs,void * arg)276c6df0db7Sjcs dwiic_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg)
277c6df0db7Sjcs {
278c6df0db7Sjcs 	struct dwiic_crs *sc_crs = arg;
279c6df0db7Sjcs 	struct aml_node *node;
280c6df0db7Sjcs 	uint16_t pin;
281f09f3dbfSkettenis 	uint8_t flags;
282c6df0db7Sjcs 
283c6df0db7Sjcs 	switch (AML_CRSTYPE(crs)) {
284c6df0db7Sjcs 	case SR_IRQ:
285c6df0db7Sjcs 		sc_crs->irq_int = ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
286f09f3dbfSkettenis 		/* Default is exclusive, active-high, edge triggered. */
287f7acadf0Skettenis 		if (AML_CRSLEN(crs) < 4)
288f09f3dbfSkettenis 			flags = SR_IRQ_MODE;
289f09f3dbfSkettenis 		else
290f09f3dbfSkettenis 			flags = crs->sr_irq.irq_flags;
291f09f3dbfSkettenis 		/* Map flags to those of the extended interrupt descriptor. */
292f09f3dbfSkettenis 		if (flags & SR_IRQ_SHR)
293f09f3dbfSkettenis 			sc_crs->irq_flags |= LR_EXTIRQ_SHR;
294f09f3dbfSkettenis 		if (flags & SR_IRQ_POLARITY)
295f09f3dbfSkettenis 			sc_crs->irq_flags |= LR_EXTIRQ_POLARITY;
296f09f3dbfSkettenis 		if (flags & SR_IRQ_MODE)
297f09f3dbfSkettenis 			sc_crs->irq_flags |= LR_EXTIRQ_MODE;
298c6df0db7Sjcs 		break;
299c6df0db7Sjcs 
300c6df0db7Sjcs 	case LR_EXTIRQ:
301c6df0db7Sjcs 		sc_crs->irq_int = letoh32(crs->lr_extirq.irq[0]);
302c6df0db7Sjcs 		sc_crs->irq_flags = crs->lr_extirq.flags;
303c6df0db7Sjcs 		break;
304c6df0db7Sjcs 
305c6df0db7Sjcs 	case LR_GPIO:
306c6df0db7Sjcs 		node = aml_searchname(sc_crs->devnode,
307c6df0db7Sjcs 		    (char *)&crs->pad[crs->lr_gpio.res_off]);
308c6df0db7Sjcs 		pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off];
309c6df0db7Sjcs 		if (crs->lr_gpio.type == LR_GPIO_INT) {
310c6df0db7Sjcs 			sc_crs->gpio_int_node = node;
311c6df0db7Sjcs 			sc_crs->gpio_int_pin = pin;
312c6df0db7Sjcs 			sc_crs->gpio_int_flags = crs->lr_gpio.tflags;
313c6df0db7Sjcs 		}
314c6df0db7Sjcs 		break;
315c6df0db7Sjcs 
316c6df0db7Sjcs 	case LR_SERBUS:
317c6df0db7Sjcs 		if (crs->lr_serbus.type == LR_SERBUS_I2C)
318c6df0db7Sjcs 			sc_crs->i2c_addr = letoh16(crs->lr_i2cbus._adr);
319c6df0db7Sjcs 		break;
320c6df0db7Sjcs 
321f09f3dbfSkettenis 	case LR_MEM32:
322f09f3dbfSkettenis 	case LR_MEM32FIXED:
323f09f3dbfSkettenis 		break;
324f09f3dbfSkettenis 
325c6df0db7Sjcs 	default:
326c6df0db7Sjcs 		DPRINTF(("%s: unknown resource type %d\n", __func__,
327c6df0db7Sjcs 		    AML_CRSTYPE(crs)));
328c6df0db7Sjcs 	}
329c6df0db7Sjcs 
330c6df0db7Sjcs 	return 0;
331c6df0db7Sjcs }
332c6df0db7Sjcs 
333c6df0db7Sjcs void
dwiic_acpi_get_params(struct dwiic_softc * sc,char * method,uint16_t * hcnt,uint16_t * lcnt,uint32_t * sda_hold_time)334c6df0db7Sjcs dwiic_acpi_get_params(struct dwiic_softc *sc, char *method, uint16_t *hcnt,
335c6df0db7Sjcs     uint16_t *lcnt, uint32_t *sda_hold_time)
336c6df0db7Sjcs {
337c6df0db7Sjcs 	struct aml_value res;
338c6df0db7Sjcs 
339c6df0db7Sjcs 	if (!aml_searchname(sc->sc_devnode, method))
340c6df0db7Sjcs 		return;
341c6df0db7Sjcs 
342c6df0db7Sjcs 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, method, 0, NULL, &res)) {
343c6df0db7Sjcs 		printf(": eval of %s at %s failed", method,
344c6df0db7Sjcs 		    aml_nodename(sc->sc_devnode));
345c6df0db7Sjcs 		return;
346c6df0db7Sjcs 	}
347c6df0db7Sjcs 
348c6df0db7Sjcs 	if (res.type != AML_OBJTYPE_PACKAGE) {
349c6df0db7Sjcs 		printf(": %s is not a package (%d)", method, res.type);
350c6df0db7Sjcs 		aml_freevalue(&res);
351c6df0db7Sjcs 		return;
352c6df0db7Sjcs 	}
353c6df0db7Sjcs 
354c6df0db7Sjcs 	if (res.length <= 2) {
355c6df0db7Sjcs 		printf(": %s returned package of len %d", method, res.length);
356c6df0db7Sjcs 		aml_freevalue(&res);
357c6df0db7Sjcs 		return;
358c6df0db7Sjcs 	}
359c6df0db7Sjcs 
360c6df0db7Sjcs 	*hcnt = aml_val2int(res.v_package[0]);
361c6df0db7Sjcs 	*lcnt = aml_val2int(res.v_package[1]);
362c6df0db7Sjcs 	if (sda_hold_time)
363c6df0db7Sjcs 		*sda_hold_time = aml_val2int(res.v_package[2]);
364c6df0db7Sjcs 	aml_freevalue(&res);
365c6df0db7Sjcs }
366c6df0db7Sjcs 
367c6df0db7Sjcs void
dwiic_acpi_bus_scan(struct device * iic,struct i2cbus_attach_args * iba,void * aux)368c6df0db7Sjcs dwiic_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba,
369c6df0db7Sjcs     void *aux)
370c6df0db7Sjcs {
371c6df0db7Sjcs 	struct dwiic_softc *sc = (struct dwiic_softc *)aux;
372c6df0db7Sjcs 
373c6df0db7Sjcs 	sc->sc_iic = iic;
374c6df0db7Sjcs 	aml_find_node(sc->sc_devnode, "_HID", dwiic_acpi_found_hid, sc);
375c6df0db7Sjcs }
376c6df0db7Sjcs 
377c6df0db7Sjcs void *
dwiic_i2c_intr_establish(void * cookie,void * ih,int level,int (* func)(void *),void * arg,const char * name)378c6df0db7Sjcs dwiic_i2c_intr_establish(void *cookie, void *ih, int level,
379c6df0db7Sjcs     int (*func)(void *), void *arg, const char *name)
380c6df0db7Sjcs {
381c6df0db7Sjcs 	struct dwiic_crs *crs = ih;
382c6df0db7Sjcs 
38355893340Sjcs 	if (crs->gpio_int_node) {
38455893340Sjcs 		if (!crs->gpio_int_node->gpio)
38555893340Sjcs 			/* found ACPI device but no driver for it */
38655893340Sjcs 			return NULL;
38755893340Sjcs 
388c6df0db7Sjcs 		struct acpi_gpio *gpio = crs->gpio_int_node->gpio;
389c6df0db7Sjcs 		gpio->intr_establish(gpio->cookie, crs->gpio_int_pin,
390c6df0db7Sjcs 				     crs->gpio_int_flags, func, arg);
391c6df0db7Sjcs 		return ih;
392c6df0db7Sjcs 	}
393c6df0db7Sjcs 
394c6df0db7Sjcs 	return acpi_intr_establish(crs->irq_int, crs->irq_flags,
395c6df0db7Sjcs 	    level, func, arg, name);
396c6df0db7Sjcs }
397c6df0db7Sjcs 
398d3689130Skettenis void
dwiic_i2c_intr_disestablish(void * cookie,void * ih)399d3689130Skettenis dwiic_i2c_intr_disestablish(void *cookie, void *ih)
400d3689130Skettenis {
401d3689130Skettenis 	/* XXX GPIO interrupts */
402d3689130Skettenis 	acpi_intr_disestablish(ih);
403d3689130Skettenis }
404d3689130Skettenis 
405c6df0db7Sjcs const char *
dwiic_i2c_intr_string(void * cookie,void * ih)406c6df0db7Sjcs dwiic_i2c_intr_string(void *cookie, void *ih)
407c6df0db7Sjcs {
408c6df0db7Sjcs 	struct dwiic_crs *crs = ih;
409c6df0db7Sjcs 	static char irqstr[64];
410c6df0db7Sjcs 
41155893340Sjcs 	if (crs->gpio_int_node) {
41255893340Sjcs 		if (crs->gpio_int_node->gpio)
41355893340Sjcs 			snprintf(irqstr, sizeof(irqstr), "gpio %d",
41455893340Sjcs 			    crs->gpio_int_pin);
41555893340Sjcs 	} else
416c6df0db7Sjcs 		snprintf(irqstr, sizeof(irqstr), "irq %d", crs->irq_int);
417c6df0db7Sjcs 
418c6df0db7Sjcs 	return irqstr;
419c6df0db7Sjcs }
420c6df0db7Sjcs 
421c6df0db7Sjcs int
dwiic_matchhids(const char * hid,const char * hids[])422c6df0db7Sjcs dwiic_matchhids(const char *hid, const char *hids[])
423c6df0db7Sjcs {
424c6df0db7Sjcs 	int i;
425c6df0db7Sjcs 
426c6df0db7Sjcs 	for (i = 0; hids[i]; i++)
427c6df0db7Sjcs 		if (!strcmp(hid, hids[i]))
428c6df0db7Sjcs 			return (1);
429c6df0db7Sjcs 
430c6df0db7Sjcs 	return (0);
431c6df0db7Sjcs }
432c6df0db7Sjcs 
433c6df0db7Sjcs int
dwiic_acpi_found_hid(struct aml_node * node,void * arg)434c6df0db7Sjcs dwiic_acpi_found_hid(struct aml_node *node, void *arg)
435c6df0db7Sjcs {
436c6df0db7Sjcs 	struct dwiic_softc *sc = (struct dwiic_softc *)arg;
437c6df0db7Sjcs 	struct dwiic_crs crs;
438c6df0db7Sjcs 	struct aml_value res;
439c6df0db7Sjcs 	int64_t sta;
440c6df0db7Sjcs 	char cdev[16], dev[16];
441d31aee7eSkettenis 	struct i2c_attach_args ia;
442d31aee7eSkettenis 
443d31aee7eSkettenis 	/* Skip our own _HID. */
444d31aee7eSkettenis 	if (node->parent == sc->sc_devnode)
445d31aee7eSkettenis 		return 0;
446c6df0db7Sjcs 
447c6df0db7Sjcs 	if (acpi_parsehid(node, arg, cdev, dev, 16) != 0)
448c6df0db7Sjcs 		return 0;
449c6df0db7Sjcs 
450c6df0db7Sjcs 	if (aml_evalinteger(acpi_softc, node->parent, "_STA", 0, NULL, &sta))
451c6df0db7Sjcs 		sta = STA_PRESENT | STA_ENABLED | STA_DEV_OK | 0x1000;
452c6df0db7Sjcs 
453c6df0db7Sjcs 	if ((sta & STA_PRESENT) == 0)
454c6df0db7Sjcs 		return 0;
455c6df0db7Sjcs 
456c6df0db7Sjcs 	DPRINTF(("%s: found HID %s at %s\n", sc->sc_dev.dv_xname, dev,
457c6df0db7Sjcs 	    aml_nodename(node)));
458c6df0db7Sjcs 
459025f8b40Skettenis 	if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res))
460025f8b40Skettenis 		return 0;
461025f8b40Skettenis 
462c6df0db7Sjcs 	if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
463c6df0db7Sjcs 		printf("%s: invalid _CRS object (type %d len %d)\n",
464c6df0db7Sjcs 		    sc->sc_dev.dv_xname, res.type, res.length);
465c6df0db7Sjcs 		aml_freevalue(&res);
466c6df0db7Sjcs 		return (0);
467c6df0db7Sjcs 	}
468c6df0db7Sjcs 	memset(&crs, 0, sizeof(crs));
469c6df0db7Sjcs 	crs.devnode = sc->sc_devnode;
470c6df0db7Sjcs 	aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs);
471c6df0db7Sjcs 	aml_freevalue(&res);
472c6df0db7Sjcs 
473c6df0db7Sjcs 	acpi_attach_deps(acpi_softc, node->parent);
474c6df0db7Sjcs 
475c6df0db7Sjcs 	if (dwiic_matchhids(cdev, ihidev_hids))
476c6df0db7Sjcs 		return dwiic_acpi_found_ihidev(sc, node, dev, crs);
477c6df0db7Sjcs 	else if (dwiic_matchhids(dev, iatp_hids))
478c6df0db7Sjcs 		return dwiic_acpi_found_iatp(sc, node, dev, crs);
479*593d792cSjcs 	else if (dwiic_matchhids(dev, ietp_hids) || dwiic_matchhids(cdev, ietp_hids))
480*593d792cSjcs 		return dwiic_acpi_found_ietp(sc, node, dev, crs);
481c6df0db7Sjcs 
482d31aee7eSkettenis 	memset(&ia, 0, sizeof(ia));
483d31aee7eSkettenis 	ia.ia_tag = sc->sc_iba.iba_tag;
484d31aee7eSkettenis 	ia.ia_name = dev;
485d31aee7eSkettenis 	ia.ia_addr = crs.i2c_addr;
4869d2f97f9Skettenis 	ia.ia_cookie = node->parent;
487d31aee7eSkettenis 
4888e41a272Skettenis 	if (crs.irq_int != 0 || crs.gpio_int_node != NULL)
4898e41a272Skettenis 		ia.ia_intr = &crs;
4908e41a272Skettenis 
491d31aee7eSkettenis 	config_found(sc->sc_iic, &ia, dwiic_i2c_print);
492d31aee7eSkettenis 	node->parent->attached = 1;
493d31aee7eSkettenis 
494c6df0db7Sjcs 	return 0;
495c6df0db7Sjcs }
496c6df0db7Sjcs 
497c6df0db7Sjcs int
dwiic_acpi_found_ihidev(struct dwiic_softc * sc,struct aml_node * node,char * dev,struct dwiic_crs crs)498c6df0db7Sjcs dwiic_acpi_found_ihidev(struct dwiic_softc *sc, struct aml_node *node,
499c6df0db7Sjcs     char *dev, struct dwiic_crs crs)
500c6df0db7Sjcs {
501c6df0db7Sjcs 	struct i2c_attach_args ia;
502c6df0db7Sjcs 	struct aml_value cmd[4], res;
503c6df0db7Sjcs 
504c6df0db7Sjcs 	/* 3cdff6f7-4267-4555-ad05-b30a3d8938de */
505c6df0db7Sjcs 	static uint8_t i2c_hid_guid[] = {
506c6df0db7Sjcs 		0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
507c6df0db7Sjcs 		0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
508c6df0db7Sjcs 	};
509c6df0db7Sjcs 
510c6df0db7Sjcs 	if (!aml_searchname(node->parent, "_DSM")) {
511c6df0db7Sjcs 		printf("%s: couldn't find _DSM at %s\n", sc->sc_dev.dv_xname,
512c6df0db7Sjcs 		    aml_nodename(node->parent));
513c6df0db7Sjcs 		return 0;
514c6df0db7Sjcs 	}
515c6df0db7Sjcs 
516c6df0db7Sjcs 	bzero(&cmd, sizeof(cmd));
517c6df0db7Sjcs 	cmd[0].type = AML_OBJTYPE_BUFFER;
518c6df0db7Sjcs 	cmd[0].v_buffer = (uint8_t *)&i2c_hid_guid;
519c6df0db7Sjcs 	cmd[0].length = sizeof(i2c_hid_guid);
520c6df0db7Sjcs 	/* rev */
521c6df0db7Sjcs 	cmd[1].type = AML_OBJTYPE_INTEGER;
522c6df0db7Sjcs 	cmd[1].v_integer = 1;
523c6df0db7Sjcs 	cmd[1].length = 1;
524c6df0db7Sjcs 	/* func */
525c6df0db7Sjcs 	cmd[2].type = AML_OBJTYPE_INTEGER;
526c6df0db7Sjcs 	cmd[2].v_integer = 1; /* HID */
527c6df0db7Sjcs 	cmd[2].length = 1;
528c6df0db7Sjcs 	/* not used */
529c6df0db7Sjcs 	cmd[3].type = AML_OBJTYPE_PACKAGE;
530c6df0db7Sjcs 	cmd[3].length = 0;
531c6df0db7Sjcs 
532c6df0db7Sjcs 	if (aml_evalname(acpi_softc, node->parent, "_DSM", 4, cmd, &res)) {
533c6df0db7Sjcs 		printf("%s: eval of _DSM at %s failed\n",
534c6df0db7Sjcs 		    sc->sc_dev.dv_xname, aml_nodename(node->parent));
535c6df0db7Sjcs 		return 0;
536c6df0db7Sjcs 	}
537c6df0db7Sjcs 
538c6df0db7Sjcs 	if (res.type != AML_OBJTYPE_INTEGER) {
539c6df0db7Sjcs 		printf("%s: bad _DSM result at %s: %d\n",
540c6df0db7Sjcs 		    sc->sc_dev.dv_xname, aml_nodename(node->parent), res.type);
541c6df0db7Sjcs 		aml_freevalue(&res);
542c6df0db7Sjcs 		return 0;
543c6df0db7Sjcs 	}
544c6df0db7Sjcs 
545c6df0db7Sjcs 	memset(&ia, 0, sizeof(ia));
546c6df0db7Sjcs 	ia.ia_tag = sc->sc_iba.iba_tag;
547c6df0db7Sjcs 	ia.ia_size = 1;
548c6df0db7Sjcs 	ia.ia_name = "ihidev";
549c6df0db7Sjcs 	ia.ia_size = aml_val2int(&res); /* hid descriptor address */
550c6df0db7Sjcs 	ia.ia_addr = crs.i2c_addr;
551c6df0db7Sjcs 	ia.ia_cookie = dev;
552c6df0db7Sjcs 
553c6df0db7Sjcs 	aml_freevalue(&res);
554c6df0db7Sjcs 
555d03221a8Sjcs 	if (sc->sc_poll_ihidev)
556d03221a8Sjcs 		ia.ia_poll = 1;
557d03221a8Sjcs 	if (!(crs.irq_int == 0 && crs.gpio_int_node == NULL))
558c6df0db7Sjcs 		ia.ia_intr = &crs;
559c6df0db7Sjcs 
560c6df0db7Sjcs 	if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
561c6df0db7Sjcs 		node->parent->attached = 1;
562c6df0db7Sjcs 		return 0;
563c6df0db7Sjcs 	}
564c6df0db7Sjcs 
565c6df0db7Sjcs 	return 1;
566c6df0db7Sjcs }
567c6df0db7Sjcs 
568c6df0db7Sjcs int
dwiic_acpi_found_ietp(struct dwiic_softc * sc,struct aml_node * node,char * dev,struct dwiic_crs crs)569*593d792cSjcs dwiic_acpi_found_ietp(struct dwiic_softc *sc, struct aml_node *node,
570*593d792cSjcs     char *dev, struct dwiic_crs crs)
571*593d792cSjcs {
572*593d792cSjcs 	struct i2c_attach_args ia;
573*593d792cSjcs 
574*593d792cSjcs 	memset(&ia, 0, sizeof(ia));
575*593d792cSjcs 	ia.ia_tag = sc->sc_iba.iba_tag;
576*593d792cSjcs 	ia.ia_size = 1;
577*593d792cSjcs 	ia.ia_name = "ietp";
578*593d792cSjcs 	ia.ia_addr = crs.i2c_addr;
579*593d792cSjcs 	ia.ia_cookie = dev;
580*593d792cSjcs 
581*593d792cSjcs 	if (sc->sc_poll_ihidev)
582*593d792cSjcs 		ia.ia_poll = 1;
583*593d792cSjcs 	if (!(crs.irq_int == 0 && crs.gpio_int_node == NULL))
584*593d792cSjcs 		ia.ia_intr = &crs;
585*593d792cSjcs 
586*593d792cSjcs 	if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
587*593d792cSjcs 		node->parent->attached = 1;
588*593d792cSjcs 		return 0;
589*593d792cSjcs 	}
590*593d792cSjcs 
591*593d792cSjcs 	return 1;
592*593d792cSjcs }
593*593d792cSjcs 
594*593d792cSjcs int
dwiic_acpi_found_iatp(struct dwiic_softc * sc,struct aml_node * node,char * dev,struct dwiic_crs crs)595c6df0db7Sjcs dwiic_acpi_found_iatp(struct dwiic_softc *sc, struct aml_node *node, char *dev,
596c6df0db7Sjcs     struct dwiic_crs crs)
597c6df0db7Sjcs {
598c6df0db7Sjcs 	struct i2c_attach_args ia;
599c6df0db7Sjcs 	struct aml_value res;
600c6df0db7Sjcs 
601c6df0db7Sjcs 	if (aml_evalname(acpi_softc, node->parent, "GPIO", 0, NULL, &res))
602c6df0db7Sjcs 		/* no gpio, assume this is the bootloader interface */
603c6df0db7Sjcs 		return (0);
604c6df0db7Sjcs 
605c6df0db7Sjcs 	memset(&ia, 0, sizeof(ia));
606c6df0db7Sjcs 	ia.ia_tag = sc->sc_iba.iba_tag;
607c6df0db7Sjcs 	ia.ia_size = 1;
608c6df0db7Sjcs 	ia.ia_name = "iatp";
609c6df0db7Sjcs 	ia.ia_addr = crs.i2c_addr;
610c6df0db7Sjcs 	ia.ia_cookie = dev;
611c6df0db7Sjcs 
612c6df0db7Sjcs 	if (crs.irq_int <= 0 && crs.gpio_int_node == NULL) {
613c6df0db7Sjcs 		printf("%s: couldn't find irq for %s\n", sc->sc_dev.dv_xname,
614c6df0db7Sjcs 		   aml_nodename(node->parent));
615c6df0db7Sjcs 		return 0;
616c6df0db7Sjcs 	}
617c6df0db7Sjcs 	ia.ia_intr = &crs;
618c6df0db7Sjcs 
619c6df0db7Sjcs 	if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
620c6df0db7Sjcs 		node->parent->attached = 1;
621c6df0db7Sjcs 		return 0;
622c6df0db7Sjcs 	}
623c6df0db7Sjcs 
624c6df0db7Sjcs 	return 1;
625c6df0db7Sjcs }
626c6df0db7Sjcs 
627c6df0db7Sjcs void
dwiic_acpi_power(struct dwiic_softc * sc,int power)628c6df0db7Sjcs dwiic_acpi_power(struct dwiic_softc *sc, int power)
629c6df0db7Sjcs {
630c6df0db7Sjcs 	char ps[] = "_PS0";
631c6df0db7Sjcs 
632c6df0db7Sjcs 	if (!power)
633c6df0db7Sjcs 		ps[3] = '3';
634c6df0db7Sjcs 
635c6df0db7Sjcs 	if (aml_searchname(sc->sc_devnode, ps)) {
636c6df0db7Sjcs 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, ps, 0, NULL,
637c6df0db7Sjcs 		    NULL)) {
638c6df0db7Sjcs 			printf("%s: failed powering %s with %s\n",
639c6df0db7Sjcs 			    sc->sc_dev.dv_xname, power ? "on" : "off",
640c6df0db7Sjcs 			    ps);
641c6df0db7Sjcs 			return;
642c6df0db7Sjcs 		}
643c6df0db7Sjcs 
644c6df0db7Sjcs 		DELAY(10000); /* 10 milliseconds */
645c6df0db7Sjcs 	} else
646c6df0db7Sjcs 		DPRINTF(("%s: no %s method\n", sc->sc_dev.dv_xname, ps));
647c6df0db7Sjcs 
648c6df0db7Sjcs 	if (strcmp(sc->sc_hid, "INT3432") == 0 ||
649c6df0db7Sjcs 	    strcmp(sc->sc_hid, "INT3433") == 0) {
650c6df0db7Sjcs 		/*
651c6df0db7Sjcs 		 * XXX: broadwell i2c devices may need this for initial power
652c6df0db7Sjcs 		 * up and/or after s3 resume.
653c6df0db7Sjcs 		 *
654c6df0db7Sjcs 		 * linux does this write via LPSS -> clk_register_gate ->
655c6df0db7Sjcs 		 * clk_gate_enable -> clk_gate_endisable -> clk_writel
656c6df0db7Sjcs 		 */
657c6df0db7Sjcs 		dwiic_write(sc, 0x800, 1);
658c6df0db7Sjcs 	}
659c6df0db7Sjcs }
66075318f5bSdlg 
66175318f5bSdlg #if NIOSF > 0
66275318f5bSdlg extern int	iosf_i2c_acquire(int);
66375318f5bSdlg extern void	iosf_i2c_release(int);
66475318f5bSdlg 
66575318f5bSdlg int
dwiic_acpi_acquire_bus(void * cookie,int flags)66675318f5bSdlg dwiic_acpi_acquire_bus(void *cookie, int flags)
66775318f5bSdlg {
66875318f5bSdlg 	int rv;
66975318f5bSdlg 
67075318f5bSdlg 	rv = dwiic_i2c_acquire_bus(cookie, flags);
67175318f5bSdlg 	if (rv != 0)
67275318f5bSdlg 		return (rv);
67375318f5bSdlg 
67475318f5bSdlg 	return (iosf_i2c_acquire(flags));
67575318f5bSdlg }
67675318f5bSdlg 
67775318f5bSdlg void
dwiic_acpi_release_bus(void * cookie,int flags)67875318f5bSdlg dwiic_acpi_release_bus(void *cookie, int flags)
67975318f5bSdlg {
68075318f5bSdlg 	iosf_i2c_release(flags);
68175318f5bSdlg 	dwiic_i2c_release_bus(cookie, flags);
68275318f5bSdlg }
68375318f5bSdlg #endif /* NIOSF > 0 */
684