xref: /freebsd/sys/dev/iicbus/acpi_iicbus.c (revision 36b9e628315ba11c259a6c49f318d5acaedc9ec5)
19b7938dcSVladimir Kondratyev /*-
29b7938dcSVladimir Kondratyev  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39b7938dcSVladimir Kondratyev  *
49b7938dcSVladimir Kondratyev  * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org>
59b7938dcSVladimir Kondratyev  *
69b7938dcSVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
79b7938dcSVladimir Kondratyev  * modification, are permitted provided that the following conditions
89b7938dcSVladimir Kondratyev  * are met:
99b7938dcSVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
109b7938dcSVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
119b7938dcSVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
129b7938dcSVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
139b7938dcSVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
149b7938dcSVladimir Kondratyev  *
159b7938dcSVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
169b7938dcSVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
179b7938dcSVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
189b7938dcSVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
199b7938dcSVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
209b7938dcSVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
219b7938dcSVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
229b7938dcSVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
239b7938dcSVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
249b7938dcSVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
259b7938dcSVladimir Kondratyev  * SUCH DAMAGE.
269b7938dcSVladimir Kondratyev  */
279b7938dcSVladimir Kondratyev 
289b7938dcSVladimir Kondratyev #include <sys/cdefs.h>
299b7938dcSVladimir Kondratyev __FBSDID("$FreeBSD$");
309b7938dcSVladimir Kondratyev 
319b7938dcSVladimir Kondratyev #include <sys/param.h>
329b7938dcSVladimir Kondratyev #include <sys/bus.h>
339b7938dcSVladimir Kondratyev #include <sys/endian.h>
349b7938dcSVladimir Kondratyev #include <sys/kernel.h>
359b7938dcSVladimir Kondratyev #include <sys/malloc.h>
369b7938dcSVladimir Kondratyev #include <sys/module.h>
379b7938dcSVladimir Kondratyev #include <sys/rman.h>
389b7938dcSVladimir Kondratyev 
399b7938dcSVladimir Kondratyev #include <machine/resource.h>
409b7938dcSVladimir Kondratyev 
419b7938dcSVladimir Kondratyev #include <contrib/dev/acpica/include/acpi.h>
429b7938dcSVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h>
439b7938dcSVladimir Kondratyev #include <contrib/dev/acpica/include/amlcode.h>
449b7938dcSVladimir Kondratyev #include <dev/acpica/acpivar.h>
459b7938dcSVladimir Kondratyev 
469b7938dcSVladimir Kondratyev #include <dev/iicbus/iiconf.h>
479b7938dcSVladimir Kondratyev #include <dev/iicbus/iicbus.h>
489b7938dcSVladimir Kondratyev 
499b7938dcSVladimir Kondratyev #define	ACPI_IICBUS_LOCAL_BUFSIZE	32	/* Fits max SMBUS block size */
509b7938dcSVladimir Kondratyev 
519b7938dcSVladimir Kondratyev /*
529b7938dcSVladimir Kondratyev  * Make a copy of ACPI_RESOURCE_I2C_SERIALBUS type and replace "pointer to ACPI
539b7938dcSVladimir Kondratyev  * object name string" field with pointer to ACPI object itself.
549b7938dcSVladimir Kondratyev  * This saves us extra strdup()/free() pair on acpi_iicbus_get_i2cres call.
559b7938dcSVladimir Kondratyev  */
569b7938dcSVladimir Kondratyev typedef	ACPI_RESOURCE_I2C_SERIALBUS	ACPI_IICBUS_RESOURCE_I2C_SERIALBUS;
579b7938dcSVladimir Kondratyev #define	ResourceSource_Handle	ResourceSource.StringPtr
589b7938dcSVladimir Kondratyev 
599b7938dcSVladimir Kondratyev /* Hooks for the ACPI CA debugging infrastructure. */
609b7938dcSVladimir Kondratyev #define	_COMPONENT	ACPI_BUS
619b7938dcSVladimir Kondratyev ACPI_MODULE_NAME("IIC")
629b7938dcSVladimir Kondratyev 
639b7938dcSVladimir Kondratyev struct gsb_buffer {
649b7938dcSVladimir Kondratyev 	UINT8 status;
659b7938dcSVladimir Kondratyev 	UINT8 len;
669b7938dcSVladimir Kondratyev 	UINT8 data[];
679b7938dcSVladimir Kondratyev } __packed;
689b7938dcSVladimir Kondratyev 
699b7938dcSVladimir Kondratyev struct acpi_iicbus_softc {
709b7938dcSVladimir Kondratyev 	struct iicbus_softc	super_sc;
719b7938dcSVladimir Kondratyev 	ACPI_CONNECTION_INFO	space_handler_info;
729b7938dcSVladimir Kondratyev 	bool			space_handler_installed;
739b7938dcSVladimir Kondratyev };
749b7938dcSVladimir Kondratyev 
759b7938dcSVladimir Kondratyev struct acpi_iicbus_ivars {
769b7938dcSVladimir Kondratyev 	struct iicbus_ivar	super_ivar;
779b7938dcSVladimir Kondratyev 	ACPI_HANDLE		handle;
789b7938dcSVladimir Kondratyev };
799b7938dcSVladimir Kondratyev 
809b7938dcSVladimir Kondratyev static int install_space_handler = 0;
819b7938dcSVladimir Kondratyev TUNABLE_INT("hw.iicbus.enable_acpi_space_handler", &install_space_handler);
829b7938dcSVladimir Kondratyev 
839b7938dcSVladimir Kondratyev static inline bool
849b7938dcSVladimir Kondratyev acpi_resource_is_i2c_serialbus(ACPI_RESOURCE *res)
859b7938dcSVladimir Kondratyev {
869b7938dcSVladimir Kondratyev 
879b7938dcSVladimir Kondratyev 	return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
889b7938dcSVladimir Kondratyev 	    res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C);
899b7938dcSVladimir Kondratyev }
909b7938dcSVladimir Kondratyev 
919b7938dcSVladimir Kondratyev /*
929b7938dcSVladimir Kondratyev  * IICBUS Address space handler
939b7938dcSVladimir Kondratyev  */
949b7938dcSVladimir Kondratyev static int
959b7938dcSVladimir Kondratyev acpi_iicbus_sendb(device_t dev, u_char slave, char byte)
969b7938dcSVladimir Kondratyev {
979b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
989b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR, 1, &byte },
999b7938dcSVladimir Kondratyev 	};
1009b7938dcSVladimir Kondratyev 
1019b7938dcSVladimir Kondratyev 	return (iicbus_transfer(dev, msgs, nitems(msgs)));
1029b7938dcSVladimir Kondratyev }
1039b7938dcSVladimir Kondratyev 
1049b7938dcSVladimir Kondratyev static int
1059b7938dcSVladimir Kondratyev acpi_iicbus_recvb(device_t dev, u_char slave, char *byte)
1069b7938dcSVladimir Kondratyev {
1079b7938dcSVladimir Kondratyev 	char buf;
1089b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
1099b7938dcSVladimir Kondratyev 	    { slave, IIC_M_RD, 1, &buf },
1109b7938dcSVladimir Kondratyev 	};
1119b7938dcSVladimir Kondratyev 	int error;
1129b7938dcSVladimir Kondratyev 
1139b7938dcSVladimir Kondratyev 	error = iicbus_transfer(dev, msgs, nitems(msgs));
1149b7938dcSVladimir Kondratyev 	if (error == 0)
1159b7938dcSVladimir Kondratyev 		*byte = buf;
1169b7938dcSVladimir Kondratyev 
1179b7938dcSVladimir Kondratyev 	return (error);
1189b7938dcSVladimir Kondratyev }
1199b7938dcSVladimir Kondratyev 
1209b7938dcSVladimir Kondratyev static int
1219b7938dcSVladimir Kondratyev acpi_iicbus_write(device_t dev, u_char slave, char cmd, void *buf,
1229b7938dcSVladimir Kondratyev     uint16_t buflen)
1239b7938dcSVladimir Kondratyev {
1249b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
1259b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
1269b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTART, buflen, buf },
1279b7938dcSVladimir Kondratyev 	};
1289b7938dcSVladimir Kondratyev 
1299b7938dcSVladimir Kondratyev 	return (iicbus_transfer(dev, msgs, nitems(msgs)));
1309b7938dcSVladimir Kondratyev }
1319b7938dcSVladimir Kondratyev 
1329b7938dcSVladimir Kondratyev static int
1339b7938dcSVladimir Kondratyev acpi_iicbus_read(device_t dev, u_char slave, char cmd, void *buf,
1349b7938dcSVladimir Kondratyev     uint16_t buflen)
1359b7938dcSVladimir Kondratyev {
1369b7938dcSVladimir Kondratyev 	uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
1379b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
1389b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
1399b7938dcSVladimir Kondratyev 	    { slave, IIC_M_RD, buflen, NULL },
1409b7938dcSVladimir Kondratyev 	};
1419b7938dcSVladimir Kondratyev 	int error;
1429b7938dcSVladimir Kondratyev 
1439b7938dcSVladimir Kondratyev 	if (buflen <= sizeof(local_buffer))
1449b7938dcSVladimir Kondratyev 		msgs[1].buf = local_buffer;
1459b7938dcSVladimir Kondratyev 	else
1469b7938dcSVladimir Kondratyev 		msgs[1].buf = malloc(buflen, M_DEVBUF, M_WAITOK);
1479b7938dcSVladimir Kondratyev 	error = iicbus_transfer(dev, msgs, nitems(msgs));
1489b7938dcSVladimir Kondratyev 	if (error == 0)
1499b7938dcSVladimir Kondratyev 		memcpy(buf, msgs[1].buf, buflen);
1509b7938dcSVladimir Kondratyev 	if (msgs[1].buf != local_buffer)
1519b7938dcSVladimir Kondratyev 		free(msgs[1].buf, M_DEVBUF);
1529b7938dcSVladimir Kondratyev 
1539b7938dcSVladimir Kondratyev 	return (error);
1549b7938dcSVladimir Kondratyev }
1559b7938dcSVladimir Kondratyev 
1569b7938dcSVladimir Kondratyev static int
1579b7938dcSVladimir Kondratyev acpi_iicbus_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
1589b7938dcSVladimir Kondratyev {
1599b7938dcSVladimir Kondratyev 	uint8_t bytes[2] = { cmd, count };
1609b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
1619b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
1629b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
1639b7938dcSVladimir Kondratyev 	};
1649b7938dcSVladimir Kondratyev 
1659b7938dcSVladimir Kondratyev 	if (count == 0)
1669b7938dcSVladimir Kondratyev 		return (errno2iic(EINVAL));
1679b7938dcSVladimir Kondratyev 
1689b7938dcSVladimir Kondratyev 	return (iicbus_transfer(dev, msgs, nitems(msgs)));
1699b7938dcSVladimir Kondratyev }
1709b7938dcSVladimir Kondratyev 
1719b7938dcSVladimir Kondratyev static int
1729b7938dcSVladimir Kondratyev acpi_iicbus_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
1739b7938dcSVladimir Kondratyev {
1749b7938dcSVladimir Kondratyev 	uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
1759b7938dcSVladimir Kondratyev 	u_char len;
1769b7938dcSVladimir Kondratyev 	struct iic_msg msgs[] = {
1779b7938dcSVladimir Kondratyev 	    { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
1789b7938dcSVladimir Kondratyev 	    { slave, IIC_M_RD | IIC_M_NOSTOP, 1, &len },
1799b7938dcSVladimir Kondratyev 	};
1809b7938dcSVladimir Kondratyev 	struct iic_msg block_msg[] = {
1819b7938dcSVladimir Kondratyev 	    { slave, IIC_M_RD | IIC_M_NOSTART, 0, NULL },
1829b7938dcSVladimir Kondratyev 	};
1839b7938dcSVladimir Kondratyev 	device_t parent = device_get_parent(dev);
1849b7938dcSVladimir Kondratyev 	int error;
1859b7938dcSVladimir Kondratyev 
1869b7938dcSVladimir Kondratyev 	/* Have to do this because the command is split in two transfers. */
1879b7938dcSVladimir Kondratyev 	error = iicbus_request_bus(parent, dev, IIC_WAIT);
1889b7938dcSVladimir Kondratyev 	if (error == 0)
1899b7938dcSVladimir Kondratyev 		error = iicbus_transfer(dev, msgs, nitems(msgs));
1909b7938dcSVladimir Kondratyev 	if (error == 0) {
1919b7938dcSVladimir Kondratyev 		/*
1929b7938dcSVladimir Kondratyev 		 * If the slave offers an empty reply,
1939b7938dcSVladimir Kondratyev 		 * read one byte to generate the stop or abort.
1949b7938dcSVladimir Kondratyev 		 */
1959b7938dcSVladimir Kondratyev 		if (len == 0)
1969b7938dcSVladimir Kondratyev 			block_msg[0].len = 1;
1979b7938dcSVladimir Kondratyev 		else
1989b7938dcSVladimir Kondratyev 			block_msg[0].len = len;
1999b7938dcSVladimir Kondratyev 		if (len <= sizeof(local_buffer))
2009b7938dcSVladimir Kondratyev 			block_msg[0].buf = local_buffer;
2019b7938dcSVladimir Kondratyev 		else
2029b7938dcSVladimir Kondratyev 			block_msg[0].buf = malloc(len, M_DEVBUF, M_WAITOK);
2039b7938dcSVladimir Kondratyev 		error = iicbus_transfer(dev, block_msg, nitems(block_msg));
2049b7938dcSVladimir Kondratyev 		if (len == 0)
2059b7938dcSVladimir Kondratyev 			error = errno2iic(EBADMSG);
2069b7938dcSVladimir Kondratyev 		if (error == 0) {
2079b7938dcSVladimir Kondratyev 			*count = len;
2089b7938dcSVladimir Kondratyev 			memcpy(buf, block_msg[0].buf, len);
2099b7938dcSVladimir Kondratyev 		}
2109b7938dcSVladimir Kondratyev 		if (block_msg[0].buf != local_buffer)
2119b7938dcSVladimir Kondratyev 			free(block_msg[0].buf, M_DEVBUF);
2129b7938dcSVladimir Kondratyev 	}
2139b7938dcSVladimir Kondratyev 	(void)iicbus_release_bus(parent, dev);
2149b7938dcSVladimir Kondratyev 	return (error);
2159b7938dcSVladimir Kondratyev }
2169b7938dcSVladimir Kondratyev 
2179b7938dcSVladimir Kondratyev static ACPI_STATUS
2189b7938dcSVladimir Kondratyev acpi_iicbus_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
2199b7938dcSVladimir Kondratyev     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
2209b7938dcSVladimir Kondratyev {
2219b7938dcSVladimir Kondratyev 	struct gsb_buffer *gsb;
2229b7938dcSVladimir Kondratyev 	struct acpi_iicbus_softc *sc;
2239b7938dcSVladimir Kondratyev 	device_t dev;
2249b7938dcSVladimir Kondratyev 	ACPI_CONNECTION_INFO *info;
2259b7938dcSVladimir Kondratyev 	ACPI_RESOURCE_I2C_SERIALBUS *sb;
2269b7938dcSVladimir Kondratyev 	ACPI_RESOURCE *res;
2279b7938dcSVladimir Kondratyev 	ACPI_STATUS s;
2289b7938dcSVladimir Kondratyev 	int val;
2299b7938dcSVladimir Kondratyev 
2309b7938dcSVladimir Kondratyev 	gsb = (struct gsb_buffer *)Value;
2319b7938dcSVladimir Kondratyev 	if (gsb == NULL)
2329b7938dcSVladimir Kondratyev 		return (AE_BAD_PARAMETER);
2339b7938dcSVladimir Kondratyev 
2349b7938dcSVladimir Kondratyev 	info = HandlerContext;
2359b7938dcSVladimir Kondratyev 	s = AcpiBufferToResource(info->Connection, info->Length, &res);
2369b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(s))
2379b7938dcSVladimir Kondratyev 		return (s);
2389b7938dcSVladimir Kondratyev 
2399b7938dcSVladimir Kondratyev 	if (!acpi_resource_is_i2c_serialbus(res)) {
2409b7938dcSVladimir Kondratyev 		s = AE_BAD_PARAMETER;
2419b7938dcSVladimir Kondratyev 		goto err;
2429b7938dcSVladimir Kondratyev 	}
2439b7938dcSVladimir Kondratyev 
2449b7938dcSVladimir Kondratyev 	sb = &res->Data.I2cSerialBus;
2459b7938dcSVladimir Kondratyev 
2469b7938dcSVladimir Kondratyev 	/* XXX Ignore 10bit addressing for now */
2479b7938dcSVladimir Kondratyev 	if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
2489b7938dcSVladimir Kondratyev 		s = AE_BAD_PARAMETER;
2499b7938dcSVladimir Kondratyev 		goto err;
2509b7938dcSVladimir Kondratyev 	}
2519b7938dcSVladimir Kondratyev 
2529b7938dcSVladimir Kondratyev #define	AML_FIELD_ATTRIB_MASK		0x0F
2539b7938dcSVladimir Kondratyev #define	AML_FIELD_ATTRIO(attr, io)	(((attr) << 16) | (io))
2549b7938dcSVladimir Kondratyev 
2559b7938dcSVladimir Kondratyev 	Function &= AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_MASK, ACPI_IO_MASK);
2569b7938dcSVladimir Kondratyev 	sc = __containerof(info, struct acpi_iicbus_softc, space_handler_info);
2579b7938dcSVladimir Kondratyev 	dev = sc->super_sc.dev;
2589b7938dcSVladimir Kondratyev 
2599b7938dcSVladimir Kondratyev 	switch (Function) {
2609b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_READ):
2619b7938dcSVladimir Kondratyev 		val = acpi_iicbus_recvb(dev, sb->SlaveAddress, gsb->data);
2629b7938dcSVladimir Kondratyev 		break;
2639b7938dcSVladimir Kondratyev 
2649b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_WRITE):
2659b7938dcSVladimir Kondratyev 		val = acpi_iicbus_sendb(dev, sb->SlaveAddress, gsb->data[0]);
2669b7938dcSVladimir Kondratyev 		break;
2679b7938dcSVladimir Kondratyev 
2689b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_READ):
2699b7938dcSVladimir Kondratyev 		val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
2709b7938dcSVladimir Kondratyev 		    gsb->data, 1);
2719b7938dcSVladimir Kondratyev 		break;
2729b7938dcSVladimir Kondratyev 
2739b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_WRITE):
2749b7938dcSVladimir Kondratyev 		val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
2759b7938dcSVladimir Kondratyev 		    gsb->data, 1);
2769b7938dcSVladimir Kondratyev 		break;
2779b7938dcSVladimir Kondratyev 
2789b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_READ):
2799b7938dcSVladimir Kondratyev 		val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
2809b7938dcSVladimir Kondratyev 		    gsb->data, 2);
2819b7938dcSVladimir Kondratyev 		break;
2829b7938dcSVladimir Kondratyev 
2839b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_WRITE):
2849b7938dcSVladimir Kondratyev 		val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
2859b7938dcSVladimir Kondratyev 		    gsb->data, 2);
2869b7938dcSVladimir Kondratyev 		break;
2879b7938dcSVladimir Kondratyev 
2889b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_READ):
2899b7938dcSVladimir Kondratyev 		val = acpi_iicbus_bread(dev, sb->SlaveAddress, Address,
2909b7938dcSVladimir Kondratyev 		    &gsb->len, gsb->data);
2919b7938dcSVladimir Kondratyev 		break;
2929b7938dcSVladimir Kondratyev 
2939b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_WRITE):
2949b7938dcSVladimir Kondratyev 		val = acpi_iicbus_bwrite(dev, sb->SlaveAddress, Address,
2959b7938dcSVladimir Kondratyev 		    gsb->len, gsb->data);
2969b7938dcSVladimir Kondratyev 		break;
2979b7938dcSVladimir Kondratyev 
2989b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_READ):
2999b7938dcSVladimir Kondratyev 		val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
3009b7938dcSVladimir Kondratyev 		    gsb->data, info->AccessLength);
3019b7938dcSVladimir Kondratyev 		break;
3029b7938dcSVladimir Kondratyev 
3039b7938dcSVladimir Kondratyev 	case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_WRITE):
3049b7938dcSVladimir Kondratyev 		val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
3059b7938dcSVladimir Kondratyev 		    gsb->data, info->AccessLength);
3069b7938dcSVladimir Kondratyev 		break;
3079b7938dcSVladimir Kondratyev 
3089b7938dcSVladimir Kondratyev 	default:
3099b7938dcSVladimir Kondratyev 		device_printf(dev, "protocol(0x%04x) is not supported.\n",
3109b7938dcSVladimir Kondratyev 		    Function);
3119b7938dcSVladimir Kondratyev 		s = AE_BAD_PARAMETER;
3129b7938dcSVladimir Kondratyev 		goto err;
3139b7938dcSVladimir Kondratyev 	}
3149b7938dcSVladimir Kondratyev 
3159b7938dcSVladimir Kondratyev 	gsb->status = val;
3169b7938dcSVladimir Kondratyev 
3179b7938dcSVladimir Kondratyev err:
3189b7938dcSVladimir Kondratyev 	ACPI_FREE(res);
3199b7938dcSVladimir Kondratyev 
3209b7938dcSVladimir Kondratyev 	return (s);
3219b7938dcSVladimir Kondratyev }
3229b7938dcSVladimir Kondratyev 
3239b7938dcSVladimir Kondratyev static int
3249b7938dcSVladimir Kondratyev acpi_iicbus_install_address_space_handler(struct acpi_iicbus_softc *sc)
3259b7938dcSVladimir Kondratyev {
3269b7938dcSVladimir Kondratyev 	ACPI_HANDLE handle;
3279b7938dcSVladimir Kondratyev 	ACPI_STATUS s;
3289b7938dcSVladimir Kondratyev 
3299b7938dcSVladimir Kondratyev 	handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
3309b7938dcSVladimir Kondratyev 	s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
3319b7938dcSVladimir Kondratyev 	    &acpi_iicbus_space_handler, NULL, &sc->space_handler_info);
3329b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(s)) {
3339b7938dcSVladimir Kondratyev 		device_printf(sc->super_sc.dev,
3349b7938dcSVladimir Kondratyev 		    "Failed to install GSBUS Address Space Handler in ACPI\n");
3359b7938dcSVladimir Kondratyev 		return (ENXIO);
3369b7938dcSVladimir Kondratyev 	}
3379b7938dcSVladimir Kondratyev 
3389b7938dcSVladimir Kondratyev 	return (0);
3399b7938dcSVladimir Kondratyev }
3409b7938dcSVladimir Kondratyev 
3419b7938dcSVladimir Kondratyev static int
3429b7938dcSVladimir Kondratyev acpi_iicbus_remove_address_space_handler(struct acpi_iicbus_softc *sc)
3439b7938dcSVladimir Kondratyev {
3449b7938dcSVladimir Kondratyev 	ACPI_HANDLE handle;
3459b7938dcSVladimir Kondratyev 	ACPI_STATUS s;
3469b7938dcSVladimir Kondratyev 
3479b7938dcSVladimir Kondratyev 	handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
3489b7938dcSVladimir Kondratyev 	s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
3499b7938dcSVladimir Kondratyev 	    &acpi_iicbus_space_handler);
3509b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(s)) {
3519b7938dcSVladimir Kondratyev 		device_printf(sc->super_sc.dev,
3529b7938dcSVladimir Kondratyev 		    "Failed to remove GSBUS Address Space Handler from ACPI\n");
3539b7938dcSVladimir Kondratyev 		return (ENXIO);
3549b7938dcSVladimir Kondratyev 	}
3559b7938dcSVladimir Kondratyev 
3569b7938dcSVladimir Kondratyev 	return (0);
3579b7938dcSVladimir Kondratyev }
3589b7938dcSVladimir Kondratyev 
3599b7938dcSVladimir Kondratyev static ACPI_STATUS
3609b7938dcSVladimir Kondratyev acpi_iicbus_get_i2cres_cb(ACPI_RESOURCE *res, void *context)
3619b7938dcSVladimir Kondratyev {
3629b7938dcSVladimir Kondratyev 	ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb = context;
3639b7938dcSVladimir Kondratyev 	ACPI_STATUS status;
3649b7938dcSVladimir Kondratyev 	ACPI_HANDLE handle;
3659b7938dcSVladimir Kondratyev 
3669b7938dcSVladimir Kondratyev 	if (acpi_resource_is_i2c_serialbus(res)) {
3679b7938dcSVladimir Kondratyev 		status = AcpiGetHandle(ACPI_ROOT_OBJECT,
3689b7938dcSVladimir Kondratyev 		    res->Data.I2cSerialBus.ResourceSource.StringPtr, &handle);
3699b7938dcSVladimir Kondratyev 		if (ACPI_FAILURE(status))
3709b7938dcSVladimir Kondratyev 			return (status);
3719b7938dcSVladimir Kondratyev 		memcpy(sb, &res->Data.I2cSerialBus,
3729b7938dcSVladimir Kondratyev 		    sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
3739b7938dcSVladimir Kondratyev 		/*
3749b7938dcSVladimir Kondratyev 		 * replace "pointer to ACPI object name string" field
3759b7938dcSVladimir Kondratyev 		 * with pointer to ACPI object itself.
3769b7938dcSVladimir Kondratyev 		 */
3779b7938dcSVladimir Kondratyev 		sb->ResourceSource_Handle = handle;
3789b7938dcSVladimir Kondratyev 		return (AE_CTRL_TERMINATE);
3799b7938dcSVladimir Kondratyev 	} else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
3809b7938dcSVladimir Kondratyev 		return (AE_NOT_FOUND);
3819b7938dcSVladimir Kondratyev 
3829b7938dcSVladimir Kondratyev 	return (AE_OK);
3839b7938dcSVladimir Kondratyev }
3849b7938dcSVladimir Kondratyev 
3859b7938dcSVladimir Kondratyev static ACPI_STATUS
3869b7938dcSVladimir Kondratyev acpi_iicbus_get_i2cres(ACPI_HANDLE handle, ACPI_RESOURCE_I2C_SERIALBUS *sb)
3879b7938dcSVladimir Kondratyev {
3889b7938dcSVladimir Kondratyev 
3899b7938dcSVladimir Kondratyev 	return (AcpiWalkResources(handle, "_CRS",
3909b7938dcSVladimir Kondratyev 	    acpi_iicbus_get_i2cres_cb, sb));
3919b7938dcSVladimir Kondratyev }
3929b7938dcSVladimir Kondratyev 
3939b7938dcSVladimir Kondratyev static ACPI_STATUS
3949b7938dcSVladimir Kondratyev acpi_iicbus_parse_resources_cb(ACPI_RESOURCE *res, void *context)
3959b7938dcSVladimir Kondratyev {
3969b7938dcSVladimir Kondratyev 	device_t dev = context;
3979b7938dcSVladimir Kondratyev 	struct iicbus_ivar *super_devi = device_get_ivars(dev);
3989b7938dcSVladimir Kondratyev 	struct resource_list *rl = &super_devi->rl;
3999b7938dcSVladimir Kondratyev 	int irq, gpio_pin;
4009b7938dcSVladimir Kondratyev 
4019b7938dcSVladimir Kondratyev 	switch(res->Type) {
4029b7938dcSVladimir Kondratyev 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
4039b7938dcSVladimir Kondratyev 		if (res->Data.ExtendedIrq.InterruptCount > 0) {
4049b7938dcSVladimir Kondratyev 			irq = res->Data.ExtendedIrq.Interrupts[0];
4059b7938dcSVladimir Kondratyev 			if (bootverbose)
4069b7938dcSVladimir Kondratyev 				printf("  IRQ:               %d\n", irq);
4079b7938dcSVladimir Kondratyev 			resource_list_add_next(rl, SYS_RES_IRQ, irq, irq, 1);
4089b7938dcSVladimir Kondratyev 			return (AE_CTRL_TERMINATE);
4099b7938dcSVladimir Kondratyev 		}
4109b7938dcSVladimir Kondratyev 		break;
4119b7938dcSVladimir Kondratyev 	case ACPI_RESOURCE_TYPE_GPIO:
4129b7938dcSVladimir Kondratyev 		if (res->Data.Gpio.ConnectionType ==
4139b7938dcSVladimir Kondratyev 		    ACPI_RESOURCE_GPIO_TYPE_INT) {
4149b7938dcSVladimir Kondratyev 			/* Not supported by FreeBSD yet */
4159b7938dcSVladimir Kondratyev 			gpio_pin = res->Data.Gpio.PinTable[0];
4169b7938dcSVladimir Kondratyev 			if (bootverbose)
4179b7938dcSVladimir Kondratyev 				printf("  GPIO IRQ pin:      %d\n", gpio_pin);
4189b7938dcSVladimir Kondratyev 			return (AE_CTRL_TERMINATE);
4199b7938dcSVladimir Kondratyev 		}
4209b7938dcSVladimir Kondratyev 		break;
4219b7938dcSVladimir Kondratyev 	default:
4229b7938dcSVladimir Kondratyev 		break;
4239b7938dcSVladimir Kondratyev 	}
4249b7938dcSVladimir Kondratyev 
4259b7938dcSVladimir Kondratyev 	return (AE_OK);
4269b7938dcSVladimir Kondratyev }
4279b7938dcSVladimir Kondratyev 
4289b7938dcSVladimir Kondratyev static ACPI_STATUS
4299b7938dcSVladimir Kondratyev acpi_iicbus_parse_resources(ACPI_HANDLE handle, device_t dev)
4309b7938dcSVladimir Kondratyev {
4319b7938dcSVladimir Kondratyev 
4329b7938dcSVladimir Kondratyev 	return (AcpiWalkResources(handle, "_CRS",
4339b7938dcSVladimir Kondratyev 	    acpi_iicbus_parse_resources_cb, dev));
4349b7938dcSVladimir Kondratyev }
4359b7938dcSVladimir Kondratyev 
4369b7938dcSVladimir Kondratyev static void
4379b7938dcSVladimir Kondratyev acpi_iicbus_dump_res(device_t dev, ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb)
4389b7938dcSVladimir Kondratyev {
4399b7938dcSVladimir Kondratyev 	device_printf(dev, "found ACPI child\n");
4409b7938dcSVladimir Kondratyev 	printf("  SlaveAddress:      0x%04hx\n", sb->SlaveAddress);
4419b7938dcSVladimir Kondratyev 	printf("  ConnectionSpeed:   %uHz\n", sb->ConnectionSpeed);
4429b7938dcSVladimir Kondratyev 	printf("  SlaveMode:         %s\n",
4439b7938dcSVladimir Kondratyev 	    sb->SlaveMode == ACPI_CONTROLLER_INITIATED ?
4449b7938dcSVladimir Kondratyev 	    "ControllerInitiated" : "DeviceInitiated");
4459b7938dcSVladimir Kondratyev 	printf("  AddressingMode:    %uBit\n", sb->AccessMode == 0 ? 7 : 10);
4469b7938dcSVladimir Kondratyev 	printf("  ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ?
4479b7938dcSVladimir Kondratyev 	    "Exclusive" : "Shared");
4489b7938dcSVladimir Kondratyev }
4499b7938dcSVladimir Kondratyev 
4509b7938dcSVladimir Kondratyev static device_t
4519b7938dcSVladimir Kondratyev acpi_iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
4529b7938dcSVladimir Kondratyev {
4539b7938dcSVladimir Kondratyev 
4549b7938dcSVladimir Kondratyev 	return (iicbus_add_child_common(
4559b7938dcSVladimir Kondratyev 	    dev, order, name, unit, sizeof(struct acpi_iicbus_ivars)));
4569b7938dcSVladimir Kondratyev }
4579b7938dcSVladimir Kondratyev 
4589b7938dcSVladimir Kondratyev static ACPI_STATUS
4599b7938dcSVladimir Kondratyev acpi_iicbus_enumerate_child(ACPI_HANDLE handle, UINT32 level,
4609b7938dcSVladimir Kondratyev     void *context, void **result)
4619b7938dcSVladimir Kondratyev {
4629b7938dcSVladimir Kondratyev 	device_t iicbus, child, acpi_child, acpi0;
4639b7938dcSVladimir Kondratyev 	struct iicbus_softc *super_sc;
4649b7938dcSVladimir Kondratyev 	ACPI_IICBUS_RESOURCE_I2C_SERIALBUS sb;
4659b7938dcSVladimir Kondratyev 	ACPI_STATUS status;
4669b7938dcSVladimir Kondratyev 	UINT32 sta;
4679b7938dcSVladimir Kondratyev 
4689b7938dcSVladimir Kondratyev 	iicbus = context;
4699b7938dcSVladimir Kondratyev 	super_sc = device_get_softc(iicbus);
4709b7938dcSVladimir Kondratyev 
4719b7938dcSVladimir Kondratyev 	/*
4729b7938dcSVladimir Kondratyev 	 * If no _STA method or if it failed, then assume that
4739b7938dcSVladimir Kondratyev 	 * the device is present.
4749b7938dcSVladimir Kondratyev 	 */
4759b7938dcSVladimir Kondratyev 	if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
4769b7938dcSVladimir Kondratyev 	    !ACPI_DEVICE_PRESENT(sta))
4779b7938dcSVladimir Kondratyev 		return (AE_OK);
4789b7938dcSVladimir Kondratyev 
4799b7938dcSVladimir Kondratyev 	if (!acpi_has_hid(handle))
4809b7938dcSVladimir Kondratyev 		return (AE_OK);
4819b7938dcSVladimir Kondratyev 
4829b7938dcSVladimir Kondratyev 	/*
4839b7938dcSVladimir Kondratyev 	 * Read "I2C Serial Bus Connection Resource Descriptor"
4849b7938dcSVladimir Kondratyev 	 * described in p.19.6.57 of ACPI specification.
4859b7938dcSVladimir Kondratyev 	 */
4869b7938dcSVladimir Kondratyev 	bzero(&sb, sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
4879b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(acpi_iicbus_get_i2cres(handle, &sb)) ||
4889b7938dcSVladimir Kondratyev 	    sb.SlaveAddress == 0)
4899b7938dcSVladimir Kondratyev 		return (AE_OK);
4909b7938dcSVladimir Kondratyev 	if (sb.ResourceSource_Handle !=
4919b7938dcSVladimir Kondratyev 	    acpi_get_handle(device_get_parent(iicbus)))
4929b7938dcSVladimir Kondratyev 		return (AE_OK);
4939b7938dcSVladimir Kondratyev 	if (bootverbose)
4949b7938dcSVladimir Kondratyev 		acpi_iicbus_dump_res(iicbus, &sb);
4959b7938dcSVladimir Kondratyev 
4969b7938dcSVladimir Kondratyev 	/* Find out speed of the slowest slave */
4979b7938dcSVladimir Kondratyev 	if (super_sc->bus_freq == 0 || super_sc->bus_freq > sb.ConnectionSpeed)
4989b7938dcSVladimir Kondratyev 		super_sc->bus_freq = sb.ConnectionSpeed;
4999b7938dcSVladimir Kondratyev 
5009b7938dcSVladimir Kondratyev 	/* Delete existing child of acpi bus */
5019b7938dcSVladimir Kondratyev 	acpi_child = acpi_get_device(handle);
5029b7938dcSVladimir Kondratyev 	if (acpi_child != NULL) {
5039b7938dcSVladimir Kondratyev 		acpi0 = devclass_get_device(devclass_find("acpi"), 0);
5049b7938dcSVladimir Kondratyev 		if (device_get_parent(acpi_child) != acpi0)
5059b7938dcSVladimir Kondratyev 			return (AE_OK);
5069b7938dcSVladimir Kondratyev 
5079b7938dcSVladimir Kondratyev 		if (device_is_attached(acpi_child))
5089b7938dcSVladimir Kondratyev 			return (AE_OK);
5099b7938dcSVladimir Kondratyev 
5109b7938dcSVladimir Kondratyev 		if (device_delete_child(acpi0, acpi_child) != 0)
5119b7938dcSVladimir Kondratyev 			return (AE_OK);
5129b7938dcSVladimir Kondratyev 	}
5139b7938dcSVladimir Kondratyev 
5149b7938dcSVladimir Kondratyev 	child = BUS_ADD_CHILD(iicbus, 0, NULL, -1);
5159b7938dcSVladimir Kondratyev 	if (child == NULL) {
5169b7938dcSVladimir Kondratyev 		device_printf(iicbus, "add child failed\n");
5179b7938dcSVladimir Kondratyev 		return (AE_OK);
5189b7938dcSVladimir Kondratyev 	}
5199b7938dcSVladimir Kondratyev 
5209b7938dcSVladimir Kondratyev 	iicbus_set_addr(child, sb.SlaveAddress);
5219b7938dcSVladimir Kondratyev 	acpi_set_handle(child, handle);
5229b7938dcSVladimir Kondratyev 	(void)acpi_iicbus_parse_resources(handle, child);
5239b7938dcSVladimir Kondratyev 
5249b7938dcSVladimir Kondratyev 	/*
5259b7938dcSVladimir Kondratyev 	 * Update ACPI-CA to use the IIC enumerated device_t for this handle.
5269b7938dcSVladimir Kondratyev 	 */
5279b7938dcSVladimir Kondratyev 	status = AcpiAttachData(handle, acpi_fake_objhandler, child);
5289b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(status))
5299b7938dcSVladimir Kondratyev 		printf("WARNING: Unable to attach object data to %s - %s\n",
5309b7938dcSVladimir Kondratyev 		    acpi_name(handle), AcpiFormatException(status));
5319b7938dcSVladimir Kondratyev 
5329b7938dcSVladimir Kondratyev 	return (AE_OK);
5339b7938dcSVladimir Kondratyev }
5349b7938dcSVladimir Kondratyev 
5359b7938dcSVladimir Kondratyev static ACPI_STATUS
5369b7938dcSVladimir Kondratyev acpi_iicbus_enumerate_children(device_t dev)
5379b7938dcSVladimir Kondratyev {
5389b7938dcSVladimir Kondratyev 
5399b7938dcSVladimir Kondratyev 	return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
5409b7938dcSVladimir Kondratyev 	    ACPI_UINT32_MAX, acpi_iicbus_enumerate_child, NULL, dev, NULL));
5419b7938dcSVladimir Kondratyev }
5429b7938dcSVladimir Kondratyev 
5439b7938dcSVladimir Kondratyev static void
5449b7938dcSVladimir Kondratyev acpi_iicbus_set_power_children(device_t dev, int state, bool all_children)
5459b7938dcSVladimir Kondratyev {
5469b7938dcSVladimir Kondratyev 	device_t *devlist;
5479b7938dcSVladimir Kondratyev 	int i, numdevs;
5489b7938dcSVladimir Kondratyev 
5499b7938dcSVladimir Kondratyev 	if (device_get_children(dev, &devlist, &numdevs) != 0)
5509b7938dcSVladimir Kondratyev 		return;
5519b7938dcSVladimir Kondratyev 
5529b7938dcSVladimir Kondratyev 	for (i = 0; i < numdevs; i++)
5539b7938dcSVladimir Kondratyev 		if (all_children || device_is_attached(devlist[i]) != 0)
5549b7938dcSVladimir Kondratyev 			acpi_set_powerstate(devlist[i], state);
5559b7938dcSVladimir Kondratyev 
5569b7938dcSVladimir Kondratyev 	free(devlist, M_TEMP);
5579b7938dcSVladimir Kondratyev }
5589b7938dcSVladimir Kondratyev 
5599b7938dcSVladimir Kondratyev static int
5609b7938dcSVladimir Kondratyev acpi_iicbus_probe(device_t dev)
5619b7938dcSVladimir Kondratyev {
5629b7938dcSVladimir Kondratyev 	ACPI_HANDLE handle;
5639b7938dcSVladimir Kondratyev 	device_t controller;
5649b7938dcSVladimir Kondratyev 
5659b7938dcSVladimir Kondratyev 	if (acpi_disabled("iicbus"))
5669b7938dcSVladimir Kondratyev 		return (ENXIO);
5679b7938dcSVladimir Kondratyev 
5689b7938dcSVladimir Kondratyev 	controller = device_get_parent(dev);
5699b7938dcSVladimir Kondratyev 	if (controller == NULL)
5709b7938dcSVladimir Kondratyev 		return (ENXIO);
5719b7938dcSVladimir Kondratyev 
5729b7938dcSVladimir Kondratyev 	handle = acpi_get_handle(controller);
5739b7938dcSVladimir Kondratyev 	if (handle == NULL)
5749b7938dcSVladimir Kondratyev 		return (ENXIO);
5759b7938dcSVladimir Kondratyev 
576*36b9e628SAndriy Gapon 	device_set_desc(dev, "Philips I2C bus (ACPI-hinted)");
5779b7938dcSVladimir Kondratyev 	return (BUS_PROBE_DEFAULT);
5789b7938dcSVladimir Kondratyev }
5799b7938dcSVladimir Kondratyev 
5809b7938dcSVladimir Kondratyev static int
5819b7938dcSVladimir Kondratyev acpi_iicbus_attach(device_t dev)
5829b7938dcSVladimir Kondratyev {
5839b7938dcSVladimir Kondratyev 	struct acpi_iicbus_softc *sc = device_get_softc(dev);
5849b7938dcSVladimir Kondratyev 	int error;
5859b7938dcSVladimir Kondratyev 
5869b7938dcSVladimir Kondratyev 	if (ACPI_FAILURE(acpi_iicbus_enumerate_children(dev)))
5879b7938dcSVladimir Kondratyev 		device_printf(dev, "children enumeration failed\n");
5889b7938dcSVladimir Kondratyev 
5899b7938dcSVladimir Kondratyev 	acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, true);
5909b7938dcSVladimir Kondratyev 	error = iicbus_attach_common(dev, sc->super_sc.bus_freq);
5919b7938dcSVladimir Kondratyev 	if (error == 0 && install_space_handler != 0 &&
5929b7938dcSVladimir Kondratyev 	    acpi_iicbus_install_address_space_handler(sc) == 0)
5939b7938dcSVladimir Kondratyev 		sc->space_handler_installed = true;
5949b7938dcSVladimir Kondratyev 
5959b7938dcSVladimir Kondratyev 	return (error);
5969b7938dcSVladimir Kondratyev }
5979b7938dcSVladimir Kondratyev 
5989b7938dcSVladimir Kondratyev static int
5999b7938dcSVladimir Kondratyev acpi_iicbus_detach(device_t dev)
6009b7938dcSVladimir Kondratyev {
6019b7938dcSVladimir Kondratyev 	struct acpi_iicbus_softc *sc = device_get_softc(dev);
6029b7938dcSVladimir Kondratyev 
6039b7938dcSVladimir Kondratyev 	if (sc->space_handler_installed)
6049b7938dcSVladimir Kondratyev 		acpi_iicbus_remove_address_space_handler(sc);
6059b7938dcSVladimir Kondratyev 	acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
6069b7938dcSVladimir Kondratyev 
6079b7938dcSVladimir Kondratyev 	return (iicbus_detach(dev));
6089b7938dcSVladimir Kondratyev }
6099b7938dcSVladimir Kondratyev 
6109b7938dcSVladimir Kondratyev static int
6119b7938dcSVladimir Kondratyev acpi_iicbus_suspend(device_t dev)
6129b7938dcSVladimir Kondratyev {
6139b7938dcSVladimir Kondratyev 	int error;
6149b7938dcSVladimir Kondratyev 
6159b7938dcSVladimir Kondratyev 	error = bus_generic_suspend(dev);
6169b7938dcSVladimir Kondratyev 	if (error == 0)
6179b7938dcSVladimir Kondratyev 		acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
6189b7938dcSVladimir Kondratyev 
6199b7938dcSVladimir Kondratyev 	return (error);
6209b7938dcSVladimir Kondratyev }
6219b7938dcSVladimir Kondratyev 
6229b7938dcSVladimir Kondratyev static int
6239b7938dcSVladimir Kondratyev acpi_iicbus_resume(device_t dev)
6249b7938dcSVladimir Kondratyev {
6259b7938dcSVladimir Kondratyev 
6269b7938dcSVladimir Kondratyev 	acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, false);
6279b7938dcSVladimir Kondratyev 
6289b7938dcSVladimir Kondratyev 	return (bus_generic_resume(dev));
6299b7938dcSVladimir Kondratyev }
6309b7938dcSVladimir Kondratyev 
6319b7938dcSVladimir Kondratyev /*
6329b7938dcSVladimir Kondratyev  * If this device is an ACPI child but no one claimed it, attempt
6339b7938dcSVladimir Kondratyev  * to power it off.  We'll power it back up when a driver is added.
6349b7938dcSVladimir Kondratyev  */
6359b7938dcSVladimir Kondratyev static void
6369b7938dcSVladimir Kondratyev acpi_iicbus_probe_nomatch(device_t bus, device_t child)
6379b7938dcSVladimir Kondratyev {
6389b7938dcSVladimir Kondratyev 
6399b7938dcSVladimir Kondratyev 	iicbus_probe_nomatch(bus, child);
6409b7938dcSVladimir Kondratyev 	acpi_set_powerstate(child, ACPI_STATE_D3);
6419b7938dcSVladimir Kondratyev }
6429b7938dcSVladimir Kondratyev 
6439b7938dcSVladimir Kondratyev /*
6449b7938dcSVladimir Kondratyev  * If a new driver has a chance to probe a child, first power it up.
6459b7938dcSVladimir Kondratyev  */
6469b7938dcSVladimir Kondratyev static void
6479b7938dcSVladimir Kondratyev acpi_iicbus_driver_added(device_t dev, driver_t *driver)
6489b7938dcSVladimir Kondratyev {
6499b7938dcSVladimir Kondratyev 	device_t child, *devlist;
6509b7938dcSVladimir Kondratyev 	int i, numdevs;
6519b7938dcSVladimir Kondratyev 
6529b7938dcSVladimir Kondratyev 	DEVICE_IDENTIFY(driver, dev);
6539b7938dcSVladimir Kondratyev 	if (device_get_children(dev, &devlist, &numdevs) != 0)
6549b7938dcSVladimir Kondratyev 		return;
6559b7938dcSVladimir Kondratyev 
6569b7938dcSVladimir Kondratyev 	for (i = 0; i < numdevs; i++) {
6579b7938dcSVladimir Kondratyev 		child = devlist[i];
6589b7938dcSVladimir Kondratyev 		if (device_get_state(child) == DS_NOTPRESENT) {
6599b7938dcSVladimir Kondratyev 			acpi_set_powerstate(child, ACPI_STATE_D0);
6609b7938dcSVladimir Kondratyev 			if (device_probe_and_attach(child) != 0)
6619b7938dcSVladimir Kondratyev 				acpi_set_powerstate(child, ACPI_STATE_D3);
6629b7938dcSVladimir Kondratyev 		}
6639b7938dcSVladimir Kondratyev 	}
6649b7938dcSVladimir Kondratyev 	free(devlist, M_TEMP);
6659b7938dcSVladimir Kondratyev }
6669b7938dcSVladimir Kondratyev 
6679b7938dcSVladimir Kondratyev static void
6689b7938dcSVladimir Kondratyev acpi_iicbus_child_deleted(device_t bus, device_t child)
6699b7938dcSVladimir Kondratyev {
6709b7938dcSVladimir Kondratyev 	struct acpi_iicbus_ivars *devi = device_get_ivars(child);
6719b7938dcSVladimir Kondratyev 
6729b7938dcSVladimir Kondratyev 	if (acpi_get_device(devi->handle) == child)
6739b7938dcSVladimir Kondratyev 		AcpiDetachData(devi->handle, acpi_fake_objhandler);
6749b7938dcSVladimir Kondratyev }
6759b7938dcSVladimir Kondratyev 
6769b7938dcSVladimir Kondratyev static int
6779b7938dcSVladimir Kondratyev acpi_iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res)
6789b7938dcSVladimir Kondratyev {
6799b7938dcSVladimir Kondratyev 	struct acpi_iicbus_ivars *devi = device_get_ivars(child);
6809b7938dcSVladimir Kondratyev 
6819b7938dcSVladimir Kondratyev 	switch (which) {
6829b7938dcSVladimir Kondratyev 	case ACPI_IVAR_HANDLE:
6839b7938dcSVladimir Kondratyev 		*res = (uintptr_t)devi->handle;
6849b7938dcSVladimir Kondratyev 		break;
6859b7938dcSVladimir Kondratyev 	default:
6869b7938dcSVladimir Kondratyev 		return (iicbus_read_ivar(bus, child, which, res));
6879b7938dcSVladimir Kondratyev 	}
6889b7938dcSVladimir Kondratyev 
6899b7938dcSVladimir Kondratyev 	return (0);
6909b7938dcSVladimir Kondratyev }
6919b7938dcSVladimir Kondratyev 
6929b7938dcSVladimir Kondratyev static int
6939b7938dcSVladimir Kondratyev acpi_iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t val)
6949b7938dcSVladimir Kondratyev {
6959b7938dcSVladimir Kondratyev 	struct acpi_iicbus_ivars *devi = device_get_ivars(child);
6969b7938dcSVladimir Kondratyev 
6979b7938dcSVladimir Kondratyev 	switch (which) {
6989b7938dcSVladimir Kondratyev 	case ACPI_IVAR_HANDLE:
6999b7938dcSVladimir Kondratyev 		if (devi->handle != NULL)
7009b7938dcSVladimir Kondratyev 			return (EINVAL);
7019b7938dcSVladimir Kondratyev 		devi->handle = (ACPI_HANDLE)val;
7029b7938dcSVladimir Kondratyev 		break;
7039b7938dcSVladimir Kondratyev 	default:
7049b7938dcSVladimir Kondratyev 		return (iicbus_write_ivar(bus, child, which, val));
7059b7938dcSVladimir Kondratyev 	}
7069b7938dcSVladimir Kondratyev 
7079b7938dcSVladimir Kondratyev 	return (0);
7089b7938dcSVladimir Kondratyev }
7099b7938dcSVladimir Kondratyev 
7109b7938dcSVladimir Kondratyev /* Location hint for devctl(8). Concatenate IIC and ACPI hints. */
7119b7938dcSVladimir Kondratyev static int
7129b7938dcSVladimir Kondratyev acpi_iicbus_child_location_str(device_t bus, device_t child,
7139b7938dcSVladimir Kondratyev     char *buf, size_t buflen)
7149b7938dcSVladimir Kondratyev {
7159b7938dcSVladimir Kondratyev 	struct acpi_iicbus_ivars *devi = device_get_ivars(child);
7169b7938dcSVladimir Kondratyev 	int error;
7179b7938dcSVladimir Kondratyev 
7189b7938dcSVladimir Kondratyev 	/* read IIC location hint string into the buffer. */
7199b7938dcSVladimir Kondratyev 	error = iicbus_child_location_str(bus, child, buf, buflen);
7209b7938dcSVladimir Kondratyev 	if (error != 0)
7219b7938dcSVladimir Kondratyev 		return (error);
7229b7938dcSVladimir Kondratyev 
7239b7938dcSVladimir Kondratyev 	/* Place ACPI string right after IIC one's terminating NUL. */
7249b7938dcSVladimir Kondratyev 	if (devi->handle != NULL &&
7259b7938dcSVladimir Kondratyev 	    ((buf[0] != '\0' && strlcat(buf, " ", buflen) >= buflen) ||
7269b7938dcSVladimir Kondratyev 	     strlcat(buf, "handle=", buflen) >= buflen ||
7279b7938dcSVladimir Kondratyev 	     strlcat(buf, acpi_name(devi->handle), buflen) >= buflen))
7289b7938dcSVladimir Kondratyev 		return (EOVERFLOW);
7299b7938dcSVladimir Kondratyev 
7309b7938dcSVladimir Kondratyev 	return (0);
7319b7938dcSVladimir Kondratyev }
7329b7938dcSVladimir Kondratyev 
7339b7938dcSVladimir Kondratyev /* PnP information for devctl(8). Concatenate IIC and ACPI info strings. */
7349b7938dcSVladimir Kondratyev static int
7359b7938dcSVladimir Kondratyev acpi_iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
7369b7938dcSVladimir Kondratyev     size_t buflen)
7379b7938dcSVladimir Kondratyev {
7389b7938dcSVladimir Kondratyev 	struct acpi_iicbus_ivars *devi = device_get_ivars(child);
7399b7938dcSVladimir Kondratyev 	size_t acpi_offset;
7409b7938dcSVladimir Kondratyev 	int error;
7419b7938dcSVladimir Kondratyev 
7429b7938dcSVladimir Kondratyev 	/* read IIC PnP string into the buffer. */
7439b7938dcSVladimir Kondratyev 	error = iicbus_child_pnpinfo_str(bus, child, buf, buflen);
7449b7938dcSVladimir Kondratyev 	if (error != 0)
7459b7938dcSVladimir Kondratyev 		return (error);
7469b7938dcSVladimir Kondratyev 
7479b7938dcSVladimir Kondratyev 	if (devi->handle == NULL)
7489b7938dcSVladimir Kondratyev 		return (0);
7499b7938dcSVladimir Kondratyev 
7509b7938dcSVladimir Kondratyev 	/* Place ACPI string right after IIC one's terminating NUL. */
7519b7938dcSVladimir Kondratyev 	acpi_offset = strlen(buf);
7529b7938dcSVladimir Kondratyev 	if (acpi_offset != 0)
7539b7938dcSVladimir Kondratyev 		acpi_offset++;
7549b7938dcSVladimir Kondratyev 	error = acpi_pnpinfo_str(devi->handle, buf + acpi_offset,
7559b7938dcSVladimir Kondratyev 	    buflen - acpi_offset);
7569b7938dcSVladimir Kondratyev 
7579b7938dcSVladimir Kondratyev 	/* Coalesce both strings if they are not empty. */
7589b7938dcSVladimir Kondratyev 	if (acpi_offset > 0 && acpi_offset < buflen && buf[acpi_offset] != 0)
7599b7938dcSVladimir Kondratyev 		buf[acpi_offset - 1] = ' ';
7609b7938dcSVladimir Kondratyev 
7619b7938dcSVladimir Kondratyev 	return (error);
7629b7938dcSVladimir Kondratyev }
7639b7938dcSVladimir Kondratyev 
7649b7938dcSVladimir Kondratyev static device_method_t acpi_iicbus_methods[] = {
7659b7938dcSVladimir Kondratyev 	/* Device interface */
7669b7938dcSVladimir Kondratyev 	DEVMETHOD(device_probe,		acpi_iicbus_probe),
7679b7938dcSVladimir Kondratyev 	DEVMETHOD(device_attach,	acpi_iicbus_attach),
7689b7938dcSVladimir Kondratyev 	DEVMETHOD(device_detach,	acpi_iicbus_detach),
7699b7938dcSVladimir Kondratyev 	DEVMETHOD(device_suspend,	acpi_iicbus_suspend),
7709b7938dcSVladimir Kondratyev 	DEVMETHOD(device_resume,	acpi_iicbus_resume),
7719b7938dcSVladimir Kondratyev 
7729b7938dcSVladimir Kondratyev 	/* Bus interface */
7739b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_add_child,	acpi_iicbus_add_child),
7749b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_probe_nomatch,	acpi_iicbus_probe_nomatch),
7759b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_driver_added,	acpi_iicbus_driver_added),
7769b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_child_deleted,	acpi_iicbus_child_deleted),
7779b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_read_ivar,	acpi_iicbus_read_ivar),
7789b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_write_ivar,	acpi_iicbus_write_ivar),
7799b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_child_location_str,acpi_iicbus_child_location_str),
7809b7938dcSVladimir Kondratyev 	DEVMETHOD(bus_child_pnpinfo_str,acpi_iicbus_child_pnpinfo_str),
7819b7938dcSVladimir Kondratyev 
7829b7938dcSVladimir Kondratyev 	DEVMETHOD_END,
7839b7938dcSVladimir Kondratyev };
7849b7938dcSVladimir Kondratyev 
7859b7938dcSVladimir Kondratyev DEFINE_CLASS_1(iicbus, acpi_iicbus_driver, acpi_iicbus_methods,
7869b7938dcSVladimir Kondratyev     sizeof(struct acpi_iicbus_softc), iicbus_driver);
7879b7938dcSVladimir Kondratyev MODULE_VERSION(acpi_iicbus, 1);
7889b7938dcSVladimir Kondratyev MODULE_DEPEND(acpi_iicbus, acpi, 1, 1, 1);
789