Page MenuHomeFreeBSD

D22901.diff
No OneTemporary

D22901.diff

Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -1773,6 +1773,7 @@
dev/ida/ida.c optional ida
dev/ida/ida_disk.c optional ida
dev/ida/ida_pci.c optional ida pci
+dev/iicbus/acpi_iicbus.c optional acpi iicbus
dev/iicbus/ad7418.c optional ad7418
dev/iicbus/ads111x.c optional ads111x
dev/iicbus/ds1307.c optional ds1307
Index: head/sys/dev/ichiic/ig4_iic.c
===================================================================
--- head/sys/dev/ichiic/ig4_iic.c
+++ head/sys/dev/ichiic/ig4_iic.c
@@ -1201,5 +1201,9 @@
devclass_t ig4iic_devclass;
DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL);
+#ifdef DEV_ACPI
+DRIVER_MODULE(acpi_iicbus, ig4iic, acpi_iicbus_driver, iicbus_devclass, NULL,
+ NULL);
+#endif
MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
MODULE_VERSION(ig4iic, 1);
Index: head/sys/dev/iicbus/acpi_iicbus.c
===================================================================
--- head/sys/dev/iicbus/acpi_iicbus.c
+++ head/sys/dev/iicbus/acpi_iicbus.c
@@ -0,0 +1,789 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/resource.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+#include <contrib/dev/acpica/include/amlcode.h>
+#include <dev/acpica/acpivar.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#define ACPI_IICBUS_LOCAL_BUFSIZE 32 /* Fits max SMBUS block size */
+
+/*
+ * Make a copy of ACPI_RESOURCE_I2C_SERIALBUS type and replace "pointer to ACPI
+ * object name string" field with pointer to ACPI object itself.
+ * This saves us extra strdup()/free() pair on acpi_iicbus_get_i2cres call.
+ */
+typedef ACPI_RESOURCE_I2C_SERIALBUS ACPI_IICBUS_RESOURCE_I2C_SERIALBUS;
+#define ResourceSource_Handle ResourceSource.StringPtr
+
+/* Hooks for the ACPI CA debugging infrastructure. */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("IIC")
+
+struct gsb_buffer {
+ UINT8 status;
+ UINT8 len;
+ UINT8 data[];
+} __packed;
+
+struct acpi_iicbus_softc {
+ struct iicbus_softc super_sc;
+ ACPI_CONNECTION_INFO space_handler_info;
+ bool space_handler_installed;
+};
+
+struct acpi_iicbus_ivars {
+ struct iicbus_ivar super_ivar;
+ ACPI_HANDLE handle;
+};
+
+static int install_space_handler = 0;
+TUNABLE_INT("hw.iicbus.enable_acpi_space_handler", &install_space_handler);
+
+static inline bool
+acpi_resource_is_i2c_serialbus(ACPI_RESOURCE *res)
+{
+
+ return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
+ res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C);
+}
+
+/*
+ * IICBUS Address space handler
+ */
+static int
+acpi_iicbus_sendb(device_t dev, u_char slave, char byte)
+{
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_WR, 1, &byte },
+ };
+
+ return (iicbus_transfer(dev, msgs, nitems(msgs)));
+}
+
+static int
+acpi_iicbus_recvb(device_t dev, u_char slave, char *byte)
+{
+ char buf;
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_RD, 1, &buf },
+ };
+ int error;
+
+ error = iicbus_transfer(dev, msgs, nitems(msgs));
+ if (error == 0)
+ *byte = buf;
+
+ return (error);
+}
+
+static int
+acpi_iicbus_write(device_t dev, u_char slave, char cmd, void *buf,
+ uint16_t buflen)
+{
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
+ { slave, IIC_M_WR | IIC_M_NOSTART, buflen, buf },
+ };
+
+ return (iicbus_transfer(dev, msgs, nitems(msgs)));
+}
+
+static int
+acpi_iicbus_read(device_t dev, u_char slave, char cmd, void *buf,
+ uint16_t buflen)
+{
+ uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
+ { slave, IIC_M_RD, buflen, NULL },
+ };
+ int error;
+
+ if (buflen <= sizeof(local_buffer))
+ msgs[1].buf = local_buffer;
+ else
+ msgs[1].buf = malloc(buflen, M_DEVBUF, M_WAITOK);
+ error = iicbus_transfer(dev, msgs, nitems(msgs));
+ if (error == 0)
+ memcpy(buf, msgs[1].buf, buflen);
+ if (msgs[1].buf != local_buffer)
+ free(msgs[1].buf, M_DEVBUF);
+
+ return (error);
+}
+
+static int
+acpi_iicbus_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
+{
+ uint8_t bytes[2] = { cmd, count };
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
+ { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
+ };
+
+ if (count == 0)
+ return (errno2iic(EINVAL));
+
+ return (iicbus_transfer(dev, msgs, nitems(msgs)));
+}
+
+static int
+acpi_iicbus_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
+{
+ uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
+ u_char len;
+ struct iic_msg msgs[] = {
+ { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
+ { slave, IIC_M_RD | IIC_M_NOSTOP, 1, &len },
+ };
+ struct iic_msg block_msg[] = {
+ { slave, IIC_M_RD | IIC_M_NOSTART, 0, NULL },
+ };
+ device_t parent = device_get_parent(dev);
+ int error;
+
+ /* Have to do this because the command is split in two transfers. */
+ error = iicbus_request_bus(parent, dev, IIC_WAIT);
+ if (error == 0)
+ error = iicbus_transfer(dev, msgs, nitems(msgs));
+ if (error == 0) {
+ /*
+ * If the slave offers an empty reply,
+ * read one byte to generate the stop or abort.
+ */
+ if (len == 0)
+ block_msg[0].len = 1;
+ else
+ block_msg[0].len = len;
+ if (len <= sizeof(local_buffer))
+ block_msg[0].buf = local_buffer;
+ else
+ block_msg[0].buf = malloc(len, M_DEVBUF, M_WAITOK);
+ error = iicbus_transfer(dev, block_msg, nitems(block_msg));
+ if (len == 0)
+ error = errno2iic(EBADMSG);
+ if (error == 0) {
+ *count = len;
+ memcpy(buf, block_msg[0].buf, len);
+ }
+ if (block_msg[0].buf != local_buffer)
+ free(block_msg[0].buf, M_DEVBUF);
+ }
+ (void)iicbus_release_bus(parent, dev);
+ return (error);
+}
+
+static ACPI_STATUS
+acpi_iicbus_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
+{
+ struct gsb_buffer *gsb;
+ struct acpi_iicbus_softc *sc;
+ device_t dev;
+ ACPI_CONNECTION_INFO *info;
+ ACPI_RESOURCE_I2C_SERIALBUS *sb;
+ ACPI_RESOURCE *res;
+ ACPI_STATUS s;
+ int val;
+
+ gsb = (struct gsb_buffer *)Value;
+ if (gsb == NULL)
+ return (AE_BAD_PARAMETER);
+
+ info = HandlerContext;
+ s = AcpiBufferToResource(info->Connection, info->Length, &res);
+ if (ACPI_FAILURE(s))
+ return (s);
+
+ if (!acpi_resource_is_i2c_serialbus(res)) {
+ s = AE_BAD_PARAMETER;
+ goto err;
+ }
+
+ sb = &res->Data.I2cSerialBus;
+
+ /* XXX Ignore 10bit addressing for now */
+ if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
+ s = AE_BAD_PARAMETER;
+ goto err;
+ }
+
+#define AML_FIELD_ATTRIB_MASK 0x0F
+#define AML_FIELD_ATTRIO(attr, io) (((attr) << 16) | (io))
+
+ Function &= AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_MASK, ACPI_IO_MASK);
+ sc = __containerof(info, struct acpi_iicbus_softc, space_handler_info);
+ dev = sc->super_sc.dev;
+
+ switch (Function) {
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_READ):
+ val = acpi_iicbus_recvb(dev, sb->SlaveAddress, gsb->data);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_WRITE):
+ val = acpi_iicbus_sendb(dev, sb->SlaveAddress, gsb->data[0]);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_READ):
+ val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
+ gsb->data, 1);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_WRITE):
+ val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
+ gsb->data, 1);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_READ):
+ val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
+ gsb->data, 2);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_WRITE):
+ val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
+ gsb->data, 2);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_READ):
+ val = acpi_iicbus_bread(dev, sb->SlaveAddress, Address,
+ &gsb->len, gsb->data);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_WRITE):
+ val = acpi_iicbus_bwrite(dev, sb->SlaveAddress, Address,
+ gsb->len, gsb->data);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_READ):
+ val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
+ gsb->data, info->AccessLength);
+ break;
+
+ case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_WRITE):
+ val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
+ gsb->data, info->AccessLength);
+ break;
+
+ default:
+ device_printf(dev, "protocol(0x%04x) is not supported.\n",
+ Function);
+ s = AE_BAD_PARAMETER;
+ goto err;
+ }
+
+ gsb->status = val;
+
+err:
+ ACPI_FREE(res);
+
+ return (s);
+}
+
+static int
+acpi_iicbus_install_address_space_handler(struct acpi_iicbus_softc *sc)
+{
+ ACPI_HANDLE handle;
+ ACPI_STATUS s;
+
+ handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
+ s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
+ &acpi_iicbus_space_handler, NULL, &sc->space_handler_info);
+ if (ACPI_FAILURE(s)) {
+ device_printf(sc->super_sc.dev,
+ "Failed to install GSBUS Address Space Handler in ACPI\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+acpi_iicbus_remove_address_space_handler(struct acpi_iicbus_softc *sc)
+{
+ ACPI_HANDLE handle;
+ ACPI_STATUS s;
+
+ handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
+ s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
+ &acpi_iicbus_space_handler);
+ if (ACPI_FAILURE(s)) {
+ device_printf(sc->super_sc.dev,
+ "Failed to remove GSBUS Address Space Handler from ACPI\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static ACPI_STATUS
+acpi_iicbus_get_i2cres_cb(ACPI_RESOURCE *res, void *context)
+{
+ ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb = context;
+ ACPI_STATUS status;
+ ACPI_HANDLE handle;
+
+ if (acpi_resource_is_i2c_serialbus(res)) {
+ status = AcpiGetHandle(ACPI_ROOT_OBJECT,
+ res->Data.I2cSerialBus.ResourceSource.StringPtr, &handle);
+ if (ACPI_FAILURE(status))
+ return (status);
+ memcpy(sb, &res->Data.I2cSerialBus,
+ sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
+ /*
+ * replace "pointer to ACPI object name string" field
+ * with pointer to ACPI object itself.
+ */
+ sb->ResourceSource_Handle = handle;
+ return (AE_CTRL_TERMINATE);
+ } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
+ return (AE_NOT_FOUND);
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_iicbus_get_i2cres(ACPI_HANDLE handle, ACPI_RESOURCE_I2C_SERIALBUS *sb)
+{
+
+ return (AcpiWalkResources(handle, "_CRS",
+ acpi_iicbus_get_i2cres_cb, sb));
+}
+
+static ACPI_STATUS
+acpi_iicbus_parse_resources_cb(ACPI_RESOURCE *res, void *context)
+{
+ device_t dev = context;
+ struct iicbus_ivar *super_devi = device_get_ivars(dev);
+ struct resource_list *rl = &super_devi->rl;
+ int irq, gpio_pin;
+
+ switch(res->Type) {
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ if (res->Data.ExtendedIrq.InterruptCount > 0) {
+ irq = res->Data.ExtendedIrq.Interrupts[0];
+ if (bootverbose)
+ printf(" IRQ: %d\n", irq);
+ resource_list_add_next(rl, SYS_RES_IRQ, irq, irq, 1);
+ return (AE_CTRL_TERMINATE);
+ }
+ break;
+ case ACPI_RESOURCE_TYPE_GPIO:
+ if (res->Data.Gpio.ConnectionType ==
+ ACPI_RESOURCE_GPIO_TYPE_INT) {
+ /* Not supported by FreeBSD yet */
+ gpio_pin = res->Data.Gpio.PinTable[0];
+ if (bootverbose)
+ printf(" GPIO IRQ pin: %d\n", gpio_pin);
+ return (AE_CTRL_TERMINATE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_iicbus_parse_resources(ACPI_HANDLE handle, device_t dev)
+{
+
+ return (AcpiWalkResources(handle, "_CRS",
+ acpi_iicbus_parse_resources_cb, dev));
+}
+
+static void
+acpi_iicbus_dump_res(device_t dev, ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb)
+{
+ device_printf(dev, "found ACPI child\n");
+ printf(" SlaveAddress: 0x%04hx\n", sb->SlaveAddress);
+ printf(" ConnectionSpeed: %uHz\n", sb->ConnectionSpeed);
+ printf(" SlaveMode: %s\n",
+ sb->SlaveMode == ACPI_CONTROLLER_INITIATED ?
+ "ControllerInitiated" : "DeviceInitiated");
+ printf(" AddressingMode: %uBit\n", sb->AccessMode == 0 ? 7 : 10);
+ printf(" ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ?
+ "Exclusive" : "Shared");
+}
+
+static device_t
+acpi_iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+
+ return (iicbus_add_child_common(
+ dev, order, name, unit, sizeof(struct acpi_iicbus_ivars)));
+}
+
+static ACPI_STATUS
+acpi_iicbus_enumerate_child(ACPI_HANDLE handle, UINT32 level,
+ void *context, void **result)
+{
+ device_t iicbus, child, acpi_child, acpi0;
+ struct iicbus_softc *super_sc;
+ ACPI_IICBUS_RESOURCE_I2C_SERIALBUS sb;
+ ACPI_STATUS status;
+ UINT32 sta;
+
+ iicbus = context;
+ super_sc = device_get_softc(iicbus);
+
+ /*
+ * If no _STA method or if it failed, then assume that
+ * the device is present.
+ */
+ if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
+ !ACPI_DEVICE_PRESENT(sta))
+ return (AE_OK);
+
+ if (!acpi_has_hid(handle))
+ return (AE_OK);
+
+ /*
+ * Read "I2C Serial Bus Connection Resource Descriptor"
+ * described in p.19.6.57 of ACPI specification.
+ */
+ bzero(&sb, sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
+ if (ACPI_FAILURE(acpi_iicbus_get_i2cres(handle, &sb)) ||
+ sb.SlaveAddress == 0)
+ return (AE_OK);
+ if (sb.ResourceSource_Handle !=
+ acpi_get_handle(device_get_parent(iicbus)))
+ return (AE_OK);
+ if (bootverbose)
+ acpi_iicbus_dump_res(iicbus, &sb);
+
+ /* Find out speed of the slowest slave */
+ if (super_sc->bus_freq == 0 || super_sc->bus_freq > sb.ConnectionSpeed)
+ super_sc->bus_freq = sb.ConnectionSpeed;
+
+ /* Delete existing child of acpi bus */
+ acpi_child = acpi_get_device(handle);
+ if (acpi_child != NULL) {
+ acpi0 = devclass_get_device(devclass_find("acpi"), 0);
+ if (device_get_parent(acpi_child) != acpi0)
+ return (AE_OK);
+
+ if (device_is_attached(acpi_child))
+ return (AE_OK);
+
+ if (device_delete_child(acpi0, acpi_child) != 0)
+ return (AE_OK);
+ }
+
+ child = BUS_ADD_CHILD(iicbus, 0, NULL, -1);
+ if (child == NULL) {
+ device_printf(iicbus, "add child failed\n");
+ return (AE_OK);
+ }
+
+ iicbus_set_addr(child, sb.SlaveAddress);
+ acpi_set_handle(child, handle);
+ (void)acpi_iicbus_parse_resources(handle, child);
+
+ /*
+ * Update ACPI-CA to use the IIC enumerated device_t for this handle.
+ */
+ status = AcpiAttachData(handle, acpi_fake_objhandler, child);
+ if (ACPI_FAILURE(status))
+ printf("WARNING: Unable to attach object data to %s - %s\n",
+ acpi_name(handle), AcpiFormatException(status));
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_iicbus_enumerate_children(device_t dev)
+{
+
+ return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_iicbus_enumerate_child, NULL, dev, NULL));
+}
+
+static void
+acpi_iicbus_set_power_children(device_t dev, int state, bool all_children)
+{
+ device_t *devlist;
+ int i, numdevs;
+
+ if (device_get_children(dev, &devlist, &numdevs) != 0)
+ return;
+
+ for (i = 0; i < numdevs; i++)
+ if (all_children || device_is_attached(devlist[i]) != 0)
+ acpi_set_powerstate(devlist[i], state);
+
+ free(devlist, M_TEMP);
+}
+
+static int
+acpi_iicbus_probe(device_t dev)
+{
+ ACPI_HANDLE handle;
+ device_t controller;
+
+ if (acpi_disabled("iicbus"))
+ return (ENXIO);
+
+ controller = device_get_parent(dev);
+ if (controller == NULL)
+ return (ENXIO);
+
+ handle = acpi_get_handle(controller);
+ if (handle == NULL)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+acpi_iicbus_attach(device_t dev)
+{
+ struct acpi_iicbus_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_desc(dev, "Philips I2C bus (ACPI-hinted)");
+
+ if (ACPI_FAILURE(acpi_iicbus_enumerate_children(dev)))
+ device_printf(dev, "children enumeration failed\n");
+
+ acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, true);
+ error = iicbus_attach_common(dev, sc->super_sc.bus_freq);
+ if (error == 0 && install_space_handler != 0 &&
+ acpi_iicbus_install_address_space_handler(sc) == 0)
+ sc->space_handler_installed = true;
+
+ return (error);
+}
+
+static int
+acpi_iicbus_detach(device_t dev)
+{
+ struct acpi_iicbus_softc *sc = device_get_softc(dev);
+
+ if (sc->space_handler_installed)
+ acpi_iicbus_remove_address_space_handler(sc);
+ acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
+
+ return (iicbus_detach(dev));
+}
+
+static int
+acpi_iicbus_suspend(device_t dev)
+{
+ int error;
+
+ error = bus_generic_suspend(dev);
+ if (error == 0)
+ acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
+
+ return (error);
+}
+
+static int
+acpi_iicbus_resume(device_t dev)
+{
+
+ acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, false);
+
+ return (bus_generic_resume(dev));
+}
+
+/*
+ * If this device is an ACPI child but no one claimed it, attempt
+ * to power it off. We'll power it back up when a driver is added.
+ */
+static void
+acpi_iicbus_probe_nomatch(device_t bus, device_t child)
+{
+
+ iicbus_probe_nomatch(bus, child);
+ acpi_set_powerstate(child, ACPI_STATE_D3);
+}
+
+/*
+ * If a new driver has a chance to probe a child, first power it up.
+ */
+static void
+acpi_iicbus_driver_added(device_t dev, driver_t *driver)
+{
+ device_t child, *devlist;
+ int i, numdevs;
+
+ DEVICE_IDENTIFY(driver, dev);
+ if (device_get_children(dev, &devlist, &numdevs) != 0)
+ return;
+
+ for (i = 0; i < numdevs; i++) {
+ child = devlist[i];
+ if (device_get_state(child) == DS_NOTPRESENT) {
+ acpi_set_powerstate(child, ACPI_STATE_D0);
+ if (device_probe_and_attach(child) != 0)
+ acpi_set_powerstate(child, ACPI_STATE_D3);
+ }
+ }
+ free(devlist, M_TEMP);
+}
+
+static void
+acpi_iicbus_child_deleted(device_t bus, device_t child)
+{
+ struct acpi_iicbus_ivars *devi = device_get_ivars(child);
+
+ if (acpi_get_device(devi->handle) == child)
+ AcpiDetachData(devi->handle, acpi_fake_objhandler);
+}
+
+static int
+acpi_iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res)
+{
+ struct acpi_iicbus_ivars *devi = device_get_ivars(child);
+
+ switch (which) {
+ case ACPI_IVAR_HANDLE:
+ *res = (uintptr_t)devi->handle;
+ break;
+ default:
+ return (iicbus_read_ivar(bus, child, which, res));
+ }
+
+ return (0);
+}
+
+static int
+acpi_iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t val)
+{
+ struct acpi_iicbus_ivars *devi = device_get_ivars(child);
+
+ switch (which) {
+ case ACPI_IVAR_HANDLE:
+ if (devi->handle != NULL)
+ return (EINVAL);
+ devi->handle = (ACPI_HANDLE)val;
+ break;
+ default:
+ return (iicbus_write_ivar(bus, child, which, val));
+ }
+
+ return (0);
+}
+
+/* Location hint for devctl(8). Concatenate IIC and ACPI hints. */
+static int
+acpi_iicbus_child_location_str(device_t bus, device_t child,
+ char *buf, size_t buflen)
+{
+ struct acpi_iicbus_ivars *devi = device_get_ivars(child);
+ int error;
+
+ /* read IIC location hint string into the buffer. */
+ error = iicbus_child_location_str(bus, child, buf, buflen);
+ if (error != 0)
+ return (error);
+
+ /* Place ACPI string right after IIC one's terminating NUL. */
+ if (devi->handle != NULL &&
+ ((buf[0] != '\0' && strlcat(buf, " ", buflen) >= buflen) ||
+ strlcat(buf, "handle=", buflen) >= buflen ||
+ strlcat(buf, acpi_name(devi->handle), buflen) >= buflen))
+ return (EOVERFLOW);
+
+ return (0);
+}
+
+/* PnP information for devctl(8). Concatenate IIC and ACPI info strings. */
+static int
+acpi_iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
+ size_t buflen)
+{
+ struct acpi_iicbus_ivars *devi = device_get_ivars(child);
+ size_t acpi_offset;
+ int error;
+
+ /* read IIC PnP string into the buffer. */
+ error = iicbus_child_pnpinfo_str(bus, child, buf, buflen);
+ if (error != 0)
+ return (error);
+
+ if (devi->handle == NULL)
+ return (0);
+
+ /* Place ACPI string right after IIC one's terminating NUL. */
+ acpi_offset = strlen(buf);
+ if (acpi_offset != 0)
+ acpi_offset++;
+ error = acpi_pnpinfo_str(devi->handle, buf + acpi_offset,
+ buflen - acpi_offset);
+
+ /* Coalesce both strings if they are not empty. */
+ if (acpi_offset > 0 && acpi_offset < buflen && buf[acpi_offset] != 0)
+ buf[acpi_offset - 1] = ' ';
+
+ return (error);
+}
+
+static device_method_t acpi_iicbus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_iicbus_probe),
+ DEVMETHOD(device_attach, acpi_iicbus_attach),
+ DEVMETHOD(device_detach, acpi_iicbus_detach),
+ DEVMETHOD(device_suspend, acpi_iicbus_suspend),
+ DEVMETHOD(device_resume, acpi_iicbus_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, acpi_iicbus_add_child),
+ DEVMETHOD(bus_probe_nomatch, acpi_iicbus_probe_nomatch),
+ DEVMETHOD(bus_driver_added, acpi_iicbus_driver_added),
+ DEVMETHOD(bus_child_deleted, acpi_iicbus_child_deleted),
+ DEVMETHOD(bus_read_ivar, acpi_iicbus_read_ivar),
+ DEVMETHOD(bus_write_ivar, acpi_iicbus_write_ivar),
+ DEVMETHOD(bus_child_location_str,acpi_iicbus_child_location_str),
+ DEVMETHOD(bus_child_pnpinfo_str,acpi_iicbus_child_pnpinfo_str),
+
+ DEVMETHOD_END,
+};
+
+DEFINE_CLASS_1(iicbus, acpi_iicbus_driver, acpi_iicbus_methods,
+ sizeof(struct acpi_iicbus_softc), iicbus_driver);
+MODULE_VERSION(acpi_iicbus, 1);
+MODULE_DEPEND(acpi_iicbus, acpi, 1, 1, 1);
Index: head/sys/dev/iicbus/iicbus.h
===================================================================
--- head/sys/dev/iicbus/iicbus.h
+++ head/sys/dev/iicbus/iicbus.h
@@ -57,6 +57,7 @@
struct resource_list rl;
};
+/* Value of 0x100 is reserved for ACPI_IVAR_HANDLE used by acpi_iicbus */
enum {
IICBUS_IVAR_ADDR /* Address or base address */
};
@@ -79,9 +80,24 @@
int iicbus_generic_intr(device_t dev, int event, char *buf);
void iicbus_init_frequency(device_t dev, u_int bus_freq);
+int iicbus_attach_common(device_t dev, u_int bus_freq);
+device_t iicbus_add_child_common(device_t dev, u_int order, const char *name,
+ int unit, size_t ivars_size);
+int iicbus_detach(device_t dev);
+void iicbus_probe_nomatch(device_t bus, device_t child);
+int iicbus_read_ivar(device_t bus, device_t child, int which,
+ uintptr_t *result);
+int iicbus_write_ivar(device_t bus, device_t child, int which,
+ uintptr_t value);
+int iicbus_child_location_str(device_t bus, device_t child, char *buf,
+ size_t buflen);
+int iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
+ size_t buflen);
+
extern driver_t iicbus_driver;
extern devclass_t iicbus_devclass;
extern driver_t ofw_iicbus_driver;
extern devclass_t ofw_iicbus_devclass;
+extern driver_t acpi_iicbus_driver;
#endif
Index: head/sys/dev/iicbus/iicbus.c
===================================================================
--- head/sys/dev/iicbus/iicbus.c
+++ head/sys/dev/iicbus/iicbus.c
@@ -89,8 +89,8 @@
* We add all the devices which we know about.
* The generic attach routine will attach them if they are alive.
*/
-static int
-iicbus_attach(device_t dev)
+int
+iicbus_attach_common(device_t dev, u_int bus_freq)
{
#if SCAN_IICBUS
unsigned char addr;
@@ -100,7 +100,7 @@
sc->dev = dev;
mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF);
- iicbus_init_frequency(dev, 0);
+ iicbus_init_frequency(dev, bus_freq);
iicbus_reset(dev, IIC_FASTEST, 0, NULL);
if (resource_int_value(device_get_name(dev),
device_get_unit(dev), "strict", &strict) == 0)
@@ -131,6 +131,13 @@
}
static int
+iicbus_attach(device_t dev)
+{
+
+ return (iicbus_attach_common(dev, 0));
+}
+
+int
iicbus_detach(device_t dev)
{
struct iicbus_softc *sc = IICBUS_SOFTC(dev);
@@ -158,7 +165,7 @@
return (retval);
}
-static void
+void
iicbus_probe_nomatch(device_t bus, device_t child)
{
struct iicbus_ivar *devi = IICBUS_IVAR(child);
@@ -166,7 +173,7 @@
device_printf(bus, "<unknown card> at addr %#x\n", devi->addr);
}
-static int
+int
iicbus_child_location_str(device_t bus, device_t child, char *buf,
size_t buflen)
{
@@ -176,7 +183,7 @@
return (0);
}
-static int
+int
iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
size_t buflen)
{
@@ -184,7 +191,7 @@
return (0);
}
-static int
+int
iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
{
struct iicbus_ivar *devi = IICBUS_IVAR(child);
@@ -199,7 +206,7 @@
return (0);
}
-static int
+int
iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
{
struct iicbus_ivar *devi = IICBUS_IVAR(child);
@@ -215,8 +222,9 @@
return (0);
}
-static device_t
-iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
+device_t
+iicbus_add_child_common(device_t dev, u_int order, const char *name, int unit,
+ size_t ivars_size)
{
device_t child;
struct iicbus_ivar *devi;
@@ -224,7 +232,7 @@
child = device_add_child_ordered(dev, order, name, unit);
if (child == NULL)
return (child);
- devi = malloc(sizeof(struct iicbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
+ devi = malloc(ivars_size, M_DEVBUF, M_NOWAIT | M_ZERO);
if (devi == NULL) {
device_delete_child(dev, child);
return (0);
@@ -232,6 +240,14 @@
resource_list_init(&devi->rl);
device_set_ivars(child, devi);
return (child);
+}
+
+static device_t
+iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+
+ return (iicbus_add_child_common(
+ dev, order, name, unit, sizeof(struct iicbus_ivar)));
}
static void
Index: head/sys/modules/i2c/iicbus/Makefile
===================================================================
--- head/sys/modules/i2c/iicbus/Makefile
+++ head/sys/modules/i2c/iicbus/Makefile
@@ -15,6 +15,11 @@
iiconf.h \
opt_platform.h \
+.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \
+ ${MACHINE_CPUARCH} == "i386"
+SRCS+= acpi_iicbus.c opt_acpi.h acpi_if.h
+.endif
+
.if !empty(OPT_FDT)
SRCS+= ofw_iicbus.c ofw_bus_if.h
.endif

File Metadata

Mime Type
text/plain
Expires
Sat, Feb 22, 9:10 AM (1 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16768656
Default Alt Text
D22901.diff (26 KB)

Event Timeline