1/* $NetBSD: auvitek_i2c.c,v 1.8 2022/03/13 12:49:36 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* 30 * Auvitek AU0828 USB controller - I2C access ops 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: auvitek_i2c.c,v 1.8 2022/03/13 12:49:36 riastradh Exp $"); 35 36#ifdef _KERNEL_OPT 37#include "opt_usb.h" 38#endif 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/device.h> 43#include <sys/conf.h> 44#include <sys/bus.h> 45#include <sys/module.h> 46 47#include <dev/usb/usb.h> 48#include <dev/usb/usbdi.h> 49#include <dev/usb/usbdi_util.h> 50#include <dev/usb/usbdevs.h> 51 52#include <dev/i2c/i2cvar.h> 53 54#include <dev/usb/auvitekreg.h> 55#include <dev/usb/auvitekvar.h> 56 57/* #define AUVITEK_I2C_DEBUG */ 58 59static int auvitek_i2c_exec(void *, i2c_op_t, i2c_addr_t, 60 const void *, size_t, void *, size_t, int); 61 62static int auvitek_i2c_read(struct auvitek_softc *, i2c_addr_t, 63 uint8_t *, size_t); 64static int auvitek_i2c_write(struct auvitek_softc *, i2c_addr_t, 65 const uint8_t *, size_t); 66static bool auvitek_i2c_wait(struct auvitek_softc *); 67static bool auvitek_i2c_wait_rdack(struct auvitek_softc *); 68static bool auvitek_i2c_wait_rddone(struct auvitek_softc *); 69static bool auvitek_i2c_wait_wrdone(struct auvitek_softc *); 70 71int 72auvitek_i2c_attach(struct auvitek_softc *sc) 73{ 74 75 iic_tag_init(&sc->sc_i2c); 76 sc->sc_i2c.ic_cookie = sc; 77 sc->sc_i2c.ic_exec = auvitek_i2c_exec; 78 79 auvitek_i2c_rescan(sc, NULL, NULL); 80 81 sc->sc_i2c_attached = true; 82 83 return 0; 84} 85 86int 87auvitek_i2c_detach(struct auvitek_softc *sc, int flags) 88{ 89 90 if (!sc->sc_i2c_attached) 91 return 0; 92 93 iic_tag_fini(&sc->sc_i2c); 94 95 return 0; 96} 97 98void 99auvitek_i2c_rescan(struct auvitek_softc *sc, const char *ifattr, 100 const int *locs) 101{ 102#ifdef AUVITEK_I2C_DEBUG 103 struct i2cbus_attach_args iba; 104 105 if (ifattr_match(ifattr, "i2cbus") && sc->sc_i2cdev == NULL) { 106 memset(&iba, 0, sizeof(iba)); 107 iba.iba_tag = &sc->sc_i2c; 108 sc->sc_i2cdev = config_found(sc->sc_dev, &iba, iicbus_print, 109 CFARGS(.iattr = "i2cbus")); 110 } 111#endif 112} 113 114void 115auvitek_i2c_childdet(struct auvitek_softc *sc, device_t child) 116{ 117 if (sc->sc_i2cdev == child) 118 sc->sc_i2cdev = NULL; 119} 120 121static int 122auvitek_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, 123 const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags) 124{ 125 struct auvitek_softc *sc = opaque; 126 127 if (I2C_OP_READ_P(op)) 128 return auvitek_i2c_read(sc, addr, vbuf, buflen); 129 else 130 return auvitek_i2c_write(sc, addr, cmd, cmdlen); 131} 132 133static int 134auvitek_i2c_read(struct auvitek_softc *sc, i2c_addr_t addr, 135 uint8_t *buf, size_t buflen) 136{ 137 uint8_t v; 138 unsigned int i; 139 140 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1); 141 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv); 142 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1); 143 144 if (buflen == 0) { 145 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, 146 AU0828_I2C_TRIGGER_RD); 147 if (auvitek_i2c_wait_rdack(sc) == false) 148 return EBUSY; 149 return 0; 150 } 151 152 for (i = 0; i < buflen; i++) { 153 v = AU0828_I2C_TRIGGER_RD; 154 if (i < (buflen - 1)) 155 v |= AU0828_I2C_TRIGGER_HOLD; 156 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v); 157 158 if (auvitek_i2c_wait_rddone(sc) == false) 159 return EBUSY; 160 161 buf[i] = auvitek_read_1(sc, AU0828_REG_I2C_FIFORD); 162 } 163 164 if (auvitek_i2c_wait(sc) == false) 165 return EBUSY; 166 167 return 0; 168} 169 170static int 171auvitek_i2c_write(struct auvitek_softc *sc, i2c_addr_t addr, 172 const uint8_t *buf, size_t buflen) 173{ 174 uint8_t v; 175 unsigned int i, fifolen; 176 177 auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1); 178 auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv); 179 auvitek_write_1(sc, AU0828_REG_I2C_DSTADDR, addr << 1); 180 181 if (buflen == 0) { 182 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, 183 AU0828_I2C_TRIGGER_RD); 184 if (auvitek_i2c_wait(sc) == false) 185 return EBUSY; 186 if (auvitek_i2c_wait_rdack(sc) == false) 187 return EBUSY; 188 return 0; 189 } 190 191 fifolen = 0; 192 for (i = 0; i < buflen; i++) { 193 v = AU0828_I2C_TRIGGER_WR; 194 if (i < (buflen - 1)) 195 v |= AU0828_I2C_TRIGGER_HOLD; 196 197 auvitek_write_1(sc, AU0828_REG_I2C_FIFOWR, buf[i]); 198 ++fifolen; 199 200 if (fifolen == 4 || i == (buflen - 1)) { 201 auvitek_write_1(sc, AU0828_REG_I2C_TRIGGER, v); 202 fifolen = 0; 203 204 if (auvitek_i2c_wait_wrdone(sc) == false) 205 return EBUSY; 206 } 207 } 208 209 if (auvitek_i2c_wait(sc) == false) 210 return EBUSY; 211 212 return 0; 213} 214 215static bool 216auvitek_i2c_wait(struct auvitek_softc *sc) 217{ 218 uint8_t status; 219 int retry = 1000; 220 221 while (--retry > 0) { 222 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS); 223 if (!(status & AU0828_I2C_STATUS_BUSY)) 224 break; 225 delay(10); 226 } 227 if (retry == 0) 228 return false; 229 230 return true; 231} 232 233static bool 234auvitek_i2c_wait_rdack(struct auvitek_softc *sc) 235{ 236 uint8_t status; 237 int retry = 1000; 238 239 while (--retry > 0) { 240 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS); 241 if (!(status & AU0828_I2C_STATUS_NO_RD_ACK)) 242 break; 243 delay(10); 244 } 245 if (retry == 0) 246 return false; 247 248 return true; 249} 250 251static bool 252auvitek_i2c_wait_rddone(struct auvitek_softc *sc) 253{ 254 uint8_t status; 255 int retry = 1000; 256 257 while (--retry > 0) { 258 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS); 259 if (status & AU0828_I2C_STATUS_RD_DONE) 260 break; 261 delay(10); 262 } 263 if (retry == 0) 264 return false; 265 266 return true; 267} 268 269static bool 270auvitek_i2c_wait_wrdone(struct auvitek_softc *sc) 271{ 272 uint8_t status; 273 int retry = 1000; 274 275 while (--retry > 0) { 276 status = auvitek_read_1(sc, AU0828_REG_I2C_STATUS); 277 if (status & AU0828_I2C_STATUS_WR_DONE) 278 break; 279 delay(10); 280 } 281 if (retry == 0) 282 return false; 283 284 return true; 285} 286