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: releng/10.2/sys/arm/mv/twsi.c 266152 2014-05-15 16:11:06Z ian $"); 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 50239508Shrs#include <machine/_inttypes.h> 51183840Sraj#include <machine/bus.h> 52183840Sraj#include <machine/resource.h> 53183840Sraj 54183840Sraj#include <sys/rman.h> 55183840Sraj 56183840Sraj#include <sys/lock.h> 57183840Sraj#include <sys/mutex.h> 58183840Sraj 59183840Sraj#include <dev/iicbus/iiconf.h> 60183840Sraj#include <dev/iicbus/iicbus.h> 61239508Shrs#include <dev/fdt/fdt_common.h> 62209131Sraj#include <dev/ofw/ofw_bus.h> 63209131Sraj#include <dev/ofw/ofw_bus_subr.h> 64209131Sraj 65239508Shrs#include <arm/mv/mvreg.h> 66239508Shrs#include <arm/mv/mvvar.h> 67239508Shrs 68183840Sraj#include "iicbus_if.h" 69183840Sraj 70183840Sraj#define MV_TWSI_NAME "twsi" 71239508Shrs#define IICBUS_DEVNAME "iicbus" 72183840Sraj 73183840Sraj#define TWSI_SLAVE_ADDR 0x00 74183840Sraj#define TWSI_EXT_SLAVE_ADDR 0x10 75183840Sraj#define TWSI_DATA 0x04 76183840Sraj 77183840Sraj#define TWSI_CONTROL 0x08 78183840Sraj#define TWSI_CONTROL_ACK (1 << 2) 79183840Sraj#define TWSI_CONTROL_IFLG (1 << 3) 80183840Sraj#define TWSI_CONTROL_STOP (1 << 4) 81183840Sraj#define TWSI_CONTROL_START (1 << 5) 82183840Sraj#define TWSI_CONTROL_TWSIEN (1 << 6) 83183840Sraj#define TWSI_CONTROL_INTEN (1 << 7) 84183840Sraj 85183840Sraj#define TWSI_STATUS 0x0c 86183840Sraj#define TWSI_STATUS_START 0x08 87183840Sraj#define TWSI_STATUS_RPTD_START 0x10 88183840Sraj#define TWSI_STATUS_ADDR_W_ACK 0x18 89183840Sraj#define TWSI_STATUS_DATA_WR_ACK 0x28 90183840Sraj#define TWSI_STATUS_ADDR_R_ACK 0x40 91183840Sraj#define TWSI_STATUS_DATA_RD_ACK 0x50 92183840Sraj#define TWSI_STATUS_DATA_RD_NOACK 0x58 93183840Sraj 94183840Sraj#define TWSI_BAUD_RATE 0x0c 95239508Shrs#define TWSI_BAUD_RATE_PARAM(M,N) ((((M) << 3) | ((N) & 0x7)) & 0x7f) 96239508Shrs#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1))) 97239508Shrs#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */ 98239508Shrs#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */ 99183840Sraj 100183840Sraj#define TWSI_SOFT_RESET 0x1c 101183840Sraj 102183840Sraj#define TWSI_DEBUG 103183840Sraj#undef TWSI_DEBUG 104183840Sraj 105183840Sraj#ifdef TWSI_DEBUG 106183840Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) 107183840Sraj#else 108183840Sraj#define debugf(fmt, args...) 109183840Sraj#endif 110183840Sraj 111183840Srajstruct mv_twsi_softc { 112183840Sraj device_t dev; 113183840Sraj struct resource *res[1]; /* SYS_RES_MEMORY */ 114183840Sraj struct mtx mutex; 115183840Sraj device_t iicbus; 116183840Sraj}; 117183840Sraj 118239508Shrsstatic struct mv_twsi_baud_rate { 119239508Shrs uint32_t raw; 120239508Shrs int param; 121239508Shrs int m; 122239508Shrs int n; 123239508Shrs} baud_rate[IIC_FASTEST + 1]; 124239508Shrs 125183840Srajstatic int mv_twsi_probe(device_t); 126183840Srajstatic int mv_twsi_attach(device_t); 127183840Srajstatic int mv_twsi_detach(device_t); 128183840Sraj 129183840Srajstatic int mv_twsi_reset(device_t dev, u_char speed, u_char addr, 130183840Sraj u_char *oldaddr); 131183840Srajstatic int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout); 132183840Srajstatic int mv_twsi_start(device_t dev, u_char slave, int timeout); 133183840Srajstatic int mv_twsi_stop(device_t dev); 134183840Srajstatic int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, 135183840Sraj int delay); 136194015Savgstatic int mv_twsi_write(device_t dev, const char *buf, int len, int *sent, 137183840Sraj int timeout); 138183840Sraj 139183840Srajstatic struct resource_spec res_spec[] = { 140183840Sraj { SYS_RES_MEMORY, 0, RF_ACTIVE }, 141183840Sraj { -1, 0 } 142183840Sraj}; 143183840Sraj 144183840Srajstatic device_method_t mv_twsi_methods[] = { 145183840Sraj /* device interface */ 146183840Sraj DEVMETHOD(device_probe, mv_twsi_probe), 147183840Sraj DEVMETHOD(device_attach, mv_twsi_attach), 148183840Sraj DEVMETHOD(device_detach, mv_twsi_detach), 149183840Sraj 150183840Sraj /* iicbus interface */ 151183840Sraj DEVMETHOD(iicbus_callback, iicbus_null_callback), 152183840Sraj DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start), 153183840Sraj DEVMETHOD(iicbus_start, mv_twsi_start), 154183840Sraj DEVMETHOD(iicbus_stop, mv_twsi_stop), 155183840Sraj DEVMETHOD(iicbus_write, mv_twsi_write), 156183840Sraj DEVMETHOD(iicbus_read, mv_twsi_read), 157183840Sraj DEVMETHOD(iicbus_reset, mv_twsi_reset), 158183840Sraj DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), 159183840Sraj { 0, 0 } 160183840Sraj}; 161183840Sraj 162183840Srajstatic devclass_t mv_twsi_devclass; 163183840Sraj 164183840Srajstatic driver_t mv_twsi_driver = { 165183840Sraj MV_TWSI_NAME, 166183840Sraj mv_twsi_methods, 167183840Sraj sizeof(struct mv_twsi_softc), 168183840Sraj}; 169183840Sraj 170209131SrajDRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0); 171183840SrajDRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); 172183840SrajMODULE_DEPEND(twsi, iicbus, 1, 1, 1); 173183840Sraj 174183840Srajstatic __inline uint32_t 175183840SrajTWSI_READ(struct mv_twsi_softc *sc, bus_size_t off) 176183840Sraj{ 177183840Sraj 178183840Sraj return (bus_read_4(sc->res[0], off)); 179183840Sraj} 180183840Sraj 181183840Srajstatic __inline void 182183840SrajTWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val) 183183840Sraj{ 184183840Sraj 185183840Sraj bus_write_4(sc->res[0], off, val); 186183840Sraj} 187183840Sraj 188183840Srajstatic __inline void 189183840Srajtwsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask) 190183840Sraj{ 191183840Sraj uint32_t val; 192183840Sraj 193183840Sraj val = TWSI_READ(sc, TWSI_CONTROL); 194183840Sraj val &= ~mask; 195183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, val); 196183840Sraj} 197183840Sraj 198183840Srajstatic __inline void 199183840Srajtwsi_control_set(struct mv_twsi_softc *sc, uint32_t mask) 200183840Sraj{ 201183840Sraj uint32_t val; 202183840Sraj 203183840Sraj val = TWSI_READ(sc, TWSI_CONTROL); 204183840Sraj val |= mask; 205183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, val); 206183840Sraj} 207183840Sraj 208183840Srajstatic __inline void 209183840Srajtwsi_clear_iflg(struct mv_twsi_softc *sc) 210183840Sraj{ 211183840Sraj 212183840Sraj DELAY(1000); 213183840Sraj twsi_control_clear(sc, TWSI_CONTROL_IFLG); 214183840Sraj DELAY(1000); 215183840Sraj} 216183840Sraj 217183840Sraj 218183840Sraj/* 219183840Sraj * timeout given in us 220183840Sraj * returns 221183840Sraj * 0 on sucessfull mask change 222183840Sraj * non-zero on timeout 223183840Sraj */ 224183840Srajstatic int 225183840Srajtwsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask) 226183840Sraj{ 227183840Sraj 228183840Sraj timeout /= 10; 229183840Sraj while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) { 230183840Sraj DELAY(10); 231183840Sraj if (--timeout < 0) 232183840Sraj return (timeout); 233183840Sraj } 234183840Sraj return (0); 235183840Sraj} 236183840Sraj 237183840Sraj 238183840Sraj/* 239183840Sraj * 'timeout' is given in us. Note also that timeout handling is not exact -- 240183840Sraj * twsi_locked_start() total wait can be more than 2 x timeout 241183840Sraj * (twsi_poll_ctrl() is called twice). 'mask' can be either TWSI_STATUS_START 242183840Sraj * or TWSI_STATUS_RPTD_START 243183840Sraj */ 244183840Srajstatic int 245183840Srajtwsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, 246183840Sraj u_char slave, int timeout) 247183840Sraj{ 248183840Sraj int read_access, iflg_set = 0; 249183840Sraj uint32_t status; 250183840Sraj 251183840Sraj mtx_assert(&sc->mutex, MA_OWNED); 252183840Sraj 253183840Sraj if (mask == TWSI_STATUS_RPTD_START) 254183840Sraj /* read IFLG to know if it should be cleared later; from NBSD */ 255183840Sraj iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG; 256183840Sraj 257183840Sraj twsi_control_set(sc, TWSI_CONTROL_START); 258183840Sraj 259183840Sraj if (mask == TWSI_STATUS_RPTD_START && iflg_set) { 260183840Sraj debugf("IFLG set, clearing\n"); 261183840Sraj twsi_clear_iflg(sc); 262183840Sraj } 263183840Sraj 264183840Sraj /* 265183840Sraj * Without this delay we timeout checking IFLG if the timeout is 0. 266183840Sraj * NBSD driver always waits here too. 267183840Sraj */ 268183840Sraj DELAY(1000); 269183840Sraj 270183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 271183840Sraj debugf("timeout sending %sSTART condition\n", 272183840Sraj mask == TWSI_STATUS_START ? "" : "repeated "); 273183840Sraj return (IIC_ETIMEOUT); 274183840Sraj } 275183840Sraj 276183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 277183840Sraj if (status != mask) { 278183840Sraj debugf("wrong status (%02x) after sending %sSTART condition\n", 279183840Sraj status, mask == TWSI_STATUS_START ? "" : "repeated "); 280183840Sraj return (IIC_ESTATUS); 281183840Sraj } 282183840Sraj 283183840Sraj TWSI_WRITE(sc, TWSI_DATA, slave); 284183840Sraj DELAY(1000); 285183840Sraj twsi_clear_iflg(sc); 286183840Sraj 287183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 288183840Sraj debugf("timeout sending slave address\n"); 289183840Sraj return (IIC_ETIMEOUT); 290183840Sraj } 291183840Sraj 292183840Sraj read_access = (slave & 0x1) ? 1 : 0; 293183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 294183840Sraj if (status != (read_access ? 295183840Sraj TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) { 296183840Sraj debugf("no ACK (status: %02x) after sending slave address\n", 297183840Sraj status); 298183840Sraj return (IIC_ENOACK); 299183840Sraj } 300183840Sraj 301183840Sraj return (IIC_NOERR); 302183840Sraj} 303183840Sraj 304183840Srajstatic int 305183840Srajmv_twsi_probe(device_t dev) 306183840Sraj{ 307183840Sraj 308266152Sian if (!ofw_bus_status_okay(dev)) 309266152Sian return (ENXIO); 310266152Sian 311209131Sraj if (!ofw_bus_is_compatible(dev, "mrvl,twsi")) 312209131Sraj return (ENXIO); 313209131Sraj 314183840Sraj device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); 315183840Sraj return (BUS_PROBE_DEFAULT); 316183840Sraj} 317183840Sraj 318239508Shrs#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) 319239508Shrsstatic void 320239508Shrsmv_twsi_cal_baud_rate(const uint32_t target, struct mv_twsi_baud_rate *rate) 321239508Shrs{ 322239508Shrs uint32_t clk, cur, diff, diff0; 323239508Shrs int m, n, m0, n0; 324239508Shrs 325239508Shrs /* Calculate baud rate. */ 326239508Shrs m0 = n0 = 4; /* Default values on reset */ 327239508Shrs diff0 = 0xffffffff; 328239508Shrs clk = get_tclk(); 329239508Shrs 330239508Shrs for (n = 0; n < 8; n++) { 331239508Shrs for (m = 0; m < 16; m++) { 332239508Shrs cur = TWSI_BAUD_RATE_RAW(clk,m,n); 333239508Shrs diff = ABSSUB(target, cur); 334239508Shrs if (diff < diff0) { 335239508Shrs m0 = m; 336239508Shrs n0 = n; 337239508Shrs diff0 = diff; 338239508Shrs } 339239508Shrs } 340239508Shrs } 341239508Shrs rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0); 342239508Shrs rate->param = TWSI_BAUD_RATE_PARAM(m0, n0); 343239508Shrs rate->m = m0; 344239508Shrs rate->n = n0; 345239508Shrs} 346239508Shrs 347183840Srajstatic int 348183840Srajmv_twsi_attach(device_t dev) 349183840Sraj{ 350183840Sraj struct mv_twsi_softc *sc; 351239508Shrs phandle_t child, iicbusnode; 352239508Shrs device_t childdev; 353239508Shrs struct iicbus_ivar *devi; 354239508Shrs char dname[32]; /* 32 is taken from struct u_device */ 355239508Shrs uint32_t paddr; 356239508Shrs int len, error; 357183840Sraj 358183840Sraj sc = device_get_softc(dev); 359183840Sraj sc->dev = dev; 360239508Shrs bzero(baud_rate, sizeof(baud_rate)); 361183840Sraj 362183840Sraj mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF); 363183840Sraj 364183840Sraj /* Allocate IO resources */ 365183840Sraj if (bus_alloc_resources(dev, res_spec, sc->res)) { 366183840Sraj device_printf(dev, "could not allocate resources\n"); 367183840Sraj mv_twsi_detach(dev); 368183840Sraj return (ENXIO); 369183840Sraj } 370183840Sraj 371239508Shrs mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &baud_rate[IIC_SLOW]); 372239508Shrs mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &baud_rate[IIC_FAST]); 373239508Shrs if (bootverbose) 374239508Shrs device_printf(dev, "calculated baud rates are:\n" 375239508Shrs " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" 376239508Shrs " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n", 377239508Shrs baud_rate[IIC_SLOW].raw / 1000, 378239508Shrs baud_rate[IIC_SLOW].m, 379239508Shrs baud_rate[IIC_SLOW].n, 380239508Shrs baud_rate[IIC_FAST].raw / 1000, 381239508Shrs baud_rate[IIC_FAST].m, 382239508Shrs baud_rate[IIC_FAST].n); 383239508Shrs 384239508Shrs sc->iicbus = device_add_child(dev, IICBUS_DEVNAME, -1); 385183840Sraj if (sc->iicbus == NULL) { 386183840Sraj device_printf(dev, "could not add iicbus child\n"); 387183840Sraj mv_twsi_detach(dev); 388183840Sraj return (ENXIO); 389183840Sraj } 390239508Shrs /* Attach iicbus. */ 391239508Shrs bus_generic_attach(dev); 392183840Sraj 393239508Shrs iicbusnode = 0; 394239508Shrs /* Find iicbus as the child devices in the device tree. */ 395239508Shrs for (child = OF_child(ofw_bus_get_node(dev)); child != 0; 396239508Shrs child = OF_peer(child)) { 397239508Shrs len = OF_getproplen(child, "model"); 398239508Shrs if (len <= 0 || len > sizeof(dname) - 1) 399239508Shrs continue; 400239508Shrs error = OF_getprop(child, "model", &dname, len); 401239508Shrs dname[len + 1] = '\0'; 402239508Shrs if (error == -1) 403239508Shrs continue; 404239508Shrs len = strlen(dname); 405239508Shrs if (len == strlen(IICBUS_DEVNAME) && 406239508Shrs strncasecmp(dname, IICBUS_DEVNAME, len) == 0) { 407239508Shrs iicbusnode = child; 408239508Shrs break; 409239508Shrs } 410239508Shrs } 411239508Shrs if (iicbusnode == 0) 412239508Shrs goto attach_end; 413239508Shrs 414239508Shrs /* Attach child devices onto iicbus. */ 415239508Shrs for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) { 416239508Shrs /* Get slave address. */ 417239508Shrs error = OF_getprop(child, "i2c-address", &paddr, sizeof(paddr)); 418239508Shrs if (error == -1) 419239508Shrs error = OF_getprop(child, "reg", &paddr, sizeof(paddr)); 420239508Shrs if (error == -1) 421239508Shrs continue; 422239508Shrs 423239508Shrs /* Get device driver name. */ 424239508Shrs len = OF_getproplen(child, "model"); 425239508Shrs if (len <= 0 || len > sizeof(dname) - 1) 426239508Shrs continue; 427239508Shrs OF_getprop(child, "model", &dname, len); 428239508Shrs dname[len + 1] = '\0'; 429239508Shrs 430239508Shrs if (bootverbose) 431239508Shrs device_printf(dev, "adding a device %s at %d.\n", 432239508Shrs dname, fdt32_to_cpu(paddr)); 433239508Shrs childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1); 434239508Shrs devi = IICBUS_IVAR(childdev); 435239508Shrs devi->addr = fdt32_to_cpu(paddr); 436239508Shrs } 437239508Shrs 438239508Shrsattach_end: 439239508Shrs bus_generic_attach(sc->iicbus); 440239508Shrs 441183840Sraj return (0); 442183840Sraj} 443183840Sraj 444183840Srajstatic int 445183840Srajmv_twsi_detach(device_t dev) 446183840Sraj{ 447183840Sraj struct mv_twsi_softc *sc; 448183840Sraj int rv; 449183840Sraj 450183840Sraj sc = device_get_softc(dev); 451183840Sraj 452183840Sraj if ((rv = bus_generic_detach(dev)) != 0) 453183840Sraj return (rv); 454183840Sraj 455183840Sraj if (sc->iicbus != NULL) 456183840Sraj if ((rv = device_delete_child(dev, sc->iicbus)) != 0) 457183840Sraj return (rv); 458183840Sraj 459183840Sraj bus_release_resources(dev, res_spec, sc->res); 460183840Sraj 461183840Sraj mtx_destroy(&sc->mutex); 462183840Sraj return (0); 463183840Sraj} 464183840Sraj 465183840Sraj/* 466183840Sraj * Only slave mode supported, disregard [old]addr 467183840Sraj */ 468183840Srajstatic int 469183840Srajmv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 470183840Sraj{ 471183840Sraj struct mv_twsi_softc *sc; 472239508Shrs uint32_t param; 473183840Sraj 474183840Sraj sc = device_get_softc(dev); 475183840Sraj 476183840Sraj switch (speed) { 477183840Sraj case IIC_SLOW: 478183840Sraj case IIC_FAST: 479239508Shrs param = baud_rate[speed].param; 480183840Sraj break; 481239508Shrs case IIC_FASTEST: 482183840Sraj case IIC_UNKNOWN: 483183840Sraj default: 484239508Shrs param = baud_rate[IIC_FAST].param; 485183840Sraj break; 486183840Sraj } 487183840Sraj 488183840Sraj mtx_lock(&sc->mutex); 489183840Sraj TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0); 490183840Sraj DELAY(2000); 491239508Shrs TWSI_WRITE(sc, TWSI_BAUD_RATE, param); 492183840Sraj TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK); 493183840Sraj DELAY(1000); 494183840Sraj mtx_unlock(&sc->mutex); 495183840Sraj 496183840Sraj return (0); 497183840Sraj} 498183840Sraj 499183840Sraj/* 500183840Sraj * timeout is given in us 501183840Sraj */ 502183840Srajstatic int 503183840Srajmv_twsi_repeated_start(device_t dev, u_char slave, int timeout) 504183840Sraj{ 505183840Sraj struct mv_twsi_softc *sc; 506183840Sraj int rv; 507183840Sraj 508183840Sraj sc = device_get_softc(dev); 509183840Sraj 510183840Sraj mtx_lock(&sc->mutex); 511183840Sraj rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, 512183840Sraj timeout); 513183840Sraj mtx_unlock(&sc->mutex); 514183840Sraj 515183840Sraj if (rv) { 516183840Sraj mv_twsi_stop(dev); 517183840Sraj return (rv); 518183840Sraj } else 519183840Sraj return (IIC_NOERR); 520183840Sraj} 521183840Sraj 522183840Sraj/* 523183840Sraj * timeout is given in us 524183840Sraj */ 525183840Srajstatic int 526183840Srajmv_twsi_start(device_t dev, u_char slave, int timeout) 527183840Sraj{ 528183840Sraj struct mv_twsi_softc *sc; 529183840Sraj int rv; 530183840Sraj 531183840Sraj sc = device_get_softc(dev); 532183840Sraj 533183840Sraj mtx_lock(&sc->mutex); 534183840Sraj rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); 535183840Sraj mtx_unlock(&sc->mutex); 536183840Sraj 537183840Sraj if (rv) { 538183840Sraj mv_twsi_stop(dev); 539183840Sraj return (rv); 540183840Sraj } else 541183840Sraj return (IIC_NOERR); 542183840Sraj} 543183840Sraj 544183840Srajstatic int 545183840Srajmv_twsi_stop(device_t dev) 546183840Sraj{ 547183840Sraj struct mv_twsi_softc *sc; 548183840Sraj 549183840Sraj sc = device_get_softc(dev); 550183840Sraj 551183840Sraj mtx_lock(&sc->mutex); 552183840Sraj twsi_control_set(sc, TWSI_CONTROL_STOP); 553183840Sraj DELAY(1000); 554183840Sraj twsi_clear_iflg(sc); 555183840Sraj mtx_unlock(&sc->mutex); 556183840Sraj 557183840Sraj return (IIC_NOERR); 558183840Sraj} 559183840Sraj 560183840Srajstatic int 561183840Srajmv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) 562183840Sraj{ 563183840Sraj struct mv_twsi_softc *sc; 564183840Sraj uint32_t status; 565183840Sraj int last_byte, rv; 566183840Sraj 567183840Sraj sc = device_get_softc(dev); 568183840Sraj 569183840Sraj mtx_lock(&sc->mutex); 570183840Sraj *read = 0; 571183840Sraj while (*read < len) { 572183840Sraj /* 573183840Sraj * Check if we are reading last byte of the last buffer, 574183840Sraj * do not send ACK then, per I2C specs 575183840Sraj */ 576183840Sraj last_byte = ((*read == len - 1) && last) ? 1 : 0; 577183840Sraj if (last_byte) 578183840Sraj twsi_control_clear(sc, TWSI_CONTROL_ACK); 579183840Sraj else 580183840Sraj twsi_control_set(sc, TWSI_CONTROL_ACK); 581183840Sraj 582183840Sraj DELAY (1000); 583183840Sraj twsi_clear_iflg(sc); 584183840Sraj 585183840Sraj if (twsi_poll_ctrl(sc, delay, TWSI_CONTROL_IFLG)) { 586183840Sraj debugf("timeout reading data\n"); 587183840Sraj rv = IIC_ETIMEOUT; 588183840Sraj goto out; 589183840Sraj } 590183840Sraj 591183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 592183840Sraj if (status != (last_byte ? 593183840Sraj TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) { 594183840Sraj debugf("wrong status (%02x) while reading\n", status); 595183840Sraj rv = IIC_ESTATUS; 596183840Sraj goto out; 597183840Sraj } 598183840Sraj 599183840Sraj *buf++ = TWSI_READ(sc, TWSI_DATA); 600183840Sraj (*read)++; 601183840Sraj } 602183840Sraj rv = IIC_NOERR; 603183840Srajout: 604183840Sraj mtx_unlock(&sc->mutex); 605183840Sraj return (rv); 606183840Sraj} 607183840Sraj 608183840Srajstatic int 609194015Savgmv_twsi_write(device_t dev, const char *buf, int len, int *sent, int timeout) 610183840Sraj{ 611183840Sraj struct mv_twsi_softc *sc; 612183840Sraj uint32_t status; 613183840Sraj int rv; 614183840Sraj 615183840Sraj sc = device_get_softc(dev); 616183840Sraj 617183840Sraj mtx_lock(&sc->mutex); 618183840Sraj *sent = 0; 619183840Sraj while (*sent < len) { 620183840Sraj TWSI_WRITE(sc, TWSI_DATA, *buf++); 621183840Sraj 622183840Sraj twsi_clear_iflg(sc); 623183840Sraj if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { 624183840Sraj debugf("timeout writing data\n"); 625183840Sraj rv = IIC_ETIMEOUT; 626183840Sraj goto out; 627183840Sraj } 628183840Sraj 629183840Sraj status = TWSI_READ(sc, TWSI_STATUS); 630183840Sraj if (status != TWSI_STATUS_DATA_WR_ACK) { 631183840Sraj debugf("wrong status (%02x) while writing\n", status); 632183840Sraj rv = IIC_ESTATUS; 633183840Sraj goto out; 634183840Sraj } 635183840Sraj (*sent)++; 636183840Sraj } 637183840Sraj rv = IIC_NOERR; 638183840Srajout: 639183840Sraj mtx_unlock(&sc->mutex); 640183840Sraj return (rv); 641183840Sraj} 642