xref: /netbsd-src/sys/dev/acpi/acpi_i2c.c (revision 84b6468db257bd512ee8a1ad7a66d1ff757f2049)
1*84b6468dSjmcneill /* $NetBSD: acpi_i2c.c,v 1.19 2025/01/11 11:40:43 jmcneill Exp $ */
2db8cbfd3Sbouyer 
3db8cbfd3Sbouyer /*-
484eb440bSthorpej  * Copyright (c) 2017, 2021 The NetBSD Foundation, Inc.
5db8cbfd3Sbouyer  * All rights reserved.
6db8cbfd3Sbouyer  *
7db8cbfd3Sbouyer  * This code is derived from software contributed to The NetBSD Foundation
8db8cbfd3Sbouyer  * by Manuel Bouyer.
9db8cbfd3Sbouyer  *
10db8cbfd3Sbouyer  * Redistribution and use in source and binary forms, with or without
11db8cbfd3Sbouyer  * modification, are permitted provided that the following conditions
12db8cbfd3Sbouyer  * are met:
13db8cbfd3Sbouyer  * 1. Redistributions of source code must retain the above copyright
14db8cbfd3Sbouyer  *    notice, this list of conditions and the following disclaimer.
15db8cbfd3Sbouyer  * 2. Redistributions in binary form must reproduce the above copyright
16db8cbfd3Sbouyer  *    notice, this list of conditions and the following disclaimer in the
17db8cbfd3Sbouyer  *    documentation and/or other materials provided with the distribution.
18db8cbfd3Sbouyer  *
19db8cbfd3Sbouyer  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20db8cbfd3Sbouyer  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21db8cbfd3Sbouyer  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22db8cbfd3Sbouyer  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23db8cbfd3Sbouyer  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24db8cbfd3Sbouyer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25db8cbfd3Sbouyer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26db8cbfd3Sbouyer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27db8cbfd3Sbouyer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28db8cbfd3Sbouyer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29db8cbfd3Sbouyer  * POSSIBILITY OF SUCH DAMAGE.
30db8cbfd3Sbouyer  */
31db8cbfd3Sbouyer 
320e37d484Sjmcneill #include "iic.h"
330e37d484Sjmcneill 
34db8cbfd3Sbouyer #include <sys/cdefs.h>
35*84b6468dSjmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.19 2025/01/11 11:40:43 jmcneill Exp $");
366a650a48Sjmcneill 
376a650a48Sjmcneill #include <sys/device.h>
38db8cbfd3Sbouyer 
39db8cbfd3Sbouyer #include <dev/acpi/acpireg.h>
40db8cbfd3Sbouyer #include <dev/acpi/acpivar.h>
41db8cbfd3Sbouyer #include <dev/acpi/acpi_i2c.h>
4220086c25Sjmcneill #include <external/bsd/acpica/dist/include/acinterp.h>
439fabcb6aSjmcneill #include <external/bsd/acpica/dist/include/amlcode.h>
4402b41e14Sjmcneill #include <dev/i2c/i2cvar.h>
45db8cbfd3Sbouyer 
4684eb440bSthorpej #include <sys/kmem.h>
4784eb440bSthorpej 
48d8ae7995Sbouyer #define _COMPONENT	ACPI_BUS_COMPONENT
49d8ae7995Sbouyer ACPI_MODULE_NAME	("acpi_i2c")
50d8ae7995Sbouyer 
5120086c25Sjmcneill struct acpi_i2c_address_space_context {
5220086c25Sjmcneill 	ACPI_CONNECTION_INFO conn_info;	/* must be first */
5320086c25Sjmcneill 	i2c_tag_t tag;
5420086c25Sjmcneill };
5520086c25Sjmcneill 
566a650a48Sjmcneill static const struct device_compatible_entry hid_compat_data[] = {
576a650a48Sjmcneill 	{ .compat = "PNP0C50" },
586a650a48Sjmcneill 	DEVICE_COMPAT_EOL
596a650a48Sjmcneill };
606a650a48Sjmcneill 
610e37d484Sjmcneill #if NIIC > 0
62db8cbfd3Sbouyer struct acpi_i2c_context {
63db8cbfd3Sbouyer 	uint16_t i2c_addr;
646a650a48Sjmcneill 	struct acpi_devnode *res_src;
65db8cbfd3Sbouyer };
660e37d484Sjmcneill #endif
67db8cbfd3Sbouyer 
686a650a48Sjmcneill static struct acpi_devnode *
696a650a48Sjmcneill acpi_i2c_resource_find_source(ACPI_RESOURCE_SOURCE *rs)
706a650a48Sjmcneill {
716a650a48Sjmcneill 	ACPI_STATUS rv;
726a650a48Sjmcneill 	ACPI_HANDLE hdl;
736a650a48Sjmcneill 	struct acpi_devnode *ad;
746a650a48Sjmcneill 
756a650a48Sjmcneill 	if (rs->StringPtr == NULL) {
766a650a48Sjmcneill 		return NULL;
776a650a48Sjmcneill 	}
786a650a48Sjmcneill 
796a650a48Sjmcneill 	rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
806a650a48Sjmcneill 	if (ACPI_FAILURE(rv)) {
816a650a48Sjmcneill 		printf("%s: couldn't lookup '%s': %s\n", __func__,
826a650a48Sjmcneill 		    rs->StringPtr, AcpiFormatException(rv));
836a650a48Sjmcneill 		return NULL;
846a650a48Sjmcneill 	}
856a650a48Sjmcneill 
866a650a48Sjmcneill 	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
876a650a48Sjmcneill 		if (ad->ad_handle == hdl) {
886a650a48Sjmcneill 			return ad;
896a650a48Sjmcneill 		}
906a650a48Sjmcneill 	}
916a650a48Sjmcneill 
926a650a48Sjmcneill 	printf("%s: no acpi devnode matching resource source '%s'\n",
936a650a48Sjmcneill 	    __func__, rs->StringPtr);
946a650a48Sjmcneill 	return NULL;
956a650a48Sjmcneill }
966a650a48Sjmcneill 
97db8cbfd3Sbouyer static ACPI_STATUS
98db8cbfd3Sbouyer acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
99db8cbfd3Sbouyer {
100db8cbfd3Sbouyer 	struct acpi_i2c_context *i2cc = context;
101db8cbfd3Sbouyer 
102db8cbfd3Sbouyer 	switch (res->Type) {
103db8cbfd3Sbouyer 	case ACPI_RESOURCE_TYPE_END_TAG:
104db8cbfd3Sbouyer 		break;
105db8cbfd3Sbouyer 	case ACPI_RESOURCE_TYPE_SERIAL_BUS:
106db8cbfd3Sbouyer 		switch (res->Data.I2cSerialBus.Type) {
107db8cbfd3Sbouyer 		case ACPI_RESOURCE_SERIAL_TYPE_I2C:
108db8cbfd3Sbouyer 			i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
1096a650a48Sjmcneill 			i2cc->res_src = acpi_i2c_resource_find_source(
1106a650a48Sjmcneill 			    &res->Data.I2cSerialBus.ResourceSource);
111db8cbfd3Sbouyer 			break;
112db8cbfd3Sbouyer 		}
113db8cbfd3Sbouyer 		break;
114db8cbfd3Sbouyer 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
115db8cbfd3Sbouyer 		break;
116db8cbfd3Sbouyer 	default:
117a79639ceSjmcneill 		break;
118db8cbfd3Sbouyer 	}
119db8cbfd3Sbouyer 	return_ACPI_STATUS(AE_OK);
120db8cbfd3Sbouyer }
121db8cbfd3Sbouyer 
122db8cbfd3Sbouyer static void
123db8cbfd3Sbouyer acpi_enter_i2c_device(struct acpi_devnode *ad, prop_array_t array)
124db8cbfd3Sbouyer {
125db8cbfd3Sbouyer 	prop_dictionary_t dev;
126db8cbfd3Sbouyer 	struct acpi_i2c_context i2cc;
127db8cbfd3Sbouyer 	ACPI_STATUS rv;
12884eb440bSthorpej 	char *clist;
12984eb440bSthorpej 	size_t clist_size;
130db8cbfd3Sbouyer 
131db8cbfd3Sbouyer 	memset(&i2cc, 0, sizeof(i2cc));
132db8cbfd3Sbouyer 	rv = AcpiWalkResources(ad->ad_handle, "_CRS",
133db8cbfd3Sbouyer 	     acpi_i2c_resource_parse_callback, &i2cc);
134db8cbfd3Sbouyer 	if (ACPI_FAILURE(rv)) {
135db8cbfd3Sbouyer 		return;
136db8cbfd3Sbouyer 	}
137db8cbfd3Sbouyer 	if (i2cc.i2c_addr == 0)
138db8cbfd3Sbouyer 		return;
139db8cbfd3Sbouyer 	dev = prop_dictionary_create();
140db8cbfd3Sbouyer 	if (dev == NULL) {
141db8cbfd3Sbouyer 		aprint_error("ignoring device %s (no memory)\n",
142db8cbfd3Sbouyer 		    ad->ad_name);
143db8cbfd3Sbouyer 		return;
144db8cbfd3Sbouyer 	}
145568ac2aaSthorpej 	clist = acpi_pack_compat_list(ad, &clist_size);
14684eb440bSthorpej 	if (clist == NULL) {
14784eb440bSthorpej 		prop_object_release(dev);
14884eb440bSthorpej 		aprint_error("ignoring device %s (no _HID or _CID)\n",
14984eb440bSthorpej 		    ad->ad_name);
15084eb440bSthorpej 		return;
15184eb440bSthorpej 	}
152568ac2aaSthorpej 	prop_dictionary_set_string(dev, "name", ad->ad_name);
153db8cbfd3Sbouyer 	prop_dictionary_set_uint32(dev, "addr", i2cc.i2c_addr);
154db8cbfd3Sbouyer 	prop_dictionary_set_uint64(dev, "cookie", (uintptr_t)ad->ad_handle);
15502b41e14Sjmcneill 	prop_dictionary_set_uint32(dev, "cookietype", I2C_COOKIE_ACPI);
15684eb440bSthorpej 	prop_dictionary_set_data(dev, "compatible", clist, clist_size);
15784eb440bSthorpej 	kmem_free(clist, clist_size);
15884eb440bSthorpej 
159db8cbfd3Sbouyer 	prop_array_add(array, dev);
160db8cbfd3Sbouyer 	prop_object_release(dev);
161db8cbfd3Sbouyer }
162db8cbfd3Sbouyer 
1636a650a48Sjmcneill static void
1646a650a48Sjmcneill acpi_enter_i2chid_devs(device_t dev, struct acpi_devnode *devnode,
1656a650a48Sjmcneill     prop_array_t array)
1666a650a48Sjmcneill {
1676a650a48Sjmcneill 	struct acpi_devnode *ad;
1686a650a48Sjmcneill 
1696a650a48Sjmcneill 	KASSERT(dev != NULL);
1706a650a48Sjmcneill 
1716a650a48Sjmcneill 	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
1726a650a48Sjmcneill 		struct acpi_attach_args aa = {
1736a650a48Sjmcneill 			.aa_node = ad
1746a650a48Sjmcneill 		};
1756a650a48Sjmcneill 		struct acpi_i2c_context i2cc;
1766a650a48Sjmcneill 		ACPI_STATUS rv;
1776a650a48Sjmcneill 
1786a650a48Sjmcneill 		if (!acpi_device_present(ad->ad_handle))
1796a650a48Sjmcneill 			continue;
1806a650a48Sjmcneill 		if (ad->ad_device != NULL)
1816a650a48Sjmcneill 			continue;
1826a650a48Sjmcneill 		if (acpi_compatible_match(&aa, hid_compat_data) == 0)
1836a650a48Sjmcneill 			continue;
1846a650a48Sjmcneill 
1856a650a48Sjmcneill 		memset(&i2cc, 0, sizeof(i2cc));
1866a650a48Sjmcneill 		rv = AcpiWalkResources(ad->ad_handle, "_CRS",
1876a650a48Sjmcneill 		    acpi_i2c_resource_parse_callback, &i2cc);
1886a650a48Sjmcneill 		if (ACPI_SUCCESS(rv) &&
1896a650a48Sjmcneill 		    i2cc.i2c_addr != 0 &&
1906a650a48Sjmcneill 		    i2cc.res_src == devnode) {
1916a650a48Sjmcneill 			aprint_debug_dev(dev, "claiming %s\n", ad->ad_name);
192a93f58a9Sjmcneill 			ad->ad_device = dev;
193*84b6468dSjmcneill 			acpi_claim_childdevs(dev, ad, NULL);
1946a650a48Sjmcneill 			acpi_enter_i2c_device(ad, array);
1956a650a48Sjmcneill 		}
1966a650a48Sjmcneill 	}
1976a650a48Sjmcneill }
1986a650a48Sjmcneill 
199db8cbfd3Sbouyer prop_array_t
200172e088eSjmcneill acpi_enter_i2c_devs(device_t dev, struct acpi_devnode *devnode)
201db8cbfd3Sbouyer {
202db8cbfd3Sbouyer 	struct acpi_devnode *ad;
203db8cbfd3Sbouyer 	prop_array_t array = prop_array_create();
204db8cbfd3Sbouyer 
205db8cbfd3Sbouyer 	if (array == NULL)
206db8cbfd3Sbouyer 		return NULL;
207db8cbfd3Sbouyer 
208db8cbfd3Sbouyer 	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
209db8cbfd3Sbouyer 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
210db8cbfd3Sbouyer 			continue;
211a2cf80f2Schristos 		if (!acpi_device_present(ad->ad_handle))
212a2cf80f2Schristos 			continue;
213db8cbfd3Sbouyer 		acpi_enter_i2c_device(ad, array);
214db8cbfd3Sbouyer 	}
215172e088eSjmcneill 
216172e088eSjmcneill 	if (dev != NULL) {
217*84b6468dSjmcneill 		acpi_claim_childdevs(dev, devnode, "_CRS");
218*84b6468dSjmcneill 		acpi_claim_childdevs(dev, devnode, "_ADR");
2196a650a48Sjmcneill 		acpi_enter_i2chid_devs(dev, devnode, array);
220172e088eSjmcneill 	}
221172e088eSjmcneill 
222db8cbfd3Sbouyer 	return array;
223db8cbfd3Sbouyer }
22420086c25Sjmcneill 
2250e37d484Sjmcneill #if NIIC > 0
22620086c25Sjmcneill static ACPI_STATUS
22720086c25Sjmcneill acpi_i2c_gsb_init(ACPI_HANDLE region_hdl, UINT32 function,
22820086c25Sjmcneill     void *handler_ctx, void **region_ctx)
22920086c25Sjmcneill {
23020086c25Sjmcneill 	if (function == ACPI_REGION_DEACTIVATE) {
23120086c25Sjmcneill 		*region_ctx = NULL;
23220086c25Sjmcneill 	} else {
23320086c25Sjmcneill 		*region_ctx = region_hdl;
23420086c25Sjmcneill 	}
23520086c25Sjmcneill 	return AE_OK;
23620086c25Sjmcneill }
23720086c25Sjmcneill 
23820086c25Sjmcneill static ACPI_STATUS
23920086c25Sjmcneill acpi_i2c_gsb_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
24020086c25Sjmcneill     UINT32 bit_width, UINT64 *value, void *handler_ctx,
24120086c25Sjmcneill     void *region_ctx)
24220086c25Sjmcneill {
24320086c25Sjmcneill 	ACPI_OPERAND_OBJECT *region_obj = region_ctx;
24420086c25Sjmcneill 	struct acpi_i2c_address_space_context *context = handler_ctx;
2459fabcb6aSjmcneill 	UINT8 *buf = ACPI_CAST_PTR(uint8_t, value);
24620086c25Sjmcneill 	ACPI_PHYSICAL_ADDRESS base_address;
24720086c25Sjmcneill 	ACPI_RESOURCE *res;
24820086c25Sjmcneill 	ACPI_STATUS rv;
24920086c25Sjmcneill 	ACPI_CONNECTION_INFO *conn_info = &context->conn_info;
25020086c25Sjmcneill 	i2c_tag_t tag = context->tag;
2519fabcb6aSjmcneill 	i2c_addr_t i2c_addr;
25220086c25Sjmcneill 	i2c_op_t op;
25320086c25Sjmcneill 	union {
25420086c25Sjmcneill 		uint8_t cmd8;
25520086c25Sjmcneill 		uint16_t cmd16;
2569fabcb6aSjmcneill 		uint32_t cmd32;
25720086c25Sjmcneill 	} cmd;
2589fabcb6aSjmcneill 	size_t buflen;
25920086c25Sjmcneill 	size_t cmdlen;
2609fabcb6aSjmcneill 	bool do_xfer = true;
26120086c25Sjmcneill 
26220086c25Sjmcneill 	if (region_obj->Region.Type != ACPI_TYPE_REGION) {
26320086c25Sjmcneill 		return AE_OK;
26420086c25Sjmcneill 	}
26520086c25Sjmcneill 
26620086c25Sjmcneill 	base_address = region_obj->Region.Address;
2679fabcb6aSjmcneill 	KASSERT(region_obj->Region.SpaceId == ACPI_ADR_SPACE_GSBUS);
26820086c25Sjmcneill 
26920086c25Sjmcneill 	rv = AcpiBufferToResource(conn_info->Connection, conn_info->Length,
27020086c25Sjmcneill 	    &res);
27120086c25Sjmcneill 	if (ACPI_FAILURE(rv)) {
27220086c25Sjmcneill 		return rv;
27320086c25Sjmcneill 	}
27420086c25Sjmcneill 	if (res->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS ||
27520086c25Sjmcneill 	    res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
27620086c25Sjmcneill 		return AE_TYPE;
27720086c25Sjmcneill 	}
27820086c25Sjmcneill 
2799fabcb6aSjmcneill 	i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
28020086c25Sjmcneill 	if ((function & ACPI_IO_MASK) != 0) {
28120086c25Sjmcneill 		op = I2C_OP_WRITE_WITH_STOP;
28220086c25Sjmcneill 	} else {
28320086c25Sjmcneill 		op = I2C_OP_READ_WITH_STOP;
28420086c25Sjmcneill 	}
28520086c25Sjmcneill 
28620086c25Sjmcneill #ifdef ACPI_I2C_DEBUG
2879fabcb6aSjmcneill 	UINT32 length;
2889fabcb6aSjmcneill 	rv = AcpiExGetProtocolBufferLength(function >> 16, &length);
2899fabcb6aSjmcneill 	if (ACPI_FAILURE(rv)) {
2909fabcb6aSjmcneill 		printf("%s AcpiExGetProtocolBufferLength failed: %s\n",
2919fabcb6aSjmcneill 		    __func__, AcpiFormatException(rv));
2929fabcb6aSjmcneill 		length = UINT32_MAX;
2939fabcb6aSjmcneill 	}
29420086c25Sjmcneill 	printf("%s %s: %s Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X",
2959fabcb6aSjmcneill 	       __func__, AcpiUtGetRegionName(region_obj->Region.SpaceId),
29620086c25Sjmcneill 	       (function & ACPI_IO_MASK) ? "Write" : "Read ",
29720086c25Sjmcneill 	       (UINT32) (function >> 16),
29820086c25Sjmcneill 	       (UINT32) address, (UINT32) base_address,
2999fabcb6aSjmcneill 	       length, bit_width, buf[1]);
30020086c25Sjmcneill 	printf(" [AccessLength %.2X Connection %p]\n",
30120086c25Sjmcneill 	       conn_info->AccessLength, conn_info->Connection);
30220086c25Sjmcneill #endif
30320086c25Sjmcneill 
3049fabcb6aSjmcneill 	switch ((UINT32)(function >> 16)) {
3059fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_QUICK:
30620086c25Sjmcneill 		cmdlen = 0;
3079fabcb6aSjmcneill 		buflen = 0;
3089fabcb6aSjmcneill 		break;
3099fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_SEND_RECEIVE:
3109fabcb6aSjmcneill 		cmdlen = 0;
3119fabcb6aSjmcneill 		buflen = 1;
3129fabcb6aSjmcneill 		break;
3139fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_BYTE:
3149fabcb6aSjmcneill 		cmdlen = bit_width / NBBY;
3159fabcb6aSjmcneill 		buflen = 1;
3169fabcb6aSjmcneill 		break;
3179fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_WORD:
3189fabcb6aSjmcneill 		cmdlen = bit_width / NBBY;
3199fabcb6aSjmcneill 		buflen = 2;
3209fabcb6aSjmcneill 		break;
3219fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_BYTES:
3229fabcb6aSjmcneill 		cmdlen = bit_width / NBBY;
3239fabcb6aSjmcneill 		buflen = buf[1];
3249fabcb6aSjmcneill 		break;
3259fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_BLOCK:
3269fabcb6aSjmcneill 		cmdlen = bit_width / NBBY;
3279fabcb6aSjmcneill 		buflen = buf[1];
3289fabcb6aSjmcneill 		op |= I2C_OPMASK_BLKMODE;
3299fabcb6aSjmcneill 		break;
3309fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_RAW_BYTES:
3319fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_RAW_PROCESS_BYTES:
3329fabcb6aSjmcneill 	case AML_FIELD_ATTRIB_PROCESS_CALL:
3339fabcb6aSjmcneill 	default:
3349fabcb6aSjmcneill 		cmdlen = 0;
3359fabcb6aSjmcneill 		do_xfer = false;
3369fabcb6aSjmcneill #ifdef ACPI_I2C_DEBUG
3379fabcb6aSjmcneill 		printf("field attrib 0x%x not supported\n",
3389fabcb6aSjmcneill 		    (UINT32)(function >> 16));
3399fabcb6aSjmcneill #endif
3409fabcb6aSjmcneill 		break;
34120086c25Sjmcneill 	}
3429fabcb6aSjmcneill 
3439fabcb6aSjmcneill 	switch (cmdlen) {
3449fabcb6aSjmcneill 	case 0:
3459fabcb6aSjmcneill 	case 1:
3469fabcb6aSjmcneill 		cmd.cmd8 = (uint8_t)(base_address + address);
3479fabcb6aSjmcneill 		break;
3489fabcb6aSjmcneill 	case 2:
3499fabcb6aSjmcneill 		cmd.cmd16 = (uint16_t)(base_address + address);
3509fabcb6aSjmcneill 		break;
3519fabcb6aSjmcneill 	case 4:
3529fabcb6aSjmcneill 		cmd.cmd32 = (uint32_t)(base_address + address);
3539fabcb6aSjmcneill 		break;
3549fabcb6aSjmcneill 	default:
3559fabcb6aSjmcneill 		do_xfer = false;
3569fabcb6aSjmcneill #ifdef ACPI_I2C_DEBUG
3579fabcb6aSjmcneill 		printf("cmdlen %zu not supported\n", cmdlen);
3589fabcb6aSjmcneill #endif
3599fabcb6aSjmcneill 		break;
3609fabcb6aSjmcneill 	}
3619fabcb6aSjmcneill 
3629fabcb6aSjmcneill 	if (!do_xfer) {
3639fabcb6aSjmcneill 		buf[0] = EINVAL;
36420086c25Sjmcneill 	} else {
36520086c25Sjmcneill 		const int flags = I2C_F_POLL;
36620086c25Sjmcneill 		iic_acquire_bus(tag, flags);
3679fabcb6aSjmcneill 		buf[0] = iic_exec(tag, op, i2c_addr,
3689fabcb6aSjmcneill 				  &cmd, cmdlen, &buf[2], buflen, flags);
36920086c25Sjmcneill 		iic_release_bus(tag, flags);
3709fabcb6aSjmcneill 		if (buf[0] == 0) {
3719fabcb6aSjmcneill 			buf[1] = buflen;
37220086c25Sjmcneill 		}
37320086c25Sjmcneill #ifdef ACPI_I2C_DEBUG
3749fabcb6aSjmcneill 		printf("%s iic_exec op %u addr 0x%x len %zu/%zu returned %d\n",
3759fabcb6aSjmcneill 		    __func__, op, res->Data.I2cSerialBus.SlaveAddress, cmdlen,
3769fabcb6aSjmcneill 		    buflen, buf[0]);
37720086c25Sjmcneill #endif
37820086c25Sjmcneill 	}
37920086c25Sjmcneill 
38020086c25Sjmcneill 	ACPI_FREE(res);
38120086c25Sjmcneill 
38220086c25Sjmcneill 	return AE_OK;
38320086c25Sjmcneill }
3840e37d484Sjmcneill #endif
38520086c25Sjmcneill 
38620086c25Sjmcneill ACPI_STATUS
38720086c25Sjmcneill acpi_i2c_register(struct acpi_devnode *devnode, device_t dev, i2c_tag_t tag)
38820086c25Sjmcneill {
3890e37d484Sjmcneill #if NIIC > 0
39020086c25Sjmcneill 	struct acpi_i2c_address_space_context *context;
39120086c25Sjmcneill 	ACPI_STATUS rv;
39220086c25Sjmcneill 
39320086c25Sjmcneill 	context = kmem_zalloc(sizeof(*context), KM_SLEEP);
39420086c25Sjmcneill 	context->tag = tag;
39520086c25Sjmcneill 
39620086c25Sjmcneill 	rv = AcpiInstallAddressSpaceHandler(devnode->ad_handle,
39720086c25Sjmcneill 	    ACPI_ADR_SPACE_GSBUS, acpi_i2c_gsb_handler, acpi_i2c_gsb_init,
39820086c25Sjmcneill 	    context);
39920086c25Sjmcneill 	if (ACPI_FAILURE(rv)) {
40020086c25Sjmcneill 		aprint_error_dev(dev,
40120086c25Sjmcneill 		    "couldn't install address space handler: %s",
40220086c25Sjmcneill 		    AcpiFormatException(rv));
40320086c25Sjmcneill 	}
40420086c25Sjmcneill 
40520086c25Sjmcneill 	return rv;
4060e37d484Sjmcneill #else
4070e37d484Sjmcneill 	return AE_NOT_CONFIGURED;
4080e37d484Sjmcneill #endif
40920086c25Sjmcneill }
410