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
usb_read(struct i2ctinyusb_softc * sc,int cmd,int value,int index,void * data,int len)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
usb_write(struct i2ctinyusb_softc * sc,int cmd,int value,int index,void * data,int len)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
i2ctinyusb_probe(device_t dev)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
i2ctinyusb_attach(device_t dev)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
i2ctinyusb_detach(device_t dev)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 device_delete_children(dev);
19754e231b3SDenis Bodor
19854e231b3SDenis Bodor mtx_destroy(&sc->sc_mtx);
19954e231b3SDenis Bodor
20054e231b3SDenis Bodor return (0);
20154e231b3SDenis Bodor }
20254e231b3SDenis Bodor
20354e231b3SDenis Bodor static int
i2ctinyusb_transfer(device_t dev,struct iic_msg * msgs,uint32_t nmsgs)20454e231b3SDenis Bodor i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
20554e231b3SDenis Bodor {
20654e231b3SDenis Bodor struct i2ctinyusb_softc *sc;
20754e231b3SDenis Bodor uint32_t i;
20854e231b3SDenis Bodor int ret = 0;
20954e231b3SDenis Bodor int cmd = CMD_I2C_IO;
21054e231b3SDenis Bodor struct iic_msg *pmsg;
21154e231b3SDenis Bodor unsigned char pstatus;
21254e231b3SDenis Bodor
21354e231b3SDenis Bodor sc = device_get_softc(dev);
21454e231b3SDenis Bodor
21554e231b3SDenis Bodor mtx_lock(&sc->sc_mtx);
21654e231b3SDenis Bodor
21754e231b3SDenis Bodor for (i = 0; i < nmsgs; i++) {
21854e231b3SDenis Bodor pmsg = &msgs[i];
21954e231b3SDenis Bodor if (i == 0)
22054e231b3SDenis Bodor cmd |= CMD_I2C_IO_BEGIN;
22154e231b3SDenis Bodor if (i == nmsgs - 1)
22254e231b3SDenis Bodor cmd |= CMD_I2C_IO_END;
22354e231b3SDenis Bodor
22454e231b3SDenis Bodor if ((msgs[i].flags & IIC_M_RD) != 0) {
22554e231b3SDenis Bodor if ((ret = usb_read(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
22654e231b3SDenis Bodor pmsg->len)) != pmsg->len) {
22754e231b3SDenis Bodor printf("Read error: got %u\n", ret);
22854e231b3SDenis Bodor ret = EIO;
22954e231b3SDenis Bodor goto out;
23054e231b3SDenis Bodor }
23154e231b3SDenis Bodor } else {
23254e231b3SDenis Bodor if ((ret = usb_write(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
23354e231b3SDenis Bodor pmsg->len)) != pmsg->len) {
23454e231b3SDenis Bodor printf("Write error: got %u\n", ret);
23554e231b3SDenis Bodor ret = EIO;
23654e231b3SDenis Bodor goto out;
23754e231b3SDenis Bodor }
23854e231b3SDenis Bodor
23954e231b3SDenis Bodor }
24054e231b3SDenis Bodor // check status
24154e231b3SDenis Bodor if ((ret = usb_read(sc, CMD_GET_STATUS, 0, 0, &pstatus, 1)) != 1) {
24254e231b3SDenis Bodor ret = EIO;
24354e231b3SDenis Bodor goto out;
24454e231b3SDenis Bodor }
24554e231b3SDenis Bodor
24654e231b3SDenis Bodor if (pstatus == STATUS_ADDRESS_NAK) {
24754e231b3SDenis Bodor ret = EIO;
24854e231b3SDenis Bodor goto out;
24954e231b3SDenis Bodor }
25054e231b3SDenis Bodor }
25154e231b3SDenis Bodor
25254e231b3SDenis Bodor ret = 0;
25354e231b3SDenis Bodor
25454e231b3SDenis Bodor out:
25554e231b3SDenis Bodor mtx_unlock(&sc->sc_mtx);
25654e231b3SDenis Bodor return (ret);
25754e231b3SDenis Bodor }
25854e231b3SDenis Bodor
25954e231b3SDenis Bodor static int
i2ctinyusb_reset(device_t dev,u_char speed,u_char addr,u_char * oldaddr)26054e231b3SDenis Bodor i2ctinyusb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
26154e231b3SDenis Bodor {
26254e231b3SDenis Bodor struct i2ctinyusb_softc *sc;
26354e231b3SDenis Bodor int ret;
26454e231b3SDenis Bodor
26554e231b3SDenis Bodor sc = device_get_softc(dev);
26654e231b3SDenis Bodor
26754e231b3SDenis Bodor mtx_lock(&sc->sc_mtx);
26854e231b3SDenis Bodor ret = usb_write(sc, CMD_SET_DELAY, 10, 0, NULL, 0);
26954e231b3SDenis Bodor mtx_unlock(&sc->sc_mtx);
27054e231b3SDenis Bodor
27154e231b3SDenis Bodor if (ret < 0)
27254e231b3SDenis Bodor printf("i2ctinyusb_reset error!\n");
27354e231b3SDenis Bodor
27454e231b3SDenis Bodor return (0);
27554e231b3SDenis Bodor }
27654e231b3SDenis Bodor
27754e231b3SDenis Bodor static device_method_t i2ctinyusb_methods[] = {
27854e231b3SDenis Bodor /* Device interface */
27954e231b3SDenis Bodor DEVMETHOD(device_probe, i2ctinyusb_probe),
28054e231b3SDenis Bodor DEVMETHOD(device_attach, i2ctinyusb_attach),
28154e231b3SDenis Bodor DEVMETHOD(device_detach, i2ctinyusb_detach),
28254e231b3SDenis Bodor
28354e231b3SDenis Bodor /* I2C methods */
28454e231b3SDenis Bodor DEVMETHOD(iicbus_transfer, i2ctinyusb_transfer),
28554e231b3SDenis Bodor DEVMETHOD(iicbus_reset, i2ctinyusb_reset),
28654e231b3SDenis Bodor DEVMETHOD(iicbus_callback, iicbus_null_callback),
28754e231b3SDenis Bodor
28854e231b3SDenis Bodor DEVMETHOD_END
28954e231b3SDenis Bodor };
29054e231b3SDenis Bodor
29154e231b3SDenis Bodor static driver_t i2ctinyusb_driver = {
29254e231b3SDenis Bodor .name = "iichb",
29354e231b3SDenis Bodor .methods = i2ctinyusb_methods,
29454e231b3SDenis Bodor .size = sizeof(struct i2ctinyusb_softc),
29554e231b3SDenis Bodor };
29654e231b3SDenis Bodor
29754e231b3SDenis Bodor DRIVER_MODULE(i2ctinyusb, uhub, i2ctinyusb_driver, NULL, NULL);
29854e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, usb, 1, 1, 1);
29954e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
30054e231b3SDenis Bodor MODULE_VERSION(i2ctinyusb, 1);
30154e231b3SDenis Bodor
30254e231b3SDenis Bodor /* vi: set ts=8 sw=8: */
303