xref: /freebsd/sys/dev/ichiic/ig4_iic.c (revision 41b24e09)
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