git: 7f14bc44022d - stable/13 - linuxkpi: Add i2c support

From: Emmanuel Vadot <manu_at_FreeBSD.org>
Date: Tue, 21 Jun 2022 15:23:09 UTC
The branch stable/13 has been updated by manu:

URL: https://cgit.FreeBSD.org/src/commit/?id=7f14bc44022da73cfdb0dd6d73a4ade239c7e857

commit 7f14bc44022da73cfdb0dd6d73a4ade239c7e857
Author:     Emmanuel Vadot <manu@FreeBSD.org>
AuthorDate: 2021-11-04 09:42:37 +0000
Commit:     Emmanuel Vadot <manu@FreeBSD.org>
CommitDate: 2022-06-21 15:13:57 +0000

    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
    
    (cherry picked from commit 1961a14a47437595fb7fcdc20e327440e3eb51e2)
---
 .../linuxkpi/common/include/linux/i2c-algo-bit.h   |  49 ++++
 sys/compat/linuxkpi/common/include/linux/i2c.h     | 152 +++++++++++++
 sys/compat/linuxkpi/common/src/linux_i2c.c         | 217 ++++++++++++++++++
 sys/compat/linuxkpi/common/src/linux_i2cbb.c       | 252 +++++++++++++++++++++
 sys/compat/linuxkpi/common/src/lkpi_iic_if.m       |  37 +++
 sys/conf/files                                     |  19 +-
 sys/conf/kmod.mk                                   |   4 +
 sys/modules/linuxkpi/Makefile                      |   2 +
 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 7fc99a929947..81781ab0bc66 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1850,7 +1850,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
@@ -1861,13 +1861,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
@@ -4597,6 +4597,10 @@ compat/linuxkpi/common/src/linux_firmware.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 \
@@ -4633,6 +4637,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 629b7554f005..cc5d394bb186 100644
--- a/sys/conf/kmod.mk
+++ b/sys/conf/kmod.mk
@@ -94,6 +94,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 f478ebbe0baf..e34bd1e954d7 100644
--- a/sys/modules/linuxkpi/Makefile
+++ b/sys/modules/linuxkpi/Makefile
@@ -11,6 +11,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 \