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> 51c59aca57SVladimir Kondratyev #include <sys/kdb.h> 5271d51719SMichael Gmelin #include <sys/lock.h> 5371d51719SMichael Gmelin #include <sys/mutex.h> 54c59aca57SVladimir Kondratyev #include <sys/proc.h> 554cd6abddSMichael Gmelin #include <sys/sx.h> 5671d51719SMichael Gmelin #include <sys/syslog.h> 5771d51719SMichael Gmelin #include <sys/bus.h> 5871d51719SMichael Gmelin #include <sys/sysctl.h> 5971d51719SMichael Gmelin 6071d51719SMichael Gmelin #include <machine/bus.h> 6171d51719SMichael Gmelin #include <sys/rman.h> 6271d51719SMichael Gmelin 6371d51719SMichael Gmelin #include <dev/pci/pcivar.h> 6471d51719SMichael Gmelin #include <dev/pci/pcireg.h> 65448897d3SAndriy Gapon #include <dev/iicbus/iicbus.h> 66448897d3SAndriy Gapon #include <dev/iicbus/iiconf.h> 6771d51719SMichael Gmelin 6871d51719SMichael Gmelin #include <dev/ichiic/ig4_reg.h> 6971d51719SMichael Gmelin #include <dev/ichiic/ig4_var.h> 7071d51719SMichael Gmelin 7171d51719SMichael Gmelin #define TRANS_NORMAL 1 7271d51719SMichael Gmelin #define TRANS_PCALL 2 7371d51719SMichael Gmelin #define TRANS_BLOCK 3 7471d51719SMichael Gmelin 7541b24e09SVladimir Kondratyev #define DO_POLL(sc) (cold || kdb_active || SCHEDULER_STOPPED() || sc->poll) 76c59aca57SVladimir Kondratyev 7771d51719SMichael Gmelin static void ig4iic_start(void *xdev); 7871d51719SMichael Gmelin static void ig4iic_intr(void *cookie); 7971d51719SMichael Gmelin static void ig4iic_dump(ig4iic_softc_t *sc); 8071d51719SMichael Gmelin 8171d51719SMichael Gmelin static int ig4_dump; 8212e413beSMichael Gmelin SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW, 8312e413beSMichael Gmelin &ig4_dump, 0, "Dump controller registers"); 8471d51719SMichael Gmelin 8571d51719SMichael Gmelin /* 8671d51719SMichael Gmelin * Low-level inline support functions 8771d51719SMichael Gmelin */ 8871d51719SMichael Gmelin static __inline void 8971d51719SMichael Gmelin reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value) 9071d51719SMichael Gmelin { 9171d51719SMichael Gmelin bus_write_4(sc->regs_res, reg, value); 9271d51719SMichael Gmelin bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE); 9371d51719SMichael Gmelin } 9471d51719SMichael Gmelin 9571d51719SMichael Gmelin static __inline uint32_t 9671d51719SMichael Gmelin reg_read(ig4iic_softc_t *sc, uint32_t reg) 9771d51719SMichael Gmelin { 9871d51719SMichael Gmelin uint32_t value; 9971d51719SMichael Gmelin 10071d51719SMichael Gmelin bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ); 10171d51719SMichael Gmelin value = bus_read_4(sc->regs_res, reg); 10271d51719SMichael Gmelin return (value); 10371d51719SMichael Gmelin } 10471d51719SMichael Gmelin 10521e459c6SVladimir Kondratyev static void 10621e459c6SVladimir Kondratyev set_intr_mask(ig4iic_softc_t *sc, uint32_t val) 10721e459c6SVladimir Kondratyev { 10821e459c6SVladimir Kondratyev if (sc->intr_mask != val) { 10921e459c6SVladimir Kondratyev reg_write(sc, IG4_REG_INTR_MASK, val); 11021e459c6SVladimir Kondratyev sc->intr_mask = val; 11121e459c6SVladimir Kondratyev } 11221e459c6SVladimir Kondratyev } 11321e459c6SVladimir Kondratyev 11471d51719SMichael Gmelin /* 11571d51719SMichael Gmelin * Enable or disable the controller and wait for the controller to acknowledge 11671d51719SMichael Gmelin * the state change. 11771d51719SMichael Gmelin */ 11871d51719SMichael Gmelin static int 11971d51719SMichael Gmelin set_controller(ig4iic_softc_t *sc, uint32_t ctl) 12071d51719SMichael Gmelin { 12171d51719SMichael Gmelin int retry; 12271d51719SMichael Gmelin int error; 12371d51719SMichael Gmelin uint32_t v; 12471d51719SMichael Gmelin 1250ba5622dSMichael Gmelin /* 1260ba5622dSMichael Gmelin * When the controller is enabled, interrupt on STOP detect 1270ba5622dSMichael Gmelin * or receive character ready and clear pending interrupts. 1280ba5622dSMichael Gmelin */ 12921e459c6SVladimir Kondratyev set_intr_mask(sc, 0); 13021e459c6SVladimir Kondratyev if (ctl & IG4_I2C_ENABLE) 1310ba5622dSMichael Gmelin reg_read(sc, IG4_REG_CLR_INTR); 1320ba5622dSMichael Gmelin 13371d51719SMichael Gmelin reg_write(sc, IG4_REG_I2C_EN, ctl); 134448897d3SAndriy Gapon error = IIC_ETIMEOUT; 13571d51719SMichael Gmelin 13671d51719SMichael Gmelin for (retry = 100; retry > 0; --retry) { 13771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_ENABLE_STATUS); 13871d51719SMichael Gmelin if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) { 13971d51719SMichael Gmelin error = 0; 14071d51719SMichael Gmelin break; 14171d51719SMichael Gmelin } 14285cd895fSVladimir Kondratyev pause("i2cslv", 1); 14371d51719SMichael Gmelin } 14471d51719SMichael Gmelin return (error); 14571d51719SMichael Gmelin } 14671d51719SMichael Gmelin 14771d51719SMichael Gmelin /* 14871d51719SMichael Gmelin * Wait up to 25ms for the requested status using a 25uS polling loop. 14971d51719SMichael Gmelin */ 15071d51719SMichael Gmelin static int 15171d51719SMichael Gmelin wait_status(ig4iic_softc_t *sc, uint32_t status) 15271d51719SMichael Gmelin { 15371d51719SMichael Gmelin uint32_t v; 15471d51719SMichael Gmelin int error; 15571d51719SMichael Gmelin int txlvl = -1; 15671d51719SMichael Gmelin u_int count_us = 0; 15771d51719SMichael Gmelin u_int limit_us = 25000; /* 25ms */ 15871d51719SMichael Gmelin 159448897d3SAndriy Gapon error = IIC_ETIMEOUT; 16071d51719SMichael Gmelin 16171d51719SMichael Gmelin for (;;) { 16271d51719SMichael Gmelin /* 16371d51719SMichael Gmelin * Check requested status 16471d51719SMichael Gmelin */ 16571d51719SMichael Gmelin v = reg_read(sc, IG4_REG_I2C_STA); 16671d51719SMichael Gmelin if (v & status) { 16771d51719SMichael Gmelin error = 0; 16871d51719SMichael Gmelin break; 16971d51719SMichael Gmelin } 17071d51719SMichael Gmelin 17171d51719SMichael Gmelin /* 17271d51719SMichael Gmelin * When waiting for the transmit FIFO to become empty, 17371d51719SMichael Gmelin * reset the timeout if we see a change in the transmit 17471d51719SMichael Gmelin * FIFO level as progress is being made. 17571d51719SMichael Gmelin */ 17671d51719SMichael Gmelin if (status & IG4_STATUS_TX_EMPTY) { 17771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK; 17871d51719SMichael Gmelin if (txlvl != v) { 17971d51719SMichael Gmelin txlvl = v; 18071d51719SMichael Gmelin count_us = 0; 18171d51719SMichael Gmelin } 18271d51719SMichael Gmelin } 18371d51719SMichael Gmelin 18471d51719SMichael Gmelin /* 18571d51719SMichael Gmelin * Stop if we've run out of time. 18671d51719SMichael Gmelin */ 18771d51719SMichael Gmelin if (count_us >= limit_us) 18871d51719SMichael Gmelin break; 18971d51719SMichael Gmelin 19071d51719SMichael Gmelin /* 19171d51719SMichael Gmelin * When waiting for receive data let the interrupt do its 19271d51719SMichael Gmelin * work, otherwise poll with the lock held. 19371d51719SMichael Gmelin */ 194c59aca57SVladimir Kondratyev if ((status & IG4_STATUS_RX_NOTEMPTY) && !DO_POLL(sc)) { 195733d657aSVladimir Kondratyev mtx_lock(&sc->io_lock); 19621e459c6SVladimir Kondratyev set_intr_mask(sc, IG4_INTR_STOP_DET | IG4_INTR_RX_FULL); 1974cd6abddSMichael Gmelin mtx_sleep(sc, &sc->io_lock, 0, "i2cwait", 19871d51719SMichael Gmelin (hz + 99) / 100); /* sleep up to 10ms */ 19921e459c6SVladimir Kondratyev set_intr_mask(sc, 0); 200733d657aSVladimir Kondratyev mtx_unlock(&sc->io_lock); 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 * Set the slave address. The controller must be disabled when 21371d51719SMichael Gmelin * changing the address. 21471d51719SMichael Gmelin * 21571d51719SMichael Gmelin * This operation does not issue anything to the I2C bus but sets 21671d51719SMichael Gmelin * the target address for when the controller later issues a START. 21771d51719SMichael Gmelin */ 21871d51719SMichael Gmelin static void 219e3d25549SAndriy Gapon set_slave_addr(ig4iic_softc_t *sc, uint8_t slave) 22071d51719SMichael Gmelin { 22171d51719SMichael Gmelin uint32_t tar; 22271d51719SMichael Gmelin uint32_t ctl; 22371d51719SMichael Gmelin int use_10bit; 22471d51719SMichael Gmelin 22571d51719SMichael Gmelin use_10bit = 0; 22671d51719SMichael Gmelin if (sc->slave_valid && sc->last_slave == slave && 22771d51719SMichael Gmelin sc->use_10bit == use_10bit) { 22871d51719SMichael Gmelin return; 22971d51719SMichael Gmelin } 23071d51719SMichael Gmelin sc->use_10bit = use_10bit; 23171d51719SMichael Gmelin 23271d51719SMichael Gmelin /* 23371d51719SMichael Gmelin * Wait for TXFIFO to drain before disabling the controller. 23471d51719SMichael Gmelin * 23571d51719SMichael Gmelin * If a write message has not been completed it's really a 23671d51719SMichael Gmelin * programming error, but for now in that case issue an extra 23771d51719SMichael Gmelin * byte + STOP. 23871d51719SMichael Gmelin * 23971d51719SMichael Gmelin * If a read message has not been completed it's also a programming 24071d51719SMichael Gmelin * error, for now just ignore it. 24171d51719SMichael Gmelin */ 24271d51719SMichael Gmelin wait_status(sc, IG4_STATUS_TX_NOTFULL); 24371d51719SMichael Gmelin if (sc->write_started) { 24471d51719SMichael Gmelin reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_STOP); 24571d51719SMichael Gmelin sc->write_started = 0; 24671d51719SMichael Gmelin } 24771d51719SMichael Gmelin if (sc->read_started) 24871d51719SMichael Gmelin sc->read_started = 0; 24971d51719SMichael Gmelin wait_status(sc, IG4_STATUS_TX_EMPTY); 25071d51719SMichael Gmelin 25171d51719SMichael Gmelin set_controller(sc, 0); 25271d51719SMichael Gmelin ctl = reg_read(sc, IG4_REG_CTL); 25371d51719SMichael Gmelin ctl &= ~IG4_CTL_10BIT; 25471d51719SMichael Gmelin ctl |= IG4_CTL_RESTARTEN; 25571d51719SMichael Gmelin 25671d51719SMichael Gmelin tar = slave; 25771d51719SMichael Gmelin if (sc->use_10bit) { 25871d51719SMichael Gmelin tar |= IG4_TAR_10BIT; 25971d51719SMichael Gmelin ctl |= IG4_CTL_10BIT; 26071d51719SMichael Gmelin } 26171d51719SMichael Gmelin reg_write(sc, IG4_REG_CTL, ctl); 26271d51719SMichael Gmelin reg_write(sc, IG4_REG_TAR_ADD, tar); 26371d51719SMichael Gmelin set_controller(sc, IG4_I2C_ENABLE); 26471d51719SMichael Gmelin sc->slave_valid = 1; 26571d51719SMichael Gmelin sc->last_slave = slave; 26671d51719SMichael Gmelin } 26771d51719SMichael Gmelin 26871d51719SMichael Gmelin /* 269448897d3SAndriy Gapon * IICBUS API FUNCTIONS 270448897d3SAndriy Gapon */ 271448897d3SAndriy Gapon static int 272448897d3SAndriy Gapon ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave) 273448897d3SAndriy Gapon { 274e3d25549SAndriy Gapon set_slave_addr(sc, slave >> 1); 275448897d3SAndriy Gapon return (0); 276448897d3SAndriy Gapon } 277448897d3SAndriy Gapon 278448897d3SAndriy Gapon static int 279448897d3SAndriy Gapon ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, 280448897d3SAndriy Gapon bool repeated_start, bool stop) 281448897d3SAndriy Gapon { 282448897d3SAndriy Gapon uint32_t cmd; 283448897d3SAndriy Gapon uint16_t i; 284448897d3SAndriy Gapon int error; 285448897d3SAndriy Gapon 286448897d3SAndriy Gapon if (len == 0) 287448897d3SAndriy Gapon return (0); 288448897d3SAndriy Gapon 289448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 290448897d3SAndriy Gapon cmd |= repeated_start ? IG4_DATA_RESTART : 0; 291448897d3SAndriy Gapon cmd |= stop && len == 1 ? IG4_DATA_STOP : 0; 292448897d3SAndriy Gapon 293448897d3SAndriy Gapon /* Issue request for the first byte (could be last as well). */ 294448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 295448897d3SAndriy Gapon 296448897d3SAndriy Gapon for (i = 0; i < len; i++) { 297448897d3SAndriy Gapon /* 298448897d3SAndriy Gapon * Maintain a pipeline by queueing the allowance for the next 299448897d3SAndriy Gapon * read before waiting for the current read. 300448897d3SAndriy Gapon */ 301448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 302448897d3SAndriy Gapon if (i < len - 1) { 303448897d3SAndriy Gapon cmd = IG4_DATA_COMMAND_RD; 304448897d3SAndriy Gapon cmd |= stop && i == len - 2 ? IG4_DATA_STOP : 0; 305448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 306448897d3SAndriy Gapon } 307448897d3SAndriy Gapon error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY); 308448897d3SAndriy Gapon if (error) 309448897d3SAndriy Gapon break; 310eca74de0SVladimir Kondratyev buf[i] = (uint8_t)reg_read(sc, IG4_REG_DATA_CMD); 311448897d3SAndriy Gapon } 312448897d3SAndriy Gapon 313448897d3SAndriy Gapon (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); 314448897d3SAndriy Gapon return (error); 315448897d3SAndriy Gapon } 316448897d3SAndriy Gapon 317448897d3SAndriy Gapon static int 318448897d3SAndriy Gapon ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, 319448897d3SAndriy Gapon bool repeated_start, bool stop) 320448897d3SAndriy Gapon { 321448897d3SAndriy Gapon uint32_t cmd; 322448897d3SAndriy Gapon uint16_t i; 323448897d3SAndriy Gapon int error; 324448897d3SAndriy Gapon 325448897d3SAndriy Gapon if (len == 0) 326448897d3SAndriy Gapon return (0); 327448897d3SAndriy Gapon 328448897d3SAndriy Gapon cmd = repeated_start ? IG4_DATA_RESTART : 0; 329448897d3SAndriy Gapon for (i = 0; i < len; i++) { 330448897d3SAndriy Gapon error = wait_status(sc, IG4_STATUS_TX_NOTFULL); 331448897d3SAndriy Gapon if (error) 332448897d3SAndriy Gapon break; 333448897d3SAndriy Gapon cmd |= buf[i]; 334448897d3SAndriy Gapon cmd |= stop && i == len - 1 ? IG4_DATA_STOP : 0; 335448897d3SAndriy Gapon reg_write(sc, IG4_REG_DATA_CMD, cmd); 336448897d3SAndriy Gapon cmd = 0; 337448897d3SAndriy Gapon } 338448897d3SAndriy Gapon 339448897d3SAndriy Gapon (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); 340448897d3SAndriy Gapon return (error); 341448897d3SAndriy Gapon } 342448897d3SAndriy Gapon 343448897d3SAndriy Gapon int 344448897d3SAndriy Gapon ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 345448897d3SAndriy Gapon { 346448897d3SAndriy Gapon ig4iic_softc_t *sc = device_get_softc(dev); 347448897d3SAndriy Gapon const char *reason = NULL; 348448897d3SAndriy Gapon uint32_t i; 349448897d3SAndriy Gapon int error; 350448897d3SAndriy Gapon int unit; 351448897d3SAndriy Gapon bool rpstart; 352448897d3SAndriy Gapon bool stop; 35341b24e09SVladimir Kondratyev bool allocated; 354448897d3SAndriy Gapon 355448897d3SAndriy Gapon /* 356448897d3SAndriy Gapon * The hardware interface imposes limits on allowed I2C messages. 357448897d3SAndriy Gapon * It is not possible to explicitly send a start or stop. 358448897d3SAndriy Gapon * They are automatically sent (or not sent, depending on the 359448897d3SAndriy Gapon * configuration) when a data byte is transferred. 360448897d3SAndriy Gapon * For this reason it's impossible to send a message with no data 361448897d3SAndriy Gapon * at all (like an SMBus quick message). 362448897d3SAndriy Gapon * The start condition is automatically generated after the stop 363448897d3SAndriy Gapon * condition, so it's impossible to not have a start after a stop. 364448897d3SAndriy Gapon * The repeated start condition is automatically sent if a change 365448897d3SAndriy Gapon * of the transfer direction happens, so it's impossible to have 366448897d3SAndriy Gapon * a change of direction without a (repeated) start. 367448897d3SAndriy Gapon * The repeated start can be forced even without the change of 368448897d3SAndriy Gapon * direction. 369448897d3SAndriy Gapon * Changing the target slave address requires resetting the hardware 370448897d3SAndriy Gapon * state, so it's impossible to do that without the stop followed 371448897d3SAndriy Gapon * by the start. 372448897d3SAndriy Gapon */ 373448897d3SAndriy Gapon for (i = 0; i < nmsgs; i++) { 374448897d3SAndriy Gapon #if 0 375448897d3SAndriy Gapon if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) { 376448897d3SAndriy Gapon reason = "first message without start"; 377448897d3SAndriy Gapon break; 378448897d3SAndriy Gapon } 379448897d3SAndriy Gapon if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) { 380448897d3SAndriy Gapon reason = "last message without stop"; 381448897d3SAndriy Gapon break; 382448897d3SAndriy Gapon } 383448897d3SAndriy Gapon #endif 384448897d3SAndriy Gapon if (msgs[i].len == 0) { 385448897d3SAndriy Gapon reason = "message with no data"; 386448897d3SAndriy Gapon break; 387448897d3SAndriy Gapon } 388448897d3SAndriy Gapon if (i > 0) { 389448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) != 0 && 390448897d3SAndriy Gapon (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) { 391448897d3SAndriy Gapon reason = "stop not followed by start"; 392448897d3SAndriy Gapon break; 393448897d3SAndriy Gapon } 394448897d3SAndriy Gapon if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 && 395448897d3SAndriy Gapon msgs[i].slave != msgs[i - 1].slave) { 396448897d3SAndriy Gapon reason = "change of slave without stop"; 397448897d3SAndriy Gapon break; 398448897d3SAndriy Gapon } 399448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) != 0 && 400448897d3SAndriy Gapon (msgs[i].flags & IIC_M_RD) != 401448897d3SAndriy Gapon (msgs[i - 1].flags & IIC_M_RD)) { 402448897d3SAndriy Gapon reason = "change of direction without repeated" 403448897d3SAndriy Gapon " start"; 404448897d3SAndriy Gapon break; 405448897d3SAndriy Gapon } 406448897d3SAndriy Gapon } 407448897d3SAndriy Gapon } 408448897d3SAndriy Gapon if (reason != NULL) { 409448897d3SAndriy Gapon if (bootverbose) 410448897d3SAndriy Gapon device_printf(dev, "%s\n", reason); 411448897d3SAndriy Gapon return (IIC_ENOTSUPP); 412448897d3SAndriy Gapon } 413448897d3SAndriy Gapon 41441b24e09SVladimir Kondratyev /* Check if device is already allocated with iicbus_request_bus() */ 41541b24e09SVladimir Kondratyev allocated = sx_xlocked(&sc->call_lock) != 0; 41641b24e09SVladimir Kondratyev if (!allocated) 417448897d3SAndriy Gapon sx_xlock(&sc->call_lock); 418448897d3SAndriy Gapon 419448897d3SAndriy Gapon /* Debugging - dump registers. */ 420448897d3SAndriy Gapon if (ig4_dump) { 421448897d3SAndriy Gapon unit = device_get_unit(dev); 422448897d3SAndriy Gapon if (ig4_dump & (1 << unit)) { 423448897d3SAndriy Gapon ig4_dump &= ~(1 << unit); 424448897d3SAndriy Gapon ig4iic_dump(sc); 425448897d3SAndriy Gapon } 426448897d3SAndriy Gapon } 427448897d3SAndriy Gapon 428448897d3SAndriy Gapon /* 429448897d3SAndriy Gapon * Clear any previous abort condition that may have been holding 430448897d3SAndriy Gapon * the txfifo in reset. 431448897d3SAndriy Gapon */ 432448897d3SAndriy Gapon reg_read(sc, IG4_REG_CLR_TX_ABORT); 433448897d3SAndriy Gapon 434448897d3SAndriy Gapon rpstart = false; 435448897d3SAndriy Gapon error = 0; 436448897d3SAndriy Gapon for (i = 0; i < nmsgs; i++) { 437448897d3SAndriy Gapon if ((msgs[i].flags & IIC_M_NOSTART) == 0) { 438448897d3SAndriy Gapon error = ig4iic_xfer_start(sc, msgs[i].slave); 439448897d3SAndriy Gapon } else { 440448897d3SAndriy Gapon if (!sc->slave_valid || 441448897d3SAndriy Gapon (msgs[i].slave >> 1) != sc->last_slave) { 442448897d3SAndriy Gapon device_printf(dev, "start condition suppressed" 443448897d3SAndriy Gapon "but slave address is not set up"); 444448897d3SAndriy Gapon error = EINVAL; 445448897d3SAndriy Gapon break; 446448897d3SAndriy Gapon } 447448897d3SAndriy Gapon rpstart = false; 448448897d3SAndriy Gapon } 449448897d3SAndriy Gapon if (error != 0) 450448897d3SAndriy Gapon break; 451448897d3SAndriy Gapon 452448897d3SAndriy Gapon stop = (msgs[i].flags & IIC_M_NOSTOP) == 0; 453448897d3SAndriy Gapon if (msgs[i].flags & IIC_M_RD) 454448897d3SAndriy Gapon error = ig4iic_read(sc, msgs[i].buf, msgs[i].len, 455448897d3SAndriy Gapon rpstart, stop); 456448897d3SAndriy Gapon else 457448897d3SAndriy Gapon error = ig4iic_write(sc, msgs[i].buf, msgs[i].len, 458448897d3SAndriy Gapon rpstart, stop); 459448897d3SAndriy Gapon if (error != 0) 460448897d3SAndriy Gapon break; 461448897d3SAndriy Gapon 462448897d3SAndriy Gapon rpstart = !stop; 463448897d3SAndriy Gapon } 464448897d3SAndriy Gapon 46541b24e09SVladimir Kondratyev if (!allocated) 466448897d3SAndriy Gapon sx_unlock(&sc->call_lock); 467448897d3SAndriy Gapon return (error); 468448897d3SAndriy Gapon } 469448897d3SAndriy Gapon 470448897d3SAndriy Gapon int 471448897d3SAndriy Gapon ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 472448897d3SAndriy Gapon { 473448897d3SAndriy Gapon ig4iic_softc_t *sc = device_get_softc(dev); 47441b24e09SVladimir Kondratyev bool allocated; 475448897d3SAndriy Gapon 47641b24e09SVladimir Kondratyev allocated = sx_xlocked(&sc->call_lock) != 0; 47741b24e09SVladimir Kondratyev if (!allocated) 478448897d3SAndriy Gapon sx_xlock(&sc->call_lock); 479448897d3SAndriy Gapon 480448897d3SAndriy Gapon /* TODO handle speed configuration? */ 481448897d3SAndriy Gapon if (oldaddr != NULL) 482448897d3SAndriy Gapon *oldaddr = sc->last_slave << 1; 483e3d25549SAndriy Gapon set_slave_addr(sc, addr >> 1); 484448897d3SAndriy Gapon if (addr == IIC_UNKNOWN) 485448897d3SAndriy Gapon sc->slave_valid = false; 486448897d3SAndriy Gapon 48741b24e09SVladimir Kondratyev if (!allocated) 488448897d3SAndriy Gapon sx_unlock(&sc->call_lock); 489448897d3SAndriy Gapon return (0); 490448897d3SAndriy Gapon } 491448897d3SAndriy Gapon 49241b24e09SVladimir Kondratyev int 49341b24e09SVladimir Kondratyev ig4iic_callback(device_t dev, int index, caddr_t data) 49441b24e09SVladimir Kondratyev { 49541b24e09SVladimir Kondratyev ig4iic_softc_t *sc = device_get_softc(dev); 49641b24e09SVladimir Kondratyev int error = 0; 49741b24e09SVladimir Kondratyev int how; 49841b24e09SVladimir Kondratyev 49941b24e09SVladimir Kondratyev switch (index) { 50041b24e09SVladimir Kondratyev case IIC_REQUEST_BUS: 50141b24e09SVladimir Kondratyev /* force polling if ig4iic is requested with IIC_DONTWAIT */ 50241b24e09SVladimir Kondratyev how = *(int *)data; 50341b24e09SVladimir Kondratyev if ((how & IIC_WAIT) == 0) { 50441b24e09SVladimir Kondratyev if (sx_try_xlock(&sc->call_lock) == 0) 50541b24e09SVladimir Kondratyev error = IIC_EBUSBSY; 50641b24e09SVladimir Kondratyev else 50741b24e09SVladimir Kondratyev sc->poll = true; 50841b24e09SVladimir Kondratyev } else 50941b24e09SVladimir Kondratyev sx_xlock(&sc->call_lock); 51041b24e09SVladimir Kondratyev break; 51141b24e09SVladimir Kondratyev 51241b24e09SVladimir Kondratyev case IIC_RELEASE_BUS: 51341b24e09SVladimir Kondratyev sc->poll = false; 51441b24e09SVladimir Kondratyev sx_unlock(&sc->call_lock); 51541b24e09SVladimir Kondratyev break; 51641b24e09SVladimir Kondratyev 51741b24e09SVladimir Kondratyev default: 51841b24e09SVladimir Kondratyev error = errno2iic(EINVAL); 51941b24e09SVladimir Kondratyev } 52041b24e09SVladimir Kondratyev 52141b24e09SVladimir Kondratyev return (error); 52241b24e09SVladimir Kondratyev } 52341b24e09SVladimir Kondratyev 524448897d3SAndriy Gapon /* 52571d51719SMichael Gmelin * Called from ig4iic_pci_attach/detach() 52671d51719SMichael Gmelin */ 52771d51719SMichael Gmelin int 52871d51719SMichael Gmelin ig4iic_attach(ig4iic_softc_t *sc) 52971d51719SMichael Gmelin { 53071d51719SMichael Gmelin int error; 53171d51719SMichael Gmelin uint32_t v; 53271d51719SMichael Gmelin 5335c5bcb1dSOleksandr Tymoshenko mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF); 5345c5bcb1dSOleksandr Tymoshenko sx_init(&sc->call_lock, "IG4 call lock"); 5355c5bcb1dSOleksandr Tymoshenko 536b16d03adSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_DEVIDLE_CTRL); 537b16d03adSOleksandr Tymoshenko if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) { 538b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED); 539b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0); 540b16d03adSOleksandr Tymoshenko 541b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); 542b16d03adSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); 543b16d03adSOleksandr Tymoshenko DELAY(1000); 544b16d03adSOleksandr Tymoshenko } 545b16d03adSOleksandr Tymoshenko 546b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_ATOM) 54771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_TYPE); 548b3e8ee5dSOleksandr Tymoshenko 549b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 55071d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_PARAM1); 55171d51719SMichael Gmelin v = reg_read(sc, IG4_REG_GENERAL); 552b3e8ee5dSOleksandr Tymoshenko /* 553b3e8ee5dSOleksandr Tymoshenko * The content of IG4_REG_GENERAL is different for each 554b3e8ee5dSOleksandr Tymoshenko * controller version. 555b3e8ee5dSOleksandr Tymoshenko */ 556b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL && 557b3e8ee5dSOleksandr Tymoshenko (v & IG4_GENERAL_SWMODE) == 0) { 55871d51719SMichael Gmelin v |= IG4_GENERAL_SWMODE; 55971d51719SMichael Gmelin reg_write(sc, IG4_REG_GENERAL, v); 56071d51719SMichael Gmelin v = reg_read(sc, IG4_REG_GENERAL); 56171d51719SMichael Gmelin } 562b3e8ee5dSOleksandr Tymoshenko } 56371d51719SMichael Gmelin 564b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL) { 56571d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SW_LTR_VALUE); 56671d51719SMichael Gmelin v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); 567b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 568b3e8ee5dSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE); 569b3e8ee5dSOleksandr Tymoshenko v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE); 570b3e8ee5dSOleksandr Tymoshenko } 57171d51719SMichael Gmelin 572b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 57371d51719SMichael Gmelin v = reg_read(sc, IG4_REG_COMP_VER); 5745747fe4fSOleksandr Tymoshenko if (v < IG4_COMP_MIN_VER) { 57571d51719SMichael Gmelin error = ENXIO; 57671d51719SMichael Gmelin goto done; 57771d51719SMichael Gmelin } 578b3e8ee5dSOleksandr Tymoshenko } 579d117e363SVladimir Kondratyev 580d117e363SVladimir Kondratyev if (set_controller(sc, 0)) { 581d117e363SVladimir Kondratyev device_printf(sc->dev, "controller error during attach-1\n"); 582d117e363SVladimir Kondratyev error = ENXIO; 583d117e363SVladimir Kondratyev goto done; 584d117e363SVladimir Kondratyev } 585d117e363SVladimir Kondratyev 58671d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_HCNT); 58771d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_LCNT); 58871d51719SMichael Gmelin v = reg_read(sc, IG4_REG_FS_SCL_HCNT); 58971d51719SMichael Gmelin v = reg_read(sc, IG4_REG_FS_SCL_LCNT); 59071d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SDA_HOLD); 59171d51719SMichael Gmelin 59271d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_HCNT); 59371d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_HCNT, v); 59471d51719SMichael Gmelin v = reg_read(sc, IG4_REG_SS_SCL_LCNT); 59571d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_LCNT, v); 59671d51719SMichael Gmelin 59721e459c6SVladimir Kondratyev reg_read(sc, IG4_REG_CLR_INTR); 59821e459c6SVladimir Kondratyev reg_write(sc, IG4_REG_INTR_MASK, 0); 59921e459c6SVladimir Kondratyev sc->intr_mask = 0; 60021e459c6SVladimir Kondratyev 60171d51719SMichael Gmelin /* 60271d51719SMichael Gmelin * Program based on a 25000 Hz clock. This is a bit of a 60371d51719SMichael Gmelin * hack (obviously). The defaults are 400 and 470 for standard 60471d51719SMichael Gmelin * and 60 and 130 for fast. The defaults for standard fail 60571d51719SMichael Gmelin * utterly (presumably cause an abort) because the clock time 60671d51719SMichael Gmelin * is ~18.8ms by default. This brings it down to ~4ms (for now). 60771d51719SMichael Gmelin */ 60871d51719SMichael Gmelin reg_write(sc, IG4_REG_SS_SCL_HCNT, 100); 60971d51719SMichael Gmelin reg_write(sc, IG4_REG_SS_SCL_LCNT, 125); 61071d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_HCNT, 100); 61171d51719SMichael Gmelin reg_write(sc, IG4_REG_FS_SCL_LCNT, 125); 6123ca6000fSVladimir Kondratyev if (sc->version == IG4_SKYLAKE) 6133ca6000fSVladimir Kondratyev reg_write(sc, IG4_REG_SDA_HOLD, 28); 61471d51719SMichael Gmelin 61571d51719SMichael Gmelin /* 61671d51719SMichael Gmelin * Use a threshold of 1 so we get interrupted on each character, 61771d51719SMichael Gmelin * allowing us to use mtx_sleep() in our poll code. Not perfect 61871d51719SMichael Gmelin * but this is better than using DELAY() for receiving data. 6194cd6abddSMichael Gmelin * 6204cd6abddSMichael Gmelin * See ig4_var.h for details on interrupt handler synchronization. 62171d51719SMichael Gmelin */ 622811ff4ddSVladimir Kondratyev reg_write(sc, IG4_REG_RX_TL, 0); 62371d51719SMichael Gmelin 62471d51719SMichael Gmelin reg_write(sc, IG4_REG_CTL, 62571d51719SMichael Gmelin IG4_CTL_MASTER | 62671d51719SMichael Gmelin IG4_CTL_SLAVE_DISABLE | 62771d51719SMichael Gmelin IG4_CTL_RESTARTEN | 62871d51719SMichael Gmelin IG4_CTL_SPEED_STD); 62971d51719SMichael Gmelin 630448897d3SAndriy Gapon sc->iicbus = device_add_child(sc->dev, "iicbus", -1); 631448897d3SAndriy Gapon if (sc->iicbus == NULL) { 632448897d3SAndriy Gapon device_printf(sc->dev, "iicbus driver not found\n"); 63371d51719SMichael Gmelin error = ENXIO; 63471d51719SMichael Gmelin goto done; 63571d51719SMichael Gmelin } 63671d51719SMichael Gmelin 63771d51719SMichael Gmelin #if 0 63871d51719SMichael Gmelin /* 63971d51719SMichael Gmelin * Don't do this, it blows up the PCI config 64071d51719SMichael Gmelin */ 641b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 642b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW); 643b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW); 644b3e8ee5dSOleksandr Tymoshenko } else if (sc->version = IG4_SKYLAKE) { 645b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL); 646b3e8ee5dSOleksandr Tymoshenko reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL); 647b3e8ee5dSOleksandr Tymoshenko } 64871d51719SMichael Gmelin #endif 64971d51719SMichael Gmelin 650bf9c3c58SVladimir Kondratyev if (set_controller(sc, IG4_I2C_ENABLE)) { 65171d51719SMichael Gmelin device_printf(sc->dev, "controller error during attach-2\n"); 652bf9c3c58SVladimir Kondratyev error = ENXIO; 653bf9c3c58SVladimir Kondratyev goto done; 654bf9c3c58SVladimir Kondratyev } 655edcf6a9fSVladimir Kondratyev if (set_controller(sc, 0)) { 656edcf6a9fSVladimir Kondratyev device_printf(sc->dev, "controller error during attach-3\n"); 657edcf6a9fSVladimir Kondratyev error = ENXIO; 658edcf6a9fSVladimir Kondratyev goto done; 659edcf6a9fSVladimir Kondratyev } 66071d51719SMichael Gmelin error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, 66171d51719SMichael Gmelin NULL, ig4iic_intr, sc, &sc->intr_handle); 66271d51719SMichael Gmelin if (error) { 66371d51719SMichael Gmelin device_printf(sc->dev, 66471d51719SMichael Gmelin "Unable to setup irq: error %d\n", error); 66571d51719SMichael Gmelin } 66671d51719SMichael Gmelin 66771d51719SMichael Gmelin sc->enum_hook.ich_func = ig4iic_start; 66871d51719SMichael Gmelin sc->enum_hook.ich_arg = sc->dev; 66971d51719SMichael Gmelin 6700ba5622dSMichael Gmelin /* 6710ba5622dSMichael Gmelin * We have to wait until interrupts are enabled. I2C read and write 67271d51719SMichael Gmelin * only works if the interrupts are available. 67371d51719SMichael Gmelin */ 67471d51719SMichael Gmelin if (config_intrhook_establish(&sc->enum_hook) != 0) 67571d51719SMichael Gmelin error = ENOMEM; 67671d51719SMichael Gmelin else 67771d51719SMichael Gmelin error = 0; 67871d51719SMichael Gmelin 67971d51719SMichael Gmelin done: 68071d51719SMichael Gmelin return (error); 68171d51719SMichael Gmelin } 68271d51719SMichael Gmelin 68371d51719SMichael Gmelin void 68471d51719SMichael Gmelin ig4iic_start(void *xdev) 68571d51719SMichael Gmelin { 68671d51719SMichael Gmelin int error; 68771d51719SMichael Gmelin ig4iic_softc_t *sc; 68871d51719SMichael Gmelin device_t dev = (device_t)xdev; 68971d51719SMichael Gmelin 69071d51719SMichael Gmelin sc = device_get_softc(dev); 69171d51719SMichael Gmelin 69271d51719SMichael Gmelin config_intrhook_disestablish(&sc->enum_hook); 69371d51719SMichael Gmelin 69471d51719SMichael Gmelin error = bus_generic_attach(sc->dev); 69571d51719SMichael Gmelin if (error) { 69671d51719SMichael Gmelin device_printf(sc->dev, 69771d51719SMichael Gmelin "failed to attach child: error %d\n", error); 69871d51719SMichael Gmelin } 69971d51719SMichael Gmelin } 70071d51719SMichael Gmelin 70171d51719SMichael Gmelin int 70271d51719SMichael Gmelin ig4iic_detach(ig4iic_softc_t *sc) 70371d51719SMichael Gmelin { 70471d51719SMichael Gmelin int error; 70571d51719SMichael Gmelin 70671d51719SMichael Gmelin if (device_is_attached(sc->dev)) { 70771d51719SMichael Gmelin error = bus_generic_detach(sc->dev); 70871d51719SMichael Gmelin if (error) 70971d51719SMichael Gmelin return (error); 71071d51719SMichael Gmelin } 711448897d3SAndriy Gapon if (sc->iicbus) 712448897d3SAndriy Gapon device_delete_child(sc->dev, sc->iicbus); 71371d51719SMichael Gmelin if (sc->intr_handle) 71471d51719SMichael Gmelin bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); 71571d51719SMichael Gmelin 7164cd6abddSMichael Gmelin sx_xlock(&sc->call_lock); 71771d51719SMichael Gmelin 718448897d3SAndriy Gapon sc->iicbus = NULL; 71971d51719SMichael Gmelin sc->intr_handle = NULL; 72071d51719SMichael Gmelin reg_write(sc, IG4_REG_INTR_MASK, 0); 72171d51719SMichael Gmelin set_controller(sc, 0); 72271d51719SMichael Gmelin 7234cd6abddSMichael Gmelin sx_xunlock(&sc->call_lock); 7245c5bcb1dSOleksandr Tymoshenko 7255c5bcb1dSOleksandr Tymoshenko mtx_destroy(&sc->io_lock); 7265c5bcb1dSOleksandr Tymoshenko sx_destroy(&sc->call_lock); 7275c5bcb1dSOleksandr Tymoshenko 72871d51719SMichael Gmelin return (0); 72971d51719SMichael Gmelin } 73071d51719SMichael Gmelin 73171d51719SMichael Gmelin /* 7324cd6abddSMichael Gmelin * Interrupt Operation, see ig4_var.h for locking semantics. 73371d51719SMichael Gmelin */ 73471d51719SMichael Gmelin static void 73571d51719SMichael Gmelin ig4iic_intr(void *cookie) 73671d51719SMichael Gmelin { 73771d51719SMichael Gmelin ig4iic_softc_t *sc = cookie; 73871d51719SMichael Gmelin 7394cd6abddSMichael Gmelin mtx_lock(&sc->io_lock); 7400a6b1b56SVladimir Kondratyev /* Ignore stray interrupts */ 7410a6b1b56SVladimir Kondratyev if (sc->intr_mask != 0 && reg_read(sc, IG4_REG_INTR_STAT) != 0) { 74221e459c6SVladimir Kondratyev set_intr_mask(sc, 0); 7430ba5622dSMichael Gmelin reg_read(sc, IG4_REG_CLR_INTR); 74471d51719SMichael Gmelin wakeup(sc); 7450a6b1b56SVladimir Kondratyev } 7464cd6abddSMichael Gmelin mtx_unlock(&sc->io_lock); 74771d51719SMichael Gmelin } 74871d51719SMichael Gmelin 74971d51719SMichael Gmelin #define REGDUMP(sc, reg) \ 75071d51719SMichael Gmelin device_printf(sc->dev, " %-23s %08x\n", #reg, reg_read(sc, reg)) 75171d51719SMichael Gmelin 75271d51719SMichael Gmelin static void 75371d51719SMichael Gmelin ig4iic_dump(ig4iic_softc_t *sc) 75471d51719SMichael Gmelin { 75571d51719SMichael Gmelin device_printf(sc->dev, "ig4iic register dump:\n"); 75671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_CTL); 75771d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TAR_ADD); 75871d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SS_SCL_HCNT); 75971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SS_SCL_LCNT); 76071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_FS_SCL_HCNT); 76171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_FS_SCL_LCNT); 76271d51719SMichael Gmelin REGDUMP(sc, IG4_REG_INTR_STAT); 76371d51719SMichael Gmelin REGDUMP(sc, IG4_REG_INTR_MASK); 76471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RAW_INTR_STAT); 76571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RX_TL); 76671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TX_TL); 76771d51719SMichael Gmelin REGDUMP(sc, IG4_REG_I2C_EN); 76871d51719SMichael Gmelin REGDUMP(sc, IG4_REG_I2C_STA); 76971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TXFLR); 77071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_RXFLR); 77171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SDA_HOLD); 77271d51719SMichael Gmelin REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE); 77371d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SLV_DATA_NACK); 77471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_CTRL); 77571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_TDLR); 77671d51719SMichael Gmelin REGDUMP(sc, IG4_REG_DMA_RDLR); 77771d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SDA_SETUP); 77871d51719SMichael Gmelin REGDUMP(sc, IG4_REG_ENABLE_STATUS); 779b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 78071d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_PARAM1); 78171d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_VER); 782b3e8ee5dSOleksandr Tymoshenko } 783b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_ATOM) { 78471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_COMP_TYPE); 78571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_CLK_PARMS); 786b3e8ee5dSOleksandr Tymoshenko } 787b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) { 788b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_RESETS_HSW); 78971d51719SMichael Gmelin REGDUMP(sc, IG4_REG_GENERAL); 790b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 791b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_RESETS_SKL); 792b3e8ee5dSOleksandr Tymoshenko } 793b3e8ee5dSOleksandr Tymoshenko if (sc->version == IG4_HASWELL) { 79471d51719SMichael Gmelin REGDUMP(sc, IG4_REG_SW_LTR_VALUE); 79571d51719SMichael Gmelin REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE); 796b3e8ee5dSOleksandr Tymoshenko } else if (sc->version == IG4_SKYLAKE) { 797b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE); 798b3e8ee5dSOleksandr Tymoshenko REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE); 799b3e8ee5dSOleksandr Tymoshenko } 80071d51719SMichael Gmelin } 80171d51719SMichael Gmelin #undef REGDUMP 80271d51719SMichael Gmelin 803984ed3e4SVladimir Kondratyev devclass_t ig4iic_devclass; 804984ed3e4SVladimir Kondratyev 805984ed3e4SVladimir Kondratyev DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL); 806984ed3e4SVladimir Kondratyev MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 807984ed3e4SVladimir Kondratyev MODULE_VERSION(ig4iic, 1); 808