1580d00f4SEmmanuel Vadot /*- 2580d00f4SEmmanuel Vadot * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 3580d00f4SEmmanuel Vadot * All rights reserved. 4580d00f4SEmmanuel Vadot * 5580d00f4SEmmanuel Vadot * Developed by Semihalf. 6580d00f4SEmmanuel Vadot * 7580d00f4SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 8580d00f4SEmmanuel Vadot * modification, are permitted provided that the following conditions 9580d00f4SEmmanuel Vadot * are met: 10580d00f4SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 11580d00f4SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 12580d00f4SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 13580d00f4SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 14580d00f4SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 15580d00f4SEmmanuel Vadot * 3. Neither the name of MARVELL nor the names of contributors 16580d00f4SEmmanuel Vadot * may be used to endorse or promote products derived from this software 17580d00f4SEmmanuel Vadot * without specific prior written permission. 18580d00f4SEmmanuel Vadot * 19580d00f4SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20580d00f4SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21580d00f4SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22580d00f4SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 23580d00f4SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24580d00f4SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25580d00f4SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26580d00f4SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27580d00f4SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28580d00f4SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29580d00f4SEmmanuel Vadot * SUCH DAMAGE. 30580d00f4SEmmanuel Vadot */ 31580d00f4SEmmanuel Vadot 32580d00f4SEmmanuel Vadot /* 33580d00f4SEmmanuel Vadot * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell 34580d00f4SEmmanuel Vadot * and Allwinner SoCs. Supports master operation only. 35580d00f4SEmmanuel Vadot * 36580d00f4SEmmanuel Vadot * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software 37580d00f4SEmmanuel Vadot * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". 38580d00f4SEmmanuel Vadot */ 39580d00f4SEmmanuel Vadot 40580d00f4SEmmanuel Vadot #include <sys/param.h> 41580d00f4SEmmanuel Vadot #include <sys/systm.h> 42580d00f4SEmmanuel Vadot #include <sys/bus.h> 43580d00f4SEmmanuel Vadot #include <sys/kernel.h> 44580d00f4SEmmanuel Vadot #include <sys/lock.h> 45580d00f4SEmmanuel Vadot #include <sys/mutex.h> 46580d00f4SEmmanuel Vadot #include <sys/module.h> 47580d00f4SEmmanuel Vadot #include <sys/resource.h> 48580d00f4SEmmanuel Vadot #include <sys/rman.h> 49580d00f4SEmmanuel Vadot #include <sys/sysctl.h> 50580d00f4SEmmanuel Vadot 51580d00f4SEmmanuel Vadot #include <machine/_inttypes.h> 52580d00f4SEmmanuel Vadot #include <machine/bus.h> 53580d00f4SEmmanuel Vadot #include <machine/resource.h> 54580d00f4SEmmanuel Vadot 55580d00f4SEmmanuel Vadot #include <dev/iicbus/iiconf.h> 56580d00f4SEmmanuel Vadot #include <dev/iicbus/iicbus.h> 57580d00f4SEmmanuel Vadot 58580d00f4SEmmanuel Vadot #include <dev/iicbus/controller/twsi/twsi.h> 59580d00f4SEmmanuel Vadot 60580d00f4SEmmanuel Vadot #include "iicbus_if.h" 61580d00f4SEmmanuel Vadot 62580d00f4SEmmanuel Vadot #define TWSI_CONTROL_ACK (1 << 2) 63580d00f4SEmmanuel Vadot #define TWSI_CONTROL_IFLG (1 << 3) 64580d00f4SEmmanuel Vadot #define TWSI_CONTROL_STOP (1 << 4) 65580d00f4SEmmanuel Vadot #define TWSI_CONTROL_START (1 << 5) 66580d00f4SEmmanuel Vadot #define TWSI_CONTROL_TWSIEN (1 << 6) 67580d00f4SEmmanuel Vadot #define TWSI_CONTROL_INTEN (1 << 7) 68580d00f4SEmmanuel Vadot 69580d00f4SEmmanuel Vadot #define TWSI_STATUS_BUS_ERROR 0x00 70580d00f4SEmmanuel Vadot #define TWSI_STATUS_START 0x08 71580d00f4SEmmanuel Vadot #define TWSI_STATUS_RPTD_START 0x10 72580d00f4SEmmanuel Vadot #define TWSI_STATUS_ADDR_W_ACK 0x18 73580d00f4SEmmanuel Vadot #define TWSI_STATUS_ADDR_W_NACK 0x20 74580d00f4SEmmanuel Vadot #define TWSI_STATUS_DATA_WR_ACK 0x28 75580d00f4SEmmanuel Vadot #define TWSI_STATUS_DATA_WR_NACK 0x30 76580d00f4SEmmanuel Vadot #define TWSI_STATUS_ARBITRATION_LOST 0x38 77580d00f4SEmmanuel Vadot #define TWSI_STATUS_ADDR_R_ACK 0x40 78580d00f4SEmmanuel Vadot #define TWSI_STATUS_ADDR_R_NACK 0x48 79580d00f4SEmmanuel Vadot #define TWSI_STATUS_DATA_RD_ACK 0x50 80580d00f4SEmmanuel Vadot #define TWSI_STATUS_DATA_RD_NOACK 0x58 81580d00f4SEmmanuel Vadot #define TWSI_STATUS_IDLE 0xf8 82580d00f4SEmmanuel Vadot 83580d00f4SEmmanuel Vadot #define TWSI_DEBUG 84580d00f4SEmmanuel Vadot #undef TWSI_DEBUG 85580d00f4SEmmanuel Vadot 86580d00f4SEmmanuel Vadot #define debugf(sc, fmt, args...) if ((sc)->debug) \ 87580d00f4SEmmanuel Vadot device_printf((sc)->dev, "%s: " fmt, __func__, ##args) 88580d00f4SEmmanuel Vadot 89580d00f4SEmmanuel Vadot static struct resource_spec res_spec[] = { 90580d00f4SEmmanuel Vadot { SYS_RES_MEMORY, 0, RF_ACTIVE }, 91580d00f4SEmmanuel Vadot { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 92580d00f4SEmmanuel Vadot { -1, 0 } 93580d00f4SEmmanuel Vadot }; 94580d00f4SEmmanuel Vadot 95580d00f4SEmmanuel Vadot static __inline uint32_t 96580d00f4SEmmanuel Vadot TWSI_READ(struct twsi_softc *sc, bus_size_t off) 97580d00f4SEmmanuel Vadot { 98580d00f4SEmmanuel Vadot uint32_t val; 99580d00f4SEmmanuel Vadot 100580d00f4SEmmanuel Vadot val = bus_read_4(sc->res[0], off); 101580d00f4SEmmanuel Vadot if (sc->debug > 1) 102580d00f4SEmmanuel Vadot debugf(sc, "read %x from %lx\n", val, off); 103580d00f4SEmmanuel Vadot return (val); 104580d00f4SEmmanuel Vadot } 105580d00f4SEmmanuel Vadot 106580d00f4SEmmanuel Vadot static __inline void 107580d00f4SEmmanuel Vadot TWSI_WRITE(struct twsi_softc *sc, bus_size_t off, uint32_t val) 108580d00f4SEmmanuel Vadot { 109580d00f4SEmmanuel Vadot 110580d00f4SEmmanuel Vadot if (sc->debug > 1) 111580d00f4SEmmanuel Vadot debugf(sc, "Writing %x to %lx\n", val, off); 112580d00f4SEmmanuel Vadot bus_write_4(sc->res[0], off, val); 113580d00f4SEmmanuel Vadot } 114580d00f4SEmmanuel Vadot 115580d00f4SEmmanuel Vadot static __inline void 116580d00f4SEmmanuel Vadot twsi_control_clear(struct twsi_softc *sc, uint32_t mask) 117580d00f4SEmmanuel Vadot { 118580d00f4SEmmanuel Vadot uint32_t val; 119580d00f4SEmmanuel Vadot 120580d00f4SEmmanuel Vadot val = TWSI_READ(sc, sc->reg_control); 121580d00f4SEmmanuel Vadot debugf(sc, "read val=%x\n", val); 122580d00f4SEmmanuel Vadot val &= ~(TWSI_CONTROL_STOP | TWSI_CONTROL_START); 123580d00f4SEmmanuel Vadot val &= ~mask; 124580d00f4SEmmanuel Vadot debugf(sc, "write val=%x\n", val); 125580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, val); 126580d00f4SEmmanuel Vadot } 127580d00f4SEmmanuel Vadot 128580d00f4SEmmanuel Vadot static __inline void 129580d00f4SEmmanuel Vadot twsi_control_set(struct twsi_softc *sc, uint32_t mask) 130580d00f4SEmmanuel Vadot { 131580d00f4SEmmanuel Vadot uint32_t val; 132580d00f4SEmmanuel Vadot 133580d00f4SEmmanuel Vadot val = TWSI_READ(sc, sc->reg_control); 134580d00f4SEmmanuel Vadot debugf(sc, "read val=%x\n", val); 135580d00f4SEmmanuel Vadot val &= ~(TWSI_CONTROL_STOP | TWSI_CONTROL_START); 136580d00f4SEmmanuel Vadot val |= mask; 137580d00f4SEmmanuel Vadot debugf(sc, "write val=%x\n", val); 138580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, val); 139580d00f4SEmmanuel Vadot } 140580d00f4SEmmanuel Vadot 141580d00f4SEmmanuel Vadot static __inline void 142580d00f4SEmmanuel Vadot twsi_clear_iflg(struct twsi_softc *sc) 143580d00f4SEmmanuel Vadot { 144580d00f4SEmmanuel Vadot 145580d00f4SEmmanuel Vadot DELAY(1000); 146580d00f4SEmmanuel Vadot /* There are two ways of clearing IFLAG. */ 147580d00f4SEmmanuel Vadot if (sc->iflag_w1c) 148580d00f4SEmmanuel Vadot twsi_control_set(sc, TWSI_CONTROL_IFLG); 149580d00f4SEmmanuel Vadot else 150580d00f4SEmmanuel Vadot twsi_control_clear(sc, TWSI_CONTROL_IFLG); 151580d00f4SEmmanuel Vadot DELAY(1000); 152580d00f4SEmmanuel Vadot } 153580d00f4SEmmanuel Vadot 154580d00f4SEmmanuel Vadot 155580d00f4SEmmanuel Vadot /* 156580d00f4SEmmanuel Vadot * timeout given in us 157580d00f4SEmmanuel Vadot * returns 158580d00f4SEmmanuel Vadot * 0 on successful mask change 159580d00f4SEmmanuel Vadot * non-zero on timeout 160580d00f4SEmmanuel Vadot */ 161580d00f4SEmmanuel Vadot static int 162580d00f4SEmmanuel Vadot twsi_poll_ctrl(struct twsi_softc *sc, int timeout, uint32_t mask) 163580d00f4SEmmanuel Vadot { 164580d00f4SEmmanuel Vadot 165580d00f4SEmmanuel Vadot timeout /= 10; 166580d00f4SEmmanuel Vadot debugf(sc, "Waiting for ctrl reg to match mask %x\n", mask); 167580d00f4SEmmanuel Vadot while (!(TWSI_READ(sc, sc->reg_control) & mask)) { 168580d00f4SEmmanuel Vadot DELAY(10); 169580d00f4SEmmanuel Vadot if (--timeout < 0) 170580d00f4SEmmanuel Vadot return (timeout); 171580d00f4SEmmanuel Vadot } 172580d00f4SEmmanuel Vadot debugf(sc, "done\n"); 173580d00f4SEmmanuel Vadot return (0); 174580d00f4SEmmanuel Vadot } 175580d00f4SEmmanuel Vadot 176580d00f4SEmmanuel Vadot 177580d00f4SEmmanuel Vadot /* 178580d00f4SEmmanuel Vadot * 'timeout' is given in us. Note also that timeout handling is not exact -- 179580d00f4SEmmanuel Vadot * twsi_locked_start() total wait can be more than 2 x timeout 180580d00f4SEmmanuel Vadot * (twsi_poll_ctrl() is called twice). 'mask' can be either TWSI_STATUS_START 181580d00f4SEmmanuel Vadot * or TWSI_STATUS_RPTD_START 182580d00f4SEmmanuel Vadot */ 183580d00f4SEmmanuel Vadot static int 184580d00f4SEmmanuel Vadot twsi_locked_start(device_t dev, struct twsi_softc *sc, int32_t mask, 185580d00f4SEmmanuel Vadot u_char slave, int timeout) 186580d00f4SEmmanuel Vadot { 187580d00f4SEmmanuel Vadot int read_access, iflg_set = 0; 188580d00f4SEmmanuel Vadot uint32_t status; 189580d00f4SEmmanuel Vadot 190580d00f4SEmmanuel Vadot mtx_assert(&sc->mutex, MA_OWNED); 191580d00f4SEmmanuel Vadot 192580d00f4SEmmanuel Vadot if (mask == TWSI_STATUS_RPTD_START) 193580d00f4SEmmanuel Vadot /* read IFLG to know if it should be cleared later; from NBSD */ 194580d00f4SEmmanuel Vadot iflg_set = TWSI_READ(sc, sc->reg_control) & TWSI_CONTROL_IFLG; 195580d00f4SEmmanuel Vadot 196580d00f4SEmmanuel Vadot debugf(sc, "send start\n"); 197580d00f4SEmmanuel Vadot twsi_control_set(sc, TWSI_CONTROL_START); 198580d00f4SEmmanuel Vadot 199580d00f4SEmmanuel Vadot if (mask == TWSI_STATUS_RPTD_START && iflg_set) { 200580d00f4SEmmanuel Vadot debugf(sc, "IFLG set, clearing (mask=%x)\n", mask); 201580d00f4SEmmanuel Vadot twsi_clear_iflg(sc); 202580d00f4SEmmanuel Vadot } 203580d00f4SEmmanuel Vadot 204580d00f4SEmmanuel Vadot /* 205580d00f4SEmmanuel Vadot * Without this delay we timeout checking IFLG if the timeout is 0. 206580d00f4SEmmanuel Vadot * NBSD driver always waits here too. 207580d00f4SEmmanuel Vadot */ 208580d00f4SEmmanuel Vadot DELAY(1000); 209580d00f4SEmmanuel Vadot 210580d00f4SEmmanuel Vadot if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 211580d00f4SEmmanuel Vadot debugf(sc, "timeout sending %sSTART condition\n", 212580d00f4SEmmanuel Vadot mask == TWSI_STATUS_START ? "" : "repeated "); 213580d00f4SEmmanuel Vadot return (IIC_ETIMEOUT); 214580d00f4SEmmanuel Vadot } 215580d00f4SEmmanuel Vadot 216580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 217580d00f4SEmmanuel Vadot debugf(sc, "status=%x\n", status); 218580d00f4SEmmanuel Vadot 219580d00f4SEmmanuel Vadot if (status != mask) { 220580d00f4SEmmanuel Vadot debugf(sc, "wrong status (%02x) after sending %sSTART condition\n", 221580d00f4SEmmanuel Vadot status, mask == TWSI_STATUS_START ? "" : "repeated "); 222580d00f4SEmmanuel Vadot return (IIC_ESTATUS); 223580d00f4SEmmanuel Vadot } 224580d00f4SEmmanuel Vadot 225580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, slave); 226580d00f4SEmmanuel Vadot twsi_clear_iflg(sc); 227580d00f4SEmmanuel Vadot DELAY(1000); 228580d00f4SEmmanuel Vadot 229580d00f4SEmmanuel Vadot if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 230580d00f4SEmmanuel Vadot debugf(sc, "timeout sending slave address (timeout=%d)\n", timeout); 231580d00f4SEmmanuel Vadot return (IIC_ETIMEOUT); 232580d00f4SEmmanuel Vadot } 233580d00f4SEmmanuel Vadot 234580d00f4SEmmanuel Vadot read_access = (slave & 0x1) ? 1 : 0; 235580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 236580d00f4SEmmanuel Vadot if (status != (read_access ? 237580d00f4SEmmanuel Vadot TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) { 238580d00f4SEmmanuel Vadot debugf(sc, "no ACK (status: %02x) after sending slave address\n", 239580d00f4SEmmanuel Vadot status); 240580d00f4SEmmanuel Vadot return (IIC_ENOACK); 241580d00f4SEmmanuel Vadot } 242580d00f4SEmmanuel Vadot 243580d00f4SEmmanuel Vadot return (IIC_NOERR); 244580d00f4SEmmanuel Vadot } 245580d00f4SEmmanuel Vadot 246580d00f4SEmmanuel Vadot #define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N))) 247580d00f4SEmmanuel Vadot #define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) 248580d00f4SEmmanuel Vadot 249580d00f4SEmmanuel Vadot static int 250580d00f4SEmmanuel Vadot twsi_calc_baud_rate(struct twsi_softc *sc, const u_int target, 251580d00f4SEmmanuel Vadot int *param) 252580d00f4SEmmanuel Vadot { 253580d00f4SEmmanuel Vadot uint64_t clk; 254580d00f4SEmmanuel Vadot uint32_t cur, diff, diff0; 255580d00f4SEmmanuel Vadot int m, n, m0, n0; 256580d00f4SEmmanuel Vadot 257580d00f4SEmmanuel Vadot /* Calculate baud rate. */ 258580d00f4SEmmanuel Vadot diff0 = 0xffffffff; 259580d00f4SEmmanuel Vadot 260580d00f4SEmmanuel Vadot if (clk_get_freq(sc->clk_core, &clk) < 0) 261580d00f4SEmmanuel Vadot return (-1); 262580d00f4SEmmanuel Vadot 263580d00f4SEmmanuel Vadot debugf(sc, "Bus clock is at %ju\n", clk); 264580d00f4SEmmanuel Vadot 265580d00f4SEmmanuel Vadot for (n = 0; n < 8; n++) { 266580d00f4SEmmanuel Vadot for (m = 0; m < 16; m++) { 267580d00f4SEmmanuel Vadot cur = TWSI_BAUD_RATE_RAW(clk,m,n); 268580d00f4SEmmanuel Vadot diff = ABSSUB(target, cur); 269580d00f4SEmmanuel Vadot if (diff < diff0) { 270580d00f4SEmmanuel Vadot m0 = m; 271580d00f4SEmmanuel Vadot n0 = n; 272580d00f4SEmmanuel Vadot diff0 = diff; 273580d00f4SEmmanuel Vadot } 274580d00f4SEmmanuel Vadot } 275580d00f4SEmmanuel Vadot } 276580d00f4SEmmanuel Vadot *param = TWSI_BAUD_RATE_PARAM(m0, n0); 277580d00f4SEmmanuel Vadot 278580d00f4SEmmanuel Vadot return (0); 279580d00f4SEmmanuel Vadot } 280580d00f4SEmmanuel Vadot 281580d00f4SEmmanuel Vadot /* 282580d00f4SEmmanuel Vadot * Only slave mode supported, disregard [old]addr 283580d00f4SEmmanuel Vadot */ 284580d00f4SEmmanuel Vadot static int 285580d00f4SEmmanuel Vadot twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 286580d00f4SEmmanuel Vadot { 287580d00f4SEmmanuel Vadot struct twsi_softc *sc; 288580d00f4SEmmanuel Vadot uint32_t param; 289580d00f4SEmmanuel Vadot u_int busfreq; 290580d00f4SEmmanuel Vadot 291580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 292580d00f4SEmmanuel Vadot 293580d00f4SEmmanuel Vadot busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); 294580d00f4SEmmanuel Vadot 295580d00f4SEmmanuel Vadot if (twsi_calc_baud_rate(sc, busfreq, ¶m) == -1) { 296580d00f4SEmmanuel Vadot switch (speed) { 297580d00f4SEmmanuel Vadot case IIC_SLOW: 298580d00f4SEmmanuel Vadot case IIC_FAST: 299580d00f4SEmmanuel Vadot param = sc->baud_rate[speed].param; 300580d00f4SEmmanuel Vadot debugf(sc, "Using IIC_FAST mode with speed param=%x\n", param); 301580d00f4SEmmanuel Vadot break; 302580d00f4SEmmanuel Vadot case IIC_FASTEST: 303580d00f4SEmmanuel Vadot case IIC_UNKNOWN: 304580d00f4SEmmanuel Vadot default: 305580d00f4SEmmanuel Vadot param = sc->baud_rate[IIC_FAST].param; 306580d00f4SEmmanuel Vadot debugf(sc, "Using IIC_FASTEST/UNKNOWN mode with speed param=%x\n", param); 307580d00f4SEmmanuel Vadot break; 308580d00f4SEmmanuel Vadot } 309580d00f4SEmmanuel Vadot } 310580d00f4SEmmanuel Vadot 311580d00f4SEmmanuel Vadot debugf(sc, "Using clock param=%x\n", param); 312580d00f4SEmmanuel Vadot 313580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 314580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_soft_reset, 0x1); 315580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_baud_rate, param); 316580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, TWSI_CONTROL_TWSIEN); 317580d00f4SEmmanuel Vadot DELAY(1000); 318580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 319580d00f4SEmmanuel Vadot 320580d00f4SEmmanuel Vadot return (0); 321580d00f4SEmmanuel Vadot } 322580d00f4SEmmanuel Vadot 323580d00f4SEmmanuel Vadot static int 324580d00f4SEmmanuel Vadot twsi_stop(device_t dev) 325580d00f4SEmmanuel Vadot { 326580d00f4SEmmanuel Vadot struct twsi_softc *sc; 327580d00f4SEmmanuel Vadot 328580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 329580d00f4SEmmanuel Vadot 330580d00f4SEmmanuel Vadot debugf(sc, "%s\n", __func__); 331580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 332580d00f4SEmmanuel Vadot twsi_control_clear(sc, TWSI_CONTROL_ACK); 333580d00f4SEmmanuel Vadot twsi_control_set(sc, TWSI_CONTROL_STOP); 334580d00f4SEmmanuel Vadot twsi_clear_iflg(sc); 335580d00f4SEmmanuel Vadot DELAY(1000); 336580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 337580d00f4SEmmanuel Vadot 338580d00f4SEmmanuel Vadot return (IIC_NOERR); 339580d00f4SEmmanuel Vadot } 340580d00f4SEmmanuel Vadot 341580d00f4SEmmanuel Vadot /* 342580d00f4SEmmanuel Vadot * timeout is given in us 343580d00f4SEmmanuel Vadot */ 344580d00f4SEmmanuel Vadot static int 345580d00f4SEmmanuel Vadot twsi_repeated_start(device_t dev, u_char slave, int timeout) 346580d00f4SEmmanuel Vadot { 347580d00f4SEmmanuel Vadot struct twsi_softc *sc; 348580d00f4SEmmanuel Vadot int rv; 349580d00f4SEmmanuel Vadot 350580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 351580d00f4SEmmanuel Vadot 352580d00f4SEmmanuel Vadot debugf(sc, "%s: slave=%x\n", __func__, slave); 353580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 354580d00f4SEmmanuel Vadot rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, 355580d00f4SEmmanuel Vadot timeout); 356580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 357580d00f4SEmmanuel Vadot 358580d00f4SEmmanuel Vadot if (rv) { 359580d00f4SEmmanuel Vadot twsi_stop(dev); 360580d00f4SEmmanuel Vadot return (rv); 361580d00f4SEmmanuel Vadot } else 362580d00f4SEmmanuel Vadot return (IIC_NOERR); 363580d00f4SEmmanuel Vadot } 364580d00f4SEmmanuel Vadot 365580d00f4SEmmanuel Vadot /* 366580d00f4SEmmanuel Vadot * timeout is given in us 367580d00f4SEmmanuel Vadot */ 368580d00f4SEmmanuel Vadot static int 369580d00f4SEmmanuel Vadot twsi_start(device_t dev, u_char slave, int timeout) 370580d00f4SEmmanuel Vadot { 371580d00f4SEmmanuel Vadot struct twsi_softc *sc; 372580d00f4SEmmanuel Vadot int rv; 373580d00f4SEmmanuel Vadot 374580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 375580d00f4SEmmanuel Vadot 376580d00f4SEmmanuel Vadot debugf(sc, "%s: slave=%x\n", __func__, slave); 377580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 378580d00f4SEmmanuel Vadot rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); 379580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 380580d00f4SEmmanuel Vadot 381580d00f4SEmmanuel Vadot if (rv) { 382580d00f4SEmmanuel Vadot twsi_stop(dev); 383580d00f4SEmmanuel Vadot return (rv); 384580d00f4SEmmanuel Vadot } else 385580d00f4SEmmanuel Vadot return (IIC_NOERR); 386580d00f4SEmmanuel Vadot } 387580d00f4SEmmanuel Vadot 388580d00f4SEmmanuel Vadot static int 389580d00f4SEmmanuel Vadot twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) 390580d00f4SEmmanuel Vadot { 391580d00f4SEmmanuel Vadot struct twsi_softc *sc; 392580d00f4SEmmanuel Vadot uint32_t status; 393580d00f4SEmmanuel Vadot int last_byte, rv; 394580d00f4SEmmanuel Vadot 395580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 396580d00f4SEmmanuel Vadot 397580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 398580d00f4SEmmanuel Vadot *read = 0; 399580d00f4SEmmanuel Vadot while (*read < len) { 400580d00f4SEmmanuel Vadot /* 401580d00f4SEmmanuel Vadot * Check if we are reading last byte of the last buffer, 402580d00f4SEmmanuel Vadot * do not send ACK then, per I2C specs 403580d00f4SEmmanuel Vadot */ 404580d00f4SEmmanuel Vadot last_byte = ((*read == len - 1) && last) ? 1 : 0; 405580d00f4SEmmanuel Vadot if (last_byte) 406580d00f4SEmmanuel Vadot twsi_control_clear(sc, TWSI_CONTROL_ACK); 407580d00f4SEmmanuel Vadot else 408580d00f4SEmmanuel Vadot twsi_control_set(sc, TWSI_CONTROL_ACK); 409580d00f4SEmmanuel Vadot 410580d00f4SEmmanuel Vadot twsi_clear_iflg(sc); 411580d00f4SEmmanuel Vadot DELAY(1000); 412580d00f4SEmmanuel Vadot 413580d00f4SEmmanuel Vadot if (twsi_poll_ctrl(sc, delay, TWSI_CONTROL_IFLG)) { 414580d00f4SEmmanuel Vadot debugf(sc, "timeout reading data (delay=%d)\n", delay); 415580d00f4SEmmanuel Vadot rv = IIC_ETIMEOUT; 416580d00f4SEmmanuel Vadot goto out; 417580d00f4SEmmanuel Vadot } 418580d00f4SEmmanuel Vadot 419580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 420580d00f4SEmmanuel Vadot if (status != (last_byte ? 421580d00f4SEmmanuel Vadot TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) { 422580d00f4SEmmanuel Vadot debugf(sc, "wrong status (%02x) while reading\n", status); 423580d00f4SEmmanuel Vadot rv = IIC_ESTATUS; 424580d00f4SEmmanuel Vadot goto out; 425580d00f4SEmmanuel Vadot } 426580d00f4SEmmanuel Vadot 427580d00f4SEmmanuel Vadot *buf++ = TWSI_READ(sc, sc->reg_data); 428580d00f4SEmmanuel Vadot (*read)++; 429580d00f4SEmmanuel Vadot } 430580d00f4SEmmanuel Vadot rv = IIC_NOERR; 431580d00f4SEmmanuel Vadot out: 432580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 433580d00f4SEmmanuel Vadot return (rv); 434580d00f4SEmmanuel Vadot } 435580d00f4SEmmanuel Vadot 436580d00f4SEmmanuel Vadot static int 437580d00f4SEmmanuel Vadot twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) 438580d00f4SEmmanuel Vadot { 439580d00f4SEmmanuel Vadot struct twsi_softc *sc; 440580d00f4SEmmanuel Vadot uint32_t status; 441580d00f4SEmmanuel Vadot int rv; 442580d00f4SEmmanuel Vadot 443580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 444580d00f4SEmmanuel Vadot 445580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 446580d00f4SEmmanuel Vadot *sent = 0; 447580d00f4SEmmanuel Vadot while (*sent < len) { 448580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, *buf++); 449580d00f4SEmmanuel Vadot 450580d00f4SEmmanuel Vadot twsi_clear_iflg(sc); 451580d00f4SEmmanuel Vadot DELAY(1000); 452580d00f4SEmmanuel Vadot if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 453580d00f4SEmmanuel Vadot debugf(sc, "timeout writing data (timeout=%d)\n", timeout); 454580d00f4SEmmanuel Vadot rv = IIC_ETIMEOUT; 455580d00f4SEmmanuel Vadot goto out; 456580d00f4SEmmanuel Vadot } 457580d00f4SEmmanuel Vadot 458580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 459580d00f4SEmmanuel Vadot if (status != TWSI_STATUS_DATA_WR_ACK) { 460580d00f4SEmmanuel Vadot debugf(sc, "wrong status (%02x) while writing\n", status); 461580d00f4SEmmanuel Vadot rv = IIC_ESTATUS; 462580d00f4SEmmanuel Vadot goto out; 463580d00f4SEmmanuel Vadot } 464580d00f4SEmmanuel Vadot (*sent)++; 465580d00f4SEmmanuel Vadot } 466580d00f4SEmmanuel Vadot rv = IIC_NOERR; 467580d00f4SEmmanuel Vadot out: 468580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 469580d00f4SEmmanuel Vadot return (rv); 470580d00f4SEmmanuel Vadot } 471580d00f4SEmmanuel Vadot 472580d00f4SEmmanuel Vadot static void 473580d00f4SEmmanuel Vadot twsi_error(struct twsi_softc *sc, int err) 474580d00f4SEmmanuel Vadot { 475580d00f4SEmmanuel Vadot /* 476580d00f4SEmmanuel Vadot * Must send stop condition to abort the current transfer. 477580d00f4SEmmanuel Vadot */ 478580d00f4SEmmanuel Vadot debugf(sc, "Sending STOP condition for error %d\n", err); 479580d00f4SEmmanuel Vadot sc->transfer = 0; 480580d00f4SEmmanuel Vadot sc->error = err; 481580d00f4SEmmanuel Vadot sc->control_val = 0; 482580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_STOP); 483580d00f4SEmmanuel Vadot } 484580d00f4SEmmanuel Vadot 485580d00f4SEmmanuel Vadot static int 486580d00f4SEmmanuel Vadot twsi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 487580d00f4SEmmanuel Vadot { 488580d00f4SEmmanuel Vadot struct twsi_softc *sc; 489580d00f4SEmmanuel Vadot uint32_t status; 490580d00f4SEmmanuel Vadot int error; 491580d00f4SEmmanuel Vadot 492580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 493580d00f4SEmmanuel Vadot 494580d00f4SEmmanuel Vadot if (!sc->have_intr) 495580d00f4SEmmanuel Vadot return (iicbus_transfer_gen(dev, msgs, nmsgs)); 496580d00f4SEmmanuel Vadot 497580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 498580d00f4SEmmanuel Vadot KASSERT(sc->transfer == 0, 499580d00f4SEmmanuel Vadot ("starting a transfer while another is active")); 500580d00f4SEmmanuel Vadot 501580d00f4SEmmanuel Vadot debugf(sc, "transmitting %d messages\n", nmsgs); 502580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 503580d00f4SEmmanuel Vadot debugf(sc, "status=0x%x\n", status); 504580d00f4SEmmanuel Vadot if (status != TWSI_STATUS_IDLE) { 505580d00f4SEmmanuel Vadot debugf(sc, "Bad status at start of transfer\n"); 506580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ESTATUS); 507580d00f4SEmmanuel Vadot goto end; 508580d00f4SEmmanuel Vadot } 509580d00f4SEmmanuel Vadot 510580d00f4SEmmanuel Vadot sc->nmsgs = nmsgs; 511580d00f4SEmmanuel Vadot sc->msgs = msgs; 512580d00f4SEmmanuel Vadot sc->msg_idx = 0; 513580d00f4SEmmanuel Vadot sc->transfer = 1; 514580d00f4SEmmanuel Vadot sc->error = 0; 515580d00f4SEmmanuel Vadot 516580d00f4SEmmanuel Vadot #ifdef TWSI_DEBUG 517580d00f4SEmmanuel Vadot for (int i = 0; i < nmsgs; i++) 518580d00f4SEmmanuel Vadot debugf(sc, "msg %d is %d bytes long\n", i, msgs[i].len); 519580d00f4SEmmanuel Vadot #endif 520580d00f4SEmmanuel Vadot 521580d00f4SEmmanuel Vadot /* Send start and re-enable interrupts */ 522580d00f4SEmmanuel Vadot sc->control_val = TWSI_CONTROL_TWSIEN | TWSI_CONTROL_INTEN; 523580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START); 524580d00f4SEmmanuel Vadot msleep_sbt(sc, &sc->mutex, 0, "twsi", 3000 * SBT_1MS, SBT_1MS, 0); 525580d00f4SEmmanuel Vadot debugf(sc, "pause finish\n"); 526580d00f4SEmmanuel Vadot if (sc->error == 0 && sc->transfer != 0) { 527580d00f4SEmmanuel Vadot device_printf(sc->dev, "transfer timeout\n"); 528580d00f4SEmmanuel Vadot sc->error = IIC_ETIMEOUT; 529580d00f4SEmmanuel Vadot sc->transfer = 0; 530580d00f4SEmmanuel Vadot } 531580d00f4SEmmanuel Vadot 532580d00f4SEmmanuel Vadot if (sc->error != 0) 533580d00f4SEmmanuel Vadot debugf(sc, "Error: %d\n", sc->error); 534580d00f4SEmmanuel Vadot 535580d00f4SEmmanuel Vadot end: 536580d00f4SEmmanuel Vadot /* Disable module and interrupts */ 537580d00f4SEmmanuel Vadot debugf(sc, "status=0x%x\n", TWSI_READ(sc, sc->reg_status)); 538580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, 0); 539580d00f4SEmmanuel Vadot debugf(sc, "status=0x%x\n", TWSI_READ(sc, sc->reg_status)); 540580d00f4SEmmanuel Vadot error = sc->error; 541580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 542580d00f4SEmmanuel Vadot 543580d00f4SEmmanuel Vadot return (error); 544580d00f4SEmmanuel Vadot } 545580d00f4SEmmanuel Vadot 546580d00f4SEmmanuel Vadot static void 547580d00f4SEmmanuel Vadot twsi_intr(void *arg) 548580d00f4SEmmanuel Vadot { 549580d00f4SEmmanuel Vadot struct twsi_softc *sc; 550580d00f4SEmmanuel Vadot uint32_t status; 551580d00f4SEmmanuel Vadot bool message_done; 552580d00f4SEmmanuel Vadot bool send_start; 553580d00f4SEmmanuel Vadot 554580d00f4SEmmanuel Vadot sc = arg; 555580d00f4SEmmanuel Vadot send_start = false; 556580d00f4SEmmanuel Vadot 557580d00f4SEmmanuel Vadot mtx_lock(&sc->mutex); 558580d00f4SEmmanuel Vadot debugf(sc, "Got interrupt, current msg=%u\n", sc->msg_idx); 559580d00f4SEmmanuel Vadot 560580d00f4SEmmanuel Vadot status = TWSI_READ(sc, sc->reg_status); 561580d00f4SEmmanuel Vadot debugf(sc, "reg control = 0x%x, status = 0x%x\n", 562580d00f4SEmmanuel Vadot TWSI_READ(sc, sc->reg_control), status); 563580d00f4SEmmanuel Vadot 564580d00f4SEmmanuel Vadot if (sc->transfer == 0) { 565580d00f4SEmmanuel Vadot device_printf(sc->dev, "interrupt without active transfer, " 566580d00f4SEmmanuel Vadot "status = 0x%x\n", status); 567580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, sc->control_val | 568580d00f4SEmmanuel Vadot TWSI_CONTROL_STOP); 569580d00f4SEmmanuel Vadot goto end; 570580d00f4SEmmanuel Vadot } 571580d00f4SEmmanuel Vadot 572580d00f4SEmmanuel Vadot restart: 573580d00f4SEmmanuel Vadot message_done = false; 574580d00f4SEmmanuel Vadot 575580d00f4SEmmanuel Vadot switch (status) { 576580d00f4SEmmanuel Vadot case TWSI_STATUS_START: 577580d00f4SEmmanuel Vadot case TWSI_STATUS_RPTD_START: 578580d00f4SEmmanuel Vadot /* Transmit the address */ 579580d00f4SEmmanuel Vadot debugf(sc, "Send address 0x%x\n", 580580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].slave); 581580d00f4SEmmanuel Vadot 582580d00f4SEmmanuel Vadot if (sc->msgs[sc->msg_idx].flags & IIC_M_RD) 583580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, 584580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].slave | LSB); 585580d00f4SEmmanuel Vadot else 586580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, 587580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].slave & ~LSB); 588580d00f4SEmmanuel Vadot break; 589580d00f4SEmmanuel Vadot 590580d00f4SEmmanuel Vadot case TWSI_STATUS_ADDR_W_ACK: 591580d00f4SEmmanuel Vadot debugf(sc, "Address ACK-ed (write)\n"); 592580d00f4SEmmanuel Vadot 593580d00f4SEmmanuel Vadot if (sc->msgs[sc->msg_idx].len > 0) { 594580d00f4SEmmanuel Vadot /* Directly send the first byte */ 595580d00f4SEmmanuel Vadot sc->sent_bytes = 1; 596580d00f4SEmmanuel Vadot debugf(sc, "Sending byte 0 (of %d) = %x\n", 597580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].len, 598580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[0]); 599580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, 600580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[0]); 601580d00f4SEmmanuel Vadot } else { 602580d00f4SEmmanuel Vadot debugf(sc, "Zero-length write, sending STOP\n"); 603580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, 604580d00f4SEmmanuel Vadot sc->control_val | TWSI_CONTROL_STOP); 605580d00f4SEmmanuel Vadot } 606580d00f4SEmmanuel Vadot break; 607580d00f4SEmmanuel Vadot 608580d00f4SEmmanuel Vadot case TWSI_STATUS_ADDR_R_ACK: 609580d00f4SEmmanuel Vadot debugf(sc, "Address ACK-ed (read)\n"); 610580d00f4SEmmanuel Vadot sc->recv_bytes = 0; 611580d00f4SEmmanuel Vadot 612580d00f4SEmmanuel Vadot if (sc->msgs[sc->msg_idx].len == 0) { 613580d00f4SEmmanuel Vadot debugf(sc, "Zero-length read, sending STOP\n"); 614580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, 615580d00f4SEmmanuel Vadot sc->control_val | TWSI_CONTROL_STOP); 616580d00f4SEmmanuel Vadot } else if (sc->msgs[sc->msg_idx].len == 1) { 617580d00f4SEmmanuel Vadot sc->control_val &= ~TWSI_CONTROL_ACK; 618580d00f4SEmmanuel Vadot } else { 619580d00f4SEmmanuel Vadot sc->control_val |= TWSI_CONTROL_ACK; 620580d00f4SEmmanuel Vadot } 621580d00f4SEmmanuel Vadot break; 622580d00f4SEmmanuel Vadot 623580d00f4SEmmanuel Vadot case TWSI_STATUS_ADDR_W_NACK: 624580d00f4SEmmanuel Vadot case TWSI_STATUS_ADDR_R_NACK: 625580d00f4SEmmanuel Vadot debugf(sc, "Address NACK-ed\n"); 626580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ENOACK); 627580d00f4SEmmanuel Vadot break; 628580d00f4SEmmanuel Vadot case TWSI_STATUS_DATA_WR_NACK: 629580d00f4SEmmanuel Vadot debugf(sc, "Data byte NACK-ed\n"); 630580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ENOACK); 631580d00f4SEmmanuel Vadot break; 632580d00f4SEmmanuel Vadot case TWSI_STATUS_DATA_WR_ACK: 633580d00f4SEmmanuel Vadot KASSERT(sc->sent_bytes <= sc->msgs[sc->msg_idx].len, 634580d00f4SEmmanuel Vadot ("sent_bytes beyond message length")); 635580d00f4SEmmanuel Vadot debugf(sc, "ACK received after transmitting data\n"); 636580d00f4SEmmanuel Vadot if (sc->sent_bytes == sc->msgs[sc->msg_idx].len) { 637580d00f4SEmmanuel Vadot debugf(sc, "Done TX data\n"); 638580d00f4SEmmanuel Vadot 639580d00f4SEmmanuel Vadot /* Send stop, no interrupts on stop */ 640580d00f4SEmmanuel Vadot if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) { 641580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, 642580d00f4SEmmanuel Vadot sc->control_val | TWSI_CONTROL_STOP); 643580d00f4SEmmanuel Vadot } else { 644580d00f4SEmmanuel Vadot debugf(sc, "NOSTOP flag\n"); 645580d00f4SEmmanuel Vadot } 646580d00f4SEmmanuel Vadot message_done = true; 647580d00f4SEmmanuel Vadot break; 648580d00f4SEmmanuel Vadot } 649580d00f4SEmmanuel Vadot 650580d00f4SEmmanuel Vadot debugf(sc, "Sending byte %d (of %d) = 0x%x\n", 651580d00f4SEmmanuel Vadot sc->sent_bytes, 652580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].len, 653580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); 654580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_data, 655580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); 656580d00f4SEmmanuel Vadot sc->sent_bytes++; 657580d00f4SEmmanuel Vadot break; 658580d00f4SEmmanuel Vadot 659580d00f4SEmmanuel Vadot case TWSI_STATUS_DATA_RD_ACK: 660580d00f4SEmmanuel Vadot debugf(sc, "Received and ACK-ed data\n"); 661580d00f4SEmmanuel Vadot KASSERT(sc->recv_bytes < sc->msgs[sc->msg_idx].len, 662580d00f4SEmmanuel Vadot ("receiving beyond the end of buffer")); 663580d00f4SEmmanuel Vadot 664580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->recv_bytes] = 665580d00f4SEmmanuel Vadot TWSI_READ(sc, sc->reg_data); 666580d00f4SEmmanuel Vadot debugf(sc, "Received byte %d (of %d) = 0x%x\n", 667580d00f4SEmmanuel Vadot sc->recv_bytes, 668580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].len, 669580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->recv_bytes]); 670580d00f4SEmmanuel Vadot sc->recv_bytes++; 671580d00f4SEmmanuel Vadot 672580d00f4SEmmanuel Vadot /* If we only have one byte left, disable ACK */ 673580d00f4SEmmanuel Vadot if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1) { 674580d00f4SEmmanuel Vadot sc->control_val &= ~TWSI_CONTROL_ACK; 675580d00f4SEmmanuel Vadot } else if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) { 676580d00f4SEmmanuel Vadot /* 677580d00f4SEmmanuel Vadot * We should not have ACK-ed the last byte. 678580d00f4SEmmanuel Vadot * The protocol state machine is in invalid state. 679580d00f4SEmmanuel Vadot */ 680580d00f4SEmmanuel Vadot debugf(sc, "RX all but asked for more?\n"); 681580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ESTATUS); 682580d00f4SEmmanuel Vadot } 683580d00f4SEmmanuel Vadot break; 684580d00f4SEmmanuel Vadot 685580d00f4SEmmanuel Vadot case TWSI_STATUS_DATA_RD_NOACK: 686580d00f4SEmmanuel Vadot debugf(sc, "Received and NACK-ed data\n"); 687580d00f4SEmmanuel Vadot KASSERT(sc->recv_bytes == sc->msgs[sc->msg_idx].len - 1, 688580d00f4SEmmanuel Vadot ("sent NACK before receiving all requested data")); 689580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->recv_bytes] = 690580d00f4SEmmanuel Vadot TWSI_READ(sc, sc->reg_data); 691580d00f4SEmmanuel Vadot debugf(sc, "Received byte %d (of %d) = 0x%x\n", 692580d00f4SEmmanuel Vadot sc->recv_bytes, 693580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].len, 694580d00f4SEmmanuel Vadot sc->msgs[sc->msg_idx].buf[sc->recv_bytes]); 695580d00f4SEmmanuel Vadot sc->recv_bytes++; 696580d00f4SEmmanuel Vadot 697580d00f4SEmmanuel Vadot if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) { 698580d00f4SEmmanuel Vadot debugf(sc, "Done RX data\n"); 699580d00f4SEmmanuel Vadot if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) { 700580d00f4SEmmanuel Vadot debugf(sc, "Send STOP\n"); 701580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, 702580d00f4SEmmanuel Vadot sc->control_val | TWSI_CONTROL_STOP); 703580d00f4SEmmanuel Vadot } 704580d00f4SEmmanuel Vadot message_done = true; 705580d00f4SEmmanuel Vadot } else { 706580d00f4SEmmanuel Vadot /* 707580d00f4SEmmanuel Vadot * We should not have NACK-ed yet. 708580d00f4SEmmanuel Vadot * The protocol state machine is in invalid state. 709580d00f4SEmmanuel Vadot */ 710580d00f4SEmmanuel Vadot debugf(sc, "NACK-ed before receving all bytes?\n"); 711580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ESTATUS); 712580d00f4SEmmanuel Vadot } 713580d00f4SEmmanuel Vadot break; 714580d00f4SEmmanuel Vadot 715580d00f4SEmmanuel Vadot case TWSI_STATUS_BUS_ERROR: 716580d00f4SEmmanuel Vadot debugf(sc, "Bus error\n"); 717580d00f4SEmmanuel Vadot twsi_error(sc, IIC_EBUSERR); 718580d00f4SEmmanuel Vadot break; 719580d00f4SEmmanuel Vadot case TWSI_STATUS_ARBITRATION_LOST: 720580d00f4SEmmanuel Vadot debugf(sc, "Arbitration lost\n"); 721580d00f4SEmmanuel Vadot twsi_error(sc, IIC_EBUSBSY); 722580d00f4SEmmanuel Vadot break; 723580d00f4SEmmanuel Vadot default: 724580d00f4SEmmanuel Vadot debugf(sc, "unexpected status 0x%x\n", status); 725580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ESTATUS); 726580d00f4SEmmanuel Vadot break; 727580d00f4SEmmanuel Vadot } 728580d00f4SEmmanuel Vadot 729580d00f4SEmmanuel Vadot if (message_done) { 730580d00f4SEmmanuel Vadot sc->msg_idx++; 731580d00f4SEmmanuel Vadot if (sc->msg_idx == sc->nmsgs) { 732580d00f4SEmmanuel Vadot debugf(sc, "All messages transmitted\n"); 733580d00f4SEmmanuel Vadot sc->transfer = 0; 734580d00f4SEmmanuel Vadot sc->error = 0; 735580d00f4SEmmanuel Vadot } else if ((sc->msgs[sc->msg_idx].flags & IIC_M_NOSTART) == 0) { 736580d00f4SEmmanuel Vadot debugf(sc, "Send (repeated) start\n"); 737580d00f4SEmmanuel Vadot send_start = true; 738580d00f4SEmmanuel Vadot } else { 739580d00f4SEmmanuel Vadot /* Just keep transmitting data. */ 740580d00f4SEmmanuel Vadot KASSERT((sc->msgs[sc->msg_idx - 1].flags & IIC_M_NOSTOP) != 0, 741580d00f4SEmmanuel Vadot ("NOSTART message after STOP")); 742580d00f4SEmmanuel Vadot KASSERT((sc->msgs[sc->msg_idx].flags & IIC_M_RD) == 743580d00f4SEmmanuel Vadot (sc->msgs[sc->msg_idx - 1].flags & IIC_M_RD), 744580d00f4SEmmanuel Vadot ("change of transfer direction without a START")); 745580d00f4SEmmanuel Vadot debugf(sc, "NOSTART message after NOSTOP\n"); 746580d00f4SEmmanuel Vadot sc->sent_bytes = 0; 747580d00f4SEmmanuel Vadot sc->recv_bytes = 0; 748580d00f4SEmmanuel Vadot if ((sc->msgs[sc->msg_idx].flags & IIC_M_RD) == 0) { 749580d00f4SEmmanuel Vadot status = TWSI_STATUS_ADDR_W_ACK; 750580d00f4SEmmanuel Vadot goto restart; 751580d00f4SEmmanuel Vadot } else { 752580d00f4SEmmanuel Vadot debugf(sc, "Read+NOSTART unsupported\n"); 753580d00f4SEmmanuel Vadot twsi_error(sc, IIC_ESTATUS); 754580d00f4SEmmanuel Vadot } 755580d00f4SEmmanuel Vadot } 756580d00f4SEmmanuel Vadot } 757580d00f4SEmmanuel Vadot end: 758580d00f4SEmmanuel Vadot /* 759580d00f4SEmmanuel Vadot * Newer Allwinner chips clear IFLG after writing 1 to it. 760580d00f4SEmmanuel Vadot */ 761580d00f4SEmmanuel Vadot debugf(sc, "Refresh reg_control\n"); 762580d00f4SEmmanuel Vadot TWSI_WRITE(sc, sc->reg_control, sc->control_val | 763580d00f4SEmmanuel Vadot (sc->iflag_w1c ? TWSI_CONTROL_IFLG : 0) | 764580d00f4SEmmanuel Vadot (send_start ? TWSI_CONTROL_START : 0)); 765580d00f4SEmmanuel Vadot 766580d00f4SEmmanuel Vadot debugf(sc, "Done with interrupt, transfer = %d\n", sc->transfer); 767580d00f4SEmmanuel Vadot if (sc->transfer == 0) 768580d00f4SEmmanuel Vadot wakeup(sc); 769580d00f4SEmmanuel Vadot mtx_unlock(&sc->mutex); 770580d00f4SEmmanuel Vadot } 771580d00f4SEmmanuel Vadot 772580d00f4SEmmanuel Vadot static void 773580d00f4SEmmanuel Vadot twsi_intr_start(void *pdev) 774580d00f4SEmmanuel Vadot { 775580d00f4SEmmanuel Vadot struct twsi_softc *sc; 776580d00f4SEmmanuel Vadot 777580d00f4SEmmanuel Vadot sc = device_get_softc(pdev); 778580d00f4SEmmanuel Vadot 779580d00f4SEmmanuel Vadot if ((bus_setup_intr(pdev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, 780580d00f4SEmmanuel Vadot NULL, twsi_intr, sc, &sc->intrhand))) 781580d00f4SEmmanuel Vadot device_printf(pdev, "unable to register interrupt handler\n"); 782580d00f4SEmmanuel Vadot 783580d00f4SEmmanuel Vadot sc->have_intr = true; 784580d00f4SEmmanuel Vadot } 785580d00f4SEmmanuel Vadot 786580d00f4SEmmanuel Vadot int 787580d00f4SEmmanuel Vadot twsi_attach(device_t dev) 788580d00f4SEmmanuel Vadot { 789580d00f4SEmmanuel Vadot struct twsi_softc *sc; 790580d00f4SEmmanuel Vadot struct sysctl_ctx_list *ctx; 791580d00f4SEmmanuel Vadot struct sysctl_oid *tree_node; 792580d00f4SEmmanuel Vadot struct sysctl_oid_list *tree; 793580d00f4SEmmanuel Vadot 794580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 795580d00f4SEmmanuel Vadot sc->dev = dev; 796580d00f4SEmmanuel Vadot 797580d00f4SEmmanuel Vadot mtx_init(&sc->mutex, device_get_nameunit(dev), "twsi", MTX_DEF); 798580d00f4SEmmanuel Vadot 799580d00f4SEmmanuel Vadot if (bus_alloc_resources(dev, res_spec, sc->res)) { 800580d00f4SEmmanuel Vadot device_printf(dev, "could not allocate resources\n"); 801580d00f4SEmmanuel Vadot twsi_detach(dev); 802580d00f4SEmmanuel Vadot return (ENXIO); 803580d00f4SEmmanuel Vadot } 804580d00f4SEmmanuel Vadot 805580d00f4SEmmanuel Vadot #ifdef TWSI_DEBUG 806580d00f4SEmmanuel Vadot sc->debug = 1; 807580d00f4SEmmanuel Vadot #endif 808580d00f4SEmmanuel Vadot ctx = device_get_sysctl_ctx(dev); 809580d00f4SEmmanuel Vadot tree_node = device_get_sysctl_tree(dev); 810580d00f4SEmmanuel Vadot tree = SYSCTL_CHILDREN(tree_node); 811580d00f4SEmmanuel Vadot SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "debug", CTLFLAG_RWTUN, 812580d00f4SEmmanuel Vadot &sc->debug, 0, "Set debug level (zero to disable)"); 813580d00f4SEmmanuel Vadot 814580d00f4SEmmanuel Vadot /* Attach the iicbus. */ 815580d00f4SEmmanuel Vadot if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { 816580d00f4SEmmanuel Vadot device_printf(dev, "could not allocate iicbus instance\n"); 817580d00f4SEmmanuel Vadot twsi_detach(dev); 818580d00f4SEmmanuel Vadot return (ENXIO); 819580d00f4SEmmanuel Vadot } 820*18250ec6SJohn Baldwin bus_attach_children(dev); 821580d00f4SEmmanuel Vadot 822580d00f4SEmmanuel Vadot config_intrhook_oneshot(twsi_intr_start, dev); 823580d00f4SEmmanuel Vadot 824580d00f4SEmmanuel Vadot return (0); 825580d00f4SEmmanuel Vadot } 826580d00f4SEmmanuel Vadot 827580d00f4SEmmanuel Vadot int 828580d00f4SEmmanuel Vadot twsi_detach(device_t dev) 829580d00f4SEmmanuel Vadot { 830580d00f4SEmmanuel Vadot struct twsi_softc *sc; 831580d00f4SEmmanuel Vadot int rv; 832580d00f4SEmmanuel Vadot 833580d00f4SEmmanuel Vadot sc = device_get_softc(dev); 834580d00f4SEmmanuel Vadot 835580d00f4SEmmanuel Vadot if ((rv = bus_generic_detach(dev)) != 0) 836580d00f4SEmmanuel Vadot return (rv); 837580d00f4SEmmanuel Vadot 838580d00f4SEmmanuel Vadot if (sc->intrhand != NULL) 839580d00f4SEmmanuel Vadot bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); 840580d00f4SEmmanuel Vadot 841580d00f4SEmmanuel Vadot bus_release_resources(dev, res_spec, sc->res); 842580d00f4SEmmanuel Vadot 843580d00f4SEmmanuel Vadot mtx_destroy(&sc->mutex); 844580d00f4SEmmanuel Vadot return (0); 845580d00f4SEmmanuel Vadot } 846580d00f4SEmmanuel Vadot 847580d00f4SEmmanuel Vadot static device_method_t twsi_methods[] = { 848580d00f4SEmmanuel Vadot /* device interface */ 849580d00f4SEmmanuel Vadot DEVMETHOD(device_detach, twsi_detach), 850580d00f4SEmmanuel Vadot 851580d00f4SEmmanuel Vadot /* Bus interface */ 852580d00f4SEmmanuel Vadot DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 853580d00f4SEmmanuel Vadot DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 854580d00f4SEmmanuel Vadot DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 855580d00f4SEmmanuel Vadot DEVMETHOD(bus_release_resource, bus_generic_release_resource), 856580d00f4SEmmanuel Vadot DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 857580d00f4SEmmanuel Vadot DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 858580d00f4SEmmanuel Vadot DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 859580d00f4SEmmanuel Vadot DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 860580d00f4SEmmanuel Vadot DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 861580d00f4SEmmanuel Vadot 862580d00f4SEmmanuel Vadot /* iicbus interface */ 863580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_callback, iicbus_null_callback), 864580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_repeated_start, twsi_repeated_start), 865580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_start, twsi_start), 866580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_stop, twsi_stop), 867580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_write, twsi_write), 868580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_read, twsi_read), 869580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_reset, twsi_reset), 870580d00f4SEmmanuel Vadot DEVMETHOD(iicbus_transfer, twsi_transfer), 871580d00f4SEmmanuel Vadot { 0, 0 } 872580d00f4SEmmanuel Vadot }; 873580d00f4SEmmanuel Vadot 874580d00f4SEmmanuel Vadot DEFINE_CLASS_0(twsi, twsi_driver, twsi_methods, 875580d00f4SEmmanuel Vadot sizeof(struct twsi_softc)); 876