Path: blob/main/sys/compat/linuxkpi/common/src/linux_i2cbb.c
39586 views
/*-1* Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND13* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE14* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE15* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE16* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL17* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS18* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)19* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT20* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY21* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF22* SUCH DAMAGE.23*24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/malloc.h>3031#include <dev/iicbus/iicbus.h>32#include <dev/iicbus/iiconf.h>3334#include <linux/device.h>35#include <linux/i2c.h>36#include <linux/i2c-algo-bit.h>37#include <linux/list.h>38#include <linux/pci.h>3940#include "iicbus_if.h"41#include "iicbb_if.h"42#include "lkpi_iic_if.h"4344static void lkpi_iicbb_setsda(device_t dev, int val);45static void lkpi_iicbb_setscl(device_t dev, int val);46static int lkpi_iicbb_getscl(device_t dev);47static int lkpi_iicbb_getsda(device_t dev);48static int lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);49static int lkpi_iicbb_pre_xfer(device_t dev);50static void lkpi_iicbb_post_xfer(device_t dev);5152struct lkpi_iicbb_softc {53device_t iicbb;54struct i2c_adapter *adapter;55};5657static struct sx lkpi_sx_i2cbb;5859static void60lkpi_sysinit_i2cbb(void *arg __unused)61{6263sx_init(&lkpi_sx_i2cbb, "lkpi-i2cbb");64}6566static void67lkpi_sysuninit_i2cbb(void *arg __unused)68{6970sx_destroy(&lkpi_sx_i2cbb);71}7273SYSINIT(lkpi_i2cbb, SI_SUB_DRIVERS, SI_ORDER_ANY,74lkpi_sysinit_i2cbb, NULL);75SYSUNINIT(lkpi_i2cbb, SI_SUB_DRIVERS, SI_ORDER_ANY,76lkpi_sysuninit_i2cbb, NULL);7778static int79lkpi_iicbb_probe(device_t dev)80{8182device_set_desc(dev, "LinuxKPI I2CBB");83return (BUS_PROBE_NOWILDCARD);84}8586static int87lkpi_iicbb_attach(device_t dev)88{89struct lkpi_iicbb_softc *sc;9091sc = device_get_softc(dev);92sc->iicbb = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY);93if (sc->iicbb == NULL) {94device_printf(dev, "Couldn't add iicbb child, aborting\n");95return (ENXIO);96}97bus_attach_children(dev);98return (0);99}100101static int102lkpi_iicbb_detach(device_t dev)103{104struct lkpi_iicbb_softc *sc;105106sc = device_get_softc(dev);107if (sc->iicbb)108device_delete_child(dev, sc->iicbb);109return (0);110}111112static int113lkpi_iicbb_add_adapter(device_t dev, struct i2c_adapter *adapter)114{115struct lkpi_iicbb_softc *sc;116struct i2c_algo_bit_data *algo_data;117118sc = device_get_softc(dev);119sc->adapter = adapter;120121/*122* Set iicbb timing parameters deriving speed from the protocol delay.123*/124algo_data = adapter->algo_data;125if (algo_data->udelay != 0)126IICBUS_RESET(sc->iicbb, 1000000 / algo_data->udelay, 0, NULL);127return (0);128}129130static struct i2c_adapter *131lkpi_iicbb_get_adapter(device_t dev)132{133struct lkpi_iicbb_softc *sc;134135sc = device_get_softc(dev);136return (sc->adapter);137}138139static device_method_t lkpi_iicbb_methods[] = {140/* device interface */141DEVMETHOD(device_probe, lkpi_iicbb_probe),142DEVMETHOD(device_attach, lkpi_iicbb_attach),143DEVMETHOD(device_detach, lkpi_iicbb_detach),144DEVMETHOD(device_suspend, bus_generic_suspend),145DEVMETHOD(device_resume, bus_generic_resume),146147/* iicbb interface */148DEVMETHOD(iicbb_setsda, lkpi_iicbb_setsda),149DEVMETHOD(iicbb_setscl, lkpi_iicbb_setscl),150DEVMETHOD(iicbb_getsda, lkpi_iicbb_getsda),151DEVMETHOD(iicbb_getscl, lkpi_iicbb_getscl),152DEVMETHOD(iicbb_reset, lkpi_iicbb_reset),153DEVMETHOD(iicbb_pre_xfer, lkpi_iicbb_pre_xfer),154DEVMETHOD(iicbb_post_xfer, lkpi_iicbb_post_xfer),155156/* lkpi_iicbb interface */157DEVMETHOD(lkpi_iic_add_adapter, lkpi_iicbb_add_adapter),158DEVMETHOD(lkpi_iic_get_adapter, lkpi_iicbb_get_adapter),159160DEVMETHOD_END161};162163driver_t lkpi_iicbb_driver = {164"lkpi_iicbb",165lkpi_iicbb_methods,166sizeof(struct lkpi_iicbb_softc),167};168169DRIVER_MODULE(lkpi_iicbb, drmn, lkpi_iicbb_driver, 0, 0);170DRIVER_MODULE(lkpi_iicbb, drm, lkpi_iicbb_driver, 0, 0);171DRIVER_MODULE(iicbb, lkpi_iicbb, iicbb_driver, 0, 0);172MODULE_DEPEND(linuxkpi, iicbb, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);173174static void175lkpi_iicbb_setsda(device_t dev, int val)176{177struct lkpi_iicbb_softc *sc;178struct i2c_algo_bit_data *algo_data;179180sc = device_get_softc(dev);181algo_data = sc->adapter->algo_data;182algo_data->setsda(algo_data->data, val);183}184185static void186lkpi_iicbb_setscl(device_t dev, int val)187{188struct lkpi_iicbb_softc *sc;189struct i2c_algo_bit_data *algo_data;190191sc = device_get_softc(dev);192algo_data = sc->adapter->algo_data;193algo_data->setscl(algo_data->data, val);194}195196static int197lkpi_iicbb_getscl(device_t dev)198{199struct lkpi_iicbb_softc *sc;200struct i2c_algo_bit_data *algo_data;201int ret;202203sc = device_get_softc(dev);204algo_data = sc->adapter->algo_data;205ret = algo_data->getscl(algo_data->data);206return (ret);207}208209static int210lkpi_iicbb_getsda(device_t dev)211{212struct lkpi_iicbb_softc *sc;213struct i2c_algo_bit_data *algo_data;214int ret;215216sc = device_get_softc(dev);217algo_data = sc->adapter->algo_data;218ret = algo_data->getsda(algo_data->data);219return (ret);220}221222static int223lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)224{225226/* That doesn't seems to be supported in linux */227return (0);228}229230static int231lkpi_iicbb_pre_xfer(device_t dev)232{233struct lkpi_iicbb_softc *sc;234struct i2c_algo_bit_data *algo_data;235int rc = 0;236237sc = device_get_softc(dev);238algo_data = sc->adapter->algo_data;239if (algo_data->pre_xfer != 0)240rc = algo_data->pre_xfer(sc->adapter);241return (rc);242}243244static void245lkpi_iicbb_post_xfer(device_t dev)246{247struct lkpi_iicbb_softc *sc;248struct i2c_algo_bit_data *algo_data;249250sc = device_get_softc(dev);251algo_data = sc->adapter->algo_data;252if (algo_data->post_xfer != NULL)253algo_data->post_xfer(sc->adapter);254}255256int257lkpi_i2cbb_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,258int nmsgs)259{260struct iic_msg *bsd_msgs;261int ret = ENXIO;262263linux_set_current(curthread);264265bsd_msgs = malloc(sizeof(struct iic_msg) * nmsgs,266M_DEVBUF, M_WAITOK | M_ZERO);267268for (int i = 0; i < nmsgs; i++) {269bsd_msgs[i].slave = msgs[i].addr << 1;270bsd_msgs[i].len = msgs[i].len;271bsd_msgs[i].buf = msgs[i].buf;272if (msgs[i].flags & I2C_M_RD)273bsd_msgs[i].flags |= IIC_M_RD;274if (msgs[i].flags & I2C_M_NOSTART)275bsd_msgs[i].flags |= IIC_M_NOSTART;276}277278for (int unit = 0; ; unit++) {279device_t child;280struct lkpi_iicbb_softc *sc;281282child = device_find_child(adapter->dev.parent->bsddev,283"lkpi_iicbb", unit);284if (child == NULL)285break;286if (adapter == LKPI_IIC_GET_ADAPTER(child)) {287sc = device_get_softc(child);288ret = IICBUS_TRANSFER(sc->iicbb, bsd_msgs, nmsgs);289ret = iic2errno(ret);290break;291}292}293294free(bsd_msgs, M_DEVBUF);295296if (ret != 0)297return (-ret);298return (nmsgs);299}300301int302lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)303{304device_t lkpi_iicbb;305306if (bootverbose)307device_printf(adapter->dev.parent->bsddev,308"Adding i2c adapter %s\n", adapter->name);309sx_xlock(&lkpi_sx_i2cbb);310lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb",311DEVICE_UNIT_ANY);312if (lkpi_iicbb == NULL) {313device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");314sx_xunlock(&lkpi_sx_i2cbb);315return (ENXIO);316}317318bus_topo_lock();319bus_attach_children(adapter->dev.parent->bsddev);320bus_topo_unlock();321LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter);322sx_xunlock(&lkpi_sx_i2cbb);323return (0);324}325326327