xref: /freebsd/sys/dev/ichiic/ig4_iic.c (revision b0eb9d3e)
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 
4688512838SVladimir Kondratyev #include "opt_acpi.h"
4788512838SVladimir Kondratyev 
4871d51719SMichael Gmelin #include <sys/param.h>
4971d51719SMichael Gmelin #include <sys/systm.h>
5071d51719SMichael Gmelin #include <sys/kernel.h>
5171d51719SMichael Gmelin #include <sys/module.h>
5271d51719SMichael Gmelin #include <sys/errno.h>
53c59aca57SVladimir Kondratyev #include <sys/kdb.h>
5471d51719SMichael Gmelin #include <sys/lock.h>
5571d51719SMichael Gmelin #include <sys/mutex.h>
56c59aca57SVladimir Kondratyev #include <sys/proc.h>
574cd6abddSMichael Gmelin #include <sys/sx.h>
5871d51719SMichael Gmelin #include <sys/syslog.h>
5971d51719SMichael Gmelin #include <sys/bus.h>
6071d51719SMichael Gmelin #include <sys/sysctl.h>
6171d51719SMichael Gmelin 
6271d51719SMichael Gmelin #include <machine/bus.h>
6371d51719SMichael Gmelin #include <sys/rman.h>
6471d51719SMichael Gmelin 
6588512838SVladimir Kondratyev #ifdef DEV_ACPI
6688512838SVladimir Kondratyev #include <contrib/dev/acpica/include/acpi.h>
6788512838SVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h>
6888512838SVladimir Kondratyev #include <dev/acpica/acpivar.h>
6988512838SVladimir Kondratyev #endif
7088512838SVladimir Kondratyev 
71448897d3SAndriy Gapon #include <dev/iicbus/iicbus.h>
72448897d3SAndriy Gapon #include <dev/iicbus/iiconf.h>
7371d51719SMichael Gmelin 
7471d51719SMichael Gmelin #include <dev/ichiic/ig4_reg.h>
7571d51719SMichael Gmelin #include <dev/ichiic/ig4_var.h>
7671d51719SMichael Gmelin 
7741b24e09SVladimir Kondratyev #define DO_POLL(sc)	(cold || kdb_active || SCHEDULER_STOPPED() || sc->poll)
78c59aca57SVladimir Kondratyev 
7988512838SVladimir Kondratyev /*
8088512838SVladimir Kondratyev  * tLOW, tHIGH periods of the SCL clock and maximal falling time of both
8188512838SVladimir Kondratyev  * lines are taken from I2C specifications.
8288512838SVladimir Kondratyev  */
8388512838SVladimir Kondratyev #define	IG4_SPEED_STD_THIGH	4000	/* nsec */
8488512838SVladimir Kondratyev #define	IG4_SPEED_STD_TLOW	4700	/* nsec */
8588512838SVladimir Kondratyev #define	IG4_SPEED_STD_TF_MAX	300	/* nsec */
8688512838SVladimir Kondratyev #define	IG4_SPEED_FAST_THIGH	600	/* nsec */
8788512838SVladimir Kondratyev #define	IG4_SPEED_FAST_TLOW	1300	/* nsec */
8888512838SVladimir Kondratyev #define	IG4_SPEED_FAST_TF_MAX	300	/* nsec */
8988512838SVladimir Kondratyev 
9088512838SVladimir Kondratyev /*
9188512838SVladimir Kondratyev  * Ig4 hardware parameters except Haswell are taken from intel_lpss driver
9288512838SVladimir Kondratyev  */
9388512838SVladimir Kondratyev static const struct ig4_hw ig4iic_hw[] = {
9488512838SVladimir Kondratyev 	[IG4_HASWELL] = {
9588512838SVladimir Kondratyev 		.ic_clock_rate = 100,	/* MHz */
9688512838SVladimir Kondratyev 		.sda_hold_time = 90,	/* nsec */
9783a66b9bSVladimir Kondratyev 		.txfifo_depth = 32,
9883a66b9bSVladimir Kondratyev 		.rxfifo_depth = 32,
9988512838SVladimir Kondratyev 	},
10088512838SVladimir Kondratyev 	[IG4_ATOM] = {
10188512838SVladimir Kondratyev 		.ic_clock_rate = 100,
10288512838SVladimir Kondratyev 		.sda_fall_time = 280,
10388512838SVladimir Kondratyev 		.scl_fall_time = 240,
10488512838SVladimir Kondratyev 		.sda_hold_time = 60,
10583a66b9bSVladimir Kondratyev 		.txfifo_depth = 32,
10683a66b9bSVladimir Kondratyev 		.rxfifo_depth = 32,
10788512838SVladimir Kondratyev 	},
10888512838SVladimir Kondratyev 	[IG4_SKYLAKE] = {
10988512838SVladimir Kondratyev 		.ic_clock_rate = 120,
11088512838SVladimir Kondratyev 		.sda_hold_time = 230,
11188512838SVladimir Kondratyev 	},
11288512838SVladimir Kondratyev 	[IG4_APL] = {
11388512838SVladimir Kondratyev 		.ic_clock_rate = 133,
11488512838SVladimir Kondratyev 		.sda_fall_time = 171,
11588512838SVladimir Kondratyev 		.scl_fall_time = 208,
11688512838SVladimir Kondratyev 		.sda_hold_time = 207,
11788512838SVladimir Kondratyev 	},
11888512838SVladimir Kondratyev };
11988512838SVladimir Kondratyev 
12071d51719SMichael Gmelin static void ig4iic_intr(void *cookie);
12171d51719SMichael Gmelin static void ig4iic_dump(ig4iic_softc_t *sc);
12271d51719SMichael Gmelin 
12371d51719SMichael Gmelin static int ig4_dump;
12412e413beSMichael Gmelin SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW,
12512e413beSMichael Gmelin 	   &ig4_dump, 0, "Dump controller registers");
12671d51719SMichael Gmelin 
12771d51719SMichael Gmelin /*
12888512838SVladimir Kondratyev  * Clock registers initialization control
12988512838SVladimir Kondratyev  * 0 - Try read clock registers from ACPI and fallback to p.1.
13088512838SVladimir Kondratyev  * 1 - Calculate values based on controller type (IC clock rate).
13188512838SVladimir Kondratyev  * 2 - Use values inherited from DragonflyBSD driver (old behavior).
13288512838SVladimir Kondratyev  * 3 - Keep clock registers intact.
13388512838SVladimir Kondratyev  */
13488512838SVladimir Kondratyev static int ig4_timings;
13588512838SVladimir Kondratyev SYSCTL_INT(_debug, OID_AUTO, ig4_timings, CTLFLAG_RDTUN, &ig4_timings, 0,
13688512838SVladimir Kondratyev     "Controller timings 0=ACPI, 1=predefined, 2=legacy, 3=do not change");
13788512838SVladimir Kondratyev 
13888512838SVladimir Kondratyev /*
13971d51719SMichael Gmelin  * Low-level inline support functions
14071d51719SMichael Gmelin  */
14171d51719SMichael Gmelin static __inline void
14271d51719SMichael Gmelin reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value)
14371d51719SMichael Gmelin {
14471d51719SMichael Gmelin 	bus_write_4(sc->regs_res, reg, value);
14571d51719SMichael Gmelin 	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE);
14671d51719SMichael Gmelin }
14771d51719SMichael Gmelin 
14871d51719SMichael Gmelin static __inline uint32_t
14971d51719SMichael Gmelin reg_read(ig4iic_softc_t *sc, uint32_t reg)
15071d51719SMichael Gmelin {
15171d51719SMichael Gmelin 	uint32_t value;
15271d51719SMichael Gmelin 
15371d51719SMichael Gmelin 	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ);
15471d51719SMichael Gmelin 	value = bus_read_4(sc->regs_res, reg);
15571d51719SMichael Gmelin 	return (value);
15671d51719SMichael Gmelin }
15771d51719SMichael Gmelin 
15821e459c6SVladimir Kondratyev static void
15921e459c6SVladimir Kondratyev set_intr_mask(ig4iic_softc_t *sc, uint32_t val)
16021e459c6SVladimir Kondratyev {
16121e459c6SVladimir Kondratyev 	if (sc->intr_mask != val) {
16221e459c6SVladimir Kondratyev 		reg_write(sc, IG4_REG_INTR_MASK, val);
16321e459c6SVladimir Kondratyev 		sc->intr_mask = val;
16421e459c6SVladimir Kondratyev 	}
16521e459c6SVladimir Kondratyev }
16621e459c6SVladimir Kondratyev 
16771d51719SMichael Gmelin /*
16871d51719SMichael Gmelin  * Enable or disable the controller and wait for the controller to acknowledge
16971d51719SMichael Gmelin  * the state change.
17071d51719SMichael Gmelin  */
17171d51719SMichael Gmelin static int
17271d51719SMichael Gmelin set_controller(ig4iic_softc_t *sc, uint32_t ctl)
17371d51719SMichael Gmelin {
17471d51719SMichael Gmelin 	int retry;
17571d51719SMichael Gmelin 	int error;
17671d51719SMichael Gmelin 	uint32_t v;
17771d51719SMichael Gmelin 
1780ba5622dSMichael Gmelin 	/*
1790ba5622dSMichael Gmelin 	 * When the controller is enabled, interrupt on STOP detect
1800ba5622dSMichael Gmelin 	 * or receive character ready and clear pending interrupts.
1810ba5622dSMichael Gmelin 	 */
18221e459c6SVladimir Kondratyev 	set_intr_mask(sc, 0);
18321e459c6SVladimir Kondratyev 	if (ctl & IG4_I2C_ENABLE)
1840ba5622dSMichael Gmelin 		reg_read(sc, IG4_REG_CLR_INTR);
1850ba5622dSMichael Gmelin 
18671d51719SMichael Gmelin 	reg_write(sc, IG4_REG_I2C_EN, ctl);
187448897d3SAndriy Gapon 	error = IIC_ETIMEOUT;
18871d51719SMichael Gmelin 
18971d51719SMichael Gmelin 	for (retry = 100; retry > 0; --retry) {
19071d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_ENABLE_STATUS);
19171d51719SMichael Gmelin 		if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) {
19271d51719SMichael Gmelin 			error = 0;
19371d51719SMichael Gmelin 			break;
19471d51719SMichael Gmelin 		}
19585cd895fSVladimir Kondratyev 		pause("i2cslv", 1);
19671d51719SMichael Gmelin 	}
19771d51719SMichael Gmelin 	return (error);
19871d51719SMichael Gmelin }
19971d51719SMichael Gmelin 
20071d51719SMichael Gmelin /*
201b0eb9d3eSVladimir Kondratyev  * Wait up to 25ms for the requested interrupt using a 25uS polling loop.
20271d51719SMichael Gmelin  */
20371d51719SMichael Gmelin static int
204b0eb9d3eSVladimir Kondratyev wait_intr(ig4iic_softc_t *sc, uint32_t intr)
20571d51719SMichael Gmelin {
20671d51719SMichael Gmelin 	uint32_t v;
20771d51719SMichael Gmelin 	int error;
20871d51719SMichael Gmelin 	int txlvl = -1;
20971d51719SMichael Gmelin 	u_int count_us = 0;
21071d51719SMichael Gmelin 	u_int limit_us = 25000; /* 25ms */
21171d51719SMichael Gmelin 
212448897d3SAndriy Gapon 	error = IIC_ETIMEOUT;
21371d51719SMichael Gmelin 
21471d51719SMichael Gmelin 	for (;;) {
21571d51719SMichael Gmelin 		/*
21671d51719SMichael Gmelin 		 * Check requested status
21771d51719SMichael Gmelin 		 */
218b0eb9d3eSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_RAW_INTR_STAT);
219b0eb9d3eSVladimir Kondratyev 		if (v & intr) {
22071d51719SMichael Gmelin 			error = 0;
22171d51719SMichael Gmelin 			break;
22271d51719SMichael Gmelin 		}
22371d51719SMichael Gmelin 
22471d51719SMichael Gmelin 		/*
22571d51719SMichael Gmelin 		 * When waiting for the transmit FIFO to become empty,
22671d51719SMichael Gmelin 		 * reset the timeout if we see a change in the transmit
22771d51719SMichael Gmelin 		 * FIFO level as progress is being made.
22871d51719SMichael Gmelin 		 */
229b0eb9d3eSVladimir Kondratyev 		if (intr & IG4_INTR_TX_EMPTY) {
23071d51719SMichael Gmelin 			v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK;
23171d51719SMichael Gmelin 			if (txlvl != v) {
23271d51719SMichael Gmelin 				txlvl = v;
23371d51719SMichael Gmelin 				count_us = 0;
23471d51719SMichael Gmelin 			}
23571d51719SMichael Gmelin 		}
23671d51719SMichael Gmelin 
23771d51719SMichael Gmelin 		/*
23871d51719SMichael Gmelin 		 * Stop if we've run out of time.
23971d51719SMichael Gmelin 		 */
24071d51719SMichael Gmelin 		if (count_us >= limit_us)
24171d51719SMichael Gmelin 			break;
24271d51719SMichael Gmelin 
24371d51719SMichael Gmelin 		/*
244b0eb9d3eSVladimir Kondratyev 		 * When polling is not requested let the interrupt do its work.
24571d51719SMichael Gmelin 		 */
246b0eb9d3eSVladimir Kondratyev 		if (!DO_POLL(sc)) {
247733d657aSVladimir Kondratyev 			mtx_lock(&sc->io_lock);
248b0eb9d3eSVladimir Kondratyev 			set_intr_mask(sc, intr);
2497f6aee64SVladimir Kondratyev 			mtx_sleep(sc, &sc->io_lock, 0, "i2cwait",
2507f6aee64SVladimir Kondratyev 				  (hz + 99) / 100); /* sleep up to 10ms */
2517f6aee64SVladimir Kondratyev 			set_intr_mask(sc, 0);
2527f6aee64SVladimir Kondratyev 			mtx_unlock(&sc->io_lock);
2537f6aee64SVladimir Kondratyev 			count_us += 10000;
25471d51719SMichael Gmelin 		} else {
25571d51719SMichael Gmelin 			DELAY(25);
25671d51719SMichael Gmelin 			count_us += 25;
25771d51719SMichael Gmelin 		}
25871d51719SMichael Gmelin 	}
25971d51719SMichael Gmelin 
26071d51719SMichael Gmelin 	return (error);
26171d51719SMichael Gmelin }
26271d51719SMichael Gmelin 
26371d51719SMichael Gmelin /*
26471d51719SMichael Gmelin  * Set the slave address.  The controller must be disabled when
26571d51719SMichael Gmelin  * changing the address.
26671d51719SMichael Gmelin  *
26771d51719SMichael Gmelin  * This operation does not issue anything to the I2C bus but sets
26871d51719SMichael Gmelin  * the target address for when the controller later issues a START.
26971d51719SMichael Gmelin  */
27071d51719SMichael Gmelin static void
271e3d25549SAndriy Gapon set_slave_addr(ig4iic_softc_t *sc, uint8_t slave)
27271d51719SMichael Gmelin {
27371d51719SMichael Gmelin 	uint32_t tar;
27471d51719SMichael Gmelin 	uint32_t ctl;
27571d51719SMichael Gmelin 	int use_10bit;
27671d51719SMichael Gmelin 
27771d51719SMichael Gmelin 	use_10bit = 0;
27871d51719SMichael Gmelin 	if (sc->slave_valid && sc->last_slave == slave &&
27971d51719SMichael Gmelin 	    sc->use_10bit == use_10bit) {
28071d51719SMichael Gmelin 		return;
28171d51719SMichael Gmelin 	}
28271d51719SMichael Gmelin 	sc->use_10bit = use_10bit;
28371d51719SMichael Gmelin 
28471d51719SMichael Gmelin 	/*
28571d51719SMichael Gmelin 	 * Wait for TXFIFO to drain before disabling the controller.
28671d51719SMichael Gmelin 	 */
287b0eb9d3eSVladimir Kondratyev 	wait_intr(sc, IG4_INTR_TX_EMPTY);
28871d51719SMichael Gmelin 
28971d51719SMichael Gmelin 	set_controller(sc, 0);
29071d51719SMichael Gmelin 	ctl = reg_read(sc, IG4_REG_CTL);
29171d51719SMichael Gmelin 	ctl &= ~IG4_CTL_10BIT;
29271d51719SMichael Gmelin 	ctl |= IG4_CTL_RESTARTEN;
29371d51719SMichael Gmelin 
29471d51719SMichael Gmelin 	tar = slave;
29571d51719SMichael Gmelin 	if (sc->use_10bit) {
29671d51719SMichael Gmelin 		tar |= IG4_TAR_10BIT;
29771d51719SMichael Gmelin 		ctl |= IG4_CTL_10BIT;
29871d51719SMichael Gmelin 	}
29971d51719SMichael Gmelin 	reg_write(sc, IG4_REG_CTL, ctl);
30071d51719SMichael Gmelin 	reg_write(sc, IG4_REG_TAR_ADD, tar);
30171d51719SMichael Gmelin 	set_controller(sc, IG4_I2C_ENABLE);
30271d51719SMichael Gmelin 	sc->slave_valid = 1;
30371d51719SMichael Gmelin 	sc->last_slave = slave;
30471d51719SMichael Gmelin }
30571d51719SMichael Gmelin 
30671d51719SMichael Gmelin /*
307448897d3SAndriy Gapon  *				IICBUS API FUNCTIONS
308448897d3SAndriy Gapon  */
309448897d3SAndriy Gapon static int
310448897d3SAndriy Gapon ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave)
311448897d3SAndriy Gapon {
312e3d25549SAndriy Gapon 	set_slave_addr(sc, slave >> 1);
313448897d3SAndriy Gapon 	return (0);
314448897d3SAndriy Gapon }
315448897d3SAndriy Gapon 
31683a66b9bSVladimir Kondratyev /*
31783a66b9bSVladimir Kondratyev  * Amount of unread data before next burst to get better I2C bus utilization.
31883a66b9bSVladimir Kondratyev  * 2 bytes is enough in FAST mode. 8 bytes is better in FAST+ and HIGH modes.
31983a66b9bSVladimir Kondratyev  * Intel-recommended value is 16 for DMA transfers with 64-byte depth FIFOs.
32083a66b9bSVladimir Kondratyev  */
32183a66b9bSVladimir Kondratyev #define	IG4_FIFO_LOWAT	2
32283a66b9bSVladimir Kondratyev 
323448897d3SAndriy Gapon static int
324448897d3SAndriy Gapon ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len,
325448897d3SAndriy Gapon     bool repeated_start, bool stop)
326448897d3SAndriy Gapon {
327448897d3SAndriy Gapon 	uint32_t cmd;
32883a66b9bSVladimir Kondratyev 	int requested = 0;
32983a66b9bSVladimir Kondratyev 	int received = 0;
33083a66b9bSVladimir Kondratyev 	int burst, target, lowat = 0;
331448897d3SAndriy Gapon 	int error;
332448897d3SAndriy Gapon 
333448897d3SAndriy Gapon 	if (len == 0)
334448897d3SAndriy Gapon 		return (0);
335448897d3SAndriy Gapon 
33683a66b9bSVladimir Kondratyev 	while (received < len) {
33783a66b9bSVladimir Kondratyev 		burst = sc->cfg.txfifo_depth -
33883a66b9bSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK);
33983a66b9bSVladimir Kondratyev 		if (burst <= 0) {
340b0eb9d3eSVladimir Kondratyev 			error = wait_intr(sc, IG4_INTR_TX_EMPTY);
341448897d3SAndriy Gapon 			if (error)
342448897d3SAndriy Gapon 				break;
3437814f978SVladimir Kondratyev 			burst = sc->cfg.txfifo_depth;
344448897d3SAndriy Gapon 		}
3457814f978SVladimir Kondratyev 		/* Ensure we have enough free space in RXFIFO */
3467814f978SVladimir Kondratyev 		burst = MIN(burst, sc->cfg.rxfifo_depth - lowat);
34783a66b9bSVladimir Kondratyev 		target = MIN(requested + burst, (int)len);
34883a66b9bSVladimir Kondratyev 		while (requested < target) {
34983a66b9bSVladimir Kondratyev 			cmd = IG4_DATA_COMMAND_RD;
35083a66b9bSVladimir Kondratyev 			if (repeated_start && requested == 0)
35183a66b9bSVladimir Kondratyev 				cmd |= IG4_DATA_RESTART;
35283a66b9bSVladimir Kondratyev 			if (stop && requested == len - 1)
35383a66b9bSVladimir Kondratyev 				cmd |= IG4_DATA_STOP;
35483a66b9bSVladimir Kondratyev 			reg_write(sc, IG4_REG_DATA_CMD, cmd);
35583a66b9bSVladimir Kondratyev 			requested++;
35683a66b9bSVladimir Kondratyev 		}
35783a66b9bSVladimir Kondratyev 		/* Leave some data queued to maintain the hardware pipeline */
35883a66b9bSVladimir Kondratyev 		lowat = 0;
35983a66b9bSVladimir Kondratyev 		if (requested != len && requested - received > IG4_FIFO_LOWAT)
36083a66b9bSVladimir Kondratyev 			lowat = IG4_FIFO_LOWAT;
36183a66b9bSVladimir Kondratyev 		/* After TXFLR fills up, clear it by reading available data */
36283a66b9bSVladimir Kondratyev 		while (received < requested - lowat) {
36383a66b9bSVladimir Kondratyev 			burst = MIN((int)len - received,
36483a66b9bSVladimir Kondratyev 			    reg_read(sc, IG4_REG_RXFLR) & IG4_FIFOLVL_MASK);
36583a66b9bSVladimir Kondratyev 			if (burst > 0) {
36683a66b9bSVladimir Kondratyev 				while (burst--)
36783a66b9bSVladimir Kondratyev 					buf[received++] = 0xFF &
36883a66b9bSVladimir Kondratyev 					    reg_read(sc, IG4_REG_DATA_CMD);
36983a66b9bSVladimir Kondratyev 			} else {
370b0eb9d3eSVladimir Kondratyev 				error = wait_intr(sc, IG4_INTR_RX_FULL);
37183a66b9bSVladimir Kondratyev 				if (error)
37283a66b9bSVladimir Kondratyev 					goto out;
37383a66b9bSVladimir Kondratyev 			}
37483a66b9bSVladimir Kondratyev 		}
37583a66b9bSVladimir Kondratyev 	}
37683a66b9bSVladimir Kondratyev out:
377448897d3SAndriy Gapon 	(void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
378448897d3SAndriy Gapon 	return (error);
379448897d3SAndriy Gapon }
380448897d3SAndriy Gapon 
381448897d3SAndriy Gapon static int
382448897d3SAndriy Gapon ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len,
383448897d3SAndriy Gapon     bool repeated_start, bool stop)
384448897d3SAndriy Gapon {
385448897d3SAndriy Gapon 	uint32_t cmd;
386023c42edSVladimir Kondratyev 	int sent = 0;
387023c42edSVladimir Kondratyev 	int burst, target;
388448897d3SAndriy Gapon 	int error;
389448897d3SAndriy Gapon 
390448897d3SAndriy Gapon 	if (len == 0)
391448897d3SAndriy Gapon 		return (0);
392448897d3SAndriy Gapon 
393023c42edSVladimir Kondratyev 	while (sent < len) {
394023c42edSVladimir Kondratyev 		burst = sc->cfg.txfifo_depth -
395023c42edSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK);
396023c42edSVladimir Kondratyev 		target = MIN(sent + burst, (int)len);
397023c42edSVladimir Kondratyev 		while(sent < target) {
398023c42edSVladimir Kondratyev 			cmd = buf[sent];
399023c42edSVladimir Kondratyev 			if (repeated_start && sent == 0)
400023c42edSVladimir Kondratyev 				cmd |= IG4_DATA_RESTART;
401023c42edSVladimir Kondratyev 			if (stop && sent == len - 1)
402023c42edSVladimir Kondratyev 				cmd |= IG4_DATA_STOP;
403023c42edSVladimir Kondratyev 			reg_write(sc, IG4_REG_DATA_CMD, cmd);
404023c42edSVladimir Kondratyev 			sent++;
405023c42edSVladimir Kondratyev 		}
406023c42edSVladimir Kondratyev 		if (sent < len) {
407b0eb9d3eSVladimir Kondratyev 			error = wait_intr(sc, IG4_INTR_TX_EMPTY);
408448897d3SAndriy Gapon 			if (error)
409448897d3SAndriy Gapon 				break;
410023c42edSVladimir Kondratyev 		}
411448897d3SAndriy Gapon 	}
412448897d3SAndriy Gapon 
413448897d3SAndriy Gapon 	(void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
414448897d3SAndriy Gapon 	return (error);
415448897d3SAndriy Gapon }
416448897d3SAndriy Gapon 
417448897d3SAndriy Gapon int
418448897d3SAndriy Gapon ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
419448897d3SAndriy Gapon {
420448897d3SAndriy Gapon 	ig4iic_softc_t *sc = device_get_softc(dev);
421448897d3SAndriy Gapon 	const char *reason = NULL;
422448897d3SAndriy Gapon 	uint32_t i;
423448897d3SAndriy Gapon 	int error;
424448897d3SAndriy Gapon 	int unit;
425448897d3SAndriy Gapon 	bool rpstart;
426448897d3SAndriy Gapon 	bool stop;
42741b24e09SVladimir Kondratyev 	bool allocated;
428448897d3SAndriy Gapon 
429448897d3SAndriy Gapon 	/*
430448897d3SAndriy Gapon 	 * The hardware interface imposes limits on allowed I2C messages.
431448897d3SAndriy Gapon 	 * It is not possible to explicitly send a start or stop.
432448897d3SAndriy Gapon 	 * They are automatically sent (or not sent, depending on the
433448897d3SAndriy Gapon 	 * configuration) when a data byte is transferred.
434448897d3SAndriy Gapon 	 * For this reason it's impossible to send a message with no data
435448897d3SAndriy Gapon 	 * at all (like an SMBus quick message).
436448897d3SAndriy Gapon 	 * The start condition is automatically generated after the stop
437448897d3SAndriy Gapon 	 * condition, so it's impossible to not have a start after a stop.
438448897d3SAndriy Gapon 	 * The repeated start condition is automatically sent if a change
439448897d3SAndriy Gapon 	 * of the transfer direction happens, so it's impossible to have
440448897d3SAndriy Gapon 	 * a change of direction without a (repeated) start.
441448897d3SAndriy Gapon 	 * The repeated start can be forced even without the change of
442448897d3SAndriy Gapon 	 * direction.
443448897d3SAndriy Gapon 	 * Changing the target slave address requires resetting the hardware
444448897d3SAndriy Gapon 	 * state, so it's impossible to do that without the stop followed
445448897d3SAndriy Gapon 	 * by the start.
446448897d3SAndriy Gapon 	 */
447448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
448448897d3SAndriy Gapon #if 0
449448897d3SAndriy Gapon 		if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) {
450448897d3SAndriy Gapon 			reason = "first message without start";
451448897d3SAndriy Gapon 			break;
452448897d3SAndriy Gapon 		}
453448897d3SAndriy Gapon 		if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) {
454448897d3SAndriy Gapon 			reason = "last message without stop";
455448897d3SAndriy Gapon 			break;
456448897d3SAndriy Gapon 		}
457448897d3SAndriy Gapon #endif
458448897d3SAndriy Gapon 		if (msgs[i].len == 0) {
459448897d3SAndriy Gapon 			reason = "message with no data";
460448897d3SAndriy Gapon 			break;
461448897d3SAndriy Gapon 		}
462448897d3SAndriy Gapon 		if (i > 0) {
463448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
464448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) {
465448897d3SAndriy Gapon 				reason = "stop not followed by start";
466448897d3SAndriy Gapon 				break;
467448897d3SAndriy Gapon 			}
468448897d3SAndriy Gapon 			if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 &&
469448897d3SAndriy Gapon 			    msgs[i].slave != msgs[i - 1].slave) {
470448897d3SAndriy Gapon 				reason = "change of slave without stop";
471448897d3SAndriy Gapon 				break;
472448897d3SAndriy Gapon 			}
473448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
474448897d3SAndriy Gapon 			    (msgs[i].flags & IIC_M_RD) !=
475448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_RD)) {
476448897d3SAndriy Gapon 				reason = "change of direction without repeated"
477448897d3SAndriy Gapon 				    " start";
478448897d3SAndriy Gapon 				break;
479448897d3SAndriy Gapon 			}
480448897d3SAndriy Gapon 		}
481448897d3SAndriy Gapon 	}
482448897d3SAndriy Gapon 	if (reason != NULL) {
483448897d3SAndriy Gapon 		if (bootverbose)
484448897d3SAndriy Gapon 			device_printf(dev, "%s\n", reason);
485448897d3SAndriy Gapon 		return (IIC_ENOTSUPP);
486448897d3SAndriy Gapon 	}
487448897d3SAndriy Gapon 
48841b24e09SVladimir Kondratyev 	/* Check if device is already allocated with iicbus_request_bus() */
48941b24e09SVladimir Kondratyev 	allocated = sx_xlocked(&sc->call_lock) != 0;
49041b24e09SVladimir Kondratyev 	if (!allocated)
491448897d3SAndriy Gapon 		sx_xlock(&sc->call_lock);
492448897d3SAndriy Gapon 
493448897d3SAndriy Gapon 	/* Debugging - dump registers. */
494448897d3SAndriy Gapon 	if (ig4_dump) {
495448897d3SAndriy Gapon 		unit = device_get_unit(dev);
496448897d3SAndriy Gapon 		if (ig4_dump & (1 << unit)) {
497448897d3SAndriy Gapon 			ig4_dump &= ~(1 << unit);
498448897d3SAndriy Gapon 			ig4iic_dump(sc);
499448897d3SAndriy Gapon 		}
500448897d3SAndriy Gapon 	}
501448897d3SAndriy Gapon 
502448897d3SAndriy Gapon 	/*
503448897d3SAndriy Gapon 	 * Clear any previous abort condition that may have been holding
504448897d3SAndriy Gapon 	 * the txfifo in reset.
505448897d3SAndriy Gapon 	 */
506448897d3SAndriy Gapon 	reg_read(sc, IG4_REG_CLR_TX_ABORT);
507448897d3SAndriy Gapon 
508448897d3SAndriy Gapon 	rpstart = false;
509448897d3SAndriy Gapon 	error = 0;
510448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
511448897d3SAndriy Gapon 		if ((msgs[i].flags & IIC_M_NOSTART) == 0) {
512448897d3SAndriy Gapon 			error = ig4iic_xfer_start(sc, msgs[i].slave);
513448897d3SAndriy Gapon 		} else {
514448897d3SAndriy Gapon 			if (!sc->slave_valid ||
515448897d3SAndriy Gapon 			    (msgs[i].slave >> 1) != sc->last_slave) {
516448897d3SAndriy Gapon 				device_printf(dev, "start condition suppressed"
517448897d3SAndriy Gapon 				    "but slave address is not set up");
518448897d3SAndriy Gapon 				error = EINVAL;
519448897d3SAndriy Gapon 				break;
520448897d3SAndriy Gapon 			}
521448897d3SAndriy Gapon 			rpstart = false;
522448897d3SAndriy Gapon 		}
523448897d3SAndriy Gapon 		if (error != 0)
524448897d3SAndriy Gapon 			break;
525448897d3SAndriy Gapon 
526448897d3SAndriy Gapon 		stop = (msgs[i].flags & IIC_M_NOSTOP) == 0;
527448897d3SAndriy Gapon 		if (msgs[i].flags & IIC_M_RD)
528448897d3SAndriy Gapon 			error = ig4iic_read(sc, msgs[i].buf, msgs[i].len,
529448897d3SAndriy Gapon 			    rpstart, stop);
530448897d3SAndriy Gapon 		else
531448897d3SAndriy Gapon 			error = ig4iic_write(sc, msgs[i].buf, msgs[i].len,
532448897d3SAndriy Gapon 			    rpstart, stop);
533448897d3SAndriy Gapon 		if (error != 0)
534448897d3SAndriy Gapon 			break;
535448897d3SAndriy Gapon 
536448897d3SAndriy Gapon 		rpstart = !stop;
537448897d3SAndriy Gapon 	}
538448897d3SAndriy Gapon 
53941b24e09SVladimir Kondratyev 	if (!allocated)
540448897d3SAndriy Gapon 		sx_unlock(&sc->call_lock);
541448897d3SAndriy Gapon 	return (error);
542448897d3SAndriy Gapon }
543448897d3SAndriy Gapon 
544448897d3SAndriy Gapon int
545448897d3SAndriy Gapon ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
546448897d3SAndriy Gapon {
547448897d3SAndriy Gapon 	ig4iic_softc_t *sc = device_get_softc(dev);
54841b24e09SVladimir Kondratyev 	bool allocated;
549448897d3SAndriy Gapon 
55041b24e09SVladimir Kondratyev 	allocated = sx_xlocked(&sc->call_lock) != 0;
55141b24e09SVladimir Kondratyev 	if (!allocated)
552448897d3SAndriy Gapon 		sx_xlock(&sc->call_lock);
553448897d3SAndriy Gapon 
554448897d3SAndriy Gapon 	/* TODO handle speed configuration? */
555448897d3SAndriy Gapon 	if (oldaddr != NULL)
556448897d3SAndriy Gapon 		*oldaddr = sc->last_slave << 1;
557e3d25549SAndriy Gapon 	set_slave_addr(sc, addr >> 1);
558448897d3SAndriy Gapon 	if (addr == IIC_UNKNOWN)
559448897d3SAndriy Gapon 		sc->slave_valid = false;
560448897d3SAndriy Gapon 
56141b24e09SVladimir Kondratyev 	if (!allocated)
562448897d3SAndriy Gapon 		sx_unlock(&sc->call_lock);
563448897d3SAndriy Gapon 	return (0);
564448897d3SAndriy Gapon }
565448897d3SAndriy Gapon 
56641b24e09SVladimir Kondratyev int
56741b24e09SVladimir Kondratyev ig4iic_callback(device_t dev, int index, caddr_t data)
56841b24e09SVladimir Kondratyev {
56941b24e09SVladimir Kondratyev 	ig4iic_softc_t *sc = device_get_softc(dev);
57041b24e09SVladimir Kondratyev 	int error = 0;
57141b24e09SVladimir Kondratyev 	int how;
57241b24e09SVladimir Kondratyev 
57341b24e09SVladimir Kondratyev 	switch (index) {
57441b24e09SVladimir Kondratyev 	case IIC_REQUEST_BUS:
57541b24e09SVladimir Kondratyev 		/* force polling if ig4iic is requested with IIC_DONTWAIT */
57641b24e09SVladimir Kondratyev 		how = *(int *)data;
57741b24e09SVladimir Kondratyev 		if ((how & IIC_WAIT) == 0) {
57841b24e09SVladimir Kondratyev 			if (sx_try_xlock(&sc->call_lock) == 0)
57941b24e09SVladimir Kondratyev 				error = IIC_EBUSBSY;
58041b24e09SVladimir Kondratyev 			else
58141b24e09SVladimir Kondratyev 				sc->poll = true;
58241b24e09SVladimir Kondratyev 		} else
58341b24e09SVladimir Kondratyev 			sx_xlock(&sc->call_lock);
58441b24e09SVladimir Kondratyev 		break;
58541b24e09SVladimir Kondratyev 
58641b24e09SVladimir Kondratyev 	case IIC_RELEASE_BUS:
58741b24e09SVladimir Kondratyev 		sc->poll = false;
58841b24e09SVladimir Kondratyev 		sx_unlock(&sc->call_lock);
58941b24e09SVladimir Kondratyev 		break;
59041b24e09SVladimir Kondratyev 
59141b24e09SVladimir Kondratyev 	default:
59241b24e09SVladimir Kondratyev 		error = errno2iic(EINVAL);
59341b24e09SVladimir Kondratyev 	}
59441b24e09SVladimir Kondratyev 
59541b24e09SVladimir Kondratyev 	return (error);
59641b24e09SVladimir Kondratyev }
59741b24e09SVladimir Kondratyev 
598448897d3SAndriy Gapon /*
59988512838SVladimir Kondratyev  * Clock register values can be calculated with following rough equations:
60088512838SVladimir Kondratyev  * SCL_HCNT = ceil(IC clock rate * tHIGH)
60188512838SVladimir Kondratyev  * SCL_LCNT = ceil(IC clock rate * tLOW)
60288512838SVladimir Kondratyev  * SDA_HOLD = ceil(IC clock rate * SDA hold time)
60388512838SVladimir Kondratyev  * Precise equations take signal's falling, rising and spike suppression
60488512838SVladimir Kondratyev  * times in to account. They can be found in Synopsys or Intel documentation.
60588512838SVladimir Kondratyev  *
60688512838SVladimir Kondratyev  * Here we snarf formulas and defaults from Linux driver to be able to use
60788512838SVladimir Kondratyev  * timing values provided by Intel LPSS driver "as is".
60888512838SVladimir Kondratyev  */
60988512838SVladimir Kondratyev static int
61088512838SVladimir Kondratyev ig4iic_clk_params(const struct ig4_hw *hw, int speed,
61188512838SVladimir Kondratyev     uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
61288512838SVladimir Kondratyev {
61388512838SVladimir Kondratyev 	uint32_t thigh, tlow, tf_max;	/* nsec */
61488512838SVladimir Kondratyev 	uint32_t sda_fall_time;		/* nsec */
61588512838SVladimir Kondratyev         uint32_t scl_fall_time;		/* nsec */
61688512838SVladimir Kondratyev 
61788512838SVladimir Kondratyev 	switch (speed) {
61888512838SVladimir Kondratyev 	case IG4_CTL_SPEED_STD:
61988512838SVladimir Kondratyev 		thigh = IG4_SPEED_STD_THIGH;
62088512838SVladimir Kondratyev 		tlow = IG4_SPEED_STD_TLOW;
62188512838SVladimir Kondratyev 		tf_max = IG4_SPEED_STD_TF_MAX;
62288512838SVladimir Kondratyev 		break;
62388512838SVladimir Kondratyev 
62488512838SVladimir Kondratyev 	case IG4_CTL_SPEED_FAST:
62588512838SVladimir Kondratyev 		thigh = IG4_SPEED_FAST_THIGH;
62688512838SVladimir Kondratyev 		tlow = IG4_SPEED_FAST_TLOW;
62788512838SVladimir Kondratyev 		tf_max = IG4_SPEED_FAST_TF_MAX;
62888512838SVladimir Kondratyev 		break;
62988512838SVladimir Kondratyev 
63088512838SVladimir Kondratyev 	default:
63188512838SVladimir Kondratyev 		return (EINVAL);
63288512838SVladimir Kondratyev 	}
63388512838SVladimir Kondratyev 
63488512838SVladimir Kondratyev 	/* Use slowest falling time defaults to be on the safe side */
63588512838SVladimir Kondratyev 	sda_fall_time = hw->sda_fall_time == 0 ? tf_max : hw->sda_fall_time;
63688512838SVladimir Kondratyev 	*scl_hcnt = (uint16_t)
63788512838SVladimir Kondratyev 	    ((hw->ic_clock_rate * (thigh + sda_fall_time) + 500) / 1000 - 3);
63888512838SVladimir Kondratyev 
63988512838SVladimir Kondratyev 	scl_fall_time = hw->scl_fall_time == 0 ? tf_max : hw->scl_fall_time;
64088512838SVladimir Kondratyev 	*scl_lcnt = (uint16_t)
64188512838SVladimir Kondratyev 	    ((hw->ic_clock_rate * (tlow + scl_fall_time) + 500) / 1000 - 1);
64288512838SVladimir Kondratyev 
64388512838SVladimir Kondratyev 	/*
64488512838SVladimir Kondratyev 	 * There is no "known good" default value for tHD;DAT so keep SDA_HOLD
64588512838SVladimir Kondratyev 	 * intact if sda_hold_time value is not provided.
64688512838SVladimir Kondratyev 	 */
64788512838SVladimir Kondratyev 	if (hw->sda_hold_time != 0)
64888512838SVladimir Kondratyev 		*sda_hold = (uint16_t)
64988512838SVladimir Kondratyev 		    ((hw->ic_clock_rate * hw->sda_hold_time + 500) / 1000);
65088512838SVladimir Kondratyev 
65188512838SVladimir Kondratyev 	return (0);
65288512838SVladimir Kondratyev }
65388512838SVladimir Kondratyev 
65488512838SVladimir Kondratyev #ifdef DEV_ACPI
65588512838SVladimir Kondratyev static ACPI_STATUS
65688512838SVladimir Kondratyev ig4iic_acpi_params(ACPI_HANDLE handle, char *method,
65788512838SVladimir Kondratyev     uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
65888512838SVladimir Kondratyev {
65988512838SVladimir Kondratyev 	ACPI_BUFFER buf;
66088512838SVladimir Kondratyev 	ACPI_OBJECT *obj, *elems;
66188512838SVladimir Kondratyev 	ACPI_STATUS status;
66288512838SVladimir Kondratyev 
66388512838SVladimir Kondratyev 	buf.Pointer = NULL;
66488512838SVladimir Kondratyev 	buf.Length = ACPI_ALLOCATE_BUFFER;
66588512838SVladimir Kondratyev 
66688512838SVladimir Kondratyev 	status = AcpiEvaluateObject(handle, method, NULL, &buf);
66788512838SVladimir Kondratyev 	if (ACPI_FAILURE(status))
66888512838SVladimir Kondratyev 		return (status);
66988512838SVladimir Kondratyev 
67088512838SVladimir Kondratyev 	status = AE_TYPE;
67188512838SVladimir Kondratyev 	obj = (ACPI_OBJECT *)buf.Pointer;
67288512838SVladimir Kondratyev 	if (obj->Type == ACPI_TYPE_PACKAGE && obj->Package.Count == 3) {
67388512838SVladimir Kondratyev 		elems = obj->Package.Elements;
67488512838SVladimir Kondratyev 		*scl_hcnt = elems[0].Integer.Value & IG4_SCL_CLOCK_MASK;
67588512838SVladimir Kondratyev 		*scl_lcnt = elems[1].Integer.Value & IG4_SCL_CLOCK_MASK;
67688512838SVladimir Kondratyev 		*sda_hold = elems[2].Integer.Value & IG4_SDA_TX_HOLD_MASK;
67788512838SVladimir Kondratyev 		status = AE_OK;
67888512838SVladimir Kondratyev 	}
67988512838SVladimir Kondratyev 
68088512838SVladimir Kondratyev 	AcpiOsFree(obj);
68188512838SVladimir Kondratyev 
68288512838SVladimir Kondratyev 	return (status);
68388512838SVladimir Kondratyev }
68488512838SVladimir Kondratyev #endif /* DEV_ACPI */
68588512838SVladimir Kondratyev 
68688512838SVladimir Kondratyev static void
68788512838SVladimir Kondratyev ig4iic_get_config(ig4iic_softc_t *sc)
68888512838SVladimir Kondratyev {
68988512838SVladimir Kondratyev 	const struct ig4_hw *hw;
69083a66b9bSVladimir Kondratyev 	uint32_t v;
69188512838SVladimir Kondratyev #ifdef DEV_ACPI
69288512838SVladimir Kondratyev 	ACPI_HANDLE handle;
69388512838SVladimir Kondratyev #endif
69488512838SVladimir Kondratyev 	/* Fetch default hardware config from controller */
69588512838SVladimir Kondratyev 	sc->cfg.version = reg_read(sc, IG4_REG_COMP_VER);
69688512838SVladimir Kondratyev 	sc->cfg.bus_speed = reg_read(sc, IG4_REG_CTL) & IG4_CTL_SPEED_MASK;
69788512838SVladimir Kondratyev 	sc->cfg.ss_scl_hcnt =
69888512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SS_SCL_HCNT) & IG4_SCL_CLOCK_MASK;
69988512838SVladimir Kondratyev 	sc->cfg.ss_scl_lcnt =
70088512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SS_SCL_LCNT) & IG4_SCL_CLOCK_MASK;
70188512838SVladimir Kondratyev 	sc->cfg.fs_scl_hcnt =
70288512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_FS_SCL_HCNT) & IG4_SCL_CLOCK_MASK;
70388512838SVladimir Kondratyev 	sc->cfg.fs_scl_lcnt =
70488512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_FS_SCL_LCNT) & IG4_SCL_CLOCK_MASK;
70588512838SVladimir Kondratyev 	sc->cfg.ss_sda_hold = sc->cfg.fs_sda_hold =
70688512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SDA_HOLD) & IG4_SDA_TX_HOLD_MASK;
70788512838SVladimir Kondratyev 
70888512838SVladimir Kondratyev 	if (sc->cfg.bus_speed != IG4_CTL_SPEED_STD)
70988512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
71088512838SVladimir Kondratyev 
71183a66b9bSVladimir Kondratyev 	/* REG_COMP_PARAM1 is not documented in latest Intel specs */
71283a66b9bSVladimir Kondratyev 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
71383a66b9bSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_COMP_PARAM1);
71483a66b9bSVladimir Kondratyev 		if (IG4_PARAM1_TXFIFO_DEPTH(v) != 0)
71583a66b9bSVladimir Kondratyev 			sc->cfg.txfifo_depth = IG4_PARAM1_TXFIFO_DEPTH(v);
71683a66b9bSVladimir Kondratyev 		if (IG4_PARAM1_RXFIFO_DEPTH(v) != 0)
71783a66b9bSVladimir Kondratyev 			sc->cfg.rxfifo_depth = IG4_PARAM1_RXFIFO_DEPTH(v);
71883a66b9bSVladimir Kondratyev 	} else {
71983a66b9bSVladimir Kondratyev 		/*
72083a66b9bSVladimir Kondratyev 		 * Hardware does not allow FIFO Threshold Levels value to be
72183a66b9bSVladimir Kondratyev 		 * set larger than the depth of the buffer. If an attempt is
72283a66b9bSVladimir Kondratyev 		 * made to do that, the actual value set will be the maximum
72383a66b9bSVladimir Kondratyev 		 * depth of the buffer.
72483a66b9bSVladimir Kondratyev 		 */
72583a66b9bSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_TX_TL);
72683a66b9bSVladimir Kondratyev 		reg_write(sc, IG4_REG_TX_TL, v | IG4_FIFO_MASK);
72783a66b9bSVladimir Kondratyev 		sc->cfg.txfifo_depth =
72883a66b9bSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_TX_TL) & IG4_FIFO_MASK) + 1;
72983a66b9bSVladimir Kondratyev 		reg_write(sc, IG4_REG_TX_TL, v);
73083a66b9bSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_RX_TL);
73183a66b9bSVladimir Kondratyev 		reg_write(sc, IG4_REG_RX_TL, v | IG4_FIFO_MASK);
73283a66b9bSVladimir Kondratyev 		sc->cfg.rxfifo_depth =
73383a66b9bSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_RX_TL) & IG4_FIFO_MASK) + 1;
73483a66b9bSVladimir Kondratyev 		reg_write(sc, IG4_REG_RX_TL, v);
73583a66b9bSVladimir Kondratyev 	}
73683a66b9bSVladimir Kondratyev 
73788512838SVladimir Kondratyev 	/* Override hardware config with IC_clock-based counter values */
73888512838SVladimir Kondratyev 	if (ig4_timings < 2 && sc->version < nitems(ig4iic_hw)) {
73988512838SVladimir Kondratyev 		hw = &ig4iic_hw[sc->version];
74088512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
74188512838SVladimir Kondratyev 		ig4iic_clk_params(hw, IG4_CTL_SPEED_STD, &sc->cfg.ss_scl_hcnt,
74288512838SVladimir Kondratyev 		    &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
74388512838SVladimir Kondratyev 		ig4iic_clk_params(hw, IG4_CTL_SPEED_FAST, &sc->cfg.fs_scl_hcnt,
74488512838SVladimir Kondratyev 		    &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
74583a66b9bSVladimir Kondratyev 		if (hw->txfifo_depth != 0)
74683a66b9bSVladimir Kondratyev 			sc->cfg.txfifo_depth = hw->txfifo_depth;
74783a66b9bSVladimir Kondratyev 		if (hw->rxfifo_depth != 0)
74883a66b9bSVladimir Kondratyev 			sc->cfg.rxfifo_depth = hw->rxfifo_depth;
74988512838SVladimir Kondratyev 	} else if (ig4_timings == 2) {
75088512838SVladimir Kondratyev 		/*
75188512838SVladimir Kondratyev 		 * Timings of original ig4 driver:
75288512838SVladimir Kondratyev 		 * Program based on a 25000 Hz clock.  This is a bit of a
75388512838SVladimir Kondratyev 		 * hack (obviously).  The defaults are 400 and 470 for standard
75488512838SVladimir Kondratyev 		 * and 60 and 130 for fast.  The defaults for standard fail
75588512838SVladimir Kondratyev 		 * utterly (presumably cause an abort) because the clock time
75688512838SVladimir Kondratyev 		 * is ~18.8ms by default.  This brings it down to ~4ms.
75788512838SVladimir Kondratyev 		 */
75888512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_STD;
75988512838SVladimir Kondratyev 		sc->cfg.ss_scl_hcnt = sc->cfg.fs_scl_hcnt = 100;
76088512838SVladimir Kondratyev 		sc->cfg.ss_scl_lcnt = sc->cfg.fs_scl_lcnt = 125;
76188512838SVladimir Kondratyev 		if (sc->version == IG4_SKYLAKE)
76288512838SVladimir Kondratyev 			sc->cfg.ss_sda_hold = sc->cfg.fs_sda_hold = 28;
76388512838SVladimir Kondratyev 	}
76488512838SVladimir Kondratyev 
76588512838SVladimir Kondratyev #ifdef DEV_ACPI
76688512838SVladimir Kondratyev 	/* Evaluate SSCN and FMCN ACPI methods to fetch timings */
76788512838SVladimir Kondratyev 	if (ig4_timings == 0 && (handle = acpi_get_handle(sc->dev)) != NULL) {
76888512838SVladimir Kondratyev 		ig4iic_acpi_params(handle, "SSCN", &sc->cfg.ss_scl_hcnt,
76988512838SVladimir Kondratyev 		    &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
77088512838SVladimir Kondratyev 		ig4iic_acpi_params(handle, "FMCN", &sc->cfg.fs_scl_hcnt,
77188512838SVladimir Kondratyev 		    &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
77288512838SVladimir Kondratyev 	}
77388512838SVladimir Kondratyev #endif
77488512838SVladimir Kondratyev 
77588512838SVladimir Kondratyev 	if (bootverbose) {
77688512838SVladimir Kondratyev 		device_printf(sc->dev, "Controller parameters:\n");
77788512838SVladimir Kondratyev 		printf("  Speed: %s\n",
77888512838SVladimir Kondratyev 		    sc->cfg.bus_speed == IG4_CTL_SPEED_STD ? "Std" : "Fast");
77988512838SVladimir Kondratyev 		printf("  Regs:  HCNT  :LCNT  :SDAHLD\n");
78088512838SVladimir Kondratyev 		printf("  Std:   0x%04hx:0x%04hx:0x%04hx\n",
78188512838SVladimir Kondratyev 		    sc->cfg.ss_scl_hcnt, sc->cfg.ss_scl_lcnt,
78288512838SVladimir Kondratyev 		    sc->cfg.ss_sda_hold);
78388512838SVladimir Kondratyev 		printf("  Fast:  0x%04hx:0x%04hx:0x%04hx\n",
78488512838SVladimir Kondratyev 		    sc->cfg.fs_scl_hcnt, sc->cfg.fs_scl_lcnt,
78588512838SVladimir Kondratyev 		    sc->cfg.fs_sda_hold);
78683a66b9bSVladimir Kondratyev 		printf("  FIFO:  RX:0x%04x: TX:0x%04x\n",
78783a66b9bSVladimir Kondratyev 		    sc->cfg.rxfifo_depth, sc->cfg.txfifo_depth);
78888512838SVladimir Kondratyev 	}
78988512838SVladimir Kondratyev }
79088512838SVladimir Kondratyev 
791db7caa2eSVladimir Kondratyev static int
792db7caa2eSVladimir Kondratyev ig4iic_set_config(ig4iic_softc_t *sc)
79371d51719SMichael Gmelin {
79471d51719SMichael Gmelin 	uint32_t v;
79571d51719SMichael Gmelin 
796b16d03adSOleksandr Tymoshenko 	v = reg_read(sc, IG4_REG_DEVIDLE_CTRL);
797b16d03adSOleksandr Tymoshenko 	if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) {
798b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED);
799b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0);
800b16d03adSOleksandr Tymoshenko 
801b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
802b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
803b16d03adSOleksandr Tymoshenko 		DELAY(1000);
804b16d03adSOleksandr Tymoshenko 	}
805b16d03adSOleksandr Tymoshenko 
806b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM)
80771d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_TYPE);
808b3e8ee5dSOleksandr Tymoshenko 
809b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
81071d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_PARAM1);
81171d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_GENERAL);
812b3e8ee5dSOleksandr Tymoshenko 		/*
813b3e8ee5dSOleksandr Tymoshenko 		 * The content of IG4_REG_GENERAL is different for each
814b3e8ee5dSOleksandr Tymoshenko 		 * controller version.
815b3e8ee5dSOleksandr Tymoshenko 		 */
816b3e8ee5dSOleksandr Tymoshenko 		if (sc->version == IG4_HASWELL &&
817b3e8ee5dSOleksandr Tymoshenko 		    (v & IG4_GENERAL_SWMODE) == 0) {
81871d51719SMichael Gmelin 			v |= IG4_GENERAL_SWMODE;
81971d51719SMichael Gmelin 			reg_write(sc, IG4_REG_GENERAL, v);
82071d51719SMichael Gmelin 			v = reg_read(sc, IG4_REG_GENERAL);
82171d51719SMichael Gmelin 		}
822b3e8ee5dSOleksandr Tymoshenko 	}
82371d51719SMichael Gmelin 
824b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
82571d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
82671d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
827b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
828b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE);
829b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE);
830b3e8ee5dSOleksandr Tymoshenko 	}
83171d51719SMichael Gmelin 
832b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
83371d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_VER);
834db7caa2eSVladimir Kondratyev 		if (v < IG4_COMP_MIN_VER)
835db7caa2eSVladimir Kondratyev 			return(ENXIO);
836b3e8ee5dSOleksandr Tymoshenko 	}
837d117e363SVladimir Kondratyev 
838d117e363SVladimir Kondratyev 	if (set_controller(sc, 0)) {
839d117e363SVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-1\n");
840db7caa2eSVladimir Kondratyev 		return (ENXIO);
841d117e363SVladimir Kondratyev 	}
842d117e363SVladimir Kondratyev 
84321e459c6SVladimir Kondratyev 	reg_read(sc, IG4_REG_CLR_INTR);
84421e459c6SVladimir Kondratyev 	reg_write(sc, IG4_REG_INTR_MASK, 0);
84521e459c6SVladimir Kondratyev 	sc->intr_mask = 0;
84621e459c6SVladimir Kondratyev 
84788512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SS_SCL_HCNT, sc->cfg.ss_scl_hcnt);
84888512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SS_SCL_LCNT, sc->cfg.ss_scl_lcnt);
84988512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_FS_SCL_HCNT, sc->cfg.fs_scl_hcnt);
85088512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_FS_SCL_LCNT, sc->cfg.fs_scl_lcnt);
85188512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SDA_HOLD,
85288512838SVladimir Kondratyev 	    (sc->cfg.bus_speed  & IG4_CTL_SPEED_MASK) == IG4_CTL_SPEED_STD ?
85388512838SVladimir Kondratyev 	      sc->cfg.ss_sda_hold : sc->cfg.fs_sda_hold);
85471d51719SMichael Gmelin 
85571d51719SMichael Gmelin 	/*
85671d51719SMichael Gmelin 	 * Use a threshold of 1 so we get interrupted on each character,
85771d51719SMichael Gmelin 	 * allowing us to use mtx_sleep() in our poll code.  Not perfect
85871d51719SMichael Gmelin 	 * but this is better than using DELAY() for receiving data.
8594cd6abddSMichael Gmelin 	 *
8604cd6abddSMichael Gmelin 	 * See ig4_var.h for details on interrupt handler synchronization.
86171d51719SMichael Gmelin 	 */
862811ff4ddSVladimir Kondratyev 	reg_write(sc, IG4_REG_RX_TL, 0);
8637f6aee64SVladimir Kondratyev 	reg_write(sc, IG4_REG_TX_TL, 0);
86471d51719SMichael Gmelin 
86571d51719SMichael Gmelin 	reg_write(sc, IG4_REG_CTL,
86671d51719SMichael Gmelin 		  IG4_CTL_MASTER |
86771d51719SMichael Gmelin 		  IG4_CTL_SLAVE_DISABLE |
86871d51719SMichael Gmelin 		  IG4_CTL_RESTARTEN |
86988512838SVladimir Kondratyev 		  (sc->cfg.bus_speed & IG4_CTL_SPEED_MASK));
87071d51719SMichael Gmelin 
871db7caa2eSVladimir Kondratyev 	return (0);
872db7caa2eSVladimir Kondratyev }
873db7caa2eSVladimir Kondratyev 
874db7caa2eSVladimir Kondratyev /*
875db7caa2eSVladimir Kondratyev  * Called from ig4iic_pci_attach/detach()
876db7caa2eSVladimir Kondratyev  */
877db7caa2eSVladimir Kondratyev int
878db7caa2eSVladimir Kondratyev ig4iic_attach(ig4iic_softc_t *sc)
879db7caa2eSVladimir Kondratyev {
880db7caa2eSVladimir Kondratyev 	int error;
881db7caa2eSVladimir Kondratyev 
882db7caa2eSVladimir Kondratyev 	mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_DEF);
883db7caa2eSVladimir Kondratyev 	sx_init(&sc->call_lock, "IG4 call lock");
884db7caa2eSVladimir Kondratyev 
885db7caa2eSVladimir Kondratyev 	ig4iic_get_config(sc);
886db7caa2eSVladimir Kondratyev 
887db7caa2eSVladimir Kondratyev 	error = ig4iic_set_config(sc);
888db7caa2eSVladimir Kondratyev 	if (error)
889db7caa2eSVladimir Kondratyev 		goto done;
890db7caa2eSVladimir Kondratyev 
891448897d3SAndriy Gapon 	sc->iicbus = device_add_child(sc->dev, "iicbus", -1);
892448897d3SAndriy Gapon 	if (sc->iicbus == NULL) {
893448897d3SAndriy Gapon 		device_printf(sc->dev, "iicbus driver not found\n");
89471d51719SMichael Gmelin 		error = ENXIO;
89571d51719SMichael Gmelin 		goto done;
89671d51719SMichael Gmelin 	}
89771d51719SMichael Gmelin 
89871d51719SMichael Gmelin #if 0
89971d51719SMichael Gmelin 	/*
90071d51719SMichael Gmelin 	 * Don't do this, it blows up the PCI config
90171d51719SMichael Gmelin 	 */
902b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
903b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
904b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
905b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version = IG4_SKYLAKE) {
906b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
907b3e8ee5dSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
908b3e8ee5dSOleksandr Tymoshenko 	}
90971d51719SMichael Gmelin #endif
91071d51719SMichael Gmelin 
911bf9c3c58SVladimir Kondratyev 	if (set_controller(sc, IG4_I2C_ENABLE)) {
91271d51719SMichael Gmelin 		device_printf(sc->dev, "controller error during attach-2\n");
913bf9c3c58SVladimir Kondratyev 		error = ENXIO;
914bf9c3c58SVladimir Kondratyev 		goto done;
915bf9c3c58SVladimir Kondratyev 	}
916edcf6a9fSVladimir Kondratyev 	if (set_controller(sc, 0)) {
917edcf6a9fSVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-3\n");
918edcf6a9fSVladimir Kondratyev 		error = ENXIO;
919edcf6a9fSVladimir Kondratyev 		goto done;
920edcf6a9fSVladimir Kondratyev 	}
92171d51719SMichael Gmelin 	error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
92271d51719SMichael Gmelin 			       NULL, ig4iic_intr, sc, &sc->intr_handle);
92371d51719SMichael Gmelin 	if (error) {
92471d51719SMichael Gmelin 		device_printf(sc->dev,
92571d51719SMichael Gmelin 			      "Unable to setup irq: error %d\n", error);
92671d51719SMichael Gmelin 	}
92771d51719SMichael Gmelin 
92871d51719SMichael Gmelin 	error = bus_generic_attach(sc->dev);
92971d51719SMichael Gmelin 	if (error) {
93071d51719SMichael Gmelin 		device_printf(sc->dev,
93171d51719SMichael Gmelin 			      "failed to attach child: error %d\n", error);
93271d51719SMichael Gmelin 	}
9336777ccd9SVladimir Kondratyev 
9346777ccd9SVladimir Kondratyev done:
9356777ccd9SVladimir Kondratyev 	return (error);
93671d51719SMichael Gmelin }
93771d51719SMichael Gmelin 
93871d51719SMichael Gmelin int
93971d51719SMichael Gmelin ig4iic_detach(ig4iic_softc_t *sc)
94071d51719SMichael Gmelin {
94171d51719SMichael Gmelin 	int error;
94271d51719SMichael Gmelin 
94371d51719SMichael Gmelin 	if (device_is_attached(sc->dev)) {
94471d51719SMichael Gmelin 		error = bus_generic_detach(sc->dev);
94571d51719SMichael Gmelin 		if (error)
94671d51719SMichael Gmelin 			return (error);
94771d51719SMichael Gmelin 	}
948448897d3SAndriy Gapon 	if (sc->iicbus)
949448897d3SAndriy Gapon 		device_delete_child(sc->dev, sc->iicbus);
95071d51719SMichael Gmelin 	if (sc->intr_handle)
95171d51719SMichael Gmelin 		bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
95271d51719SMichael Gmelin 
9534cd6abddSMichael Gmelin 	sx_xlock(&sc->call_lock);
95471d51719SMichael Gmelin 
955448897d3SAndriy Gapon 	sc->iicbus = NULL;
95671d51719SMichael Gmelin 	sc->intr_handle = NULL;
95771d51719SMichael Gmelin 	reg_write(sc, IG4_REG_INTR_MASK, 0);
95871d51719SMichael Gmelin 	set_controller(sc, 0);
95971d51719SMichael Gmelin 
9604cd6abddSMichael Gmelin 	sx_xunlock(&sc->call_lock);
9615c5bcb1dSOleksandr Tymoshenko 
9625c5bcb1dSOleksandr Tymoshenko 	mtx_destroy(&sc->io_lock);
9635c5bcb1dSOleksandr Tymoshenko 	sx_destroy(&sc->call_lock);
9645c5bcb1dSOleksandr Tymoshenko 
96571d51719SMichael Gmelin 	return (0);
96671d51719SMichael Gmelin }
96771d51719SMichael Gmelin 
968db7caa2eSVladimir Kondratyev int
969db7caa2eSVladimir Kondratyev ig4iic_suspend(ig4iic_softc_t *sc)
970db7caa2eSVladimir Kondratyev {
971db7caa2eSVladimir Kondratyev 	int error;
972db7caa2eSVladimir Kondratyev 
973db7caa2eSVladimir Kondratyev 	/* suspend all children */
974db7caa2eSVladimir Kondratyev 	error = bus_generic_suspend(sc->dev);
975db7caa2eSVladimir Kondratyev 
976db7caa2eSVladimir Kondratyev 	sx_xlock(&sc->call_lock);
977db7caa2eSVladimir Kondratyev 	set_controller(sc, 0);
978db7caa2eSVladimir Kondratyev 	if (sc->version == IG4_SKYLAKE) {
979db7caa2eSVladimir Kondratyev 		/*
980db7caa2eSVladimir Kondratyev 		 * Place the device in the idle state, just to be safe
981db7caa2eSVladimir Kondratyev 		 */
982db7caa2eSVladimir Kondratyev 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE);
983db7caa2eSVladimir Kondratyev 		/*
984db7caa2eSVladimir Kondratyev 		 * Controller can become dysfunctional if I2C lines are pulled
985db7caa2eSVladimir Kondratyev 		 * down when suspend procedure turns off power to I2C device.
986db7caa2eSVladimir Kondratyev 		 * Place device in the reset state to avoid this.
987db7caa2eSVladimir Kondratyev 		 */
988db7caa2eSVladimir Kondratyev 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
989db7caa2eSVladimir Kondratyev 	}
990db7caa2eSVladimir Kondratyev 	sx_xunlock(&sc->call_lock);
991db7caa2eSVladimir Kondratyev 
992db7caa2eSVladimir Kondratyev 	return (error);
993db7caa2eSVladimir Kondratyev }
994db7caa2eSVladimir Kondratyev 
995db7caa2eSVladimir Kondratyev int ig4iic_resume(ig4iic_softc_t *sc)
996db7caa2eSVladimir Kondratyev {
997db7caa2eSVladimir Kondratyev 	int error;
998db7caa2eSVladimir Kondratyev 
999db7caa2eSVladimir Kondratyev 	sx_xlock(&sc->call_lock);
1000db7caa2eSVladimir Kondratyev 	if (ig4iic_set_config(sc))
1001db7caa2eSVladimir Kondratyev 		device_printf(sc->dev, "controller error during resume\n");
1002db7caa2eSVladimir Kondratyev 	/* Force setting of the target address on the next transfer */
1003db7caa2eSVladimir Kondratyev 	sc->slave_valid = 0;
1004db7caa2eSVladimir Kondratyev 	sx_xunlock(&sc->call_lock);
1005db7caa2eSVladimir Kondratyev 
1006db7caa2eSVladimir Kondratyev 	error = bus_generic_resume(sc->dev);
1007db7caa2eSVladimir Kondratyev 
1008db7caa2eSVladimir Kondratyev 	return (error);
1009db7caa2eSVladimir Kondratyev }
1010db7caa2eSVladimir Kondratyev 
101171d51719SMichael Gmelin /*
10124cd6abddSMichael Gmelin  * Interrupt Operation, see ig4_var.h for locking semantics.
101371d51719SMichael Gmelin  */
101471d51719SMichael Gmelin static void
101571d51719SMichael Gmelin ig4iic_intr(void *cookie)
101671d51719SMichael Gmelin {
101771d51719SMichael Gmelin 	ig4iic_softc_t *sc = cookie;
101871d51719SMichael Gmelin 
10194cd6abddSMichael Gmelin 	mtx_lock(&sc->io_lock);
10200a6b1b56SVladimir Kondratyev 	/* Ignore stray interrupts */
10210a6b1b56SVladimir Kondratyev 	if (sc->intr_mask != 0 && reg_read(sc, IG4_REG_INTR_STAT) != 0) {
102221e459c6SVladimir Kondratyev 		set_intr_mask(sc, 0);
10230ba5622dSMichael Gmelin 		reg_read(sc, IG4_REG_CLR_INTR);
102471d51719SMichael Gmelin 		wakeup(sc);
10250a6b1b56SVladimir Kondratyev 	}
10264cd6abddSMichael Gmelin 	mtx_unlock(&sc->io_lock);
102771d51719SMichael Gmelin }
102871d51719SMichael Gmelin 
102971d51719SMichael Gmelin #define REGDUMP(sc, reg)	\
103071d51719SMichael Gmelin 	device_printf(sc->dev, "  %-23s %08x\n", #reg, reg_read(sc, reg))
103171d51719SMichael Gmelin 
103271d51719SMichael Gmelin static void
103371d51719SMichael Gmelin ig4iic_dump(ig4iic_softc_t *sc)
103471d51719SMichael Gmelin {
103571d51719SMichael Gmelin 	device_printf(sc->dev, "ig4iic register dump:\n");
103671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_CTL);
103771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TAR_ADD);
103871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_HCNT);
103971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_LCNT);
104071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_HCNT);
104171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_LCNT);
104271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_STAT);
104371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_MASK);
104471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RAW_INTR_STAT);
104571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RX_TL);
104671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_TL);
104771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_EN);
104871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_STA);
104971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TXFLR);
105071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RXFLR);
105171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_HOLD);
105271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE);
105371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SLV_DATA_NACK);
105471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_CTRL);
105571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_TDLR);
105671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_RDLR);
105771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_SETUP);
105871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_ENABLE_STATUS);
105971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_COMP_PARAM1);
106071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_COMP_VER);
1061b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM) {
106271d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_COMP_TYPE);
106371d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_CLK_PARMS);
1064b3e8ee5dSOleksandr Tymoshenko 	}
1065b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
1066b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_HSW);
106771d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_GENERAL);
1068b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
1069b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_SKL);
1070b3e8ee5dSOleksandr Tymoshenko 	}
1071b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
107271d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_SW_LTR_VALUE);
107371d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE);
1074b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
1075b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE);
1076b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE);
1077b3e8ee5dSOleksandr Tymoshenko 	}
107871d51719SMichael Gmelin }
107971d51719SMichael Gmelin #undef REGDUMP
108071d51719SMichael Gmelin 
1081984ed3e4SVladimir Kondratyev devclass_t ig4iic_devclass;
1082984ed3e4SVladimir Kondratyev 
1083984ed3e4SVladimir Kondratyev DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL);
1084984ed3e4SVladimir Kondratyev MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
1085984ed3e4SVladimir Kondratyev MODULE_VERSION(ig4iic, 1);
1086