1 /*- 2 * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/malloc.h> 34 35 #include <dev/iicbus/iicbus.h> 36 #include <dev/iicbus/iiconf.h> 37 38 #include <linux/device.h> 39 #include <linux/i2c.h> 40 #include <linux/i2c-algo-bit.h> 41 #include <linux/list.h> 42 #include <linux/pci.h> 43 44 #include "iicbus_if.h" 45 #include "iicbb_if.h" 46 #include "lkpi_iic_if.h" 47 48 static int lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); 49 static int lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr); 50 51 struct lkpi_iic_softc { 52 device_t iicbus; 53 struct i2c_adapter *adapter; 54 }; 55 56 static int 57 lkpi_iic_probe(device_t dev) 58 { 59 60 device_set_desc(dev, "LinuxKPI I2C"); 61 return (BUS_PROBE_NOWILDCARD); 62 } 63 64 static int 65 lkpi_iic_attach(device_t dev) 66 { 67 struct lkpi_iic_softc *sc; 68 69 sc = device_get_softc(dev); 70 sc->iicbus = device_add_child(dev, "iicbus", -1); 71 if (sc->iicbus == NULL) { 72 device_printf(dev, "Couldn't add iicbus child, aborting\n"); 73 return (ENXIO); 74 } 75 bus_generic_attach(dev); 76 return (0); 77 } 78 79 static int 80 lkpi_iic_detach(device_t dev) 81 { 82 struct lkpi_iic_softc *sc; 83 84 sc = device_get_softc(dev); 85 if (sc->iicbus) 86 device_delete_child(dev, sc->iicbus); 87 return (0); 88 } 89 90 static int 91 lkpi_iic_add_adapter(device_t dev, struct i2c_adapter *adapter) 92 { 93 struct lkpi_iic_softc *sc; 94 95 sc = device_get_softc(dev); 96 sc->adapter = adapter; 97 98 return (0); 99 } 100 101 static device_method_t lkpi_iic_methods[] = { 102 /* device interface */ 103 DEVMETHOD(device_probe, lkpi_iic_probe), 104 DEVMETHOD(device_attach, lkpi_iic_attach), 105 DEVMETHOD(device_detach, lkpi_iic_detach), 106 DEVMETHOD(device_suspend, bus_generic_suspend), 107 DEVMETHOD(device_resume, bus_generic_resume), 108 109 /* iicbus interface */ 110 DEVMETHOD(iicbus_transfer, lkpi_i2c_transfer), 111 DEVMETHOD(iicbus_reset, lkpi_i2c_reset), 112 DEVMETHOD(iicbus_callback, iicbus_null_callback), 113 114 /* lkpi_iic interface */ 115 DEVMETHOD(lkpi_iic_add_adapter, lkpi_iic_add_adapter), 116 117 DEVMETHOD_END 118 }; 119 120 devclass_t lkpi_iic_devclass; 121 122 driver_t lkpi_iic_driver = { 123 "lkpi_iic", 124 lkpi_iic_methods, 125 sizeof(struct lkpi_iic_softc), 126 }; 127 128 DRIVER_MODULE(lkpi_iic, drmn, lkpi_iic_driver, lkpi_iic_devclass, 0, 0); 129 DRIVER_MODULE(iicbus, lkpi_iic, iicbus_driver, iicbus_devclass, 0, 0); 130 MODULE_DEPEND(linuxkpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 131 132 static int 133 lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 134 { 135 136 /* That doesn't seems to be supported in linux */ 137 return (0); 138 } 139 140 static int 141 lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 142 { 143 struct lkpi_iic_softc *sc; 144 struct i2c_msg *linux_msgs; 145 int i, ret = 0; 146 147 sc = device_get_softc(dev); 148 if (sc->adapter == NULL) 149 return (ENXIO); 150 linux_set_current(curthread); 151 152 linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs, 153 M_DEVBUF, M_WAITOK | M_ZERO); 154 155 for (i = 0; i < nmsgs; i++) { 156 linux_msgs[i].addr = msgs[i].slave; 157 linux_msgs[i].len = msgs[i].len; 158 linux_msgs[i].buf = msgs[i].buf; 159 if (msgs[i].flags & IIC_M_RD) { 160 linux_msgs[i].flags |= I2C_M_RD; 161 for (int j = 0; j < msgs[i].len; j++) 162 msgs[i].buf[j] = 0; 163 } 164 if (msgs[i].flags & IIC_M_NOSTART) 165 linux_msgs[i].flags |= I2C_M_NOSTART; 166 } 167 ret = i2c_transfer(sc->adapter, linux_msgs, nmsgs); 168 free(linux_msgs, M_DEVBUF); 169 170 if (ret < 0) 171 return (-ret); 172 return (0); 173 } 174 175 int 176 lkpi_i2c_add_adapter(struct i2c_adapter *adapter) 177 { 178 device_t lkpi_iic; 179 int error; 180 181 if (bootverbose) 182 device_printf(adapter->dev.parent->bsddev, 183 "Adding i2c adapter %s\n", adapter->name); 184 lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1); 185 if (lkpi_iic == NULL) { 186 device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n"); 187 return (ENXIO); 188 } 189 190 error = bus_generic_attach(adapter->dev.parent->bsddev); 191 if (error) { 192 device_printf(adapter->dev.parent->bsddev, 193 "failed to attach child: error %d\n", error); 194 return (ENXIO); 195 } 196 LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter); 197 return (0); 198 } 199 200 int 201 lkpi_i2c_del_adapter(struct i2c_adapter *adapter) 202 { 203 device_t child; 204 205 if (bootverbose) 206 device_printf(adapter->dev.parent->bsddev, 207 "Removing i2c adapter %s\n", adapter->name); 208 209 child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", -1); 210 if (child != NULL) 211 device_delete_child(adapter->dev.parent->bsddev, child); 212 213 child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1); 214 if (child != NULL) 215 device_delete_child(adapter->dev.parent->bsddev, child); 216 217 return (0); 218 } 219