154e231b3SDenis Bodor /*- 254e231b3SDenis Bodor * Copyright (c) 2024 Denis Bodor <dbodor@rollmops.ninja> 354e231b3SDenis Bodor * All rights reserved. 454e231b3SDenis Bodor * 554e231b3SDenis Bodor * Redistribution and use in source and binary forms, with or without 654e231b3SDenis Bodor * modification, are permitted provided that the following conditions 754e231b3SDenis Bodor * are met: 854e231b3SDenis Bodor * 1. Redistributions of source code must retain the above copyright 954e231b3SDenis Bodor * notice, this list of conditions and the following disclaimer. 1054e231b3SDenis Bodor * 2. Redistributions in binary form must reproduce the above copyright 1154e231b3SDenis Bodor * notice, this list of conditions and the following disclaimer in the 1254e231b3SDenis Bodor * documentation and/or other materials provided with the distribution. 1354e231b3SDenis Bodor * 1454e231b3SDenis Bodor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1554e231b3SDenis Bodor * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1654e231b3SDenis Bodor * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1754e231b3SDenis Bodor * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1854e231b3SDenis Bodor * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1954e231b3SDenis Bodor * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2054e231b3SDenis Bodor * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2154e231b3SDenis Bodor * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2254e231b3SDenis Bodor * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2354e231b3SDenis Bodor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2454e231b3SDenis Bodor * SUCH DAMAGE. 2554e231b3SDenis Bodor */ 2654e231b3SDenis Bodor 2754e231b3SDenis Bodor /* 2854e231b3SDenis Bodor * i2c-tiny-usb, DIY USB to IIC bridge (using AVR or RP2040) from 2954e231b3SDenis Bodor * Till Harbaum & Nicolai Electronics 3054e231b3SDenis Bodor * See : 3154e231b3SDenis Bodor * https://github.com/harbaum/I2C-Tiny-USB 3254e231b3SDenis Bodor * and 3354e231b3SDenis Bodor * https://github.com/Nicolai-Electronics/rp2040-i2c-interface 3454e231b3SDenis Bodor */ 3554e231b3SDenis Bodor 3654e231b3SDenis Bodor #include <sys/systm.h> 3754e231b3SDenis Bodor #include <sys/kernel.h> 3854e231b3SDenis Bodor #include <sys/bus.h> 3954e231b3SDenis Bodor #include <sys/module.h> 4054e231b3SDenis Bodor #include <sys/mutex.h> 4154e231b3SDenis Bodor #include <sys/condvar.h> 4254e231b3SDenis Bodor #include <sys/unistd.h> 4354e231b3SDenis Bodor #include <dev/usb/usb.h> 4454e231b3SDenis Bodor #include <dev/usb/usbdi.h> 4554e231b3SDenis Bodor #include <dev/usb/usbhid.h> 4654e231b3SDenis Bodor #include <dev/usb/usb_device.h> 4754e231b3SDenis Bodor 4854e231b3SDenis Bodor #include <dev/iicbus/iiconf.h> 4954e231b3SDenis Bodor #include <dev/iicbus/iicbus.h> 5054e231b3SDenis Bodor #include "iicbus_if.h" 5154e231b3SDenis Bodor 5254e231b3SDenis Bodor // commands via USB, must match command ids in the firmware 5354e231b3SDenis Bodor #define CMD_ECHO 0 5454e231b3SDenis Bodor #define CMD_GET_FUNC 1 5554e231b3SDenis Bodor #define CMD_SET_DELAY 2 5654e231b3SDenis Bodor #define CMD_GET_STATUS 3 5754e231b3SDenis Bodor #define CMD_I2C_IO 4 5854e231b3SDenis Bodor #define CMD_SET_LED 8 5954e231b3SDenis Bodor #define CMD_I2C_IO_BEGIN (1 << 0) 6054e231b3SDenis Bodor #define CMD_I2C_IO_END (1 << 1) 6154e231b3SDenis Bodor #define STATUS_IDLE 0 6254e231b3SDenis Bodor #define STATUS_ADDRESS_ACK 1 6354e231b3SDenis Bodor #define STATUS_ADDRESS_NAK 2 6454e231b3SDenis Bodor 6554e231b3SDenis Bodor struct i2ctinyusb_softc { 6654e231b3SDenis Bodor struct usb_device *sc_udev; 6754e231b3SDenis Bodor device_t sc_iic_dev; 6854e231b3SDenis Bodor device_t iicbus_dev; 6954e231b3SDenis Bodor struct mtx sc_mtx; 7054e231b3SDenis Bodor }; 7154e231b3SDenis Bodor 7254e231b3SDenis Bodor #define USB_VENDOR_EZPROTOTYPES 0x1c40 7354e231b3SDenis Bodor #define USB_VENDOR_FTDI 0x0403 7454e231b3SDenis Bodor 7554e231b3SDenis Bodor static const STRUCT_USB_HOST_ID i2ctinyusb_devs[] = { 7654e231b3SDenis Bodor { USB_VPI(USB_VENDOR_EZPROTOTYPES, 0x0534, 0) }, 7754e231b3SDenis Bodor { USB_VPI(USB_VENDOR_FTDI, 0xc631, 0) }, 7854e231b3SDenis Bodor }; 7954e231b3SDenis Bodor 8054e231b3SDenis Bodor /* Prototypes. */ 8154e231b3SDenis Bodor static int i2ctinyusb_probe(device_t dev); 8254e231b3SDenis Bodor static int i2ctinyusb_attach(device_t dev); 8354e231b3SDenis Bodor static int i2ctinyusb_detach(device_t dev); 8454e231b3SDenis Bodor static int i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs, 8554e231b3SDenis Bodor uint32_t nmsgs); 8654e231b3SDenis Bodor static int i2ctinyusb_reset(device_t dev, u_char speed, u_char addr, 8754e231b3SDenis Bodor u_char *oldaddr); 8854e231b3SDenis Bodor 8954e231b3SDenis Bodor static int 9054e231b3SDenis Bodor usb_read(struct i2ctinyusb_softc *sc, int cmd, int value, int index, 9154e231b3SDenis Bodor void *data, int len) 9254e231b3SDenis Bodor { 9354e231b3SDenis Bodor int error; 9454e231b3SDenis Bodor struct usb_device_request req; 9554e231b3SDenis Bodor uint16_t actlen; 9654e231b3SDenis Bodor 9754e231b3SDenis Bodor req.bmRequestType = UT_READ_VENDOR_INTERFACE; 9854e231b3SDenis Bodor req.bRequest = cmd; 9954e231b3SDenis Bodor USETW(req.wValue, value); 10054e231b3SDenis Bodor USETW(req.wIndex, (index >> 1)); 10154e231b3SDenis Bodor USETW(req.wLength, len); 10254e231b3SDenis Bodor 10354e231b3SDenis Bodor error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0, 10454e231b3SDenis Bodor &actlen, 2000); 10554e231b3SDenis Bodor 10654e231b3SDenis Bodor if (error) 10754e231b3SDenis Bodor actlen = -1; 10854e231b3SDenis Bodor 10954e231b3SDenis Bodor return (actlen); 11054e231b3SDenis Bodor } 11154e231b3SDenis Bodor 11254e231b3SDenis Bodor static int 11354e231b3SDenis Bodor usb_write(struct i2ctinyusb_softc *sc, int cmd, int value, int index, 11454e231b3SDenis Bodor void *data, int len) 11554e231b3SDenis Bodor { 11654e231b3SDenis Bodor int error; 11754e231b3SDenis Bodor struct usb_device_request req; 11854e231b3SDenis Bodor uint16_t actlen; 11954e231b3SDenis Bodor 12054e231b3SDenis Bodor req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; 12154e231b3SDenis Bodor req.bRequest = cmd; 12254e231b3SDenis Bodor USETW(req.wValue, value); 12354e231b3SDenis Bodor USETW(req.wIndex, (index >> 1)); 12454e231b3SDenis Bodor USETW(req.wLength, len); 12554e231b3SDenis Bodor 12654e231b3SDenis Bodor error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0, 12754e231b3SDenis Bodor &actlen, 2000); 12854e231b3SDenis Bodor 12954e231b3SDenis Bodor if (error) { 13054e231b3SDenis Bodor actlen = -1; 13154e231b3SDenis Bodor } 13254e231b3SDenis Bodor 13354e231b3SDenis Bodor return (actlen); 13454e231b3SDenis Bodor } 13554e231b3SDenis Bodor 13654e231b3SDenis Bodor static int 13754e231b3SDenis Bodor i2ctinyusb_probe(device_t dev) 13854e231b3SDenis Bodor { 13954e231b3SDenis Bodor struct usb_attach_arg *uaa; 14054e231b3SDenis Bodor 14154e231b3SDenis Bodor uaa = device_get_ivars(dev); 14254e231b3SDenis Bodor 14354e231b3SDenis Bodor if (uaa->usb_mode != USB_MODE_HOST) 14454e231b3SDenis Bodor return (ENXIO); 14554e231b3SDenis Bodor 14654e231b3SDenis Bodor if (usbd_lookup_id_by_uaa(i2ctinyusb_devs, sizeof(i2ctinyusb_devs), 14754e231b3SDenis Bodor uaa) == 0) { 14854e231b3SDenis Bodor device_set_desc(dev, "I2C-Tiny-USB I2C interface"); 14954e231b3SDenis Bodor return (BUS_PROBE_DEFAULT); 15054e231b3SDenis Bodor } 15154e231b3SDenis Bodor 15254e231b3SDenis Bodor return (ENXIO); 15354e231b3SDenis Bodor } 15454e231b3SDenis Bodor 15554e231b3SDenis Bodor static int 15654e231b3SDenis Bodor i2ctinyusb_attach(device_t dev) 15754e231b3SDenis Bodor { 15854e231b3SDenis Bodor struct i2ctinyusb_softc *sc; 15954e231b3SDenis Bodor struct usb_attach_arg *uaa; 16054e231b3SDenis Bodor int err; 16154e231b3SDenis Bodor 16254e231b3SDenis Bodor sc = device_get_softc(dev); 16354e231b3SDenis Bodor 16454e231b3SDenis Bodor uaa = device_get_ivars(dev); 16554e231b3SDenis Bodor device_set_usb_desc(dev); 16654e231b3SDenis Bodor 16754e231b3SDenis Bodor sc->sc_udev = uaa->device; 16854e231b3SDenis Bodor mtx_init(&sc->sc_mtx, "i2ctinyusb lock", NULL, MTX_DEF | MTX_RECURSE); 16954e231b3SDenis Bodor 17054e231b3SDenis Bodor sc->iicbus_dev = device_add_child(dev, "iicbus", -1); 17154e231b3SDenis Bodor if (sc->iicbus_dev == NULL) { 17254e231b3SDenis Bodor device_printf(dev, "iicbus creation failed\n"); 17354e231b3SDenis Bodor err = ENXIO; 17454e231b3SDenis Bodor goto detach; 17554e231b3SDenis Bodor } 176*18250ec6SJohn Baldwin bus_attach_children(dev); 17754e231b3SDenis Bodor 17854e231b3SDenis Bodor return (0); 17954e231b3SDenis Bodor 18054e231b3SDenis Bodor detach: 18154e231b3SDenis Bodor i2ctinyusb_detach(dev); 18254e231b3SDenis Bodor return (err); 18354e231b3SDenis Bodor } 18454e231b3SDenis Bodor 18554e231b3SDenis Bodor static int 18654e231b3SDenis Bodor i2ctinyusb_detach(device_t dev) 18754e231b3SDenis Bodor { 18854e231b3SDenis Bodor struct i2ctinyusb_softc *sc; 18954e231b3SDenis Bodor int err; 19054e231b3SDenis Bodor 19154e231b3SDenis Bodor sc = device_get_softc(dev); 19254e231b3SDenis Bodor 19354e231b3SDenis Bodor err = bus_generic_detach(dev); 19454e231b3SDenis Bodor if (err != 0) 19554e231b3SDenis Bodor return (err); 19654e231b3SDenis Bodor 19754e231b3SDenis Bodor mtx_destroy(&sc->sc_mtx); 19854e231b3SDenis Bodor 19954e231b3SDenis Bodor return (0); 20054e231b3SDenis Bodor } 20154e231b3SDenis Bodor 20254e231b3SDenis Bodor static int 20354e231b3SDenis Bodor i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 20454e231b3SDenis Bodor { 20554e231b3SDenis Bodor struct i2ctinyusb_softc *sc; 20654e231b3SDenis Bodor uint32_t i; 20754e231b3SDenis Bodor int ret = 0; 20854e231b3SDenis Bodor int cmd = CMD_I2C_IO; 20954e231b3SDenis Bodor struct iic_msg *pmsg; 21054e231b3SDenis Bodor unsigned char pstatus; 21154e231b3SDenis Bodor 21254e231b3SDenis Bodor sc = device_get_softc(dev); 21354e231b3SDenis Bodor 21454e231b3SDenis Bodor mtx_lock(&sc->sc_mtx); 21554e231b3SDenis Bodor 21654e231b3SDenis Bodor for (i = 0; i < nmsgs; i++) { 21754e231b3SDenis Bodor pmsg = &msgs[i]; 21854e231b3SDenis Bodor if (i == 0) 21954e231b3SDenis Bodor cmd |= CMD_I2C_IO_BEGIN; 22054e231b3SDenis Bodor if (i == nmsgs - 1) 22154e231b3SDenis Bodor cmd |= CMD_I2C_IO_END; 22254e231b3SDenis Bodor 22354e231b3SDenis Bodor if ((msgs[i].flags & IIC_M_RD) != 0) { 22454e231b3SDenis Bodor if ((ret = usb_read(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf, 22554e231b3SDenis Bodor pmsg->len)) != pmsg->len) { 22654e231b3SDenis Bodor printf("Read error: got %u\n", ret); 22754e231b3SDenis Bodor ret = EIO; 22854e231b3SDenis Bodor goto out; 22954e231b3SDenis Bodor } 23054e231b3SDenis Bodor } else { 23154e231b3SDenis Bodor if ((ret = usb_write(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf, 23254e231b3SDenis Bodor pmsg->len)) != pmsg->len) { 23354e231b3SDenis Bodor printf("Write error: got %u\n", ret); 23454e231b3SDenis Bodor ret = EIO; 23554e231b3SDenis Bodor goto out; 23654e231b3SDenis Bodor } 23754e231b3SDenis Bodor 23854e231b3SDenis Bodor } 23954e231b3SDenis Bodor // check status 24054e231b3SDenis Bodor if ((ret = usb_read(sc, CMD_GET_STATUS, 0, 0, &pstatus, 1)) != 1) { 24154e231b3SDenis Bodor ret = EIO; 24254e231b3SDenis Bodor goto out; 24354e231b3SDenis Bodor } 24454e231b3SDenis Bodor 24554e231b3SDenis Bodor if (pstatus == STATUS_ADDRESS_NAK) { 24654e231b3SDenis Bodor ret = EIO; 24754e231b3SDenis Bodor goto out; 24854e231b3SDenis Bodor } 24954e231b3SDenis Bodor } 25054e231b3SDenis Bodor 25154e231b3SDenis Bodor ret = 0; 25254e231b3SDenis Bodor 25354e231b3SDenis Bodor out: 25454e231b3SDenis Bodor mtx_unlock(&sc->sc_mtx); 25554e231b3SDenis Bodor return (ret); 25654e231b3SDenis Bodor } 25754e231b3SDenis Bodor 25854e231b3SDenis Bodor static int 25954e231b3SDenis Bodor i2ctinyusb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 26054e231b3SDenis Bodor { 26154e231b3SDenis Bodor struct i2ctinyusb_softc *sc; 26254e231b3SDenis Bodor int ret; 26354e231b3SDenis Bodor 26454e231b3SDenis Bodor sc = device_get_softc(dev); 26554e231b3SDenis Bodor 26654e231b3SDenis Bodor mtx_lock(&sc->sc_mtx); 26754e231b3SDenis Bodor ret = usb_write(sc, CMD_SET_DELAY, 10, 0, NULL, 0); 26854e231b3SDenis Bodor mtx_unlock(&sc->sc_mtx); 26954e231b3SDenis Bodor 27054e231b3SDenis Bodor if (ret < 0) 27154e231b3SDenis Bodor printf("i2ctinyusb_reset error!\n"); 27254e231b3SDenis Bodor 27354e231b3SDenis Bodor return (0); 27454e231b3SDenis Bodor } 27554e231b3SDenis Bodor 27654e231b3SDenis Bodor static device_method_t i2ctinyusb_methods[] = { 27754e231b3SDenis Bodor /* Device interface */ 27854e231b3SDenis Bodor DEVMETHOD(device_probe, i2ctinyusb_probe), 27954e231b3SDenis Bodor DEVMETHOD(device_attach, i2ctinyusb_attach), 28054e231b3SDenis Bodor DEVMETHOD(device_detach, i2ctinyusb_detach), 28154e231b3SDenis Bodor 28254e231b3SDenis Bodor /* I2C methods */ 28354e231b3SDenis Bodor DEVMETHOD(iicbus_transfer, i2ctinyusb_transfer), 28454e231b3SDenis Bodor DEVMETHOD(iicbus_reset, i2ctinyusb_reset), 28554e231b3SDenis Bodor DEVMETHOD(iicbus_callback, iicbus_null_callback), 28654e231b3SDenis Bodor 28754e231b3SDenis Bodor DEVMETHOD_END 28854e231b3SDenis Bodor }; 28954e231b3SDenis Bodor 29054e231b3SDenis Bodor static driver_t i2ctinyusb_driver = { 29154e231b3SDenis Bodor .name = "iichb", 29254e231b3SDenis Bodor .methods = i2ctinyusb_methods, 29354e231b3SDenis Bodor .size = sizeof(struct i2ctinyusb_softc), 29454e231b3SDenis Bodor }; 29554e231b3SDenis Bodor 29654e231b3SDenis Bodor DRIVER_MODULE(i2ctinyusb, uhub, i2ctinyusb_driver, NULL, NULL); 29754e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, usb, 1, 1, 1); 29854e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 29954e231b3SDenis Bodor MODULE_VERSION(i2ctinyusb, 1); 30054e231b3SDenis Bodor 30154e231b3SDenis Bodor /* vi: set ts=8 sw=8: */ 302