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