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 789b7938dcSVladimir Kondratyev static int install_space_handler = 0; 799b7938dcSVladimir Kondratyev TUNABLE_INT("hw.iicbus.enable_acpi_space_handler", &install_space_handler); 809b7938dcSVladimir Kondratyev 819b7938dcSVladimir Kondratyev static inline bool 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 515*a05a6804SWarner 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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