aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Vadot <manu@FreeBSD.org>2021-11-04 09:42:37 +0000
committerEmmanuel Vadot <manu@FreeBSD.org>2022-01-25 15:15:39 +0000
commit1961a14a47437595fb7fcdc20e327440e3eb51e2 (patch)
treeeba554e083f6b3b8f987029bec14b440ea6d55a4
parent9caeb82eab2805782b9c3d9205007c1b0795e808 (diff)
downloadsrc-1961a14a47437595fb7fcdc20e327440e3eb51e2.tar.gz
src-1961a14a47437595fb7fcdc20e327440e3eb51e2.zip
linuxkpi: Add i2c support
Add i2c support to linuxkpi. This is needed by drm-kmod. For every i2c_adapter added by i2c_add_adapter we add a child to the device named "lkpi_iic". This child handle the conversion between Linux i2c_msgs to FreeBSD iic_msgs. For every i2c_adapter added by i2c_bit_add_bus we add a child to the device named "lkpi_iicbb". This child handle the conversion between Linux i2c_msgs to FreeBSD iic_msgs. With the help of iic(4), this expose the i2c controller to userspace allowing a user to query DDC information from a monitor. e.g.: i2c -f /dev/iic0 -a 0x28 -c 128 -d r will query the standard EDID from the monitor if plugged. The bitbang part (lkpi_iicbb) isn't tested at all for now as I don't have compatible hardware (all my hardware have native i2c controller). Tested on: Intel (SandyBridge, Skylake, ApolloLake) Tested on: AMD (Picasso, Polaris (amd64 and arm64)) MFC after: 1 month Reviewed by: hselasky Sponsored by: Beckhoff Automation GmbH & Co. KG Differential Revision: https://reviews.freebsd.org/D33053
-rw-r--r--sys/compat/linuxkpi/common/include/linux/i2c-algo-bit.h49
-rw-r--r--sys/compat/linuxkpi/common/include/linux/i2c.h152
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2c.c217
-rw-r--r--sys/compat/linuxkpi/common/src/linux_i2cbb.c252
-rw-r--r--sys/compat/linuxkpi/common/src/lkpi_iic_if.m37
-rw-r--r--sys/conf/files19
-rw-r--r--sys/conf/kmod.mk4
-rw-r--r--sys/modules/linuxkpi/Makefile2
8 files changed, 725 insertions, 7 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/i2c-algo-bit.h b/sys/compat/linuxkpi/common/include/linux/i2c-algo-bit.h
new file mode 100644
index 000000000000..4e8f00f9bebc
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/i2c-algo-bit.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_I2C_ALGO_BIT_H_
+#define _LINUX_I2C_ALGO_BIT_H_
+
+#include <linux/i2c.h>
+
+struct i2c_algo_bit_data {
+ void *data;
+ void (*setsda) (void *data, int state);
+ void (*setscl) (void *data, int state);
+ int (*getsda) (void *data);
+ int (*getscl) (void *data);
+ int (*pre_xfer) (struct i2c_adapter *);
+ void (*post_xfer) (struct i2c_adapter *);
+
+ int udelay;
+ int timeout;
+};
+
+int lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter);
+
+#define i2c_bit_add_bus(adapter) lkpi_i2c_bit_add_bus(adapter)
+
+#endif /*_LINUX_I2C_ALGO_BIT_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/i2c.h b/sys/compat/linuxkpi/common/include/linux/i2c.h
new file mode 100644
index 000000000000..0bb8b470edd7
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/i2c.h
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_I2C_H_
+#define _LINUX_I2C_H_
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <linux/device.h>
+
+#define I2C_MAX_ADAPTER_NAME_LENGTH 32
+
+#define I2C_M_RD 0x0001
+#define I2C_M_NOSTART 0x0002
+#define I2C_M_STOP 0x0004
+
+/* No need for us */
+#define I2C_FUNC_I2C 0
+#define I2C_FUNC_SMBUS_EMUL 0
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0
+#define I2C_FUNC_10BIT_ADDR 0
+
+#define I2C_CLASS_DDC 0x8
+#define I2C_CLASS_SPD 0x80
+
+struct i2c_adapter {
+ struct module *owner;
+ unsigned int class;
+
+ char name[I2C_MAX_ADAPTER_NAME_LENGTH];
+ struct device dev;
+
+ const struct i2c_lock_operations *lock_ops;
+ const struct i2c_algorithm *algo;
+ void *algo_data;
+
+ int retries;
+ void *data;
+};
+
+struct i2c_msg {
+ uint16_t addr;
+ uint16_t flags;
+ uint16_t len;
+ uint8_t *buf;
+};
+
+struct i2c_algorithm {
+ int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int);
+ uint32_t (*functionality)(struct i2c_adapter *);
+};
+
+struct i2c_lock_operations {
+ void (*lock_bus)(struct i2c_adapter *, unsigned int);
+ int (*trylock_bus)(struct i2c_adapter *, unsigned int);
+ void (*unlock_bus)(struct i2c_adapter *, unsigned int);
+};
+
+int lkpi_i2c_add_adapter(struct i2c_adapter *adapter);
+int lkpi_i2c_del_adapter(struct i2c_adapter *adapter);
+
+int lkpi_i2cbb_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs);
+
+#define i2c_add_adapter(adapter) lkpi_i2c_add_adapter(adapter)
+#define i2c_del_adapter(adapter) lkpi_i2c_del_adapter(adapter)
+
+#define i2c_get_adapter(x) NULL
+#define i2c_put_adapter(x)
+
+static inline int
+do_i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs)
+{
+ int ret, retries;
+
+ retries = adapter->retries == 0 ? 1 : adapter->retries;
+ for (; retries != 0; retries--) {
+ if (adapter->algo->master_xfer != NULL)
+ ret = adapter->algo->master_xfer(adapter, msgs, nmsgs);
+ else
+ ret = lkpi_i2cbb_transfer(adapter, msgs, nmsgs);
+ if (ret != -EAGAIN)
+ break;
+ }
+
+ return (ret);
+}
+
+static inline int
+i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs)
+{
+ int ret;
+
+ if (!adapter->algo)
+ return (-EOPNOTSUPP);
+
+ if (adapter->lock_ops)
+ adapter->lock_ops->lock_bus(adapter, 0);
+
+ ret = do_i2c_transfer(adapter, msgs, nmsgs);
+
+ if (adapter->lock_ops)
+ adapter->lock_ops->unlock_bus(adapter, 0);
+
+ return (ret);
+}
+
+/* Unlocked version of i2c_transfer */
+static inline int
+__i2c_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs)
+{
+ return (do_i2c_transfer(adapter, msgs, nmsgs));
+}
+
+static inline void
+i2c_set_adapdata(struct i2c_adapter *adapter, void *data)
+{
+ adapter->data = data;
+}
+
+static inline void *
+i2c_get_adapdata(struct i2c_adapter *adapter)
+{
+ return (adapter->data);
+}
+
+#endif /* _LINUX_I2C_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
new file mode 100644
index 000000000000..cd002da49b19
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -0,0 +1,217 @@
+/*-
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * 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/systm.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+
+#include "iicbus_if.h"
+#include "iicbb_if.h"
+#include "lkpi_iic_if.h"
+
+static int lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
+static int lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);
+
+struct lkpi_iic_softc {
+ device_t iicbus;
+ struct i2c_adapter *adapter;
+};
+
+static int
+lkpi_iic_probe(device_t dev)
+{
+
+ device_set_desc(dev, "LinuxKPI I2C");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+lkpi_iic_attach(device_t dev)
+{
+ struct lkpi_iic_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "Couldn't add iicbus child, aborting\n");
+ return (ENXIO);
+ }
+ bus_generic_attach(dev);
+ return (0);
+}
+
+static int
+lkpi_iic_detach(device_t dev)
+{
+ struct lkpi_iic_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->iicbus)
+ device_delete_child(dev, sc->iicbus);
+ return (0);
+}
+
+static int
+lkpi_iic_add_adapter(device_t dev, struct i2c_adapter *adapter)
+{
+ struct lkpi_iic_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->adapter = adapter;
+
+ return (0);
+}
+
+static device_method_t lkpi_iic_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, lkpi_iic_probe),
+ DEVMETHOD(device_attach, lkpi_iic_attach),
+ DEVMETHOD(device_detach, lkpi_iic_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_transfer, lkpi_i2c_transfer),
+ DEVMETHOD(iicbus_reset, lkpi_i2c_reset),
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+
+ /* lkpi_iic interface */
+ DEVMETHOD(lkpi_iic_add_adapter, lkpi_iic_add_adapter),
+
+ DEVMETHOD_END
+};
+
+devclass_t lkpi_iic_devclass;
+
+driver_t lkpi_iic_driver = {
+ "lkpi_iic",
+ lkpi_iic_methods,
+ sizeof(struct lkpi_iic_softc),
+};
+
+DRIVER_MODULE(lkpi_iic, drmn, lkpi_iic_driver, lkpi_iic_devclass, 0, 0);
+DRIVER_MODULE(iicbus, lkpi_iic, iicbus_driver, iicbus_devclass, 0, 0);
+
+static int
+lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+
+ /* That doesn't seems to be supported in linux */
+ return (0);
+}
+
+static int
+lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ struct lkpi_iic_softc *sc;
+ struct i2c_msg *linux_msgs;
+ int i, ret = 0;
+
+ sc = device_get_softc(dev);
+ if (sc->adapter == NULL)
+ return (ENXIO);
+ linux_set_current(curthread);
+
+ linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < nmsgs; i++) {
+ linux_msgs[i].addr = msgs[i].slave;
+ linux_msgs[i].len = msgs[i].len;
+ linux_msgs[i].buf = msgs[i].buf;
+ if (msgs[i].flags & IIC_M_RD) {
+ linux_msgs[i].flags |= I2C_M_RD;
+ for (int j = 0; j < msgs[i].len; j++)
+ msgs[i].buf[j] = 0;
+ }
+ if (msgs[i].flags & IIC_M_NOSTART)
+ linux_msgs[i].flags |= I2C_M_NOSTART;
+ }
+ ret = i2c_transfer(sc->adapter, linux_msgs, nmsgs);
+ free(linux_msgs, M_DEVBUF);
+
+ if (ret < 0)
+ return (-ret);
+ return (0);
+}
+
+int
+lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
+{
+ device_t lkpi_iic;
+ int error;
+
+ if (bootverbose)
+ device_printf(adapter->dev.parent->bsddev,
+ "Adding i2c adapter %s\n", adapter->name);
+ lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
+ if (lkpi_iic == NULL) {
+ device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
+ return (ENXIO);
+ }
+
+ error = bus_generic_attach(adapter->dev.parent->bsddev);
+ if (error) {
+ device_printf(adapter->dev.parent->bsddev,
+ "failed to attach child: error %d\n", error);
+ return (ENXIO);
+ }
+ LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
+ return (0);
+}
+
+int
+lkpi_i2c_del_adapter(struct i2c_adapter *adapter)
+{
+ device_t child;
+
+ if (bootverbose)
+ device_printf(adapter->dev.parent->bsddev,
+ "Removing i2c adapter %s\n", adapter->name);
+
+ child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
+ if (child != NULL)
+ device_delete_child(adapter->dev.parent->bsddev, child);
+
+ child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
+ if (child != NULL)
+ device_delete_child(adapter->dev.parent->bsddev, child);
+
+ return (0);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_i2cbb.c b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
new file mode 100644
index 000000000000..06d9ecd6a1fa
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_i2cbb.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * 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/systm.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+
+#include "iicbb_if.h"
+#include "lkpi_iic_if.h"
+
+static void lkpi_iicbb_setsda(device_t dev, int val);
+static void lkpi_iicbb_setscl(device_t dev, int val);
+static int lkpi_iicbb_getscl(device_t dev);
+static int lkpi_iicbb_getsda(device_t dev);
+static int lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);
+
+struct lkpi_iicbb_softc {
+ device_t iicbb;
+ struct i2c_adapter *adapter;
+};
+
+static int
+lkpi_iicbb_probe(device_t dev)
+{
+
+ device_set_desc(dev, "LinuxKPI I2CBB");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+lkpi_iicbb_attach(device_t dev)
+{
+ struct lkpi_iicbb_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->iicbb = device_add_child(dev, "iicbb", -1);
+ if (sc->iicbb == NULL) {
+ device_printf(dev, "Couldn't add iicbb child, aborting\n");
+ return (ENXIO);
+ }
+ bus_generic_attach(dev);
+ return (0);
+}
+
+static int
+lkpi_iicbb_detach(device_t dev)
+{
+ struct lkpi_iicbb_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->iicbb)
+ device_delete_child(dev, sc->iicbb);
+ return (0);
+}
+
+static int
+lkpi_iicbb_add_adapter(device_t dev, struct i2c_adapter *adapter)
+{
+ struct lkpi_iicbb_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->adapter = adapter;
+
+ return (0);
+}
+
+static device_method_t lkpi_iicbb_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, lkpi_iicbb_probe),
+ DEVMETHOD(device_attach, lkpi_iicbb_attach),
+ DEVMETHOD(device_detach, lkpi_iicbb_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* iicbb interface */
+ DEVMETHOD(iicbb_setsda, lkpi_iicbb_setsda),
+ DEVMETHOD(iicbb_setscl, lkpi_iicbb_setscl),
+ DEVMETHOD(iicbb_getsda, lkpi_iicbb_getsda),
+ DEVMETHOD(iicbb_getscl, lkpi_iicbb_getscl),
+ DEVMETHOD(iicbb_reset, lkpi_iicbb_reset),
+
+ /* lkpi_iicbb interface */
+ DEVMETHOD(lkpi_iic_add_adapter, lkpi_iicbb_add_adapter),
+
+ DEVMETHOD_END
+};
+
+static devclass_t lkpi_iicbb_devclass;
+
+driver_t lkpi_iicbb_driver = {
+ "lkpi_iicbb",
+ lkpi_iicbb_methods,
+ sizeof(struct lkpi_iicbb_softc),
+};
+
+DRIVER_MODULE(lkpi_iicbb, lkpi_iic, lkpi_iicbb_driver, lkpi_iicbb_devclass, 0, 0);
+DRIVER_MODULE(iicbb, lkpi_iicbb, iicbb_driver, iicbb_devclass, 0, 0);
+MODULE_DEPEND(lkpi_iicbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
+
+static void
+lkpi_iicbb_setsda(device_t dev, int val)
+{
+ struct lkpi_iicbb_softc *sc;
+ struct i2c_algo_bit_data *algo_data;
+
+ sc = device_get_softc(dev);
+ algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
+ algo_data->setsda(algo_data->data, val);
+ cpu_spinwait();
+ DELAY(algo_data->udelay);
+}
+
+static void
+lkpi_iicbb_setscl(device_t dev, int val)
+{
+ struct lkpi_iicbb_softc *sc;
+ struct i2c_algo_bit_data *algo_data;
+
+ sc = device_get_softc(dev);
+
+ algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
+ algo_data->setscl(algo_data->data, val);
+ cpu_spinwait();
+ DELAY(algo_data->udelay);
+}
+
+static int
+lkpi_iicbb_getscl(device_t dev)
+{
+ struct lkpi_iicbb_softc *sc;
+ struct i2c_algo_bit_data *algo_data;
+ unsigned long orig_ticks;
+ int ret = 0;
+
+ sc = device_get_softc(dev);
+
+ algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
+
+ orig_ticks = ticks;
+ while (!ret) {
+ ret = algo_data->getscl(algo_data->data);
+
+ if (ret)
+ break;
+
+ if (ticks > orig_ticks + algo_data->timeout)
+ return (ETIMEDOUT);
+
+ cpu_spinwait();
+ DELAY(algo_data->udelay);
+ }
+ DELAY(algo_data->udelay);
+ return (ret);
+}
+
+static int
+lkpi_iicbb_getsda(device_t dev)
+{
+ struct lkpi_iicbb_softc *sc;
+ struct i2c_algo_bit_data *algo_data;
+ int ret = 0;
+
+ sc = device_get_softc(dev);
+ algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
+
+ cpu_spinwait();
+ DELAY(algo_data->udelay);
+ ret = algo_data->getsda(algo_data->data);
+ cpu_spinwait();
+ DELAY(algo_data->udelay);
+ return (ret);
+}
+
+static int
+lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+ struct lkpi_iicbb_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (0);
+}
+
+int
+lkpi_i2cbb_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs)
+{
+
+ /* TODO: convert from i2c_msg to iic_msg and call IICBUS_TRANFER */
+ return (0);
+}
+
+int
+lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
+{
+ device_t lkpi_iicbb;
+ int error;
+
+ if (bootverbose)
+ device_printf(adapter->dev.parent->bsddev,
+ "Adding i2c adapter %s\n", adapter->name);
+ lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
+ if (lkpi_iicbb == NULL) {
+ device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");
+ return (ENXIO);
+ }
+
+ error = bus_generic_attach(adapter->dev.parent->bsddev);
+ if (error) {
+ device_printf(adapter->dev.parent->bsddev,
+ "failed to attach child: error %d\n", error);
+ return (ENXIO);
+ }
+ LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter);
+ return (0);
+}
+
diff --git a/sys/compat/linuxkpi/common/src/lkpi_iic_if.m b/sys/compat/linuxkpi/common/src/lkpi_iic_if.m
new file mode 100644
index 000000000000..2379182c409b
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/lkpi_iic_if.m
@@ -0,0 +1,37 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+#
+# 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.
+#
+
+INTERFACE lkpi_iic;
+
+HEADER {
+ struct i2c_adapter;
+}
+
+METHOD int add_adapter {
+ device_t dev;
+ struct i2c_adapter *adapter;
+};
diff --git a/sys/conf/files b/sys/conf/files
index 5b33d66f3377..4ff4d1761a41 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1820,7 +1820,7 @@ dev/ichsmb/ichsmb_pci.c optional ichsmb pci
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/acpi_iicbus.c optional acpi iicbus | acpi compat_linuxkpi
dev/iicbus/ad7418.c optional ad7418
dev/iicbus/ads111x.c optional ads111x
dev/iicbus/ds1307.c optional ds1307
@@ -1831,13 +1831,13 @@ dev/iicbus/htu21.c optional htu21
dev/iicbus/icee.c optional icee
dev/iicbus/if_ic.c optional ic
dev/iicbus/iic.c optional iic
-dev/iicbus/iic_recover_bus.c optional iicbus
-dev/iicbus/iicbb.c optional iicbb
-dev/iicbus/iicbb_if.m optional iicbb
-dev/iicbus/iicbus.c optional iicbus
-dev/iicbus/iicbus_if.m optional iicbus
+dev/iicbus/iic_recover_bus.c optional iicbus | compat_linuxkpi
+dev/iicbus/iicbb.c optional iicbb | compat_linuxkpi
+dev/iicbus/iicbb_if.m optional iicbb | compat_linuxkpi
+dev/iicbus/iicbus.c optional iicbus | compat_linuxkpi
+dev/iicbus/iicbus_if.m optional iicbus | compat_linuxkpi
dev/iicbus/iichid.c optional iichid acpi hid iicbus
-dev/iicbus/iiconf.c optional iicbus
+dev/iicbus/iiconf.c optional iicbus | compat_linuxkpi
dev/iicbus/iicsmb.c optional iicsmb \
dependency "iicbus_if.h"
dev/iicbus/iicoc.c optional iicoc
@@ -4552,6 +4552,10 @@ compat/linuxkpi/common/src/linux_fpu.c optional compat_linuxkpi \
compile-with "${LINUXKPI_C}"
compat/linuxkpi/common/src/linux_hrtimer.c optional compat_linuxkpi \
compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_i2c.c optional compat_linuxkpi \
+ compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_i2cbb.c optional compat_linuxkpi \
+ compile-with "${LINUXKPI_C}"
compat/linuxkpi/common/src/linux_interrupt.c optional compat_linuxkpi \
compile-with "${LINUXKPI_C}"
compat/linuxkpi/common/src/linux_kthread.c optional compat_linuxkpi \
@@ -4588,6 +4592,7 @@ compat/linuxkpi/common/src/linux_work.c optional compat_linuxkpi \
compile-with "${LINUXKPI_C}"
compat/linuxkpi/common/src/linux_xarray.c optional compat_linuxkpi \
compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/lkpi_iic_if.m optional compat_linuxkpi
compat/linuxkpi/common/src/linux_seq_file.c optional compat_linuxkpi | lindebugfs \
compile-with "${LINUXKPI_C}"
diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk
index cf39de5e87cf..54d57b069435 100644
--- a/sys/conf/kmod.mk
+++ b/sys/conf/kmod.mk
@@ -96,6 +96,10 @@ LINUXKPI_GENSRCS+= \
backlight_if.h \
bus_if.h \
device_if.h \
+ iicbus_if.h \
+ iicbb_if.h \
+ lkpi_iic_if.c \
+ lkpi_iic_if.h \
pci_if.h \
pci_iov_if.h \
pcib_if.h \
diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile
index 4d15ac9fa962..6a256bf1f8e1 100644
--- a/sys/modules/linuxkpi/Makefile
+++ b/sys/modules/linuxkpi/Makefile
@@ -12,6 +12,8 @@ SRCS= linux_compat.c \
linux_hrtimer.c \
linux_idr.c \
linux_interrupt.c \
+ linux_i2c.c \
+ linux_i2cbb.c \
linux_kmod.c \
linux_kthread.c \
linux_lock.c \