twsi.c revision 183840
1183840Sraj/*- 2183840Sraj * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 3183840Sraj * All rights reserved. 4183840Sraj * 5183840Sraj * Developed by Semihalf. 6183840Sraj * 7183840Sraj * Redistribution and use in source and binary forms, with or without 8183840Sraj * modification, are permitted provided that the following conditions 9183840Sraj * are met: 10183840Sraj * 1. Redistributions of source code must retain the above copyright 11183840Sraj * notice, this list of conditions and the following disclaimer. 12183840Sraj * 2. Redistributions in binary form must reproduce the above copyright 13183840Sraj * notice, this list of conditions and the following disclaimer in the 14183840Sraj * documentation and/or other materials provided with the distribution. 15183840Sraj * 3. Neither the name of MARVELL nor the names of contributors 16183840Sraj * may be used to endorse or promote products derived from this software 17183840Sraj * without specific prior written permission. 18183840Sraj * 19183840Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20183840Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21183840Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22183840Sraj * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 23183840Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24183840Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25183840Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26183840Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27183840Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28183840Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29183840Sraj * SUCH DAMAGE. 30183840Sraj */ 31183840Sraj 32183840Sraj/* 33183840Sraj * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell 34183840Sraj * SoCs. Supports master operation only, and works in polling mode. 35183840Sraj * 36183840Sraj * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software 37183840Sraj * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". 38183840Sraj */ 39183840Sraj 40183840Sraj#include <sys/cdefs.h> 41183840Sraj__FBSDID("$FreeBSD: head/sys/arm/mv/twsi.c 183840 2008-10-13 20:07:13Z raj $"); 42183840Sraj 43183840Sraj#include <sys/param.h> 44183840Sraj#include <sys/systm.h> 45183840Sraj#include <sys/bus.h> 46183840Sraj#include <sys/kernel.h> 47183840Sraj#include <sys/module.h> 48183840Sraj#include <sys/resource.h> 49183840Sraj 50183840Sraj#include <machine/bus.h> 51183840Sraj#include <machine/resource.h> 52183840Sraj 53183840Sraj#include <sys/rman.h> 54183840Sraj 55183840Sraj#include <sys/lock.h> 56183840Sraj#include <sys/mutex.h> 57183840Sraj 58183840Sraj#include <dev/iicbus/iiconf.h> 59183840Sraj#include <dev/iicbus/iicbus.h> 60183840Sraj#include "iicbus_if.h" 61183840Sraj 62183840Sraj#define MV_TWSI_NAME "twsi" 63183840Sraj 64183840Sraj#define TWSI_SLAVE_ADDR 0x00 65183840Sraj#define TWSI_EXT_SLAVE_ADDR 0x10 66183840Sraj#define TWSI_DATA 0x04 67183840Sraj 68183840Sraj#define TWSI_CONTROL 0x08 69183840Sraj#define TWSI_CONTROL_ACK (1 << 2) 70183840Sraj#define TWSI_CONTROL_IFLG (1 << 3) 71183840Sraj#define TWSI_CONTROL_STOP (1 << 4) 72183840Sraj#define TWSI_CONTROL_START (1 << 5) 73183840Sraj#define TWSI_CONTROL_TWSIEN (1 << 6) 74183840Sraj#define TWSI_CONTROL_INTEN (1 << 7) 75183840Sraj 76183840Sraj#define TWSI_STATUS 0x0c 77183840Sraj#define TWSI_STATUS_START 0x08 78183840Sraj#define TWSI_STATUS_RPTD_START 0x10 79183840Sraj#define TWSI_STATUS_ADDR_W_ACK 0x18 80183840Sraj#define TWSI_STATUS_DATA_WR_ACK 0x28 81183840Sraj#define TWSI_STATUS_ADDR_R_ACK 0x40 82183840Sraj#define TWSI_STATUS_DATA_RD_ACK 0x50 83183840Sraj#define TWSI_STATUS_DATA_RD_NOACK 0x58 84183840Sraj 85183840Sraj#define TWSI_BAUD_RATE 0x0c 86183840Sraj#define TWSI_BAUD_RATE_94DOT3 0x53 /* N=3, M=10 */ 87183840Sraj#define TWSI_BAUD_RATE_74DOT1 0x6b /* N=3, M=13 */ 88183840Sraj#define TWSI_BAUD_RATE_51DOT8 0x4c /* N=4, M=9 */ 89183840Sraj#define TWSI_BAUD_RATE_99DOT7 0x66 /* N=6, M=12 */ 90183840Sraj 91183840Sraj#define TWSI_SOFT_RESET 0x1c 92183840Sraj 93183840Sraj#define TWSI_DEBUG 94183840Sraj#undef TWSI_DEBUG 95183840Sraj 96183840Sraj#ifdef TWSI_DEBUG 97183840Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) 98183840Sraj#else 99183840Sraj#define debugf(fmt, args...) 100183840Sraj#endif 101183840Sraj 102183840Srajstruct mv_twsi_softc { 103183840Sraj device_t dev; 104183840Sraj struct resource *res[1]; /* SYS_RES_MEMORY */ 105183840Sraj struct mtx mutex; 106183840Sraj device_t iicbus; 107183840Sraj}; 108183840Sraj 109183840Srajstatic int mv_twsi_probe(device_t); 110183840Srajstatic int mv_twsi_attach(device_t); 111183840Srajstatic int mv_twsi_detach(device_t); 112183840Sraj 113183840Srajstatic int mv_twsi_reset(device_t dev, u_char speed, u_char addr, 114183840Sraj u_char *oldaddr); 115183840Srajstatic int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout); 116183840Srajstatic int mv_twsi_start(device_t dev, u_char slave, int timeout); 117183840Srajstatic int mv_twsi_stop(device_t dev); 118183840Srajstatic int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, 119183840Sraj int delay); 120183840Srajstatic int mv_twsi_write(device_t dev, char *buf, int len, int *sent, 121183840Sraj int timeout); 122183840Sraj 123183840Srajstatic struct resource_spec res_spec[] = { 124183840Sraj { SYS_RES_MEMORY, 0, RF_ACTIVE }, 125183840Sraj { -1, 0 } 126183840Sraj}; 127183840Sraj 128183840Srajstatic device_method_t mv_twsi_methods[] = { 129183840Sraj /* device interface */ 130183840Sraj DEVMETHOD(device_probe, mv_twsi_probe), 131183840Sraj DEVMETHOD(device_attach, mv_twsi_attach), 132183840Sraj DEVMETHOD(device_detach, mv_twsi_detach), 133183840Sraj 134183840Sraj /* iicbus interface */ 135183840Sraj DEVMETHOD(iicbus_callback, iicbus_null_callback), 136183840Sraj DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start), 137183840Sraj DEVMETHOD(iicbus_start, mv_twsi_start), 138183840Sraj DEVMETHOD(iicbus_stop, mv_twsi_stop), 139183840Sraj DEVMETHOD(iicbus_write, mv_twsi_write), 140183840Sraj DEVMETHOD(iicbus_read, mv_twsi_read), 141183840Sraj DEVMETHOD(iicbus_reset, mv_twsi_reset), 142183840Sraj DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), 143183840Sraj { 0, 0 } 144183840Sraj}; 145183840Sraj 146183840Srajstatic devclass_t mv_twsi_devclass; 147183840Sraj 148183840Srajstatic driver_t mv_twsi_driver = { 149183840Sraj MV_TWSI_NAME, 150183840Sraj mv_twsi_methods, 151183840Sraj sizeof(struct mv_twsi_softc), 152183840Sraj}; 153183840Sraj 154183840SrajDRIVER_MODULE(twsi, mbus, mv_twsi_driver, mv_twsi_devclass, 0, 0); 155183840SrajDRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); 156183840SrajMODULE_DEPEND(twsi, iicbus, 1, 1, 1); 157183840Sraj 158183840Srajstatic __inline uint32_t 159183840SrajTWSI_READ(struct mv_twsi_softc *sc, bus_size_t off) 160183840Sraj{ 161183840Sraj 162183840Sraj return (bus_read_4(sc->res[0], off)); 163183840Sraj} 164183840Sraj 165183840Srajstatic __inline void 166183840SrajTWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val) 167183840Sraj{ 168183840Sraj 169183840Sraj bus_write_4(sc->res[0], off, val); 170183840Sraj} 171183840Sraj 172183840Srajstatic __inline void 173183840Srajtwsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask) 174183840Sraj{ 175183840Sraj uint32_t val; 176183840Sraj 177183840Sraj val = TWSI_READ(sc, TWSI_CONTROL); 178183840Sraj val &= ~mask; 179183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, val); 180183840Sraj} 181183840Sraj 182183840Srajstatic __inline void 183183840Srajtwsi_control_set(struct mv_twsi_softc *sc, uint32_t mask) 184183840Sraj{ 185183840Sraj uint32_t val; 186183840Sraj 187183840Sraj val = TWSI_READ(sc, TWSI_CONTROL); 188183840Sraj val |= mask; 189183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, val); 190183840Sraj} 191183840Sraj 192183840Srajstatic __inline void 193183840Srajtwsi_clear_iflg(struct mv_twsi_softc *sc) 194183840Sraj{ 195183840Sraj 196183840Sraj DELAY(1000); 197183840Sraj twsi_control_clear(sc, TWSI_CONTROL_IFLG); 198183840Sraj DELAY(1000); 199183840Sraj} 200183840Sraj 201183840Sraj 202183840Sraj/* 203183840Sraj * timeout given in us 204183840Sraj * returns 205183840Sraj * 0 on sucessfull mask change 206183840Sraj * non-zero on timeout 207183840Sraj */ 208183840Srajstatic int 209183840Srajtwsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask) 210183840Sraj{ 211183840Sraj 212183840Sraj timeout /= 10; 213183840Sraj while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) { 214183840Sraj DELAY(10); 215183840Sraj if (--timeout < 0) 216183840Sraj return (timeout); 217183840Sraj } 218183840Sraj return (0); 219183840Sraj} 220183840Sraj 221183840Sraj 222183840Sraj/* 223183840Sraj * 'timeout' is given in us. Note also that timeout handling is not exact -- 224183840Sraj * twsi_locked_start() total wait can be more than 2 x timeout 225183840Sraj * (twsi_poll_ctrl() is called twice). 'mask' can be either TWSI_STATUS_START 226183840Sraj * or TWSI_STATUS_RPTD_START 227183840Sraj */ 228183840Srajstatic int 229183840Srajtwsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, 230183840Sraj u_char slave, int timeout) 231183840Sraj{ 232183840Sraj int read_access, iflg_set = 0; 233183840Sraj uint32_t status; 234183840Sraj 235183840Sraj mtx_assert(&sc->mutex, MA_OWNED); 236183840Sraj 237183840Sraj if (mask == TWSI_STATUS_RPTD_START) 238183840Sraj /* read IFLG to know if it should be cleared later; from NBSD */ 239183840Sraj iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG; 240183840Sraj 241183840Sraj twsi_control_set(sc, TWSI_CONTROL_START); 242183840Sraj 243183840Sraj if (mask == TWSI_STATUS_RPTD_START && iflg_set) { 244183840Sraj debugf("IFLG set, clearing\n"); 245183840Sraj twsi_clear_iflg(sc); 246183840Sraj } 247183840Sraj 248183840Sraj /* 249183840Sraj * Without this delay we timeout checking IFLG if the timeout is 0. 250183840Sraj * NBSD driver always waits here too. 251183840Sraj */ 252183840Sraj DELAY(1000); 253183840Sraj 254183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 255183840Sraj debugf("timeout sending %sSTART condition\n", 256183840Sraj mask == TWSI_STATUS_START ? "" : "repeated "); 257183840Sraj return (IIC_ETIMEOUT); 258183840Sraj } 259183840Sraj 260183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 261183840Sraj if (status != mask) { 262183840Sraj debugf("wrong status (%02x) after sending %sSTART condition\n", 263183840Sraj status, mask == TWSI_STATUS_START ? "" : "repeated "); 264183840Sraj return (IIC_ESTATUS); 265183840Sraj } 266183840Sraj 267183840Sraj TWSI_WRITE(sc, TWSI_DATA, slave); 268183840Sraj DELAY(1000); 269183840Sraj twsi_clear_iflg(sc); 270183840Sraj 271183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 272183840Sraj debugf("timeout sending slave address\n"); 273183840Sraj return (IIC_ETIMEOUT); 274183840Sraj } 275183840Sraj 276183840Sraj read_access = (slave & 0x1) ? 1 : 0; 277183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 278183840Sraj if (status != (read_access ? 279183840Sraj TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) { 280183840Sraj debugf("no ACK (status: %02x) after sending slave address\n", 281183840Sraj status); 282183840Sraj return (IIC_ENOACK); 283183840Sraj } 284183840Sraj 285183840Sraj return (IIC_NOERR); 286183840Sraj} 287183840Sraj 288183840Srajstatic int 289183840Srajmv_twsi_probe(device_t dev) 290183840Sraj{ 291183840Sraj 292183840Sraj device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); 293183840Sraj return (BUS_PROBE_DEFAULT); 294183840Sraj} 295183840Sraj 296183840Srajstatic int 297183840Srajmv_twsi_attach(device_t dev) 298183840Sraj{ 299183840Sraj struct mv_twsi_softc *sc; 300183840Sraj 301183840Sraj sc = device_get_softc(dev); 302183840Sraj sc->dev = dev; 303183840Sraj 304183840Sraj mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF); 305183840Sraj 306183840Sraj /* Allocate IO resources */ 307183840Sraj if (bus_alloc_resources(dev, res_spec, sc->res)) { 308183840Sraj device_printf(dev, "could not allocate resources\n"); 309183840Sraj mv_twsi_detach(dev); 310183840Sraj return (ENXIO); 311183840Sraj } 312183840Sraj 313183840Sraj sc->iicbus = device_add_child(dev, "iicbus", -1); 314183840Sraj if (sc->iicbus == NULL) { 315183840Sraj device_printf(dev, "could not add iicbus child\n"); 316183840Sraj mv_twsi_detach(dev); 317183840Sraj return (ENXIO); 318183840Sraj } 319183840Sraj 320183840Sraj bus_generic_attach(dev); 321183840Sraj return (0); 322183840Sraj} 323183840Sraj 324183840Srajstatic int 325183840Srajmv_twsi_detach(device_t dev) 326183840Sraj{ 327183840Sraj struct mv_twsi_softc *sc; 328183840Sraj int rv; 329183840Sraj 330183840Sraj sc = device_get_softc(dev); 331183840Sraj 332183840Sraj if ((rv = bus_generic_detach(dev)) != 0) 333183840Sraj return (rv); 334183840Sraj 335183840Sraj if (sc->iicbus != NULL) 336183840Sraj if ((rv = device_delete_child(dev, sc->iicbus)) != 0) 337183840Sraj return (rv); 338183840Sraj 339183840Sraj bus_release_resources(dev, res_spec, sc->res); 340183840Sraj 341183840Sraj mtx_destroy(&sc->mutex); 342183840Sraj return (0); 343183840Sraj} 344183840Sraj 345183840Sraj/* 346183840Sraj * Only slave mode supported, disregard [old]addr 347183840Sraj */ 348183840Srajstatic int 349183840Srajmv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 350183840Sraj{ 351183840Sraj struct mv_twsi_softc *sc; 352183840Sraj uint32_t baud_rate; 353183840Sraj 354183840Sraj sc = device_get_softc(dev); 355183840Sraj 356183840Sraj switch (speed) { 357183840Sraj case IIC_SLOW: 358183840Sraj baud_rate = TWSI_BAUD_RATE_51DOT8; 359183840Sraj break; 360183840Sraj case IIC_FAST: 361183840Sraj baud_rate = TWSI_BAUD_RATE_51DOT8; 362183840Sraj break; 363183840Sraj case IIC_UNKNOWN: 364183840Sraj case IIC_FASTEST: 365183840Sraj default: 366183840Sraj baud_rate = TWSI_BAUD_RATE_99DOT7; 367183840Sraj break; 368183840Sraj } 369183840Sraj 370183840Sraj mtx_lock(&sc->mutex); 371183840Sraj TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0); 372183840Sraj DELAY(2000); 373183840Sraj TWSI_WRITE(sc, TWSI_BAUD_RATE, baud_rate); 374183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK); 375183840Sraj DELAY(1000); 376183840Sraj mtx_unlock(&sc->mutex); 377183840Sraj 378183840Sraj return (0); 379183840Sraj} 380183840Sraj 381183840Sraj/* 382183840Sraj * timeout is given in us 383183840Sraj */ 384183840Srajstatic int 385183840Srajmv_twsi_repeated_start(device_t dev, u_char slave, int timeout) 386183840Sraj{ 387183840Sraj struct mv_twsi_softc *sc; 388183840Sraj int rv; 389183840Sraj 390183840Sraj sc = device_get_softc(dev); 391183840Sraj 392183840Sraj mtx_lock(&sc->mutex); 393183840Sraj rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, 394183840Sraj timeout); 395183840Sraj mtx_unlock(&sc->mutex); 396183840Sraj 397183840Sraj if (rv) { 398183840Sraj mv_twsi_stop(dev); 399183840Sraj return (rv); 400183840Sraj } else 401183840Sraj return (IIC_NOERR); 402183840Sraj} 403183840Sraj 404183840Sraj/* 405183840Sraj * timeout is given in us 406183840Sraj */ 407183840Srajstatic int 408183840Srajmv_twsi_start(device_t dev, u_char slave, int timeout) 409183840Sraj{ 410183840Sraj struct mv_twsi_softc *sc; 411183840Sraj int rv; 412183840Sraj 413183840Sraj sc = device_get_softc(dev); 414183840Sraj 415183840Sraj mtx_lock(&sc->mutex); 416183840Sraj rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); 417183840Sraj mtx_unlock(&sc->mutex); 418183840Sraj 419183840Sraj if (rv) { 420183840Sraj mv_twsi_stop(dev); 421183840Sraj return (rv); 422183840Sraj } else 423183840Sraj return (IIC_NOERR); 424183840Sraj} 425183840Sraj 426183840Srajstatic int 427183840Srajmv_twsi_stop(device_t dev) 428183840Sraj{ 429183840Sraj struct mv_twsi_softc *sc; 430183840Sraj 431183840Sraj sc = device_get_softc(dev); 432183840Sraj 433183840Sraj mtx_lock(&sc->mutex); 434183840Sraj twsi_control_set(sc, TWSI_CONTROL_STOP); 435183840Sraj DELAY(1000); 436183840Sraj twsi_clear_iflg(sc); 437183840Sraj mtx_unlock(&sc->mutex); 438183840Sraj 439183840Sraj return (IIC_NOERR); 440183840Sraj} 441183840Sraj 442183840Srajstatic int 443183840Srajmv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) 444183840Sraj{ 445183840Sraj struct mv_twsi_softc *sc; 446183840Sraj uint32_t status; 447183840Sraj int last_byte, rv; 448183840Sraj 449183840Sraj sc = device_get_softc(dev); 450183840Sraj 451183840Sraj mtx_lock(&sc->mutex); 452183840Sraj *read = 0; 453183840Sraj while (*read < len) { 454183840Sraj /* 455183840Sraj * Check if we are reading last byte of the last buffer, 456183840Sraj * do not send ACK then, per I2C specs 457183840Sraj */ 458183840Sraj last_byte = ((*read == len - 1) && last) ? 1 : 0; 459183840Sraj if (last_byte) 460183840Sraj twsi_control_clear(sc, TWSI_CONTROL_ACK); 461183840Sraj else 462183840Sraj twsi_control_set(sc, TWSI_CONTROL_ACK); 463183840Sraj 464183840Sraj DELAY (1000); 465183840Sraj twsi_clear_iflg(sc); 466183840Sraj 467183840Sraj if (twsi_poll_ctrl(sc, delay, TWSI_CONTROL_IFLG)) { 468183840Sraj debugf("timeout reading data\n"); 469183840Sraj rv = IIC_ETIMEOUT; 470183840Sraj goto out; 471183840Sraj } 472183840Sraj 473183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 474183840Sraj if (status != (last_byte ? 475183840Sraj TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) { 476183840Sraj debugf("wrong status (%02x) while reading\n", status); 477183840Sraj rv = IIC_ESTATUS; 478183840Sraj goto out; 479183840Sraj } 480183840Sraj 481183840Sraj *buf++ = TWSI_READ(sc, TWSI_DATA); 482183840Sraj (*read)++; 483183840Sraj } 484183840Sraj rv = IIC_NOERR; 485183840Srajout: 486183840Sraj mtx_unlock(&sc->mutex); 487183840Sraj return (rv); 488183840Sraj} 489183840Sraj 490183840Srajstatic int 491183840Srajmv_twsi_write(device_t dev, char *buf, int len, int *sent, int timeout) 492183840Sraj{ 493183840Sraj struct mv_twsi_softc *sc; 494183840Sraj uint32_t status; 495183840Sraj int rv; 496183840Sraj 497183840Sraj sc = device_get_softc(dev); 498183840Sraj 499183840Sraj mtx_lock(&sc->mutex); 500183840Sraj *sent = 0; 501183840Sraj while (*sent < len) { 502183840Sraj TWSI_WRITE(sc, TWSI_DATA, *buf++); 503183840Sraj 504183840Sraj twsi_clear_iflg(sc); 505183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 506183840Sraj debugf("timeout writing data\n"); 507183840Sraj rv = IIC_ETIMEOUT; 508183840Sraj goto out; 509183840Sraj } 510183840Sraj 511183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 512183840Sraj if (status != TWSI_STATUS_DATA_WR_ACK) { 513183840Sraj debugf("wrong status (%02x) while writing\n", status); 514183840Sraj rv = IIC_ESTATUS; 515183840Sraj goto out; 516183840Sraj } 517183840Sraj (*sent)++; 518183840Sraj } 519183840Sraj rv = IIC_NOERR; 520183840Srajout: 521183840Sraj mtx_unlock(&sc->mutex); 522183840Sraj return (rv); 523183840Sraj} 524