Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146400081
D22901.id65938.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
D22901.id65938.diff
View Options
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1780,6 +1780,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: sys/dev/iicbus/acpi_iicbus.c
===================================================================
--- /dev/null
+++ sys/dev/iicbus/acpi_iicbus.c
@@ -0,0 +1,439 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 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 <dev/acpica/acpivar.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+/* Hooks for the ACPI CA debugging infrastructure. */
+#define _COMPONENT ACPI_BUS
+ACPI_MODULE_NAME("IIC")
+
+static devclass_t acpi_iicdev_devclass;
+
+struct acpi_iicbus_ivars {
+ struct iicbus_ivar ivar;
+ ACPI_HANDLE handle;
+};
+
+static ACPI_STATUS
+acpi_iicdev_get_i2cres_cb(ACPI_RESOURCE *res, void *context)
+{
+
+ if (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
+ res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
+ if (context != NULL)
+ memcpy(context, &res->Data.I2cSerialBus,
+ sizeof(struct acpi_resource_i2c_serialbus));
+ return (AE_CTRL_TERMINATE);
+ } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
+ return (AE_NOT_FOUND);
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_iicdev_get_i2cres(ACPI_HANDLE handle,
+ struct acpi_resource_i2c_serialbus *i2cres)
+{
+ ACPI_STATUS status;
+
+ status = AcpiWalkResources(handle, "_CRS",
+ acpi_iicdev_get_i2cres_cb, i2cres);
+
+ return (status);
+}
+
+static int
+acpi_iicbus_copy_resources(device_t from, device_t to)
+{
+ struct resource_list_entry *from_rle;
+ struct resource_list *from_rl;
+
+ /* Loop through all current resources and copy them to the target. */
+ from_rl = BUS_GET_RESOURCE_LIST(device_get_parent(from), from);
+ STAILQ_FOREACH(from_rle, from_rl, link) {
+
+ bus_set_resource(to, from_rle->type, from_rle->rid,
+ from_rle->start, from_rle->count);
+ }
+
+ return (0);
+}
+
+static void
+acpi_iicbus_dump_res(device_t dev, struct acpi_resource_i2c_serialbus *res)
+{
+ device_printf(dev, "found ACPI child\n");
+ printf(" SlaveAddress: 0x%04hx\n", res->SlaveAddress);
+ printf(" ConnectionSpeed: %uHz\n", res->ConnectionSpeed);
+ printf(" SlaveMode: %s\n",
+ res->SlaveMode == ACPI_CONTROLLER_INITIATED ?
+ "ControllerInitiated" : "DeviceInitiated");
+ printf(" AddressingMode: %uBit\n", res->AccessMode == 0 ? 7 : 10);
+ printf(" ConnectionSharing: %s\n", res->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 **status)
+{
+ device_t iicbus = context;
+ device_t child, acpi_iicdev, acpi0;
+ struct acpi_resource_i2c_serialbus res;
+ struct iicbus_softc *sc = device_get_softc(iicbus);
+
+ acpi_iicdev = acpi_get_device(handle);
+ if (acpi_iicdev == NULL)
+ return (AE_OK);
+
+ if (!device_is_enabled(acpi_iicdev))
+ return (AE_OK);
+
+ /* Check that device is a child of the ACPI bus. */
+ acpi0 = devclass_get_device(devclass_find("acpi"), 0);
+ if (device_get_parent(acpi_iicdev) != acpi0)
+ return (AE_OK);
+
+ /*
+ * Read "I2C Serial Bus Connection Resource Descriptor"
+ * described in p.19.6.57 of ACPI specification.
+ */
+ bzero(&res, sizeof(struct acpi_resource_i2c_serialbus));
+ if (ACPI_FAILURE(acpi_iicdev_get_i2cres(handle, &res)) ||
+ res.SlaveAddress == 0)
+ return (AE_OK);
+ if (bootverbose)
+ acpi_iicbus_dump_res(iicbus, &res);
+
+ /* Find out speed of the slowest slave */
+ if (sc->bus_freq == 0 || sc->bus_freq > res.ConnectionSpeed)
+ sc->bus_freq = res.ConnectionSpeed;
+
+ /*
+ * Ensure dummy driver is attached. We are going to use resources from
+ * the ACPI device so don't let other drivers occupy his place.
+ */
+ if (device_get_devclass(acpi_iicdev) != acpi_iicdev_devclass &&
+ device_set_devclass_fixed(acpi_iicdev, "acpi_iicdev") != 0) {
+ device_printf(iicbus, "Can't set ACPI child %s devclass. "
+ "Ignore it.\n", device_get_nameunit(acpi_iicdev));
+ return (AE_OK);
+ }
+ if (device_probe_and_attach(acpi_iicdev) != 0) {
+ device_printf(iicbus, "failed to attach dummy driver\n");
+ return (AE_OK);
+ }
+
+ /* Appropriate ACPI I2C device found. Add a child to I2C bus. */
+ 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, res.SlaveAddress);
+ acpi_set_handle(child, handle);
+
+ /* Copy all resources including IRQs from ACPI to I2C device. */
+ acpi_iicbus_copy_resources(acpi_iicdev, child);
+
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_iicbus_enumerate_children(device_t dev, ACPI_HANDLE handle)
+{
+
+ return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle,
+ 1, acpi_iicbus_enumerate_child, NULL, dev, NULL));
+}
+
+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 iicbus_softc *sc = device_get_softc(dev);
+ ACPI_HANDLE handle;
+
+ device_set_desc(dev, "Philips I2C bus (ACPI-hinted)");
+
+ /* Walk through ACPI children of I2C controller. */
+ handle = acpi_get_handle(device_get_parent(dev));
+ if (ACPI_FAILURE(acpi_iicbus_enumerate_children(dev, handle)))
+ device_printf(dev, "children enumeration failed\n");
+
+ return (iicbus_attach_common(dev, sc->bus_freq));
+}
+
+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);
+}
+
+/* Finds ACPI-hosted device corresponding IIC-hosted one. */
+static device_t
+acpi_iicbus_get_acpi_dev(device_t iic_dev)
+{
+ ACPI_HANDLE handle;
+ device_t acpi_dev, acpi0;
+
+ handle = acpi_get_handle(iic_dev);
+ if (handle == NULL)
+ return (NULL);
+
+ acpi_dev = acpi_get_device(handle);
+ if (acpi_dev == NULL)
+ return (NULL);
+
+ /* Check that device is a child of acpi bus. */
+ acpi0 = devclass_get_device(devclass_find("acpi"), 0);
+ if (device_get_parent(acpi_dev) != acpi0)
+ return (NULL);
+
+ return (acpi_dev);
+}
+
+/* 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)
+{
+ device_t acpi_dev;
+ size_t acpi_offset;
+ int error;
+
+ /* read IIC location hint string into the buffer. */
+ error = iicbus_child_location_str(bus, child, buf, buflen);
+ if (error)
+ return (error);
+
+ acpi_dev = acpi_iicbus_get_acpi_dev(child);
+ if (acpi_dev == NULL)
+ return (0);
+
+ if ((buf[0] != '\0' && strlcat(buf, " ", buflen) >= buflen) ||
+ (strlcat(buf, "acpi_iicdev=", buflen) >= buflen) ||
+ (strlcat(buf, device_get_nameunit(acpi_dev), buflen) >= buflen))
+ return (EOVERFLOW);
+
+ /* Place ACPI string right after IIC one's terminating NUL. */
+ acpi_offset = strlen(buf) + 1;
+ error = BUS_CHILD_LOCATION_STR(device_get_parent(acpi_dev), acpi_dev,
+ buf + acpi_offset, buflen - acpi_offset);
+ if (error)
+ return (error);
+
+ /* Coalesce both strings if they are not empty. */
+ if (acpi_offset < buflen && buf[acpi_offset] != '\0')
+ buf[acpi_offset - 1] = ' ';
+
+ 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)
+{
+ device_t acpi_dev;
+ size_t acpi_offset;
+ int error;
+
+ /* read IIC PnP string into the buffer. */
+ error = iicbus_child_pnpinfo_str(bus, child, buf, buflen);
+ if (error)
+ return (error);
+
+ acpi_dev = acpi_iicbus_get_acpi_dev(child);
+ if (acpi_dev == NULL)
+ return (0);
+
+ /* Place ACPI string right after IIC one's terminating NUL. */
+ acpi_offset = strlen(buf);
+ if (acpi_offset != 0)
+ acpi_offset++;
+ error = BUS_CHILD_PNPINFO_STR(device_get_parent(acpi_dev), acpi_dev,
+ buf + acpi_offset, buflen - acpi_offset);
+ if (error)
+ return (error);
+
+ /* Coalesce both strings if they are not empty. */
+ if (acpi_offset > 0 && acpi_offset < buflen && buf[acpi_offset] != 0)
+ buf[acpi_offset - 1] = ' ';
+
+ return (0);
+}
+
+static device_method_t acpi_iicbus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_iicbus_probe),
+ DEVMETHOD(device_attach, acpi_iicbus_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_add_child, acpi_iicbus_add_child),
+ 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 iicbus_softc), iicbus_driver);
+MODULE_VERSION(acpi_iicbus, 1);
+MODULE_DEPEND(acpi_iicbus, acpi, 1, 1, 1);
+DRIVER_MODULE(acpi_iicbus, ig4iic, acpi_iicbus_driver, iicbus_devclass, 0, 0);
+
+/*
+ * Dummy ACPI driver. Used as bus resource holder for iicbus children. Also,
+ * being attached it triggers _PS0/3 ACPI methods execution on suspend/resume.
+ */
+
+static int
+acpi_iicdev_probe(device_t dev)
+{
+ ACPI_HANDLE handle;
+
+ /* Check if device has both fixed devclass and I2C address */
+ if (!device_is_devclass_fixed(dev) ||
+ (handle = acpi_get_handle(dev)) == NULL ||
+ ACPI_FAILURE(acpi_iicdev_get_i2cres(handle, NULL)))
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI Generic I2C Device");
+ device_quiet(dev);
+
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+acpi_iicdev_attach(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+acpi_iicdev_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t acpi_iicdev_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, acpi_iicdev_probe),
+ DEVMETHOD(device_attach, acpi_iicdev_attach),
+ DEVMETHOD(device_detach, acpi_iicdev_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t acpi_iicdev_driver = {
+ .name = "acpi_iicdev",
+ .methods = acpi_iicdev_methods,
+ .size = 0,
+};
+
+DRIVER_MODULE(acpi_iicdev, acpi, acpi_iicdev_driver, acpi_iicdev_devclass,
+ NULL, 0);
+MODULE_DEPEND(acpi_iicdev, acpi, 1, 1, 1);
+MODULE_VERSION(acpi_iicdev, 1);
Index: sys/dev/iicbus/iicbus.h
===================================================================
--- sys/dev/iicbus/iicbus.h
+++ 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,6 +80,19 @@
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_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;
Index: sys/dev/iicbus/iicbus.c
===================================================================
--- sys/dev/iicbus/iicbus.c
+++ 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)
@@ -130,6 +130,13 @@
return (0);
}
+static int
+iicbus_attach(device_t dev)
+{
+
+ return (iicbus_attach_common(dev, 0));
+}
+
static int
iicbus_detach(device_t dev)
{
@@ -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);
@@ -234,6 +242,14 @@
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
iicbus_hinted_child(device_t bus, const char *dname, int dunit)
{
Index: sys/modules/i2c/iicbus/Makefile
===================================================================
--- sys/modules/i2c/iicbus/Makefile
+++ 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
Details
Attached
Mime Type
text/plain
Expires
Tue, Mar 3, 9:50 AM (14 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29182536
Default Alt Text
D22901.id65938.diff (16 KB)
Attached To
Mode
D22901: [iicbus] Add support for ACPI-based children enumeration
Attached
Detach File
Event Timeline
Log In to Comment