171d51719SMichael Gmelin /* 271d51719SMichael Gmelin * Copyright (c) 2014 The DragonFly Project. All rights reserved. 371d51719SMichael Gmelin * 471d51719SMichael Gmelin * This code is derived from software contributed to The DragonFly Project 571d51719SMichael Gmelin * by Matthew Dillon <dillon@backplane.com> and was subsequently ported 671d51719SMichael Gmelin * to FreeBSD by Michael Gmelin <freebsd@grem.de> 771d51719SMichael Gmelin * 871d51719SMichael Gmelin * Redistribution and use in source and binary forms, with or without 971d51719SMichael Gmelin * modification, are permitted provided that the following conditions 1071d51719SMichael Gmelin * are met: 1171d51719SMichael Gmelin * 1271d51719SMichael Gmelin * 1. Redistributions of source code must retain the above copyright 1371d51719SMichael Gmelin * notice, this list of conditions and the following disclaimer. 1471d51719SMichael Gmelin * 2. Redistributions in binary form must reproduce the above copyright 1571d51719SMichael Gmelin * notice, this list of conditions and the following disclaimer in 1671d51719SMichael Gmelin * the documentation and/or other materials provided with the 1771d51719SMichael Gmelin * distribution. 1871d51719SMichael Gmelin * 3. Neither the name of The DragonFly Project nor the names of its 1971d51719SMichael Gmelin * contributors may be used to endorse or promote products derived 2071d51719SMichael Gmelin * from this software without specific, prior written permission. 2171d51719SMichael Gmelin * 2271d51719SMichael Gmelin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2371d51719SMichael Gmelin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2471d51719SMichael Gmelin * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2571d51719SMichael Gmelin * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2671d51719SMichael Gmelin * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2771d51719SMichael Gmelin * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2871d51719SMichael Gmelin * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2971d51719SMichael Gmelin * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 3071d51719SMichael Gmelin * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3171d51719SMichael Gmelin * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3271d51719SMichael Gmelin * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3371d51719SMichael Gmelin * SUCH DAMAGE. 3471d51719SMichael Gmelin */ 3571d51719SMichael Gmelin 3671d51719SMichael Gmelin #include <sys/cdefs.h> 3771d51719SMichael Gmelin __FBSDID("$FreeBSD$"); 3871d51719SMichael Gmelin 3971d51719SMichael Gmelin /* 40eb6befbaSAndriy Gapon * Intel fourth generation mobile cpus integrated I2C device. 4171d51719SMichael Gmelin * 4271d51719SMichael Gmelin * See ig4_reg.h for datasheet reference and notes. 434cd6abddSMichael Gmelin * See ig4_var.h for locking semantics. 4471d51719SMichael Gmelin */ 4571d51719SMichael Gmelin 4671d51719SMichael Gmelin #include <sys/param.h> 4771d51719SMichael Gmelin #include <sys/systm.h> 4871d51719SMichael Gmelin #include <sys/kernel.h> 4971d51719SMichael Gmelin #include <sys/module.h> 5071d51719SMichael Gmelin #include <sys/errno.h> 5171d51719SMichael Gmelin #include <sys/lock.h> 5271d51719SMichael Gmelin #include <sys/mutex.h> 534cd6abddSMichael Gmelin #include <sys/sx.h> 5471d51719SMichael Gmelin #include <sys/syslog.h> 5571d51719SMichael Gmelin #include <sys/bus.h> 5671d51719SMichael Gmelin #include <sys/sysctl.h> 5771d51719SMichael Gmelin 5871d51719SMichael Gmelin #include <machine/bus.h> 5971d51719SMichael Gmelin #include <sys/rman.h> 6071d51719SMichael Gmelin 6171d51719SMichael Gmelin #include <dev/pci/pcivar.h> 6271d51719SMichael Gmelin #include <dev/pci/pcireg.h> 63448897d3SAndriy Gapon #include <dev/iicbus/iicbus.h> 64448897d3SAndriy Gapon #include <dev/iicbus/iiconf.h> 6571d51719SMichael Gmelin 6671d51719SMichael Gmelin #include <dev/ichiic/ig4_reg.h> 6771d51719SMichael Gmelin #include <dev/ichiic/ig4_var.h> 6871d51719SMichael Gmelin 6971d51719SMichael Gmelin #define TRANS_NORMAL 1 7071d51719SMichael Gmelin #define TRANS_PCALL 2 7171d51719SMichael Gmelin #define TRANS_BLOCK 3 7271d51719SMichael Gmelin 7371d51719SMichael Gmelin static void ig4iic_start(void *xdev); 7471d51719SMichael Gmelin static void ig4iic_intr(void *cookie); 7571d51719SMichael Gmelin static void ig4iic_dump(ig4iic_softc_t *sc); 7671d51719SMichael Gmelin 7771d51719SMichael Gmelin static int ig4_dump; 7812e413beSMichael Gmelin SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW, 7912e413beSMichael Gmelin &ig4_dump, 0, "Dump controller registers"); 8071d51719SMichael Gmelin 8171d51719SMichael Gmelin /* 8271d51719SMichael Gmelin * Low-level inline support functions 8371d51719SMichael Gmelin */ 8471d51719SMichael Gmelin static __inline void 8571d51719SMichael Gmelin reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value) 8671d51719SMichael Gmelin { 8771d51719SMichael Gmelin bus_write_4(sc->regs_res, reg, value); 8871d51719SMichael Gmelin bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE); 8971d51719SMichael Gmelin } 9071d51719SMichael Gmelin 9171d51719SMichael Gmelin static __inline uint32_t 9271d51719SMichael Gmelin reg_read(ig4iic_softc_t *sc, uint32_t reg) 9371d51719SMichael Gmelin { 9471d51719SMichael Gmelin uint32_t value; 9571d51719SMichael Gmelin 9671d51719SMichael Gmelin bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ); 9771d51719SMichael Gmelin value = bus_read_4(sc->regs_res, reg); 9871d51719SMichael Gmelin return (value); 9971d51719SMichael Gmelin } 10071d51719SMichael Gmelin 10171d51719SMichael Gmelin /* 10271d51719SMichael Gmelin * Enable or disable the controller and wait for the controller to acknowledge 10371d51719SMichael Gmelin * the state change. 10471d51719SMichael Gmelin */ 10571d51719SMichael Gmelin static int 10671d51719SMichael Gmelin set_controller(ig4iic_softc_t *sc, uint32_t ctl) 10771d51719SMichael Gmelin { 10871d51719SMichael Gmelin int retry; 10971d51719SMichael Gmelin int error; 11071d51719SMichael Gmelin uint32_t v; 11171d51719SMichael Gmelin 1120ba5622dSMichael Gmelin /* 1130ba5622dSMichael Gmelin * When the controller is enabled, interrupt on STOP detect 1140ba5622dSMichael Gmelin * or receive character ready and clear pending interrupts. 1150ba5622dSMichael Gmelin */ 1160ba5622dSMichael Gmelin if (ctl & IG4_I2C_ENABLE) { 1170ba5622dSMichael Gmelin reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET | 1180ba5622dSMichael Gmelin IG4_INTR_RX_FULL); 1190ba5622dSMichael Gmelin reg_read(sc, IG4_REG_CLR_INTR); 1200ba5622dSMichael Gmelin } else 1210ba5622dSMichael Gmelin reg_write(sc, IG4_REG_INTR_MASK, 0); 1220ba5622dSMichael Gmelin 12371d51719SMichael Gmelin reg_write(sc, IG4_REG_I2C_EN, ctl); 124448897d3SAndriy Gapon error = IIC_ETIMEOUT; 12571d51719SMichael Gmelin 12671d51719SMichael Gmelin for (retry = 100; retry > 0; --retry) { 12771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_ENABLE_STATUS); 12871d51719SMichael Gmelin if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) { 12971d51719SMichael Gmelin error = 0; 13071d51719SMichael Gmelin break; 13171d51719SMichael Gmelin } 13259e4a8cdSJohn Baldwin if (cold) 13359e4a8cdSJohn Baldwin DELAY(1000); 13459e4a8cdSJohn Baldwin else 1354cd6abddSMichael Gmelin mtx_sleep(sc, &sc->io_lock, 0, "i2cslv", 1); 13671d51719SMichael Gmelin } 13771d51719SMichael Gmelin return (error); 13871d51719SMichael Gmelin } 13971d51719SMichael Gmelin 14071d51719SMichael Gmelin /* 14171d51719SMichael Gmelin * Wait up to 25ms for the requested status using a 25uS polling loop. 14271d51719SMichael Gmelin */ 14371d51719SMichael Gmelin static int 14471d51719SMichael Gmelin wait_status(ig4iic_softc_t *sc, uint32_t status) 14571d51719SMichael Gmelin { 14671d51719SMichael Gmelin uint32_t v; 14771d51719SMichael Gmelin int error; 14871d51719SMichael Gmelin int txlvl = -1; 14971d51719SMichael Gmelin u_int count_us = 0; 15071d51719SMichael Gmelin u_int limit_us = 25000; /* 25ms */ 15171d51719SMichael Gmelin 152448897d3SAndriy Gapon error = IIC_ETIMEOUT; 15371d51719SMichael Gmelin 15471d51719SMichael Gmelin for (;;) { 15571d51719SMichael Gmelin /* 15671d51719SMichael Gmelin * Check requested status 15771d51719SMichael Gmelin */ 15871d51719SMichael Gmelin v = reg_read(sc, IG4_REG_I2C_STA); 15971d51719SMichael Gmelin if (v & status) { 16071d51719SMichael Gmelin error = 0; 16171d51719SMichael Gmelin break; 16271d51719SMichael Gmelin } 16371d51719SMichael Gmelin 16471d51719SMichael Gmelin /* 16571d51719SMichael Gmelin * When waiting for receive data break-out if the interrupt 16671d51719SMichael Gmelin * loaded data into the FIFO. 16771d51719SMichael Gmelin */ 16871d51719SMichael Gmelin if (status & IG4_STATUS_RX_NOTEMPTY) { 16971d51719SMichael Gmelin if (sc->rpos != sc->rnext) { 17071d51719SMichael Gmelin error = 0; 17171d51719SMichael Gmelin break; 17271d51719SMichael Gmelin } 17371d51719SMichael Gmelin } 17471d51719SMichael Gmelin 17571d51719SMichael Gmelin /* 17671d51719SMichael Gmelin * When waiting for the transmit FIFO to become empty, 17771d51719SMichael Gmelin * reset the timeout if we see a change in the transmit 17871d51719SMichael Gmelin * FIFO level as progress is being made. 17971d51719SMichael Gmelin */ 18071d51719SMichael Gmelin if (status & IG4_STATUS_TX_EMPTY) { 18171d51719SMichael Gmelin v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK; 18271d51719SMichael Gmelin if (txlvl != v) { 18371d51719SMichael Gmelin txlvl = v; 18471d51719SMichael Gmelin count_us = 0; 18571d51719SMichael Gmelin } 18671d51719SMichael Gmelin } 18771d51719SMichael Gmelin 18871d51719SMichael Gmelin /* 18971d51719SMichael Gmelin * Stop if we've run out of time. 19071d51719SMichael Gmelin */ 19171d51719SMichael Gmelin if (count_us >= limit_us) 19271d51719SMichael Gmelin break; 19371d51719SMichael Gmelin 19471d51719SMichael Gmelin /* 19571d51719SMichael Gmelin * When waiting for receive data let the interrupt do its 19671d51719SMichael Gmelin * work, otherwise poll with the lock held. 19771d51719SMichael Gmelin */ 19871d51719SMichael Gmelin if (status & IG4_STATUS_RX_NOTEMPTY) { 1994cd6abddSMichael Gmelin mtx_sleep(sc, &sc->io_lock, 0, "i2cwait", 20071d51719SMichael Gmelin (hz + 99) / 100); /* sleep up to 10ms */ 20171d51719SMichael Gmelin count_us += 10000; 20271d51719SMichael Gmelin } else { 20371d51719SMichael Gmelin DELAY(25); 20471d51719SMichael Gmelin count_us += 25; 20571d51719SMichael Gmelin } 20671d51719SMichael Gmelin } 20771d51719SMichael Gmelin 20871d51719SMichael Gmelin return (error); 20971d51719SMichael Gmelin } 21071d51719SMichael Gmelin 21171d51719SMichael Gmelin /* 21271d51719SMichael Gmelin * Read I2C data. The data might have already been read by 21371d51719SMichael Gmelin * the interrupt code, otherwise it is sitting in the data 21471d51719SMichael Gmelin * register. 21571d51719SMichael Gmelin */ 21671d51719SMichael Gmelin static uint8_t 21771d51719SMichael Gmelin data_read(ig4iic_softc_t *sc) 21871d51719SMichael Gmelin { 21971d51719SMichael Gmelin uint8_t c; 22071d51719SMichael Gmelin 22171d51719SMichael Gmelin if (sc->rpos == sc->rnext) { 22271d51719SMichael Gmelin c = (uint8_t)reg_read(sc, IG4_REG_DATA_CMD); 22371d51719SMichael Gmelin } else { 22471d51719SMichael Gmelin c = sc->rbuf[sc->rpos & IG4_RBUFMASK]; 22571d51719SMichael Gmelin ++sc->rpos; 22671d51719SMichael Gmelin } 22771d51719SMichael Gmelin return (c); 22871d51719SMichael Gmelin } 22971d51719SMichael Gmelin 23071d51719SMichael Gmelin /* 23171d51719SMichael Gmelin * Set the slave address. The controller must be disabled when 23271d51719SMichael Gmelin * changing the address. 23371d51719SMichael Gmelin * 23471d51719SMichael Gmelin * This operation does not issue anything to the I2C bus but sets 23571d51719SMichael Gmelin * the target address for when the controller later issues a START. 23671d51719SMichael Gmelin */ 23771d51719SMichael Gmelin static void 238e3d25549SAndriy Gapon set_slave_addr(ig4iic_softc_t *sc, uint8_t slave) 23971d51719SMichael Gmelin { 24071d51719SMichael Gmelin uint32_t tar; 24171d51719SMichael Gmelin uint32_t ctl; 24271d51719SMichael Gmelin int use_10bit; 24371d51719SMichael Gmelin 24471d51719SMichael Gmelin use_10bit = 0; 24571d51719SMichael Gmelin if (sc->slave_valid && sc->last_slave == slave && 24671d51719SMichael Gmelin sc->use_10bit == use_10bit) { 24771d51719SMichael Gmelin return; 24871d51719SMichael Gmelin } 24971d51719SMichael Gmelin sc->use_10bit = use_10bit; 25071d51719SMichael Gmelin 25171d51719SMichael Gmelin /* 25271d51719SMichael Gmelin * Wait for TXFIFO to drain before disabling the controller. 25371d51719SMichael Gmelin * 25471d51719SMichael Gmelin * If a write message has not been completed it's really a 25571d51719SMichael Gmelin * programming error, but for now in that case issue an extra 25671d51719SMichael Gmelin * byte + STOP. 25771d51719SMichael Gmelin * 25871d51719SMichael Gmelin * If a read message has not been completed it's also a programming 25971d51719SMichael Gmelin * error, for now just ignore it. 26071d51719SMichael Gmelin */ 26171d51719SMichael Gmelin wait_status(sc, IG4_STATUS_TX_NOTFULL); 26271d51719SMichael Gmelin if (sc->write_started) { 26371d51719SMichael Gmelin reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_STOP); 26471d51719SMichael Gmelin sc->write_started = 0; 26571d51719SMichael Gmelin } 26671d51719SMichael Gmelin if (sc->read_started) 26771d51719SMichael Gmelin sc->read_started = 0; 26871d51719SMichael Gmelin wait_status(sc, IG4_STATUS_TX_EMPTY); 26971d51719SMichael Gmelin 27071d51719SMichael Gmelin set_controller(sc, 0); 27171d51719SMichael Gmelin ctl = reg_read(sc, IG4_REG_CTL); 27271d51719SMichael Gmelin ctl &= ~IG4_CTL_10BIT; 27371d51719SMichael Gmelin ctl |= IG4_CTL_RESTARTEN; 27471d51719SMichael Gmelin 27571d51719SMichael Gmelin tar = slave; 27671d51719SMichael Gmelin if (sc->use_10bit) { 27771d51719SMichael Gmelin tar |= IG4_TAR_10BIT; 27871d51719SMichael Gmelin ctl |= IG4_CTL_10BIT; 27971d51719SMichael Gmelin } 28071d51719SMichael Gmelin reg_write(sc, IG4_REG_CTL, ctl); 28171d51719SMichael Gmelin reg_write(sc, IG4_REG_TAR_ADD, tar); 28271d51719SMichael Gmelin set_controller(sc, IG4_I2C_ENABLE); 28371d51719SMichael Gmelin sc->slave_valid = 1; 28471d51719SMichael Gmelin sc->last_slave = slave; 28571d51719SMichael Gmelin } 28671d51719SMichael Gmelin 28771d51719SMichael Gmelin /* 288448897d3SAndriy Gapon * IICBUS API FUNCTIONS 289448897d3SAndriy Gapon */ 290448897d3SAndriy Gapon static int 291448897d3SAndriy Gapon ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave) 292448897d3SAndriy Gapon { 293e3d25549SAndriy Gapon set_slave_addr(sc, slave >> 1); 294448897d3SAndriy Gapon return (0); 295448897d3SAndriy Gapon } 296448897d3SAndriy Gapon 297448897d3SAndriy Gapon static int 298448897d3SAndriy Gapon ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, 299448897d3SAndriy Gapon bool repeated_start, bool stop) 300448897d3SAndriy Gapon { 301448897d3SAndriy Gapon uint32_t cmd; 302448897d3SAndriy Gapon uint16_t i; 303448897d3SAndriy Gapon int error; 304448897d3SAndriy Gapon 305448897d3SAndriy Gapon if (len == 0) 306448897d3SAndriy Gapon return (0); 307448897d3SAndriy Gapon 308448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 309448897d3SAndriy Gapon cmd |= repeated_start ? IG4_DATA_RESTART : 0; 310448897d3SAndriy Gapon cmd |= stop && len == 1 ? IG4_DATA_STOP : 0; 311448897d3SAndriy Gapon 312448897d3SAndriy Gapon /* Issue request for the first byte (could be last as well). */ 313448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 314448897d3SAndriy Gapon 315448897d3SAndriy Gapon for (i = 0; i < len; i++) { 316448897d3SAndriy Gapon /* 317448897d3SAndriy Gapon * Maintain a pipeline by queueing the allowance for the next 318448897d3SAndriy Gapon * read before waiting for the current read. 319448897d3SAndriy Gapon */ 320448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 321448897d3SAndriy Gapon if (i < len - 1) { 322448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 323448897d3SAndriy Gapon cmd |= stop && i == len - 2 ? IG4_DATA_STOP : 0; 324448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 325448897d3SAndriy Gapon } 326448897d3SAndriy Gapon error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY); 327448897d3SAndriy Gapon if (error) 328448897d3SAndriy Gapon break; 329448897d3SAndriy Gapon buf[i] = data_read(sc); 330448897d3SAndriy Gapon } 331448897d3SAndriy Gapon 332448897d3SAndriy Gapon (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); 333448897d3SAndriy Gapon return (error); 334448897d3SAndriy Gapon } 335448897d3SAndriy Gapon 336448897d3SAndriy Gapon static int 337448897d3SAndriy Gapon ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, 338448897d3SAndriy Gapon bool repeated_start, bool stop) 339448897d3SAndriy Gapon { 340448897d3SAndriy Gapon uint32_t cmd; 341448897d3SAndriy Gapon uint16_t i; 342448897d3SAndriy Gapon int error; 343448897d3SAndriy Gapon 344448897d3SAndriy Gapon if (len == 0) 345448897d3SAndriy Gapon return (0); 346448897d3SAndriy Gapon 347448897d3SAndriy Gapon cmd = repeated_start ? IG4_DATA_RESTART : 0; 348448897d3SAndriy Gapon for (i = 0; i < len; i++) { 349448897d3SAndriy Gapon error = wait_status(sc, IG4_STATUS_TX_NOTFULL); 350448897d3SAndriy Gapon if (error) 351448897d3SAndriy Gapon break; 352448897d3SAndriy Gapon cmd |= buf[i]; 353448897d3SAndriy Gapon cmd |= stop && i == len - 1 ? IG4_DATA_STOP : 0; 354448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 355448897d3SAndriy Gapon cmd = 0; 356448897d3SAndriy Gapon } 357448897d3SAndriy Gapon 358448897d3SAndriy Gapon (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); 359448897d3SAndriy Gapon return (error); 360448897d3SAndriy Gapon } 361448897d3SAndriy Gapon 362448897d3SAndriy Gapon int 363448897d3SAndriy Gapon ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 364448897d3SAndriy Gapon { 365448897d3SAndriy Gapon ig4iic_softc_t *sc = device_get_softc(dev); 366448897d3SAndriy Gapon const char *reason = NULL; 367448897d3SAndriy Gapon uint32_t i; 368448897d3SAndriy Gapon int error; 369448897d3SAndriy Gapon int unit; 370448897d3SAndriy Gapon bool rpstart; 371448897d3SAndriy Gapon bool stop; 372448897d3SAndriy Gapon 373448897d3SAndriy Gapon /* 374448897d3SAndriy Gapon * The hardware interface imposes limits on allowed I2C messages. 375448897d3SAndriy Gapon * It is not possible to explicitly send a start or stop. 376448897d3SAndriy Gapon * They are automatically sent (or not sent, depending on the 377448897d3SAndriy Gapon * configuration) when a data byte is transferred. 378448897d3SAndriy Gapon * For this reason it's impossible to send a message with no data 379448897d3SAndriy Gapon * at all (like an SMBus quick message). 380448897d3SAndriy Gapon * The start condition is automatically generated after the stop 381448897d3SAndriy Gapon * condition, so it's impossible to not have a start after a stop. 382448897d3SAndriy Gapon * The repeated start condition is automatically sent if a change 383448897d3SAndriy Gapon * of the transfer direction happens, so it's impossible to have 384448897d3SAndriy Gapon * a change of direction without a (repeated) start. 385448897d3SAndriy Gapon * The repeated start can be forced even without the change of 386448897d3SAndriy Gapon * direction. 387448897d3SAndriy Gapon * Changing the target slave address requires resetting the hardware 388448897d3SAndriy Gapon * state, so it's impossible to do that without the stop followed 389448897d3SAndriy Gapon * by the start. 390448897d3SAndriy Gapon */ 391448897d3SAndriy Gapon for (i = 0; i < nmsgs; i++) { 392448897d3SAndriy Gapon #if 0 393448897d3SAndriy Gapon if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) { 394448897d3SAndriy Gapon reason = "first message without start"; 395448897d3SAndriy Gapon break; 396448897d3SAndriy Gapon } 397448897d3SAndriy Gapon if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) { 398448897d3SAndriy Gapon reason = "last message without stop"; 399448897d3SAndriy Gapon break; 400448897d3SAndriy Gapon } 401448897d3SAndriy Gapon #endif 402448897d3SAndriy Gapon if (msgs[i].len == 0) { 403448897d3SAndriy Gapon reason = "message with no data"; 404448897d3SAndriy Gapon break; 405448897d3SAndriy Gapon } 406448897d3SAndriy Gapon if (i > 0) { 407448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) != 0 && 408448897d3SAndriy Gapon (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) { 409448897d3SAndriy Gapon reason = "stop not followed by start"; 410448897d3SAndriy Gapon break; 411448897d3SAndriy Gapon } 412448897d3SAndriy Gapon if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 && 413448897d3SAndriy Gapon msgs[i].slave != msgs[i - 1].slave) { 414448897d3SAndriy Gapon reason = "change of slave without stop"; 415448897d3SAndriy Gapon break; 416448897d3SAndriy Gapon } 417448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) != 0 && 418448897d3SAndriy Gapon (msgs[i].flags & IIC_M_RD) != 419448897d3SAndriy Gapon (msgs[i - 1].flags & IIC_M_RD)) { 420448897d3SAndriy Gapon reason = "change of direction without repeated" 421448897d3SAndriy Gapon " start"; 422448897d3SAndriy Gapon break; 423448897d3SAndriy Gapon } 424448897d3SAndriy Gapon } 425448897d3SAndriy Gapon } 426448897d3SAndriy Gapon if (reason != NULL) { 427448897d3SAndriy Gapon if (bootverbose) 428448897d3SAndriy Gapon device_printf(dev, "%s\n", reason); 429448897d3SAndriy Gapon return (IIC_ENOTSUPP); 430448897d3SAndriy Gapon } 431448897d3SAndriy Gapon 432448897d3SAndriy Gapon sx_xlock(&sc->call_lock); 433448897d3SAndriy Gapon mtx_lock(&sc->io_lock); 434448897d3SAndriy Gapon 435448897d3SAndriy Gapon /* Debugging - dump registers. */ 436448897d3SAndriy Gapon if (ig4_dump) { 437448897d3SAndriy Gapon unit = device_get_unit(dev); 438448897d3SAndriy Gapon if (ig4_dump & (1 << unit)) { 439448897d3SAndriy Gapon ig4_dump &= ~(1 << unit); 440448897d3SAndriy Gapon ig4iic_dump(sc); 441448897d3SAndriy Gapon } 442448897d3SAndriy Gapon } 443448897d3SAndriy Gapon 444448897d3SAndriy Gapon /* 445448897d3SAndriy Gapon * Clear any previous abort condition that may have been holding 446448897d3SAndriy Gapon * the txfifo in reset. 447448897d3SAndriy Gapon */ 448448897d3SAndriy Gapon reg_read(sc, IG4_REG_CLR_TX_ABORT); 449448897d3SAndriy Gapon 450448897d3SAndriy Gapon /* 451448897d3SAndriy Gapon * Clean out any previously received data. 452448897d3SAndriy Gapon */ 453448897d3SAndriy Gapon if (sc->rpos != sc->rnext && bootverbose) { 454448897d3SAndriy Gapon device_printf(sc->dev, "discarding %d bytes of spurious data\n", 455448897d3SAndriy Gapon sc->rnext - sc->rpos); 456448897d3SAndriy Gapon } 457448897d3SAndriy Gapon sc->rpos = 0; 458448897d3SAndriy Gapon sc->rnext = 0; 459448897d3SAndriy Gapon 460448897d3SAndriy Gapon rpstart = false; 461448897d3SAndriy Gapon error = 0; 462448897d3SAndriy Gapon for (i = 0; i < nmsgs; i++) { 463448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) == 0) { 464448897d3SAndriy Gapon error = ig4iic_xfer_start(sc, msgs[i].slave); 465448897d3SAndriy Gapon } else { 466448897d3SAndriy Gapon if (!sc->slave_valid || 467448897d3SAndriy Gapon (msgs[i].slave >> 1) != sc->last_slave) { 468448897d3SAndriy Gapon device_printf(dev, "start condition suppressed" 469448897d3SAndriy Gapon "but slave address is not set up"); 470448897d3SAndriy Gapon error = EINVAL; 471448897d3SAndriy Gapon break; 472448897d3SAndriy Gapon } 473448897d3SAndriy Gapon rpstart = false; 474448897d3SAndriy Gapon } 475448897d3SAndriy Gapon if (error != 0) 476448897d3SAndriy Gapon break; 477448897d3SAndriy Gapon 478448897d3SAndriy Gapon stop = (msgs[i].flags & IIC_M_NOSTOP) == 0; 479448897d3SAndriy Gapon if (msgs[i].flags & IIC_M_RD) 480448897d3SAndriy Gapon error = ig4iic_read(sc, msgs[i].buf, msgs[i].len, 481448897d3SAndriy Gapon rpstart, stop); 482448897d3SAndriy Gapon else 483448897d3SAndriy Gapon error = ig4iic_write(sc, msgs[i].buf, msgs[i].len, 484448897d3SAndriy Gapon rpstart, stop); 485448897d3SAndriy Gapon if (error != 0) 486448897d3SAndriy Gapon break; 487448897d3SAndriy Gapon 488448897d3SAndriy Gapon rpstart = !stop; 489448897d3SAndriy Gapon } 490448897d3SAndriy Gapon 491448897d3SAndriy Gapon mtx_unlock(&sc->io_lock); 492448897d3SAndriy Gapon sx_unlock(&sc->call_lock); 493448897d3SAndriy Gapon return (error); 494448897d3SAndriy Gapon } 495448897d3SAndriy Gapon 496448897d3SAndriy Gapon int 497448897d3SAndriy Gapon ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 498448897d3SAndriy Gapon { 499448897d3SAndriy Gapon ig4iic_softc_t *sc = device_get_softc(dev); 500448897d3SAndriy Gapon 501448897d3SAndriy Gapon sx_xlock(&sc->call_lock); 502448897d3SAndriy Gapon mtx_lock(&sc->io_lock); 503448897d3SAndriy Gapon 504448897d3SAndriy Gapon /* TODO handle speed configuration? */ 505448897d3SAndriy Gapon if (oldaddr != NULL) 506448897d3SAndriy Gapon *oldaddr = sc->last_slave << 1; 507e3d25549SAndriy Gapon set_slave_addr(sc, addr >> 1); 508448897d3SAndriy Gapon if (addr == IIC_UNKNOWN) 509448897d3SAndriy Gapon sc->slave_valid = false; 510448897d3SAndriy Gapon 511448897d3SAndriy Gapon mtx_unlock(&sc->io_lock); 512448897d3SAndriy Gapon sx_unlock(&sc->call_lock); 513448897d3SAndriy Gapon return (0); 514448897d3SAndriy Gapon } 515448897d3SAndriy Gapon 516448897d3SAndriy Gapon /* 51771d51719SMichael Gmelin * Called from ig4iic_pci_attach/detach() 51871d51719SMichael Gmelin */ 51971d51719SMichael Gmelin int 52071d51719SMichael Gmelin ig4iic_attach(ig4iic_softc_t *sc) 52171d51719SMichael Gmelin { 52271d51719SMichael Gmelin int error; 52371d51719SMichael Gmelin uint32_t v; 52471d51719SMichael Gmelin 5255c5bcb1dSOleksandr Tymoshenko mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); 5265c5bcb1dSOleksandr Tymoshenko sx_init(&sc->call_lock, "IG4 call lock"); 5275c5bcb1dSOleksandr Tymoshenko 528b16d03adSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_DEVIDLE_CTRL); 529b16d03adSOleksandr Tymoshenko if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) { 530b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED); 531b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0); 532b16d03adSOleksandr Tymoshenko 533b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); 534b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); 535b16d03adSOleksandr Tymoshenko DELAY(1000); 536b16d03adSOleksandr Tymoshenko } 537b16d03adSOleksandr Tymoshenko 538b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_ATOM) 53971d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_TYPE); 540b3e8ee5dSOleksandr Tymoshenko 541b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 54271d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_PARAM1); 54371d51719SMichael Gmelin v = reg_read(sc, IG4_REG_GENERAL); 544b3e8ee5dSOleksandr Tymoshenko /* 545b3e8ee5dSOleksandr Tymoshenko * The content of IG4_REG_GENERAL is different for each 546b3e8ee5dSOleksandr Tymoshenko * controller version. 547b3e8ee5dSOleksandr Tymoshenko */ 548b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL && 549b3e8ee5dSOleksandr Tymoshenko (v & IG4_GENERAL_SWMODE) == 0) { 55071d51719SMichael Gmelin v |= IG4_GENERAL_SWMODE; 55171d51719SMichael Gmelin reg_write(sc, IG4_REG_GENERAL, v); 55271d51719SMichael Gmelin v = reg_read(sc, IG4_REG_GENERAL); 55371d51719SMichael Gmelin } 554b3e8ee5dSOleksandr Tymoshenko } 55571d51719SMichael Gmelin 556b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL) { 55771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SW_LTR_VALUE); 55871d51719SMichael Gmelin v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); 559b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 560b3e8ee5dSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE); 561b3e8ee5dSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE); 562b3e8ee5dSOleksandr Tymoshenko } 56371d51719SMichael Gmelin 564b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 56571d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_VER); 5665747fe4fSOleksandr Tymoshenko if (v < IG4_COMP_MIN_VER) { 56771d51719SMichael Gmelin error = ENXIO; 56871d51719SMichael Gmelin goto done; 56971d51719SMichael Gmelin } 570b3e8ee5dSOleksandr Tymoshenko } 57171d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_HCNT); 57271d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_LCNT); 57371d51719SMichael Gmelin v = reg_read(sc, IG4_REG_FS_SCL_HCNT); 57471d51719SMichael Gmelin v = reg_read(sc, IG4_REG_FS_SCL_LCNT); 57571d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SDA_HOLD); 57671d51719SMichael Gmelin 57771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_HCNT); 57871d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_HCNT, v); 57971d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_LCNT); 58071d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_LCNT, v); 58171d51719SMichael Gmelin 58271d51719SMichael Gmelin /* 58371d51719SMichael Gmelin * Program based on a 25000 Hz clock. This is a bit of a 58471d51719SMichael Gmelin * hack (obviously). The defaults are 400 and 470 for standard 58571d51719SMichael Gmelin * and 60 and 130 for fast. The defaults for standard fail 58671d51719SMichael Gmelin * utterly (presumably cause an abort) because the clock time 58771d51719SMichael Gmelin * is ~18.8ms by default. This brings it down to ~4ms (for now). 58871d51719SMichael Gmelin */ 58971d51719SMichael Gmelin reg_write(sc, IG4_REG_SS_SCL_HCNT, 100); 59071d51719SMichael Gmelin reg_write(sc, IG4_REG_SS_SCL_LCNT, 125); 59171d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_HCNT, 100); 59271d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_LCNT, 125); 5933ca6000fSVladimir Kondratyev if (sc->version == IG4_SKYLAKE) 5943ca6000fSVladimir Kondratyev reg_write(sc, IG4_REG_SDA_HOLD, 28); 59571d51719SMichael Gmelin 59671d51719SMichael Gmelin /* 59771d51719SMichael Gmelin * Use a threshold of 1 so we get interrupted on each character, 59871d51719SMichael Gmelin * allowing us to use mtx_sleep() in our poll code. Not perfect 59971d51719SMichael Gmelin * but this is better than using DELAY() for receiving data. 6004cd6abddSMichael Gmelin * 6014cd6abddSMichael Gmelin * See ig4_var.h for details on interrupt handler synchronization. 60271d51719SMichael Gmelin */ 60371d51719SMichael Gmelin reg_write(sc, IG4_REG_RX_TL, 1); 60471d51719SMichael Gmelin 60571d51719SMichael Gmelin reg_write(sc, IG4_REG_CTL, 60671d51719SMichael Gmelin IG4_CTL_MASTER | 60771d51719SMichael Gmelin IG4_CTL_SLAVE_DISABLE | 60871d51719SMichael Gmelin IG4_CTL_RESTARTEN | 60971d51719SMichael Gmelin IG4_CTL_SPEED_STD); 61071d51719SMichael Gmelin 611448897d3SAndriy Gapon sc->iicbus = device_add_child(sc->dev, "iicbus", -1); 612448897d3SAndriy Gapon if (sc->iicbus == NULL) { 613448897d3SAndriy Gapon device_printf(sc->dev, "iicbus driver not found\n"); 61471d51719SMichael Gmelin error = ENXIO; 61571d51719SMichael Gmelin goto done; 61671d51719SMichael Gmelin } 61771d51719SMichael Gmelin 61871d51719SMichael Gmelin #if 0 61971d51719SMichael Gmelin /* 62071d51719SMichael Gmelin * Don't do this, it blows up the PCI config 62171d51719SMichael Gmelin */ 622b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 623b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW); 624b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW); 625b3e8ee5dSOleksandr Tymoshenko } else if (sc->version = IG4_SKYLAKE) { 626b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); 627b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); 628b3e8ee5dSOleksandr Tymoshenko } 62971d51719SMichael Gmelin #endif 63071d51719SMichael Gmelin 6314cd6abddSMichael Gmelin mtx_lock(&sc->io_lock); 632bf9c3c58SVladimir Kondratyev if (set_controller(sc, 0)) { 63371d51719SMichael Gmelin device_printf(sc->dev, "controller error during attach-1\n"); 634bf9c3c58SVladimir Kondratyev mtx_unlock(&sc->io_lock); 635bf9c3c58SVladimir Kondratyev error = ENXIO; 636bf9c3c58SVladimir Kondratyev goto done; 637bf9c3c58SVladimir Kondratyev } 638bf9c3c58SVladimir Kondratyev if (set_controller(sc, IG4_I2C_ENABLE)) { 63971d51719SMichael Gmelin device_printf(sc->dev, "controller error during attach-2\n"); 6404cd6abddSMichael Gmelin mtx_unlock(&sc->io_lock); 641bf9c3c58SVladimir Kondratyev error = ENXIO; 642bf9c3c58SVladimir Kondratyev goto done; 643bf9c3c58SVladimir Kondratyev } 644bf9c3c58SVladimir Kondratyev mtx_unlock(&sc->io_lock); 64571d51719SMichael Gmelin error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, 64671d51719SMichael Gmelin NULL, ig4iic_intr, sc, &sc->intr_handle); 64771d51719SMichael Gmelin if (error) { 64871d51719SMichael Gmelin device_printf(sc->dev, 64971d51719SMichael Gmelin "Unable to setup irq: error %d\n", error); 65071d51719SMichael Gmelin } 65171d51719SMichael Gmelin 65271d51719SMichael Gmelin sc->enum_hook.ich_func = ig4iic_start; 65371d51719SMichael Gmelin sc->enum_hook.ich_arg = sc->dev; 65471d51719SMichael Gmelin 6550ba5622dSMichael Gmelin /* 6560ba5622dSMichael Gmelin * We have to wait until interrupts are enabled. I2C read and write 65771d51719SMichael Gmelin * only works if the interrupts are available. 65871d51719SMichael Gmelin */ 65971d51719SMichael Gmelin if (config_intrhook_establish(&sc->enum_hook) != 0) 66071d51719SMichael Gmelin error = ENOMEM; 66171d51719SMichael Gmelin else 66271d51719SMichael Gmelin error = 0; 66371d51719SMichael Gmelin 66471d51719SMichael Gmelin done: 66571d51719SMichael Gmelin return (error); 66671d51719SMichael Gmelin } 66771d51719SMichael Gmelin 66871d51719SMichael Gmelin void 66971d51719SMichael Gmelin ig4iic_start(void *xdev) 67071d51719SMichael Gmelin { 67171d51719SMichael Gmelin int error; 67271d51719SMichael Gmelin ig4iic_softc_t *sc; 67371d51719SMichael Gmelin device_t dev = (device_t)xdev; 67471d51719SMichael Gmelin 67571d51719SMichael Gmelin sc = device_get_softc(dev); 67671d51719SMichael Gmelin 67771d51719SMichael Gmelin config_intrhook_disestablish(&sc->enum_hook); 67871d51719SMichael Gmelin 67971d51719SMichael Gmelin error = bus_generic_attach(sc->dev); 68071d51719SMichael Gmelin if (error) { 68171d51719SMichael Gmelin device_printf(sc->dev, 68271d51719SMichael Gmelin "failed to attach child: error %d\n", error); 68371d51719SMichael Gmelin } 68471d51719SMichael Gmelin } 68571d51719SMichael Gmelin 68671d51719SMichael Gmelin int 68771d51719SMichael Gmelin ig4iic_detach(ig4iic_softc_t *sc) 68871d51719SMichael Gmelin { 68971d51719SMichael Gmelin int error; 69071d51719SMichael Gmelin 69171d51719SMichael Gmelin if (device_is_attached(sc->dev)) { 69271d51719SMichael Gmelin error = bus_generic_detach(sc->dev); 69371d51719SMichael Gmelin if (error) 69471d51719SMichael Gmelin return (error); 69571d51719SMichael Gmelin } 696448897d3SAndriy Gapon if (sc->iicbus) 697448897d3SAndriy Gapon device_delete_child(sc->dev, sc->iicbus); 69871d51719SMichael Gmelin if (sc->intr_handle) 69971d51719SMichael Gmelin bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); 70071d51719SMichael Gmelin 7014cd6abddSMichael Gmelin sx_xlock(&sc->call_lock); 7024cd6abddSMichael Gmelin mtx_lock(&sc->io_lock); 70371d51719SMichael Gmelin 704448897d3SAndriy Gapon sc->iicbus = NULL; 70571d51719SMichael Gmelin sc->intr_handle = NULL; 70671d51719SMichael Gmelin reg_write(sc, IG4_REG_INTR_MASK, 0); 70771d51719SMichael Gmelin set_controller(sc, 0); 70871d51719SMichael Gmelin 7094cd6abddSMichael Gmelin mtx_unlock(&sc->io_lock); 7104cd6abddSMichael Gmelin sx_xunlock(&sc->call_lock); 7115c5bcb1dSOleksandr Tymoshenko 7125c5bcb1dSOleksandr Tymoshenko mtx_destroy(&sc->io_lock); 7135c5bcb1dSOleksandr Tymoshenko sx_destroy(&sc->call_lock); 7145c5bcb1dSOleksandr Tymoshenko 71571d51719SMichael Gmelin return (0); 71671d51719SMichael Gmelin } 71771d51719SMichael Gmelin 71871d51719SMichael Gmelin /* 7194cd6abddSMichael Gmelin * Interrupt Operation, see ig4_var.h for locking semantics. 72071d51719SMichael Gmelin */ 72171d51719SMichael Gmelin static void 72271d51719SMichael Gmelin ig4iic_intr(void *cookie) 72371d51719SMichael Gmelin { 72471d51719SMichael Gmelin ig4iic_softc_t *sc = cookie; 72571d51719SMichael Gmelin uint32_t status; 72671d51719SMichael Gmelin 7274cd6abddSMichael Gmelin mtx_lock(&sc->io_lock); 72871d51719SMichael Gmelin /* reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/ 7290ba5622dSMichael Gmelin reg_read(sc, IG4_REG_CLR_INTR); 73071d51719SMichael Gmelin status = reg_read(sc, IG4_REG_I2C_STA); 73171d51719SMichael Gmelin while (status & IG4_STATUS_RX_NOTEMPTY) { 73271d51719SMichael Gmelin sc->rbuf[sc->rnext & IG4_RBUFMASK] = 73371d51719SMichael Gmelin (uint8_t)reg_read(sc, IG4_REG_DATA_CMD); 73471d51719SMichael Gmelin ++sc->rnext; 73571d51719SMichael Gmelin status = reg_read(sc, IG4_REG_I2C_STA); 73671d51719SMichael Gmelin } 737ad4c75f7SOleksandr Tymoshenko 738ad4c75f7SOleksandr Tymoshenko /* 739ad4c75f7SOleksandr Tymoshenko * Workaround to trigger pending interrupt if IG4_REG_INTR_STAT 740ad4c75f7SOleksandr Tymoshenko * is changed after clearing it 741ad4c75f7SOleksandr Tymoshenko */ 742627e5af8SOleksandr Tymoshenko if (sc->access_intr_mask != 0) { 743ad4c75f7SOleksandr Tymoshenko status = reg_read(sc, IG4_REG_INTR_MASK); 744627e5af8SOleksandr Tymoshenko if (status != 0) { 745ad4c75f7SOleksandr Tymoshenko reg_write(sc, IG4_REG_INTR_MASK, 0); 746ad4c75f7SOleksandr Tymoshenko reg_write(sc, IG4_REG_INTR_MASK, status); 747ad4c75f7SOleksandr Tymoshenko } 748ad4c75f7SOleksandr Tymoshenko } 749ad4c75f7SOleksandr Tymoshenko 75071d51719SMichael Gmelin wakeup(sc); 7514cd6abddSMichael Gmelin mtx_unlock(&sc->io_lock); 75271d51719SMichael Gmelin } 75371d51719SMichael Gmelin 75471d51719SMichael Gmelin #define REGDUMP(sc, reg) \ 75571d51719SMichael Gmelin device_printf(sc->dev, " %-23s %08x\n", #reg, reg_read(sc, reg)) 75671d51719SMichael Gmelin 75771d51719SMichael Gmelin static void 75871d51719SMichael Gmelin ig4iic_dump(ig4iic_softc_t *sc) 75971d51719SMichael Gmelin { 76071d51719SMichael Gmelin device_printf(sc->dev, "ig4iic register dump:\n"); 76171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_CTL); 76271d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TAR_ADD); 76371d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SS_SCL_HCNT); 76471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SS_SCL_LCNT); 76571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_FS_SCL_HCNT); 76671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_FS_SCL_LCNT); 76771d51719SMichael Gmelin REGDUMP(sc, IG4_REG_INTR_STAT); 76871d51719SMichael Gmelin REGDUMP(sc, IG4_REG_INTR_MASK); 76971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RAW_INTR_STAT); 77071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RX_TL); 77171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TX_TL); 77271d51719SMichael Gmelin REGDUMP(sc, IG4_REG_I2C_EN); 77371d51719SMichael Gmelin REGDUMP(sc, IG4_REG_I2C_STA); 77471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TXFLR); 77571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RXFLR); 77671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SDA_HOLD); 77771d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE); 77871d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SLV_DATA_NACK); 77971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_CTRL); 78071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_TDLR); 78171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_RDLR); 78271d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SDA_SETUP); 78371d51719SMichael Gmelin REGDUMP(sc, IG4_REG_ENABLE_STATUS); 784b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 78571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_PARAM1); 78671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_VER); 787b3e8ee5dSOleksandr Tymoshenko } 788b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_ATOM) { 78971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_TYPE); 79071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_CLK_PARMS); 791b3e8ee5dSOleksandr Tymoshenko } 792b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 793b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_RESETS_HSW); 79471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_GENERAL); 795b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 796b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_RESETS_SKL); 797b3e8ee5dSOleksandr Tymoshenko } 798b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL) { 79971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SW_LTR_VALUE); 80071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE); 801b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 802b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE); 803b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE); 804b3e8ee5dSOleksandr Tymoshenko } 80571d51719SMichael Gmelin } 80671d51719SMichael Gmelin #undef REGDUMP 80771d51719SMichael Gmelin 808984ed3e4SVladimir Kondratyev devclass_t ig4iic_devclass; 809984ed3e4SVladimir Kondratyev 810984ed3e4SVladimir Kondratyev DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL); 811984ed3e4SVladimir Kondratyev MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 812984ed3e4SVladimir Kondratyev MODULE_VERSION(ig4iic, 1); 813