Page MenuHomeFreeBSD

D22901.id65938.diff
No OneTemporary

D22901.id65938.diff

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

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)

Event Timeline