xref: /freebsd/sys/dev/ichiic/ig4_iic.c (revision c59aca57)
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 
75c59aca57SVladimir Kondratyev #define DO_POLL(sc)	(cold || kdb_active || SCHEDULER_STOPPED())
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;
353448897d3SAndriy Gapon 
354448897d3SAndriy Gapon 	/*
355448897d3SAndriy Gapon 	 * The hardware interface imposes limits on allowed I2C messages.
356448897d3SAndriy Gapon 	 * It is not possible to explicitly send a start or stop.
357448897d3SAndriy Gapon 	 * They are automatically sent (or not sent, depending on the
358448897d3SAndriy Gapon 	 * configuration) when a data byte is transferred.
359448897d3SAndriy Gapon 	 * For this reason it's impossible to send a message with no data
360448897d3SAndriy Gapon 	 * at all (like an SMBus quick message).
361448897d3SAndriy Gapon 	 * The start condition is automatically generated after the stop
362448897d3SAndriy Gapon 	 * condition, so it's impossible to not have a start after a stop.
363448897d3SAndriy Gapon 	 * The repeated start condition is automatically sent if a change
364448897d3SAndriy Gapon 	 * of the transfer direction happens, so it's impossible to have
365448897d3SAndriy Gapon 	 * a change of direction without a (repeated) start.
366448897d3SAndriy Gapon 	 * The repeated start can be forced even without the change of
367448897d3SAndriy Gapon 	 * direction.
368448897d3SAndriy Gapon 	 * Changing the target slave address requires resetting the hardware
369448897d3SAndriy Gapon 	 * state, so it's impossible to do that without the stop followed
370448897d3SAndriy Gapon 	 * by the start.
371448897d3SAndriy Gapon 	 */
372448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
373448897d3SAndriy Gapon #if 0
374448897d3SAndriy Gapon 		if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) {
375448897d3SAndriy Gapon 			reason = "first message without start";
376448897d3SAndriy Gapon 			break;
377448897d3SAndriy Gapon 		}
378448897d3SAndriy Gapon 		if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) {
379448897d3SAndriy Gapon 			reason = "last message without stop";
380448897d3SAndriy Gapon 			break;
381448897d3SAndriy Gapon 		}
382448897d3SAndriy Gapon #endif
383448897d3SAndriy Gapon 		if (msgs[i].len == 0) {
384448897d3SAndriy Gapon 			reason = "message with no data";
385448897d3SAndriy Gapon 			break;
386448897d3SAndriy Gapon 		}
387448897d3SAndriy Gapon 		if (i > 0) {
388448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
389448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) {
390448897d3SAndriy Gapon 				reason = "stop not followed by start";
391448897d3SAndriy Gapon 				break;
392448897d3SAndriy Gapon 			}
393448897d3SAndriy Gapon 			if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 &&
394448897d3SAndriy Gapon 			    msgs[i].slave != msgs[i - 1].slave) {
395448897d3SAndriy Gapon 				reason = "change of slave without stop";
396448897d3SAndriy Gapon 				break;
397448897d3SAndriy Gapon 			}
398448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
399448897d3SAndriy Gapon 			    (msgs[i].flags & IIC_M_RD) !=
400448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_RD)) {
401448897d3SAndriy Gapon 				reason = "change of direction without repeated"
402448897d3SAndriy Gapon 				    " start";
403448897d3SAndriy Gapon 				break;
404448897d3SAndriy Gapon 			}
405448897d3SAndriy Gapon 		}
406448897d3SAndriy Gapon 	}
407448897d3SAndriy Gapon 	if (reason != NULL) {
408448897d3SAndriy Gapon 		if (bootverbose)
409448897d3SAndriy Gapon 			device_printf(dev, "%s\n", reason);
410448897d3SAndriy Gapon 		return (IIC_ENOTSUPP);
411448897d3SAndriy Gapon 	}
412448897d3SAndriy Gapon 
413448897d3SAndriy Gapon 	sx_xlock(&sc->call_lock);
414448897d3SAndriy Gapon 
415448897d3SAndriy Gapon 	/* Debugging - dump registers. */
416448897d3SAndriy Gapon 	if (ig4_dump) {
417448897d3SAndriy Gapon 		unit = device_get_unit(dev);
418448897d3SAndriy Gapon 		if (ig4_dump & (1 << unit)) {
419448897d3SAndriy Gapon 			ig4_dump &= ~(1 << unit);
420448897d3SAndriy Gapon 			ig4iic_dump(sc);
421448897d3SAndriy Gapon 		}
422448897d3SAndriy Gapon 	}
423448897d3SAndriy Gapon 
424448897d3SAndriy Gapon 	/*
425448897d3SAndriy Gapon 	 * Clear any previous abort condition that may have been holding
426448897d3SAndriy Gapon 	 * the txfifo in reset.
427448897d3SAndriy Gapon 	 */
428448897d3SAndriy Gapon 	reg_read(sc, IG4_REG_CLR_TX_ABORT);
429448897d3SAndriy Gapon 
430448897d3SAndriy Gapon 	rpstart = false;
431448897d3SAndriy Gapon 	error = 0;
432448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
433448897d3SAndriy Gapon 		if ((msgs[i].flags & IIC_M_NOSTART) == 0) {
434448897d3SAndriy Gapon 			error = ig4iic_xfer_start(sc, msgs[i].slave);
435448897d3SAndriy Gapon 		} else {
436448897d3SAndriy Gapon 			if (!sc->slave_valid ||
437448897d3SAndriy Gapon 			    (msgs[i].slave >> 1) != sc->last_slave) {
438448897d3SAndriy Gapon 				device_printf(dev, "start condition suppressed"
439448897d3SAndriy Gapon 				    "but slave address is not set up");
440448897d3SAndriy Gapon 				error = EINVAL;
441448897d3SAndriy Gapon 				break;
442448897d3SAndriy Gapon 			}
443448897d3SAndriy Gapon 			rpstart = false;
444448897d3SAndriy Gapon 		}
445448897d3SAndriy Gapon 		if (error != 0)
446448897d3SAndriy Gapon 			break;
447448897d3SAndriy Gapon 
448448897d3SAndriy Gapon 		stop = (msgs[i].flags & IIC_M_NOSTOP) == 0;
449448897d3SAndriy Gapon 		if (msgs[i].flags & IIC_M_RD)
450448897d3SAndriy Gapon 			error = ig4iic_read(sc, msgs[i].buf, msgs[i].len,
451448897d3SAndriy Gapon 			    rpstart, stop);
452448897d3SAndriy Gapon 		else
453448897d3SAndriy Gapon 			error = ig4iic_write(sc, msgs[i].buf, msgs[i].len,
454448897d3SAndriy Gapon 			    rpstart, stop);
455448897d3SAndriy Gapon 		if (error != 0)
456448897d3SAndriy Gapon 			break;
457448897d3SAndriy Gapon 
458448897d3SAndriy Gapon 		rpstart = !stop;
459448897d3SAndriy Gapon 	}
460448897d3SAndriy Gapon 
461448897d3SAndriy Gapon 	sx_unlock(&sc->call_lock);
462448897d3SAndriy Gapon 	return (error);
463448897d3SAndriy Gapon }
464448897d3SAndriy Gapon 
465448897d3SAndriy Gapon int
466448897d3SAndriy Gapon ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
467448897d3SAndriy Gapon {
468448897d3SAndriy Gapon 	ig4iic_softc_t *sc = device_get_softc(dev);
469448897d3SAndriy Gapon 
470448897d3SAndriy Gapon 	sx_xlock(&sc->call_lock);
471448897d3SAndriy Gapon 
472448897d3SAndriy Gapon 	/* TODO handle speed configuration? */
473448897d3SAndriy Gapon 	if (oldaddr != NULL)
474448897d3SAndriy Gapon 		*oldaddr = sc->last_slave << 1;
475e3d25549SAndriy Gapon 	set_slave_addr(sc, addr >> 1);
476448897d3SAndriy Gapon 	if (addr == IIC_UNKNOWN)
477448897d3SAndriy Gapon 		sc->slave_valid = false;
478448897d3SAndriy Gapon 
479448897d3SAndriy Gapon 	sx_unlock(&sc->call_lock);
480448897d3SAndriy Gapon 	return (0);
481448897d3SAndriy Gapon }
482448897d3SAndriy Gapon 
483448897d3SAndriy Gapon /*
48471d51719SMichael Gmelin  * Called from ig4iic_pci_attach/detach()
48571d51719SMichael Gmelin  */
48671d51719SMichael Gmelin int
48771d51719SMichael Gmelin ig4iic_attach(ig4iic_softc_t *sc)
48871d51719SMichael Gmelin {
48971d51719SMichael Gmelin 	int error;
49071d51719SMichael Gmelin 	uint32_t v;
49171d51719SMichael Gmelin 
4925c5bcb1dSOleksandr Tymoshenko 	mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
4935c5bcb1dSOleksandr Tymoshenko 	sx_init(&sc->call_lock, "IG4 call lock");
4945c5bcb1dSOleksandr Tymoshenko 
495b16d03adSOleksandr Tymoshenko 	v = reg_read(sc, IG4_REG_DEVIDLE_CTRL);
496b16d03adSOleksandr Tymoshenko 	if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) {
497b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED);
498b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0);
499b16d03adSOleksandr Tymoshenko 
500b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
501b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
502b16d03adSOleksandr Tymoshenko 		DELAY(1000);
503b16d03adSOleksandr Tymoshenko 	}
504b16d03adSOleksandr Tymoshenko 
505b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM)
50671d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_TYPE);
507b3e8ee5dSOleksandr Tymoshenko 
508b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
50971d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_PARAM1);
51071d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_GENERAL);
511b3e8ee5dSOleksandr Tymoshenko 		/*
512b3e8ee5dSOleksandr Tymoshenko 		 * The content of IG4_REG_GENERAL is different for each
513b3e8ee5dSOleksandr Tymoshenko 		 * controller version.
514b3e8ee5dSOleksandr Tymoshenko 		 */
515b3e8ee5dSOleksandr Tymoshenko 		if (sc->version == IG4_HASWELL &&
516b3e8ee5dSOleksandr Tymoshenko 		    (v & IG4_GENERAL_SWMODE) == 0) {
51771d51719SMichael Gmelin 			v |= IG4_GENERAL_SWMODE;
51871d51719SMichael Gmelin 			reg_write(sc, IG4_REG_GENERAL, v);
51971d51719SMichael Gmelin 			v = reg_read(sc, IG4_REG_GENERAL);
52071d51719SMichael Gmelin 		}
521b3e8ee5dSOleksandr Tymoshenko 	}
52271d51719SMichael Gmelin 
523b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
52471d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
52571d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
526b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
527b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE);
528b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE);
529b3e8ee5dSOleksandr Tymoshenko 	}
53071d51719SMichael Gmelin 
531b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
53271d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_VER);
5335747fe4fSOleksandr Tymoshenko 		if (v < IG4_COMP_MIN_VER) {
53471d51719SMichael Gmelin 			error = ENXIO;
53571d51719SMichael Gmelin 			goto done;
53671d51719SMichael Gmelin 		}
537b3e8ee5dSOleksandr Tymoshenko 	}
538d117e363SVladimir Kondratyev 
539d117e363SVladimir Kondratyev 	if (set_controller(sc, 0)) {
540d117e363SVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-1\n");
541d117e363SVladimir Kondratyev 		error = ENXIO;
542d117e363SVladimir Kondratyev 		goto done;
543d117e363SVladimir Kondratyev 	}
544d117e363SVladimir Kondratyev 
54571d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
54671d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
54771d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_FS_SCL_HCNT);
54871d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_FS_SCL_LCNT);
54971d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_SDA_HOLD);
55071d51719SMichael Gmelin 
55171d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
55271d51719SMichael Gmelin 	reg_write(sc, IG4_REG_FS_SCL_HCNT, v);
55371d51719SMichael Gmelin 	v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
55471d51719SMichael Gmelin 	reg_write(sc, IG4_REG_FS_SCL_LCNT, v);
55571d51719SMichael Gmelin 
55621e459c6SVladimir Kondratyev 	reg_read(sc, IG4_REG_CLR_INTR);
55721e459c6SVladimir Kondratyev 	reg_write(sc, IG4_REG_INTR_MASK, 0);
55821e459c6SVladimir Kondratyev 	sc->intr_mask = 0;
55921e459c6SVladimir Kondratyev 
56071d51719SMichael Gmelin 	/*
56171d51719SMichael Gmelin 	 * Program based on a 25000 Hz clock.  This is a bit of a
56271d51719SMichael Gmelin 	 * hack (obviously).  The defaults are 400 and 470 for standard
56371d51719SMichael Gmelin 	 * and 60 and 130 for fast.  The defaults for standard fail
56471d51719SMichael Gmelin 	 * utterly (presumably cause an abort) because the clock time
56571d51719SMichael Gmelin 	 * is ~18.8ms by default.  This brings it down to ~4ms (for now).
56671d51719SMichael Gmelin 	 */
56771d51719SMichael Gmelin 	reg_write(sc, IG4_REG_SS_SCL_HCNT, 100);
56871d51719SMichael Gmelin 	reg_write(sc, IG4_REG_SS_SCL_LCNT, 125);
56971d51719SMichael Gmelin 	reg_write(sc, IG4_REG_FS_SCL_HCNT, 100);
57071d51719SMichael Gmelin 	reg_write(sc, IG4_REG_FS_SCL_LCNT, 125);
5713ca6000fSVladimir Kondratyev 	if (sc->version == IG4_SKYLAKE)
5723ca6000fSVladimir Kondratyev 		reg_write(sc, IG4_REG_SDA_HOLD, 28);
57371d51719SMichael Gmelin 
57471d51719SMichael Gmelin 	/*
57571d51719SMichael Gmelin 	 * Use a threshold of 1 so we get interrupted on each character,
57671d51719SMichael Gmelin 	 * allowing us to use mtx_sleep() in our poll code.  Not perfect
57771d51719SMichael Gmelin 	 * but this is better than using DELAY() for receiving data.
5784cd6abddSMichael Gmelin 	 *
5794cd6abddSMichael Gmelin 	 * See ig4_var.h for details on interrupt handler synchronization.
58071d51719SMichael Gmelin 	 */
581811ff4ddSVladimir Kondratyev 	reg_write(sc, IG4_REG_RX_TL, 0);
58271d51719SMichael Gmelin 
58371d51719SMichael Gmelin 	reg_write(sc, IG4_REG_CTL,
58471d51719SMichael Gmelin 		  IG4_CTL_MASTER |
58571d51719SMichael Gmelin 		  IG4_CTL_SLAVE_DISABLE |
58671d51719SMichael Gmelin 		  IG4_CTL_RESTARTEN |
58771d51719SMichael Gmelin 		  IG4_CTL_SPEED_STD);
58871d51719SMichael Gmelin 
589448897d3SAndriy Gapon 	sc->iicbus = device_add_child(sc->dev, "iicbus", -1);
590448897d3SAndriy Gapon 	if (sc->iicbus == NULL) {
591448897d3SAndriy Gapon 		device_printf(sc->dev, "iicbus driver not found\n");
59271d51719SMichael Gmelin 		error = ENXIO;
59371d51719SMichael Gmelin 		goto done;
59471d51719SMichael Gmelin 	}
59571d51719SMichael Gmelin 
59671d51719SMichael Gmelin #if 0
59771d51719SMichael Gmelin 	/*
59871d51719SMichael Gmelin 	 * Don't do this, it blows up the PCI config
59971d51719SMichael Gmelin 	 */
600b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
601b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
602b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
603b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version = IG4_SKYLAKE) {
604b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
605b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
606b3e8ee5dSOleksandr Tymoshenko 	}
60771d51719SMichael Gmelin #endif
60871d51719SMichael Gmelin 
609bf9c3c58SVladimir Kondratyev 	if (set_controller(sc, IG4_I2C_ENABLE)) {
61071d51719SMichael Gmelin 		device_printf(sc->dev, "controller error during attach-2\n");
611bf9c3c58SVladimir Kondratyev 		error = ENXIO;
612bf9c3c58SVladimir Kondratyev 		goto done;
613bf9c3c58SVladimir Kondratyev 	}
614edcf6a9fSVladimir Kondratyev 	if (set_controller(sc, 0)) {
615edcf6a9fSVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-3\n");
616edcf6a9fSVladimir Kondratyev 		error = ENXIO;
617edcf6a9fSVladimir Kondratyev 		goto done;
618edcf6a9fSVladimir Kondratyev 	}
61971d51719SMichael Gmelin 	error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
62071d51719SMichael Gmelin 			       NULL, ig4iic_intr, sc, &sc->intr_handle);
62171d51719SMichael Gmelin 	if (error) {
62271d51719SMichael Gmelin 		device_printf(sc->dev,
62371d51719SMichael Gmelin 			      "Unable to setup irq: error %d\n", error);
62471d51719SMichael Gmelin 	}
62571d51719SMichael Gmelin 
62671d51719SMichael Gmelin 	sc->enum_hook.ich_func = ig4iic_start;
62771d51719SMichael Gmelin 	sc->enum_hook.ich_arg = sc->dev;
62871d51719SMichael Gmelin 
6290ba5622dSMichael Gmelin 	/*
6300ba5622dSMichael Gmelin 	 * We have to wait until interrupts are enabled. I2C read and write
63171d51719SMichael Gmelin 	 * only works if the interrupts are available.
63271d51719SMichael Gmelin 	 */
63371d51719SMichael Gmelin 	if (config_intrhook_establish(&sc->enum_hook) != 0)
63471d51719SMichael Gmelin 		error = ENOMEM;
63571d51719SMichael Gmelin 	else
63671d51719SMichael Gmelin 		error = 0;
63771d51719SMichael Gmelin 
63871d51719SMichael Gmelin done:
63971d51719SMichael Gmelin 	return (error);
64071d51719SMichael Gmelin }
64171d51719SMichael Gmelin 
64271d51719SMichael Gmelin void
64371d51719SMichael Gmelin ig4iic_start(void *xdev)
64471d51719SMichael Gmelin {
64571d51719SMichael Gmelin 	int error;
64671d51719SMichael Gmelin 	ig4iic_softc_t *sc;
64771d51719SMichael Gmelin 	device_t dev = (device_t)xdev;
64871d51719SMichael Gmelin 
64971d51719SMichael Gmelin 	sc = device_get_softc(dev);
65071d51719SMichael Gmelin 
65171d51719SMichael Gmelin 	config_intrhook_disestablish(&sc->enum_hook);
65271d51719SMichael Gmelin 
65371d51719SMichael Gmelin 	error = bus_generic_attach(sc->dev);
65471d51719SMichael Gmelin 	if (error) {
65571d51719SMichael Gmelin 		device_printf(sc->dev,
65671d51719SMichael Gmelin 			      "failed to attach child: error %d\n", error);
65771d51719SMichael Gmelin 	}
65871d51719SMichael Gmelin }
65971d51719SMichael Gmelin 
66071d51719SMichael Gmelin int
66171d51719SMichael Gmelin ig4iic_detach(ig4iic_softc_t *sc)
66271d51719SMichael Gmelin {
66371d51719SMichael Gmelin 	int error;
66471d51719SMichael Gmelin 
66571d51719SMichael Gmelin 	if (device_is_attached(sc->dev)) {
66671d51719SMichael Gmelin 		error = bus_generic_detach(sc->dev);
66771d51719SMichael Gmelin 		if (error)
66871d51719SMichael Gmelin 			return (error);
66971d51719SMichael Gmelin 	}
670448897d3SAndriy Gapon 	if (sc->iicbus)
671448897d3SAndriy Gapon 		device_delete_child(sc->dev, sc->iicbus);
67271d51719SMichael Gmelin 	if (sc->intr_handle)
67371d51719SMichael Gmelin 		bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
67471d51719SMichael Gmelin 
6754cd6abddSMichael Gmelin 	sx_xlock(&sc->call_lock);
67671d51719SMichael Gmelin 
677448897d3SAndriy Gapon 	sc->iicbus = NULL;
67871d51719SMichael Gmelin 	sc->intr_handle = NULL;
67971d51719SMichael Gmelin 	reg_write(sc, IG4_REG_INTR_MASK, 0);
68071d51719SMichael Gmelin 	set_controller(sc, 0);
68171d51719SMichael Gmelin 
6824cd6abddSMichael Gmelin 	sx_xunlock(&sc->call_lock);
6835c5bcb1dSOleksandr Tymoshenko 
6845c5bcb1dSOleksandr Tymoshenko 	mtx_destroy(&sc->io_lock);
6855c5bcb1dSOleksandr Tymoshenko 	sx_destroy(&sc->call_lock);
6865c5bcb1dSOleksandr Tymoshenko 
68771d51719SMichael Gmelin 	return (0);
68871d51719SMichael Gmelin }
68971d51719SMichael Gmelin 
69071d51719SMichael Gmelin /*
6914cd6abddSMichael Gmelin  * Interrupt Operation, see ig4_var.h for locking semantics.
69271d51719SMichael Gmelin  */
69371d51719SMichael Gmelin static void
69471d51719SMichael Gmelin ig4iic_intr(void *cookie)
69571d51719SMichael Gmelin {
69671d51719SMichael Gmelin 	ig4iic_softc_t *sc = cookie;
69771d51719SMichael Gmelin 
6984cd6abddSMichael Gmelin 	mtx_lock(&sc->io_lock);
6990a6b1b56SVladimir Kondratyev 	/* Ignore stray interrupts */
7000a6b1b56SVladimir Kondratyev 	if (sc->intr_mask != 0 && reg_read(sc, IG4_REG_INTR_STAT) != 0) {
70121e459c6SVladimir Kondratyev 		set_intr_mask(sc, 0);
7020ba5622dSMichael Gmelin 		reg_read(sc, IG4_REG_CLR_INTR);
70371d51719SMichael Gmelin 		wakeup(sc);
7040a6b1b56SVladimir Kondratyev 	}
7054cd6abddSMichael Gmelin 	mtx_unlock(&sc->io_lock);
70671d51719SMichael Gmelin }
70771d51719SMichael Gmelin 
70871d51719SMichael Gmelin #define REGDUMP(sc, reg)	\
70971d51719SMichael Gmelin 	device_printf(sc->dev, "  %-23s %08x\n", #reg, reg_read(sc, reg))
71071d51719SMichael Gmelin 
71171d51719SMichael Gmelin static void
71271d51719SMichael Gmelin ig4iic_dump(ig4iic_softc_t *sc)
71371d51719SMichael Gmelin {
71471d51719SMichael Gmelin 	device_printf(sc->dev, "ig4iic register dump:\n");
71571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_CTL);
71671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TAR_ADD);
71771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_HCNT);
71871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_LCNT);
71971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_HCNT);
72071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_LCNT);
72171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_STAT);
72271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_MASK);
72371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RAW_INTR_STAT);
72471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RX_TL);
72571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_TL);
72671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_EN);
72771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_STA);
72871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TXFLR);
72971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RXFLR);
73071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_HOLD);
73171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE);
73271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SLV_DATA_NACK);
73371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_CTRL);
73471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_TDLR);
73571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_RDLR);
73671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_SETUP);
73771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_ENABLE_STATUS);
738b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
73971d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_COMP_PARAM1);
74071d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_COMP_VER);
741b3e8ee5dSOleksandr Tymoshenko 	}
742b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM) {
74371d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_COMP_TYPE);
74471d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_CLK_PARMS);
745b3e8ee5dSOleksandr Tymoshenko 	}
746b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
747b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_HSW);
74871d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_GENERAL);
749b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
750b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_SKL);
751b3e8ee5dSOleksandr Tymoshenko 	}
752b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
75371d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_SW_LTR_VALUE);
75471d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE);
755b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
756b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE);
757b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE);
758b3e8ee5dSOleksandr Tymoshenko 	}
75971d51719SMichael Gmelin }
76071d51719SMichael Gmelin #undef REGDUMP
76171d51719SMichael Gmelin 
762984ed3e4SVladimir Kondratyev devclass_t ig4iic_devclass;
763984ed3e4SVladimir Kondratyev 
764984ed3e4SVladimir Kondratyev DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL);
765984ed3e4SVladimir Kondratyev MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
766984ed3e4SVladimir Kondratyev MODULE_VERSION(ig4iic, 1);
767