xref: /freebsd/sys/dev/ichiic/ig4_iic.c (revision 493715f9)
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 /*
38eb6befbaSAndriy Gapon  * Intel fourth generation mobile cpus integrated I2C device.
3971d51719SMichael Gmelin  *
4071d51719SMichael Gmelin  * See ig4_reg.h for datasheet reference and notes.
414cd6abddSMichael Gmelin  * See ig4_var.h for locking semantics.
4271d51719SMichael Gmelin  */
4371d51719SMichael Gmelin 
4488512838SVladimir Kondratyev #include "opt_acpi.h"
4588512838SVladimir Kondratyev 
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 
6388512838SVladimir Kondratyev #ifdef DEV_ACPI
6488512838SVladimir Kondratyev #include <contrib/dev/acpica/include/acpi.h>
6588512838SVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h>
6688512838SVladimir Kondratyev #include <dev/acpica/acpivar.h>
6788512838SVladimir Kondratyev #endif
6888512838SVladimir Kondratyev 
69448897d3SAndriy Gapon #include <dev/iicbus/iicbus.h>
70448897d3SAndriy Gapon #include <dev/iicbus/iiconf.h>
7171d51719SMichael Gmelin 
7271d51719SMichael Gmelin #include <dev/ichiic/ig4_reg.h>
7371d51719SMichael Gmelin #include <dev/ichiic/ig4_var.h>
7471d51719SMichael Gmelin 
7541b24e09SVladimir Kondratyev #define DO_POLL(sc)	(cold || kdb_active || SCHEDULER_STOPPED() || sc->poll)
76c59aca57SVladimir Kondratyev 
7788512838SVladimir Kondratyev /*
7888512838SVladimir Kondratyev  * tLOW, tHIGH periods of the SCL clock and maximal falling time of both
7988512838SVladimir Kondratyev  * lines are taken from I2C specifications.
8088512838SVladimir Kondratyev  */
8188512838SVladimir Kondratyev #define	IG4_SPEED_STD_THIGH	4000	/* nsec */
8288512838SVladimir Kondratyev #define	IG4_SPEED_STD_TLOW	4700	/* nsec */
8388512838SVladimir Kondratyev #define	IG4_SPEED_STD_TF_MAX	300	/* nsec */
8488512838SVladimir Kondratyev #define	IG4_SPEED_FAST_THIGH	600	/* nsec */
8588512838SVladimir Kondratyev #define	IG4_SPEED_FAST_TLOW	1300	/* nsec */
8688512838SVladimir Kondratyev #define	IG4_SPEED_FAST_TF_MAX	300	/* nsec */
8788512838SVladimir Kondratyev 
8888512838SVladimir Kondratyev /*
8988512838SVladimir Kondratyev  * Ig4 hardware parameters except Haswell are taken from intel_lpss driver
9088512838SVladimir Kondratyev  */
9188512838SVladimir Kondratyev static const struct ig4_hw ig4iic_hw[] = {
925972ffdeSAndrew Gallatin 	[IG4_EMAG] = {
935972ffdeSAndrew Gallatin 		.ic_clock_rate = 100,	/* MHz */
945972ffdeSAndrew Gallatin 	},
9588512838SVladimir Kondratyev 	[IG4_HASWELL] = {
9688512838SVladimir Kondratyev 		.ic_clock_rate = 100,	/* MHz */
9788512838SVladimir Kondratyev 		.sda_hold_time = 90,	/* nsec */
9883a66b9bSVladimir Kondratyev 		.txfifo_depth = 32,
9983a66b9bSVladimir Kondratyev 		.rxfifo_depth = 32,
10088512838SVladimir Kondratyev 	},
10188512838SVladimir Kondratyev 	[IG4_ATOM] = {
10288512838SVladimir Kondratyev 		.ic_clock_rate = 100,
10388512838SVladimir Kondratyev 		.sda_fall_time = 280,
10488512838SVladimir Kondratyev 		.scl_fall_time = 240,
10588512838SVladimir Kondratyev 		.sda_hold_time = 60,
10683a66b9bSVladimir Kondratyev 		.txfifo_depth = 32,
10783a66b9bSVladimir Kondratyev 		.rxfifo_depth = 32,
10888512838SVladimir Kondratyev 	},
10988512838SVladimir Kondratyev 	[IG4_SKYLAKE] = {
11088512838SVladimir Kondratyev 		.ic_clock_rate = 120,
11188512838SVladimir Kondratyev 		.sda_hold_time = 230,
11288512838SVladimir Kondratyev 	},
11388512838SVladimir Kondratyev 	[IG4_APL] = {
11488512838SVladimir Kondratyev 		.ic_clock_rate = 133,
11588512838SVladimir Kondratyev 		.sda_fall_time = 171,
11688512838SVladimir Kondratyev 		.scl_fall_time = 208,
11788512838SVladimir Kondratyev 		.sda_hold_time = 207,
11888512838SVladimir Kondratyev 	},
11981e81838SVladimir Kondratyev 	[IG4_CANNONLAKE] = {
12081e81838SVladimir Kondratyev 		.ic_clock_rate = 216,
12181e81838SVladimir Kondratyev 		.sda_hold_time = 230,
12281e81838SVladimir Kondratyev 	},
1236c6fd2b4SVladimir Kondratyev 	[IG4_TIGERLAKE] = {
1246c6fd2b4SVladimir Kondratyev 		.ic_clock_rate = 133,
1256c6fd2b4SVladimir Kondratyev 		.sda_fall_time = 171,
1266c6fd2b4SVladimir Kondratyev 		.scl_fall_time = 208,
1276c6fd2b4SVladimir Kondratyev 		.sda_hold_time = 42,
1286c6fd2b4SVladimir Kondratyev 	},
129bbacb7ceSVladimir Kondratyev 	[IG4_GEMINILAKE] = {
130bbacb7ceSVladimir Kondratyev 		.ic_clock_rate = 133,
131bbacb7ceSVladimir Kondratyev 		.sda_fall_time = 171,
132bbacb7ceSVladimir Kondratyev 		.scl_fall_time = 290,
133bbacb7ceSVladimir Kondratyev 		.sda_hold_time = 313,
134bbacb7ceSVladimir Kondratyev 	},
13588512838SVladimir Kondratyev };
13688512838SVladimir Kondratyev 
137847f557cSVladimir Kondratyev static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset);
138371d37d8SVladimir Kondratyev static driver_filter_t ig4iic_intr;
13971d51719SMichael Gmelin static void ig4iic_dump(ig4iic_softc_t *sc);
14071d51719SMichael Gmelin 
14171d51719SMichael Gmelin static int ig4_dump;
14212e413beSMichael Gmelin SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLFLAG_RW,
14312e413beSMichael Gmelin 	   &ig4_dump, 0, "Dump controller registers");
14471d51719SMichael Gmelin 
14571d51719SMichael Gmelin /*
14688512838SVladimir Kondratyev  * Clock registers initialization control
14788512838SVladimir Kondratyev  * 0 - Try read clock registers from ACPI and fallback to p.1.
14888512838SVladimir Kondratyev  * 1 - Calculate values based on controller type (IC clock rate).
14988512838SVladimir Kondratyev  * 2 - Use values inherited from DragonflyBSD driver (old behavior).
15088512838SVladimir Kondratyev  * 3 - Keep clock registers intact.
15188512838SVladimir Kondratyev  */
15288512838SVladimir Kondratyev static int ig4_timings;
15388512838SVladimir Kondratyev SYSCTL_INT(_debug, OID_AUTO, ig4_timings, CTLFLAG_RDTUN, &ig4_timings, 0,
15488512838SVladimir Kondratyev     "Controller timings 0=ACPI, 1=predefined, 2=legacy, 3=do not change");
15588512838SVladimir Kondratyev 
15688512838SVladimir Kondratyev /*
15771d51719SMichael Gmelin  * Low-level inline support functions
15871d51719SMichael Gmelin  */
15971d51719SMichael Gmelin static __inline void
reg_write(ig4iic_softc_t * sc,uint32_t reg,uint32_t value)16071d51719SMichael Gmelin reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value)
16171d51719SMichael Gmelin {
16271d51719SMichael Gmelin 	bus_write_4(sc->regs_res, reg, value);
16371d51719SMichael Gmelin 	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE);
16471d51719SMichael Gmelin }
16571d51719SMichael Gmelin 
16671d51719SMichael Gmelin static __inline uint32_t
reg_read(ig4iic_softc_t * sc,uint32_t reg)16771d51719SMichael Gmelin reg_read(ig4iic_softc_t *sc, uint32_t reg)
16871d51719SMichael Gmelin {
16971d51719SMichael Gmelin 	uint32_t value;
17071d51719SMichael Gmelin 
17171d51719SMichael Gmelin 	bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ);
17271d51719SMichael Gmelin 	value = bus_read_4(sc->regs_res, reg);
17371d51719SMichael Gmelin 	return (value);
17471d51719SMichael Gmelin }
17571d51719SMichael Gmelin 
17621e459c6SVladimir Kondratyev static void
ig4iic_set_intr_mask(ig4iic_softc_t * sc,uint32_t val)17769fac7c3SVladimir Kondratyev ig4iic_set_intr_mask(ig4iic_softc_t *sc, uint32_t val)
17821e459c6SVladimir Kondratyev {
17921e459c6SVladimir Kondratyev 	if (sc->intr_mask != val) {
18021e459c6SVladimir Kondratyev 		reg_write(sc, IG4_REG_INTR_MASK, val);
18121e459c6SVladimir Kondratyev 		sc->intr_mask = val;
18221e459c6SVladimir Kondratyev 	}
18321e459c6SVladimir Kondratyev }
18421e459c6SVladimir Kondratyev 
185e3a56ba2SVladimir Kondratyev static int
intrstat2iic(ig4iic_softc_t * sc,uint32_t val)186e3a56ba2SVladimir Kondratyev intrstat2iic(ig4iic_softc_t *sc, uint32_t val)
187e3a56ba2SVladimir Kondratyev {
188e3a56ba2SVladimir Kondratyev 	uint32_t src;
189e3a56ba2SVladimir Kondratyev 
190e3a56ba2SVladimir Kondratyev 	if (val & IG4_INTR_RX_UNDER)
191e3a56ba2SVladimir Kondratyev 		reg_read(sc, IG4_REG_CLR_RX_UNDER);
192e3a56ba2SVladimir Kondratyev 	if (val & IG4_INTR_RX_OVER)
193e3a56ba2SVladimir Kondratyev 		reg_read(sc, IG4_REG_CLR_RX_OVER);
194e3a56ba2SVladimir Kondratyev 	if (val & IG4_INTR_TX_OVER)
195e3a56ba2SVladimir Kondratyev 		reg_read(sc, IG4_REG_CLR_TX_OVER);
196e3a56ba2SVladimir Kondratyev 
197e3a56ba2SVladimir Kondratyev 	if (val & IG4_INTR_TX_ABRT) {
198e3a56ba2SVladimir Kondratyev 		src = reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
199e3a56ba2SVladimir Kondratyev 		reg_read(sc, IG4_REG_CLR_TX_ABORT);
200e3a56ba2SVladimir Kondratyev 		/* User-requested abort. Not really a error */
201e3a56ba2SVladimir Kondratyev 		if (src & IG4_ABRTSRC_TRANSFER)
202e3a56ba2SVladimir Kondratyev 			return (IIC_ESTATUS);
203e3a56ba2SVladimir Kondratyev 		/* Master has lost arbitration */
204e3a56ba2SVladimir Kondratyev 		if (src & IG4_ABRTSRC_ARBLOST)
205e3a56ba2SVladimir Kondratyev 			return (IIC_EBUSBSY);
206e3a56ba2SVladimir Kondratyev 		/* Did not receive an acknowledge from the remote slave */
207e3a56ba2SVladimir Kondratyev 		if (src & (IG4_ABRTSRC_TXNOACK_ADDR7 |
208e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_TXNOACK_ADDR10_1 |
209e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_TXNOACK_ADDR10_2 |
210e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_TXNOACK_DATA |
211e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_GENCALL_NOACK))
212e3a56ba2SVladimir Kondratyev 			return (IIC_ENOACK);
213e3a56ba2SVladimir Kondratyev 		/* Programming errors */
214e3a56ba2SVladimir Kondratyev 		if (src & (IG4_ABRTSRC_GENCALL_READ |
215e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_NORESTART_START |
216e3a56ba2SVladimir Kondratyev 			   IG4_ABRTSRC_NORESTART_10))
217e3a56ba2SVladimir Kondratyev 			return (IIC_ENOTSUPP);
218e3a56ba2SVladimir Kondratyev 		/* Other errors */
219e3a56ba2SVladimir Kondratyev 		if (src & IG4_ABRTSRC_ACKED_START)
220e3a56ba2SVladimir Kondratyev 			return (IIC_EBUSERR);
221e3a56ba2SVladimir Kondratyev 	}
222e3a56ba2SVladimir Kondratyev 	/*
223e3a56ba2SVladimir Kondratyev 	 * TX_OVER, RX_OVER and RX_UNDER are caused by wrong RX/TX FIFO depth
224e3a56ba2SVladimir Kondratyev 	 * detection or driver's read/write pipelining errors.
225e3a56ba2SVladimir Kondratyev 	 */
226e3a56ba2SVladimir Kondratyev 	if (val & (IG4_INTR_TX_OVER | IG4_INTR_RX_OVER))
227e3a56ba2SVladimir Kondratyev 		return (IIC_EOVERFLOW);
228e3a56ba2SVladimir Kondratyev 	if (val & IG4_INTR_RX_UNDER)
229e3a56ba2SVladimir Kondratyev 		return (IIC_EUNDERFLOW);
230e3a56ba2SVladimir Kondratyev 
231e3a56ba2SVladimir Kondratyev 	return (IIC_NOERR);
232e3a56ba2SVladimir Kondratyev }
233e3a56ba2SVladimir Kondratyev 
23471d51719SMichael Gmelin /*
23571d51719SMichael Gmelin  * Enable or disable the controller and wait for the controller to acknowledge
23671d51719SMichael Gmelin  * the state change.
23771d51719SMichael Gmelin  */
23871d51719SMichael Gmelin static int
set_controller(ig4iic_softc_t * sc,uint32_t ctl)23971d51719SMichael Gmelin set_controller(ig4iic_softc_t *sc, uint32_t ctl)
24071d51719SMichael Gmelin {
24171d51719SMichael Gmelin 	int retry;
24271d51719SMichael Gmelin 	int error;
24371d51719SMichael Gmelin 	uint32_t v;
24471d51719SMichael Gmelin 
2450ba5622dSMichael Gmelin 	/*
2460ba5622dSMichael Gmelin 	 * When the controller is enabled, interrupt on STOP detect
2470ba5622dSMichael Gmelin 	 * or receive character ready and clear pending interrupts.
2480ba5622dSMichael Gmelin 	 */
24969fac7c3SVladimir Kondratyev 	ig4iic_set_intr_mask(sc, 0);
25021e459c6SVladimir Kondratyev 	if (ctl & IG4_I2C_ENABLE)
2510ba5622dSMichael Gmelin 		reg_read(sc, IG4_REG_CLR_INTR);
2520ba5622dSMichael Gmelin 
25371d51719SMichael Gmelin 	reg_write(sc, IG4_REG_I2C_EN, ctl);
254448897d3SAndriy Gapon 	error = IIC_ETIMEOUT;
25571d51719SMichael Gmelin 
25671d51719SMichael Gmelin 	for (retry = 100; retry > 0; --retry) {
25771d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_ENABLE_STATUS);
25871d51719SMichael Gmelin 		if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) {
25971d51719SMichael Gmelin 			error = 0;
26071d51719SMichael Gmelin 			break;
26171d51719SMichael Gmelin 		}
26285cd895fSVladimir Kondratyev 		pause("i2cslv", 1);
26371d51719SMichael Gmelin 	}
26471d51719SMichael Gmelin 	return (error);
26571d51719SMichael Gmelin }
26671d51719SMichael Gmelin 
26771d51719SMichael Gmelin /*
268b0eb9d3eSVladimir Kondratyev  * Wait up to 25ms for the requested interrupt using a 25uS polling loop.
26971d51719SMichael Gmelin  */
27071d51719SMichael Gmelin static int
wait_intr(ig4iic_softc_t * sc,uint32_t intr)271b0eb9d3eSVladimir Kondratyev wait_intr(ig4iic_softc_t *sc, uint32_t intr)
27271d51719SMichael Gmelin {
27371d51719SMichael Gmelin 	uint32_t v;
27471d51719SMichael Gmelin 	int error;
27571d51719SMichael Gmelin 	int txlvl = -1;
27671d51719SMichael Gmelin 	u_int count_us = 0;
27737ef8d4cSAllan Jude 	u_int limit_us = 1000000; /* 1sec */
27871d51719SMichael Gmelin 
27971d51719SMichael Gmelin 	for (;;) {
28071d51719SMichael Gmelin 		/*
28171d51719SMichael Gmelin 		 * Check requested status
28271d51719SMichael Gmelin 		 */
283b0eb9d3eSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_RAW_INTR_STAT);
284e3a56ba2SVladimir Kondratyev 		error = intrstat2iic(sc, v & IG4_INTR_ERR_MASK);
285e3a56ba2SVladimir Kondratyev 		if (error || (v & intr))
28671d51719SMichael Gmelin 			break;
28771d51719SMichael Gmelin 
28871d51719SMichael Gmelin 		/*
28971d51719SMichael Gmelin 		 * When waiting for the transmit FIFO to become empty,
29071d51719SMichael Gmelin 		 * reset the timeout if we see a change in the transmit
29171d51719SMichael Gmelin 		 * FIFO level as progress is being made.
29271d51719SMichael Gmelin 		 */
293847f557cSVladimir Kondratyev 		if (intr & (IG4_INTR_TX_EMPTY | IG4_INTR_STOP_DET)) {
29471d51719SMichael Gmelin 			v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK;
29571d51719SMichael Gmelin 			if (txlvl != v) {
29671d51719SMichael Gmelin 				txlvl = v;
29771d51719SMichael Gmelin 				count_us = 0;
29871d51719SMichael Gmelin 			}
29971d51719SMichael Gmelin 		}
30071d51719SMichael Gmelin 
30171d51719SMichael Gmelin 		/*
30271d51719SMichael Gmelin 		 * Stop if we've run out of time.
30371d51719SMichael Gmelin 		 */
304e3a56ba2SVladimir Kondratyev 		if (count_us >= limit_us) {
305e3a56ba2SVladimir Kondratyev 			error = IIC_ETIMEOUT;
30671d51719SMichael Gmelin 			break;
307e3a56ba2SVladimir Kondratyev 		}
30871d51719SMichael Gmelin 
30971d51719SMichael Gmelin 		/*
310b0eb9d3eSVladimir Kondratyev 		 * When polling is not requested let the interrupt do its work.
31171d51719SMichael Gmelin 		 */
312b0eb9d3eSVladimir Kondratyev 		if (!DO_POLL(sc)) {
313371d37d8SVladimir Kondratyev 			mtx_lock_spin(&sc->io_lock);
31469fac7c3SVladimir Kondratyev 			ig4iic_set_intr_mask(sc, intr | IG4_INTR_ERR_MASK);
315371d37d8SVladimir Kondratyev 			msleep_spin(sc, &sc->io_lock, "i2cwait",
3167f6aee64SVladimir Kondratyev 				  (hz + 99) / 100); /* sleep up to 10ms */
31769fac7c3SVladimir Kondratyev 			ig4iic_set_intr_mask(sc, 0);
318371d37d8SVladimir Kondratyev 			mtx_unlock_spin(&sc->io_lock);
3197f6aee64SVladimir Kondratyev 			count_us += 10000;
32071d51719SMichael Gmelin 		} else {
32171d51719SMichael Gmelin 			DELAY(25);
32271d51719SMichael Gmelin 			count_us += 25;
32371d51719SMichael Gmelin 		}
32471d51719SMichael Gmelin 	}
32571d51719SMichael Gmelin 
32671d51719SMichael Gmelin 	return (error);
32771d51719SMichael Gmelin }
32871d51719SMichael Gmelin 
32971d51719SMichael Gmelin /*
33071d51719SMichael Gmelin  * Set the slave address.  The controller must be disabled when
33171d51719SMichael Gmelin  * changing the address.
33271d51719SMichael Gmelin  *
33371d51719SMichael Gmelin  * This operation does not issue anything to the I2C bus but sets
33471d51719SMichael Gmelin  * the target address for when the controller later issues a START.
33571d51719SMichael Gmelin  */
33671d51719SMichael Gmelin static void
set_slave_addr(ig4iic_softc_t * sc,uint8_t slave)337e3d25549SAndriy Gapon set_slave_addr(ig4iic_softc_t *sc, uint8_t slave)
33871d51719SMichael Gmelin {
33971d51719SMichael Gmelin 	uint32_t tar;
34071d51719SMichael Gmelin 	uint32_t ctl;
34187f55ab0SDimitry Andric 	bool use_10bit;
34271d51719SMichael Gmelin 
34387f55ab0SDimitry Andric 	use_10bit = false;
34471d51719SMichael Gmelin 	if (sc->slave_valid && sc->last_slave == slave &&
34571d51719SMichael Gmelin 	    sc->use_10bit == use_10bit) {
34671d51719SMichael Gmelin 		return;
34771d51719SMichael Gmelin 	}
34871d51719SMichael Gmelin 	sc->use_10bit = use_10bit;
34971d51719SMichael Gmelin 
35071d51719SMichael Gmelin 	/*
35171d51719SMichael Gmelin 	 * Wait for TXFIFO to drain before disabling the controller.
35271d51719SMichael Gmelin 	 */
35313037eaaSAlexander Motin 	reg_write(sc, IG4_REG_TX_TL, 0);
354b0eb9d3eSVladimir Kondratyev 	wait_intr(sc, IG4_INTR_TX_EMPTY);
35571d51719SMichael Gmelin 
35671d51719SMichael Gmelin 	set_controller(sc, 0);
35771d51719SMichael Gmelin 	ctl = reg_read(sc, IG4_REG_CTL);
35871d51719SMichael Gmelin 	ctl &= ~IG4_CTL_10BIT;
35971d51719SMichael Gmelin 	ctl |= IG4_CTL_RESTARTEN;
36071d51719SMichael Gmelin 
36171d51719SMichael Gmelin 	tar = slave;
36271d51719SMichael Gmelin 	if (sc->use_10bit) {
36371d51719SMichael Gmelin 		tar |= IG4_TAR_10BIT;
36471d51719SMichael Gmelin 		ctl |= IG4_CTL_10BIT;
36571d51719SMichael Gmelin 	}
36671d51719SMichael Gmelin 	reg_write(sc, IG4_REG_CTL, ctl);
36771d51719SMichael Gmelin 	reg_write(sc, IG4_REG_TAR_ADD, tar);
36871d51719SMichael Gmelin 	set_controller(sc, IG4_I2C_ENABLE);
36987f55ab0SDimitry Andric 	sc->slave_valid = true;
37071d51719SMichael Gmelin 	sc->last_slave = slave;
37171d51719SMichael Gmelin }
37271d51719SMichael Gmelin 
37371d51719SMichael Gmelin /*
374448897d3SAndriy Gapon  *				IICBUS API FUNCTIONS
375448897d3SAndriy Gapon  */
376448897d3SAndriy Gapon static int
ig4iic_xfer_start(ig4iic_softc_t * sc,uint16_t slave,bool repeated_start)377e3a56ba2SVladimir Kondratyev ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave, bool repeated_start)
378448897d3SAndriy Gapon {
379e3d25549SAndriy Gapon 	set_slave_addr(sc, slave >> 1);
380e3a56ba2SVladimir Kondratyev 
381e3a56ba2SVladimir Kondratyev 	if (!repeated_start) {
382e3a56ba2SVladimir Kondratyev 		/*
3830eb21d1bSVladimir Kondratyev 		 * Clear any previous TX/RX FIFOs overflow/underflow bits
3840eb21d1bSVladimir Kondratyev 		 * and I2C bus STOP condition.
385e3a56ba2SVladimir Kondratyev 		 */
386e3a56ba2SVladimir Kondratyev 		reg_read(sc, IG4_REG_CLR_INTR);
387e3a56ba2SVladimir Kondratyev 	}
388e3a56ba2SVladimir Kondratyev 
389448897d3SAndriy Gapon 	return (0);
390448897d3SAndriy Gapon }
391448897d3SAndriy Gapon 
392847f557cSVladimir Kondratyev static bool
ig4iic_xfer_is_started(ig4iic_softc_t * sc)393847f557cSVladimir Kondratyev ig4iic_xfer_is_started(ig4iic_softc_t *sc)
394847f557cSVladimir Kondratyev {
395847f557cSVladimir Kondratyev 	/*
396847f557cSVladimir Kondratyev 	 * It requires that no IG4_REG_CLR_INTR or IG4_REG_CLR_START/STOP_DET
397847f557cSVladimir Kondratyev 	 * register reads is issued after START condition.
398847f557cSVladimir Kondratyev 	 */
399847f557cSVladimir Kondratyev 	return ((reg_read(sc, IG4_REG_RAW_INTR_STAT) &
400847f557cSVladimir Kondratyev 	    (IG4_INTR_START_DET | IG4_INTR_STOP_DET)) == IG4_INTR_START_DET);
401847f557cSVladimir Kondratyev }
402847f557cSVladimir Kondratyev 
403847f557cSVladimir Kondratyev static int
ig4iic_xfer_abort(ig4iic_softc_t * sc)404847f557cSVladimir Kondratyev ig4iic_xfer_abort(ig4iic_softc_t *sc)
405847f557cSVladimir Kondratyev {
406847f557cSVladimir Kondratyev 	int error;
407847f557cSVladimir Kondratyev 
408847f557cSVladimir Kondratyev 	/* Request send of STOP condition and flush of TX FIFO */
409847f557cSVladimir Kondratyev 	set_controller(sc, IG4_I2C_ABORT | IG4_I2C_ENABLE);
410847f557cSVladimir Kondratyev 	/*
411847f557cSVladimir Kondratyev 	 * Wait for the TX_ABRT interrupt with ABRTSRC_TRANSFER
412847f557cSVladimir Kondratyev 	 * bit set in TX_ABRT_SOURCE register.
413847f557cSVladimir Kondratyev 	 */
414847f557cSVladimir Kondratyev 	error = wait_intr(sc, IG4_INTR_STOP_DET);
415847f557cSVladimir Kondratyev 	set_controller(sc, IG4_I2C_ENABLE);
416847f557cSVladimir Kondratyev 
417847f557cSVladimir Kondratyev 	return (error == IIC_ESTATUS ? 0 : error);
418847f557cSVladimir Kondratyev }
419847f557cSVladimir Kondratyev 
42083a66b9bSVladimir Kondratyev /*
42183a66b9bSVladimir Kondratyev  * Amount of unread data before next burst to get better I2C bus utilization.
42283a66b9bSVladimir Kondratyev  * 2 bytes is enough in FAST mode. 8 bytes is better in FAST+ and HIGH modes.
42383a66b9bSVladimir Kondratyev  * Intel-recommended value is 16 for DMA transfers with 64-byte depth FIFOs.
42483a66b9bSVladimir Kondratyev  */
42583a66b9bSVladimir Kondratyev #define	IG4_FIFO_LOWAT	2
42683a66b9bSVladimir Kondratyev 
427448897d3SAndriy Gapon static int
ig4iic_read(ig4iic_softc_t * sc,uint8_t * buf,uint16_t len,bool repeated_start,bool stop)428448897d3SAndriy Gapon ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len,
429448897d3SAndriy Gapon     bool repeated_start, bool stop)
430448897d3SAndriy Gapon {
431448897d3SAndriy Gapon 	uint32_t cmd;
43283a66b9bSVladimir Kondratyev 	int requested = 0;
43383a66b9bSVladimir Kondratyev 	int received = 0;
43483a66b9bSVladimir Kondratyev 	int burst, target, lowat = 0;
435448897d3SAndriy Gapon 	int error;
436448897d3SAndriy Gapon 
437448897d3SAndriy Gapon 	if (len == 0)
438448897d3SAndriy Gapon 		return (0);
439448897d3SAndriy Gapon 
44083a66b9bSVladimir Kondratyev 	while (received < len) {
44113037eaaSAlexander Motin 		/* Ensure we have some free space in TXFIFO */
44283a66b9bSVladimir Kondratyev 		burst = sc->cfg.txfifo_depth -
44383a66b9bSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK);
44483a66b9bSVladimir Kondratyev 		if (burst <= 0) {
44513037eaaSAlexander Motin 			reg_write(sc, IG4_REG_TX_TL, IG4_FIFO_LOWAT);
446b0eb9d3eSVladimir Kondratyev 			error = wait_intr(sc, IG4_INTR_TX_EMPTY);
447448897d3SAndriy Gapon 			if (error)
448448897d3SAndriy Gapon 				break;
44913037eaaSAlexander Motin 			burst = sc->cfg.txfifo_depth -
45013037eaaSAlexander Motin 			    (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK);
451448897d3SAndriy Gapon 		}
4527814f978SVladimir Kondratyev 		/* Ensure we have enough free space in RXFIFO */
45313037eaaSAlexander Motin 		burst = MIN(burst, sc->cfg.rxfifo_depth -
45413037eaaSAlexander Motin 		    (requested - received));
45583a66b9bSVladimir Kondratyev 		target = MIN(requested + burst, (int)len);
45683a66b9bSVladimir Kondratyev 		while (requested < target) {
45783a66b9bSVladimir Kondratyev 			cmd = IG4_DATA_COMMAND_RD;
45883a66b9bSVladimir Kondratyev 			if (repeated_start && requested == 0)
45983a66b9bSVladimir Kondratyev 				cmd |= IG4_DATA_RESTART;
46083a66b9bSVladimir Kondratyev 			if (stop && requested == len - 1)
46183a66b9bSVladimir Kondratyev 				cmd |= IG4_DATA_STOP;
46283a66b9bSVladimir Kondratyev 			reg_write(sc, IG4_REG_DATA_CMD, cmd);
46383a66b9bSVladimir Kondratyev 			requested++;
46483a66b9bSVladimir Kondratyev 		}
46583a66b9bSVladimir Kondratyev 		/* Leave some data queued to maintain the hardware pipeline */
46683a66b9bSVladimir Kondratyev 		lowat = 0;
46783a66b9bSVladimir Kondratyev 		if (requested != len && requested - received > IG4_FIFO_LOWAT)
46883a66b9bSVladimir Kondratyev 			lowat = IG4_FIFO_LOWAT;
46983a66b9bSVladimir Kondratyev 		/* After TXFLR fills up, clear it by reading available data */
47083a66b9bSVladimir Kondratyev 		while (received < requested - lowat) {
47113037eaaSAlexander Motin 			burst = MIN(requested - received,
47283a66b9bSVladimir Kondratyev 			    reg_read(sc, IG4_REG_RXFLR) & IG4_FIFOLVL_MASK);
47383a66b9bSVladimir Kondratyev 			if (burst > 0) {
47483a66b9bSVladimir Kondratyev 				while (burst--)
47583a66b9bSVladimir Kondratyev 					buf[received++] = 0xFF &
47683a66b9bSVladimir Kondratyev 					    reg_read(sc, IG4_REG_DATA_CMD);
47783a66b9bSVladimir Kondratyev 			} else {
47813037eaaSAlexander Motin 				reg_write(sc, IG4_REG_RX_TL,
47913037eaaSAlexander Motin 				    requested - received - lowat - 1);
480b0eb9d3eSVladimir Kondratyev 				error = wait_intr(sc, IG4_INTR_RX_FULL);
48183a66b9bSVladimir Kondratyev 				if (error)
48283a66b9bSVladimir Kondratyev 					goto out;
48383a66b9bSVladimir Kondratyev 			}
48483a66b9bSVladimir Kondratyev 		}
48583a66b9bSVladimir Kondratyev 	}
48683a66b9bSVladimir Kondratyev out:
487448897d3SAndriy Gapon 	return (error);
488448897d3SAndriy Gapon }
489448897d3SAndriy Gapon 
490448897d3SAndriy Gapon static int
ig4iic_write(ig4iic_softc_t * sc,uint8_t * buf,uint16_t len,bool repeated_start,bool stop)491448897d3SAndriy Gapon ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len,
492448897d3SAndriy Gapon     bool repeated_start, bool stop)
493448897d3SAndriy Gapon {
494448897d3SAndriy Gapon 	uint32_t cmd;
495023c42edSVladimir Kondratyev 	int sent = 0;
496023c42edSVladimir Kondratyev 	int burst, target;
49713037eaaSAlexander Motin 	int error, lowat;
498448897d3SAndriy Gapon 
499448897d3SAndriy Gapon 	if (len == 0)
500448897d3SAndriy Gapon 		return (0);
501448897d3SAndriy Gapon 
502023c42edSVladimir Kondratyev 	while (sent < len) {
503023c42edSVladimir Kondratyev 		burst = sc->cfg.txfifo_depth -
504023c42edSVladimir Kondratyev 		    (reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK);
505023c42edSVladimir Kondratyev 		target = MIN(sent + burst, (int)len);
506023c42edSVladimir Kondratyev 		while (sent < target) {
507023c42edSVladimir Kondratyev 			cmd = buf[sent];
508023c42edSVladimir Kondratyev 			if (repeated_start && sent == 0)
509023c42edSVladimir Kondratyev 				cmd |= IG4_DATA_RESTART;
510023c42edSVladimir Kondratyev 			if (stop && sent == len - 1)
511023c42edSVladimir Kondratyev 				cmd |= IG4_DATA_STOP;
512023c42edSVladimir Kondratyev 			reg_write(sc, IG4_REG_DATA_CMD, cmd);
513023c42edSVladimir Kondratyev 			sent++;
514023c42edSVladimir Kondratyev 		}
515023c42edSVladimir Kondratyev 		if (sent < len) {
51613037eaaSAlexander Motin 			if (len - sent <= sc->cfg.txfifo_depth)
51713037eaaSAlexander Motin 				lowat = sc->cfg.txfifo_depth - (len - sent);
51813037eaaSAlexander Motin 			else
51913037eaaSAlexander Motin 				lowat = IG4_FIFO_LOWAT;
52013037eaaSAlexander Motin 			reg_write(sc, IG4_REG_TX_TL, lowat);
521b0eb9d3eSVladimir Kondratyev 			error = wait_intr(sc, IG4_INTR_TX_EMPTY);
522448897d3SAndriy Gapon 			if (error)
523448897d3SAndriy Gapon 				break;
524023c42edSVladimir Kondratyev 		}
525448897d3SAndriy Gapon 	}
526448897d3SAndriy Gapon 
527448897d3SAndriy Gapon 	return (error);
528448897d3SAndriy Gapon }
529448897d3SAndriy Gapon 
530448897d3SAndriy Gapon int
ig4iic_transfer(device_t dev,struct iic_msg * msgs,uint32_t nmsgs)531448897d3SAndriy Gapon ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
532448897d3SAndriy Gapon {
533448897d3SAndriy Gapon 	ig4iic_softc_t *sc = device_get_softc(dev);
534448897d3SAndriy Gapon 	const char *reason = NULL;
535448897d3SAndriy Gapon 	uint32_t i;
536448897d3SAndriy Gapon 	int error;
537448897d3SAndriy Gapon 	int unit;
538448897d3SAndriy Gapon 	bool rpstart;
539448897d3SAndriy Gapon 	bool stop;
54041b24e09SVladimir Kondratyev 	bool allocated;
541448897d3SAndriy Gapon 
542448897d3SAndriy Gapon 	/*
543448897d3SAndriy Gapon 	 * The hardware interface imposes limits on allowed I2C messages.
544448897d3SAndriy Gapon 	 * It is not possible to explicitly send a start or stop.
545448897d3SAndriy Gapon 	 * They are automatically sent (or not sent, depending on the
546448897d3SAndriy Gapon 	 * configuration) when a data byte is transferred.
547448897d3SAndriy Gapon 	 * For this reason it's impossible to send a message with no data
548448897d3SAndriy Gapon 	 * at all (like an SMBus quick message).
549448897d3SAndriy Gapon 	 * The start condition is automatically generated after the stop
550448897d3SAndriy Gapon 	 * condition, so it's impossible to not have a start after a stop.
551448897d3SAndriy Gapon 	 * The repeated start condition is automatically sent if a change
552448897d3SAndriy Gapon 	 * of the transfer direction happens, so it's impossible to have
553448897d3SAndriy Gapon 	 * a change of direction without a (repeated) start.
554448897d3SAndriy Gapon 	 * The repeated start can be forced even without the change of
555448897d3SAndriy Gapon 	 * direction.
556448897d3SAndriy Gapon 	 * Changing the target slave address requires resetting the hardware
557448897d3SAndriy Gapon 	 * state, so it's impossible to do that without the stop followed
558448897d3SAndriy Gapon 	 * by the start.
559448897d3SAndriy Gapon 	 */
560448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
561448897d3SAndriy Gapon #if 0
562448897d3SAndriy Gapon 		if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) {
563448897d3SAndriy Gapon 			reason = "first message without start";
564448897d3SAndriy Gapon 			break;
565448897d3SAndriy Gapon 		}
566448897d3SAndriy Gapon 		if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) {
567448897d3SAndriy Gapon 			reason = "last message without stop";
568448897d3SAndriy Gapon 			break;
569448897d3SAndriy Gapon 		}
570448897d3SAndriy Gapon #endif
571448897d3SAndriy Gapon 		if (msgs[i].len == 0) {
572448897d3SAndriy Gapon 			reason = "message with no data";
573448897d3SAndriy Gapon 			break;
574448897d3SAndriy Gapon 		}
575448897d3SAndriy Gapon 		if (i > 0) {
576448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
577448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) {
578448897d3SAndriy Gapon 				reason = "stop not followed by start";
579448897d3SAndriy Gapon 				break;
580448897d3SAndriy Gapon 			}
581448897d3SAndriy Gapon 			if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 &&
582448897d3SAndriy Gapon 			    msgs[i].slave != msgs[i - 1].slave) {
583448897d3SAndriy Gapon 				reason = "change of slave without stop";
584448897d3SAndriy Gapon 				break;
585448897d3SAndriy Gapon 			}
586448897d3SAndriy Gapon 			if ((msgs[i].flags & IIC_M_NOSTART) != 0 &&
587448897d3SAndriy Gapon 			    (msgs[i].flags & IIC_M_RD) !=
588448897d3SAndriy Gapon 			    (msgs[i - 1].flags & IIC_M_RD)) {
589448897d3SAndriy Gapon 				reason = "change of direction without repeated"
590448897d3SAndriy Gapon 				    " start";
591448897d3SAndriy Gapon 				break;
592448897d3SAndriy Gapon 			}
593448897d3SAndriy Gapon 		}
594448897d3SAndriy Gapon 	}
595448897d3SAndriy Gapon 	if (reason != NULL) {
596448897d3SAndriy Gapon 		if (bootverbose)
597448897d3SAndriy Gapon 			device_printf(dev, "%s\n", reason);
598448897d3SAndriy Gapon 		return (IIC_ENOTSUPP);
599448897d3SAndriy Gapon 	}
600448897d3SAndriy Gapon 
60141b24e09SVladimir Kondratyev 	/* Check if device is already allocated with iicbus_request_bus() */
60241b24e09SVladimir Kondratyev 	allocated = sx_xlocked(&sc->call_lock) != 0;
60341b24e09SVladimir Kondratyev 	if (!allocated)
604448897d3SAndriy Gapon 		sx_xlock(&sc->call_lock);
605448897d3SAndriy Gapon 
606448897d3SAndriy Gapon 	/* Debugging - dump registers. */
607448897d3SAndriy Gapon 	if (ig4_dump) {
608448897d3SAndriy Gapon 		unit = device_get_unit(dev);
609448897d3SAndriy Gapon 		if (ig4_dump & (1 << unit)) {
610448897d3SAndriy Gapon 			ig4_dump &= ~(1 << unit);
611448897d3SAndriy Gapon 			ig4iic_dump(sc);
612448897d3SAndriy Gapon 		}
613448897d3SAndriy Gapon 	}
614448897d3SAndriy Gapon 
615448897d3SAndriy Gapon 	/*
616448897d3SAndriy Gapon 	 * Clear any previous abort condition that may have been holding
617448897d3SAndriy Gapon 	 * the txfifo in reset.
618448897d3SAndriy Gapon 	 */
619448897d3SAndriy Gapon 	reg_read(sc, IG4_REG_CLR_TX_ABORT);
620448897d3SAndriy Gapon 
621448897d3SAndriy Gapon 	rpstart = false;
622448897d3SAndriy Gapon 	error = 0;
623448897d3SAndriy Gapon 	for (i = 0; i < nmsgs; i++) {
624448897d3SAndriy Gapon 		if ((msgs[i].flags & IIC_M_NOSTART) == 0) {
625e3a56ba2SVladimir Kondratyev 			error = ig4iic_xfer_start(sc, msgs[i].slave, rpstart);
626448897d3SAndriy Gapon 		} else {
627448897d3SAndriy Gapon 			if (!sc->slave_valid ||
628448897d3SAndriy Gapon 			    (msgs[i].slave >> 1) != sc->last_slave) {
629448897d3SAndriy Gapon 				device_printf(dev, "start condition suppressed"
630448897d3SAndriy Gapon 				    "but slave address is not set up");
631448897d3SAndriy Gapon 				error = EINVAL;
632448897d3SAndriy Gapon 				break;
633448897d3SAndriy Gapon 			}
634448897d3SAndriy Gapon 			rpstart = false;
635448897d3SAndriy Gapon 		}
636448897d3SAndriy Gapon 		if (error != 0)
637448897d3SAndriy Gapon 			break;
638448897d3SAndriy Gapon 
639448897d3SAndriy Gapon 		stop = (msgs[i].flags & IIC_M_NOSTOP) == 0;
640448897d3SAndriy Gapon 		if (msgs[i].flags & IIC_M_RD)
641448897d3SAndriy Gapon 			error = ig4iic_read(sc, msgs[i].buf, msgs[i].len,
642448897d3SAndriy Gapon 			    rpstart, stop);
643448897d3SAndriy Gapon 		else
644448897d3SAndriy Gapon 			error = ig4iic_write(sc, msgs[i].buf, msgs[i].len,
645448897d3SAndriy Gapon 			    rpstart, stop);
646847f557cSVladimir Kondratyev 
6470eb21d1bSVladimir Kondratyev 		/* Wait for error or stop condition occurred on the I2C bus */
6480eb21d1bSVladimir Kondratyev 		if (stop && error == 0) {
6490eb21d1bSVladimir Kondratyev 			error = wait_intr(sc, IG4_INTR_STOP_DET);
6500eb21d1bSVladimir Kondratyev 			if (error == 0)
6510eb21d1bSVladimir Kondratyev 				reg_read(sc, IG4_REG_CLR_INTR);
6520eb21d1bSVladimir Kondratyev 		}
6530eb21d1bSVladimir Kondratyev 
654847f557cSVladimir Kondratyev 		if (error != 0) {
655847f557cSVladimir Kondratyev 			/*
656847f557cSVladimir Kondratyev 			 * Send STOP condition if it's not done yet and flush
657847f557cSVladimir Kondratyev 			 * both FIFOs. Do a controller soft reset if transfer
658847f557cSVladimir Kondratyev 			 * abort is failed.
659847f557cSVladimir Kondratyev 			 */
660847f557cSVladimir Kondratyev 			if (ig4iic_xfer_is_started(sc) &&
661847f557cSVladimir Kondratyev 			    ig4iic_xfer_abort(sc) != 0) {
662847f557cSVladimir Kondratyev 				device_printf(sc->dev, "Failed to abort "
663847f557cSVladimir Kondratyev 				    "transfer. Do the controller reset.\n");
664847f557cSVladimir Kondratyev 				ig4iic_set_config(sc, true);
665847f557cSVladimir Kondratyev 			} else {
666847f557cSVladimir Kondratyev 				while (reg_read(sc, IG4_REG_I2C_STA) &
667847f557cSVladimir Kondratyev 				    IG4_STATUS_RX_NOTEMPTY)
668847f557cSVladimir Kondratyev 					reg_read(sc, IG4_REG_DATA_CMD);
669847f557cSVladimir Kondratyev 				reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
670847f557cSVladimir Kondratyev 				reg_read(sc, IG4_REG_CLR_INTR);
671847f557cSVladimir Kondratyev 			}
672448897d3SAndriy Gapon 			break;
673847f557cSVladimir Kondratyev 		}
674448897d3SAndriy Gapon 
675448897d3SAndriy Gapon 		rpstart = !stop;
676448897d3SAndriy Gapon 	}
677448897d3SAndriy Gapon 
678493715f9SAhmad Khalifa 	if (error == IIC_ENOACK && bootverbose)
679493715f9SAhmad Khalifa 		device_printf(dev, "Warning: NACK for slave address 0x%x\n",
680493715f9SAhmad Khalifa 		    msgs[i].slave >> 1);
681493715f9SAhmad Khalifa 
68241b24e09SVladimir Kondratyev 	if (!allocated)
683448897d3SAndriy Gapon 		sx_unlock(&sc->call_lock);
684448897d3SAndriy Gapon 	return (error);
685448897d3SAndriy Gapon }
686448897d3SAndriy Gapon 
687448897d3SAndriy Gapon int
ig4iic_reset(device_t dev,u_char speed,u_char addr,u_char * oldaddr)688448897d3SAndriy Gapon ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
689448897d3SAndriy Gapon {
690448897d3SAndriy Gapon 	ig4iic_softc_t *sc = device_get_softc(dev);
69141b24e09SVladimir Kondratyev 	bool allocated;
692448897d3SAndriy Gapon 
69341b24e09SVladimir Kondratyev 	allocated = sx_xlocked(&sc->call_lock) != 0;
69441b24e09SVladimir Kondratyev 	if (!allocated)
695448897d3SAndriy Gapon 		sx_xlock(&sc->call_lock);
696448897d3SAndriy Gapon 
697448897d3SAndriy Gapon 	/* TODO handle speed configuration? */
698448897d3SAndriy Gapon 	if (oldaddr != NULL)
699448897d3SAndriy Gapon 		*oldaddr = sc->last_slave << 1;
700e3d25549SAndriy Gapon 	set_slave_addr(sc, addr >> 1);
701448897d3SAndriy Gapon 	if (addr == IIC_UNKNOWN)
702448897d3SAndriy Gapon 		sc->slave_valid = false;
703448897d3SAndriy Gapon 
70441b24e09SVladimir Kondratyev 	if (!allocated)
705448897d3SAndriy Gapon 		sx_unlock(&sc->call_lock);
706448897d3SAndriy Gapon 	return (0);
707448897d3SAndriy Gapon }
708448897d3SAndriy Gapon 
70941b24e09SVladimir Kondratyev int
ig4iic_callback(device_t dev,int index,caddr_t data)71041b24e09SVladimir Kondratyev ig4iic_callback(device_t dev, int index, caddr_t data)
71141b24e09SVladimir Kondratyev {
71241b24e09SVladimir Kondratyev 	ig4iic_softc_t *sc = device_get_softc(dev);
71341b24e09SVladimir Kondratyev 	int error = 0;
71441b24e09SVladimir Kondratyev 	int how;
71541b24e09SVladimir Kondratyev 
71641b24e09SVladimir Kondratyev 	switch (index) {
71741b24e09SVladimir Kondratyev 	case IIC_REQUEST_BUS:
71841b24e09SVladimir Kondratyev 		/* force polling if ig4iic is requested with IIC_DONTWAIT */
71941b24e09SVladimir Kondratyev 		how = *(int *)data;
72041b24e09SVladimir Kondratyev 		if ((how & IIC_WAIT) == 0) {
72141b24e09SVladimir Kondratyev 			if (sx_try_xlock(&sc->call_lock) == 0)
72241b24e09SVladimir Kondratyev 				error = IIC_EBUSBSY;
72341b24e09SVladimir Kondratyev 			else
72441b24e09SVladimir Kondratyev 				sc->poll = true;
72541b24e09SVladimir Kondratyev 		} else
72641b24e09SVladimir Kondratyev 			sx_xlock(&sc->call_lock);
72741b24e09SVladimir Kondratyev 		break;
72841b24e09SVladimir Kondratyev 
72941b24e09SVladimir Kondratyev 	case IIC_RELEASE_BUS:
73041b24e09SVladimir Kondratyev 		sc->poll = false;
73141b24e09SVladimir Kondratyev 		sx_unlock(&sc->call_lock);
73241b24e09SVladimir Kondratyev 		break;
73341b24e09SVladimir Kondratyev 
73441b24e09SVladimir Kondratyev 	default:
73541b24e09SVladimir Kondratyev 		error = errno2iic(EINVAL);
73641b24e09SVladimir Kondratyev 	}
73741b24e09SVladimir Kondratyev 
73841b24e09SVladimir Kondratyev 	return (error);
73941b24e09SVladimir Kondratyev }
74041b24e09SVladimir Kondratyev 
741448897d3SAndriy Gapon /*
74288512838SVladimir Kondratyev  * Clock register values can be calculated with following rough equations:
74388512838SVladimir Kondratyev  * SCL_HCNT = ceil(IC clock rate * tHIGH)
74488512838SVladimir Kondratyev  * SCL_LCNT = ceil(IC clock rate * tLOW)
74588512838SVladimir Kondratyev  * SDA_HOLD = ceil(IC clock rate * SDA hold time)
74688512838SVladimir Kondratyev  * Precise equations take signal's falling, rising and spike suppression
74788512838SVladimir Kondratyev  * times in to account. They can be found in Synopsys or Intel documentation.
74888512838SVladimir Kondratyev  *
74988512838SVladimir Kondratyev  * Here we snarf formulas and defaults from Linux driver to be able to use
75088512838SVladimir Kondratyev  * timing values provided by Intel LPSS driver "as is".
75188512838SVladimir Kondratyev  */
75288512838SVladimir Kondratyev static int
ig4iic_clk_params(const struct ig4_hw * hw,int speed,uint16_t * scl_hcnt,uint16_t * scl_lcnt,uint16_t * sda_hold)75388512838SVladimir Kondratyev ig4iic_clk_params(const struct ig4_hw *hw, int speed,
75488512838SVladimir Kondratyev     uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
75588512838SVladimir Kondratyev {
75688512838SVladimir Kondratyev 	uint32_t thigh, tlow, tf_max;	/* nsec */
75788512838SVladimir Kondratyev 	uint32_t sda_fall_time;		/* nsec */
75888512838SVladimir Kondratyev         uint32_t scl_fall_time;		/* nsec */
75988512838SVladimir Kondratyev 
76088512838SVladimir Kondratyev 	switch (speed) {
76188512838SVladimir Kondratyev 	case IG4_CTL_SPEED_STD:
76288512838SVladimir Kondratyev 		thigh = IG4_SPEED_STD_THIGH;
76388512838SVladimir Kondratyev 		tlow = IG4_SPEED_STD_TLOW;
76488512838SVladimir Kondratyev 		tf_max = IG4_SPEED_STD_TF_MAX;
76588512838SVladimir Kondratyev 		break;
76688512838SVladimir Kondratyev 
76788512838SVladimir Kondratyev 	case IG4_CTL_SPEED_FAST:
76888512838SVladimir Kondratyev 		thigh = IG4_SPEED_FAST_THIGH;
76988512838SVladimir Kondratyev 		tlow = IG4_SPEED_FAST_TLOW;
77088512838SVladimir Kondratyev 		tf_max = IG4_SPEED_FAST_TF_MAX;
77188512838SVladimir Kondratyev 		break;
77288512838SVladimir Kondratyev 
77388512838SVladimir Kondratyev 	default:
77488512838SVladimir Kondratyev 		return (EINVAL);
77588512838SVladimir Kondratyev 	}
77688512838SVladimir Kondratyev 
77788512838SVladimir Kondratyev 	/* Use slowest falling time defaults to be on the safe side */
77888512838SVladimir Kondratyev 	sda_fall_time = hw->sda_fall_time == 0 ? tf_max : hw->sda_fall_time;
77988512838SVladimir Kondratyev 	*scl_hcnt = (uint16_t)
78088512838SVladimir Kondratyev 	    ((hw->ic_clock_rate * (thigh + sda_fall_time) + 500) / 1000 - 3);
78188512838SVladimir Kondratyev 
78288512838SVladimir Kondratyev 	scl_fall_time = hw->scl_fall_time == 0 ? tf_max : hw->scl_fall_time;
78388512838SVladimir Kondratyev 	*scl_lcnt = (uint16_t)
78488512838SVladimir Kondratyev 	    ((hw->ic_clock_rate * (tlow + scl_fall_time) + 500) / 1000 - 1);
78588512838SVladimir Kondratyev 
78688512838SVladimir Kondratyev 	/*
78788512838SVladimir Kondratyev 	 * There is no "known good" default value for tHD;DAT so keep SDA_HOLD
78888512838SVladimir Kondratyev 	 * intact if sda_hold_time value is not provided.
78988512838SVladimir Kondratyev 	 */
79088512838SVladimir Kondratyev 	if (hw->sda_hold_time != 0)
79188512838SVladimir Kondratyev 		*sda_hold = (uint16_t)
79288512838SVladimir Kondratyev 		    ((hw->ic_clock_rate * hw->sda_hold_time + 500) / 1000);
79388512838SVladimir Kondratyev 
79488512838SVladimir Kondratyev 	return (0);
79588512838SVladimir Kondratyev }
79688512838SVladimir Kondratyev 
79788512838SVladimir Kondratyev #ifdef DEV_ACPI
79888512838SVladimir Kondratyev static ACPI_STATUS
ig4iic_acpi_params(ACPI_HANDLE handle,char * method,uint16_t * scl_hcnt,uint16_t * scl_lcnt,uint16_t * sda_hold)79988512838SVladimir Kondratyev ig4iic_acpi_params(ACPI_HANDLE handle, char *method,
80088512838SVladimir Kondratyev     uint16_t *scl_hcnt, uint16_t *scl_lcnt, uint16_t *sda_hold)
80188512838SVladimir Kondratyev {
80288512838SVladimir Kondratyev 	ACPI_BUFFER buf;
80388512838SVladimir Kondratyev 	ACPI_OBJECT *obj, *elems;
80488512838SVladimir Kondratyev 	ACPI_STATUS status;
80588512838SVladimir Kondratyev 
80688512838SVladimir Kondratyev 	buf.Pointer = NULL;
80788512838SVladimir Kondratyev 	buf.Length = ACPI_ALLOCATE_BUFFER;
80888512838SVladimir Kondratyev 
80988512838SVladimir Kondratyev 	status = AcpiEvaluateObject(handle, method, NULL, &buf);
81088512838SVladimir Kondratyev 	if (ACPI_FAILURE(status))
81188512838SVladimir Kondratyev 		return (status);
81288512838SVladimir Kondratyev 
81388512838SVladimir Kondratyev 	status = AE_TYPE;
81488512838SVladimir Kondratyev 	obj = (ACPI_OBJECT *)buf.Pointer;
81588512838SVladimir Kondratyev 	if (obj->Type == ACPI_TYPE_PACKAGE && obj->Package.Count == 3) {
81688512838SVladimir Kondratyev 		elems = obj->Package.Elements;
81788512838SVladimir Kondratyev 		*scl_hcnt = elems[0].Integer.Value & IG4_SCL_CLOCK_MASK;
81888512838SVladimir Kondratyev 		*scl_lcnt = elems[1].Integer.Value & IG4_SCL_CLOCK_MASK;
81988512838SVladimir Kondratyev 		*sda_hold = elems[2].Integer.Value & IG4_SDA_TX_HOLD_MASK;
82088512838SVladimir Kondratyev 		status = AE_OK;
82188512838SVladimir Kondratyev 	}
82288512838SVladimir Kondratyev 
82388512838SVladimir Kondratyev 	AcpiOsFree(obj);
82488512838SVladimir Kondratyev 
82588512838SVladimir Kondratyev 	return (status);
82688512838SVladimir Kondratyev }
82788512838SVladimir Kondratyev #endif /* DEV_ACPI */
82888512838SVladimir Kondratyev 
82988512838SVladimir Kondratyev static void
ig4iic_get_config(ig4iic_softc_t * sc)83088512838SVladimir Kondratyev ig4iic_get_config(ig4iic_softc_t *sc)
83188512838SVladimir Kondratyev {
83288512838SVladimir Kondratyev 	const struct ig4_hw *hw;
83383a66b9bSVladimir Kondratyev 	uint32_t v;
83488512838SVladimir Kondratyev #ifdef DEV_ACPI
83588512838SVladimir Kondratyev 	ACPI_HANDLE handle;
83688512838SVladimir Kondratyev #endif
83788512838SVladimir Kondratyev 	/* Fetch default hardware config from controller */
83888512838SVladimir Kondratyev 	sc->cfg.version = reg_read(sc, IG4_REG_COMP_VER);
83988512838SVladimir Kondratyev 	sc->cfg.bus_speed = reg_read(sc, IG4_REG_CTL) & IG4_CTL_SPEED_MASK;
84088512838SVladimir Kondratyev 	sc->cfg.ss_scl_hcnt =
84188512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SS_SCL_HCNT) & IG4_SCL_CLOCK_MASK;
84288512838SVladimir Kondratyev 	sc->cfg.ss_scl_lcnt =
84388512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SS_SCL_LCNT) & IG4_SCL_CLOCK_MASK;
84488512838SVladimir Kondratyev 	sc->cfg.fs_scl_hcnt =
84588512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_FS_SCL_HCNT) & IG4_SCL_CLOCK_MASK;
84688512838SVladimir Kondratyev 	sc->cfg.fs_scl_lcnt =
84788512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_FS_SCL_LCNT) & IG4_SCL_CLOCK_MASK;
84888512838SVladimir Kondratyev 	sc->cfg.ss_sda_hold = sc->cfg.fs_sda_hold =
84988512838SVladimir Kondratyev 	    reg_read(sc, IG4_REG_SDA_HOLD) & IG4_SDA_TX_HOLD_MASK;
85088512838SVladimir Kondratyev 
85188512838SVladimir Kondratyev 	if (sc->cfg.bus_speed != IG4_CTL_SPEED_STD)
85288512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
85388512838SVladimir Kondratyev 
85483a66b9bSVladimir Kondratyev 	/* REG_COMP_PARAM1 is not documented in latest Intel specs */
85583a66b9bSVladimir Kondratyev 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
85683a66b9bSVladimir Kondratyev 		v = reg_read(sc, IG4_REG_COMP_PARAM1);
85783a66b9bSVladimir Kondratyev 		if (IG4_PARAM1_TXFIFO_DEPTH(v) != 0)
85883a66b9bSVladimir Kondratyev 			sc->cfg.txfifo_depth = IG4_PARAM1_TXFIFO_DEPTH(v);
85983a66b9bSVladimir Kondratyev 		if (IG4_PARAM1_RXFIFO_DEPTH(v) != 0)
86083a66b9bSVladimir Kondratyev 			sc->cfg.rxfifo_depth = IG4_PARAM1_RXFIFO_DEPTH(v);
86183a66b9bSVladimir Kondratyev 	}
86283a66b9bSVladimir Kondratyev 
86388512838SVladimir Kondratyev 	/* Override hardware config with IC_clock-based counter values */
86488512838SVladimir Kondratyev 	if (ig4_timings < 2 && sc->version < nitems(ig4iic_hw)) {
86588512838SVladimir Kondratyev 		hw = &ig4iic_hw[sc->version];
86688512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_FAST;
86788512838SVladimir Kondratyev 		ig4iic_clk_params(hw, IG4_CTL_SPEED_STD, &sc->cfg.ss_scl_hcnt,
86888512838SVladimir Kondratyev 		    &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
86988512838SVladimir Kondratyev 		ig4iic_clk_params(hw, IG4_CTL_SPEED_FAST, &sc->cfg.fs_scl_hcnt,
87088512838SVladimir Kondratyev 		    &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
87183a66b9bSVladimir Kondratyev 		if (hw->txfifo_depth != 0)
87283a66b9bSVladimir Kondratyev 			sc->cfg.txfifo_depth = hw->txfifo_depth;
87383a66b9bSVladimir Kondratyev 		if (hw->rxfifo_depth != 0)
87483a66b9bSVladimir Kondratyev 			sc->cfg.rxfifo_depth = hw->rxfifo_depth;
87588512838SVladimir Kondratyev 	} else if (ig4_timings == 2) {
87688512838SVladimir Kondratyev 		/*
87788512838SVladimir Kondratyev 		 * Timings of original ig4 driver:
87888512838SVladimir Kondratyev 		 * Program based on a 25000 Hz clock.  This is a bit of a
87988512838SVladimir Kondratyev 		 * hack (obviously).  The defaults are 400 and 470 for standard
88088512838SVladimir Kondratyev 		 * and 60 and 130 for fast.  The defaults for standard fail
88188512838SVladimir Kondratyev 		 * utterly (presumably cause an abort) because the clock time
88288512838SVladimir Kondratyev 		 * is ~18.8ms by default.  This brings it down to ~4ms.
88388512838SVladimir Kondratyev 		 */
88488512838SVladimir Kondratyev 		sc->cfg.bus_speed = IG4_CTL_SPEED_STD;
88588512838SVladimir Kondratyev 		sc->cfg.ss_scl_hcnt = sc->cfg.fs_scl_hcnt = 100;
88688512838SVladimir Kondratyev 		sc->cfg.ss_scl_lcnt = sc->cfg.fs_scl_lcnt = 125;
88788512838SVladimir Kondratyev 		if (sc->version == IG4_SKYLAKE)
88888512838SVladimir Kondratyev 			sc->cfg.ss_sda_hold = sc->cfg.fs_sda_hold = 28;
88988512838SVladimir Kondratyev 	}
89088512838SVladimir Kondratyev 
89188512838SVladimir Kondratyev #ifdef DEV_ACPI
89288512838SVladimir Kondratyev 	/* Evaluate SSCN and FMCN ACPI methods to fetch timings */
89388512838SVladimir Kondratyev 	if (ig4_timings == 0 && (handle = acpi_get_handle(sc->dev)) != NULL) {
89488512838SVladimir Kondratyev 		ig4iic_acpi_params(handle, "SSCN", &sc->cfg.ss_scl_hcnt,
89588512838SVladimir Kondratyev 		    &sc->cfg.ss_scl_lcnt, &sc->cfg.ss_sda_hold);
89688512838SVladimir Kondratyev 		ig4iic_acpi_params(handle, "FMCN", &sc->cfg.fs_scl_hcnt,
89788512838SVladimir Kondratyev 		    &sc->cfg.fs_scl_lcnt, &sc->cfg.fs_sda_hold);
89888512838SVladimir Kondratyev 	}
89988512838SVladimir Kondratyev #endif
90088512838SVladimir Kondratyev 
90188512838SVladimir Kondratyev 	if (bootverbose) {
90288512838SVladimir Kondratyev 		device_printf(sc->dev, "Controller parameters:\n");
90388512838SVladimir Kondratyev 		printf("  Speed: %s\n",
90488512838SVladimir Kondratyev 		    sc->cfg.bus_speed == IG4_CTL_SPEED_STD ? "Std" : "Fast");
90588512838SVladimir Kondratyev 		printf("  Regs:  HCNT  :LCNT  :SDAHLD\n");
90688512838SVladimir Kondratyev 		printf("  Std:   0x%04hx:0x%04hx:0x%04hx\n",
90788512838SVladimir Kondratyev 		    sc->cfg.ss_scl_hcnt, sc->cfg.ss_scl_lcnt,
90888512838SVladimir Kondratyev 		    sc->cfg.ss_sda_hold);
90988512838SVladimir Kondratyev 		printf("  Fast:  0x%04hx:0x%04hx:0x%04hx\n",
91088512838SVladimir Kondratyev 		    sc->cfg.fs_scl_hcnt, sc->cfg.fs_scl_lcnt,
91188512838SVladimir Kondratyev 		    sc->cfg.fs_sda_hold);
91288512838SVladimir Kondratyev 	}
91388512838SVladimir Kondratyev }
91488512838SVladimir Kondratyev 
915db7caa2eSVladimir Kondratyev static int
ig4iic_set_config(ig4iic_softc_t * sc,bool reset)916847f557cSVladimir Kondratyev ig4iic_set_config(ig4iic_softc_t *sc, bool reset)
91771d51719SMichael Gmelin {
91871d51719SMichael Gmelin 	uint32_t v;
91971d51719SMichael Gmelin 
920b16d03adSOleksandr Tymoshenko 	v = reg_read(sc, IG4_REG_DEVIDLE_CTRL);
92181e81838SVladimir Kondratyev 	if (IG4_HAS_ADDREGS(sc->version) && (v & IG4_RESTORE_REQUIRED)) {
922b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED);
923b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0);
924847f557cSVladimir Kondratyev 		pause("i2crst", 1);
925847f557cSVladimir Kondratyev 		reset = true;
926847f557cSVladimir Kondratyev 	}
927b16d03adSOleksandr Tymoshenko 
928847f557cSVladimir Kondratyev 	if ((sc->version == IG4_HASWELL || sc->version == IG4_ATOM) && reset) {
929847f557cSVladimir Kondratyev 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
930847f557cSVladimir Kondratyev 		reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
93181e81838SVladimir Kondratyev 	} else if (IG4_HAS_ADDREGS(sc->version) && reset) {
932b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
933b16d03adSOleksandr Tymoshenko 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
934b16d03adSOleksandr Tymoshenko 	}
935b16d03adSOleksandr Tymoshenko 
936b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM)
93771d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_TYPE);
938b3e8ee5dSOleksandr Tymoshenko 
939b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
94071d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_PARAM1);
94171d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_GENERAL);
942b3e8ee5dSOleksandr Tymoshenko 		/*
943b3e8ee5dSOleksandr Tymoshenko 		 * The content of IG4_REG_GENERAL is different for each
944b3e8ee5dSOleksandr Tymoshenko 		 * controller version.
945b3e8ee5dSOleksandr Tymoshenko 		 */
946b3e8ee5dSOleksandr Tymoshenko 		if (sc->version == IG4_HASWELL &&
947b3e8ee5dSOleksandr Tymoshenko 		    (v & IG4_GENERAL_SWMODE) == 0) {
94871d51719SMichael Gmelin 			v |= IG4_GENERAL_SWMODE;
94971d51719SMichael Gmelin 			reg_write(sc, IG4_REG_GENERAL, v);
95071d51719SMichael Gmelin 			v = reg_read(sc, IG4_REG_GENERAL);
95171d51719SMichael Gmelin 		}
952b3e8ee5dSOleksandr Tymoshenko 	}
95371d51719SMichael Gmelin 
954b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
95571d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
95671d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
95781e81838SVladimir Kondratyev 	} else if (IG4_HAS_ADDREGS(sc->version)) {
958b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_ACTIVE_LTR_VALUE);
959b3e8ee5dSOleksandr Tymoshenko 		v = reg_read(sc, IG4_REG_IDLE_LTR_VALUE);
960b3e8ee5dSOleksandr Tymoshenko 	}
96171d51719SMichael Gmelin 
962b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
96371d51719SMichael Gmelin 		v = reg_read(sc, IG4_REG_COMP_VER);
964db7caa2eSVladimir Kondratyev 		if (v < IG4_COMP_MIN_VER)
965db7caa2eSVladimir Kondratyev 			return(ENXIO);
966b3e8ee5dSOleksandr Tymoshenko 	}
967d117e363SVladimir Kondratyev 
968d117e363SVladimir Kondratyev 	if (set_controller(sc, 0)) {
969d117e363SVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-1\n");
970db7caa2eSVladimir Kondratyev 		return (ENXIO);
971d117e363SVladimir Kondratyev 	}
972d117e363SVladimir Kondratyev 
97321e459c6SVladimir Kondratyev 	reg_read(sc, IG4_REG_CLR_INTR);
97421e459c6SVladimir Kondratyev 	reg_write(sc, IG4_REG_INTR_MASK, 0);
97521e459c6SVladimir Kondratyev 	sc->intr_mask = 0;
97621e459c6SVladimir Kondratyev 
97788512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SS_SCL_HCNT, sc->cfg.ss_scl_hcnt);
97888512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SS_SCL_LCNT, sc->cfg.ss_scl_lcnt);
97988512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_FS_SCL_HCNT, sc->cfg.fs_scl_hcnt);
98088512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_FS_SCL_LCNT, sc->cfg.fs_scl_lcnt);
98188512838SVladimir Kondratyev 	reg_write(sc, IG4_REG_SDA_HOLD,
98288512838SVladimir Kondratyev 	    (sc->cfg.bus_speed  & IG4_CTL_SPEED_MASK) == IG4_CTL_SPEED_STD ?
98388512838SVladimir Kondratyev 	      sc->cfg.ss_sda_hold : sc->cfg.fs_sda_hold);
98471d51719SMichael Gmelin 
985811ff4ddSVladimir Kondratyev 	reg_write(sc, IG4_REG_RX_TL, 0);
9867f6aee64SVladimir Kondratyev 	reg_write(sc, IG4_REG_TX_TL, 0);
98771d51719SMichael Gmelin 
98871d51719SMichael Gmelin 	reg_write(sc, IG4_REG_CTL,
98971d51719SMichael Gmelin 		  IG4_CTL_MASTER |
99071d51719SMichael Gmelin 		  IG4_CTL_SLAVE_DISABLE |
99171d51719SMichael Gmelin 		  IG4_CTL_RESTARTEN |
99288512838SVladimir Kondratyev 		  (sc->cfg.bus_speed & IG4_CTL_SPEED_MASK));
99371d51719SMichael Gmelin 
994847f557cSVladimir Kondratyev 	/* Force setting of the target address on the next transfer */
99587f55ab0SDimitry Andric 	sc->slave_valid = false;
996847f557cSVladimir Kondratyev 
997db7caa2eSVladimir Kondratyev 	return (0);
998db7caa2eSVladimir Kondratyev }
999db7caa2eSVladimir Kondratyev 
10009c9d7fddSAlexander Motin static void
ig4iic_get_fifo(ig4iic_softc_t * sc)10019c9d7fddSAlexander Motin ig4iic_get_fifo(ig4iic_softc_t *sc)
10029c9d7fddSAlexander Motin {
10039c9d7fddSAlexander Motin 	uint32_t v;
10049c9d7fddSAlexander Motin 
10059c9d7fddSAlexander Motin 	/*
10069c9d7fddSAlexander Motin 	 * Hardware does not allow FIFO Threshold Levels value to be set larger
10079c9d7fddSAlexander Motin 	 * than the depth of the buffer.  If an attempt is made to do that, the
10089c9d7fddSAlexander Motin 	 * actual value set will be the maximum depth of the buffer.
10099c9d7fddSAlexander Motin 	 */
10109c9d7fddSAlexander Motin 	if (sc->cfg.txfifo_depth == 0) {
10119c9d7fddSAlexander Motin 		v = reg_read(sc, IG4_REG_TX_TL);
10129c9d7fddSAlexander Motin 		reg_write(sc, IG4_REG_TX_TL, v | IG4_FIFO_MASK);
10139c9d7fddSAlexander Motin 		sc->cfg.txfifo_depth =
10149c9d7fddSAlexander Motin 		    (reg_read(sc, IG4_REG_TX_TL) & IG4_FIFO_MASK) + 1;
10159c9d7fddSAlexander Motin 		reg_write(sc, IG4_REG_TX_TL, v);
10169c9d7fddSAlexander Motin 	}
10179c9d7fddSAlexander Motin 	if (sc->cfg.rxfifo_depth == 0) {
10189c9d7fddSAlexander Motin 		v = reg_read(sc, IG4_REG_RX_TL);
10199c9d7fddSAlexander Motin 		reg_write(sc, IG4_REG_RX_TL, v | IG4_FIFO_MASK);
10209c9d7fddSAlexander Motin 		sc->cfg.rxfifo_depth =
10219c9d7fddSAlexander Motin 		    (reg_read(sc, IG4_REG_RX_TL) & IG4_FIFO_MASK) + 1;
10229c9d7fddSAlexander Motin 		reg_write(sc, IG4_REG_RX_TL, v);
10239c9d7fddSAlexander Motin 	}
10249c9d7fddSAlexander Motin 	if (bootverbose) {
10259c9d7fddSAlexander Motin 		printf("  FIFO:  RX:0x%04x: TX:0x%04x\n",
10269c9d7fddSAlexander Motin 		    sc->cfg.rxfifo_depth, sc->cfg.txfifo_depth);
10279c9d7fddSAlexander Motin 	}
10289c9d7fddSAlexander Motin }
10299c9d7fddSAlexander Motin 
1030db7caa2eSVladimir Kondratyev /*
1031db7caa2eSVladimir Kondratyev  * Called from ig4iic_pci_attach/detach()
1032db7caa2eSVladimir Kondratyev  */
1033db7caa2eSVladimir Kondratyev int
ig4iic_attach(ig4iic_softc_t * sc)1034db7caa2eSVladimir Kondratyev ig4iic_attach(ig4iic_softc_t *sc)
1035db7caa2eSVladimir Kondratyev {
1036db7caa2eSVladimir Kondratyev 	int error;
1037db7caa2eSVladimir Kondratyev 
1038371d37d8SVladimir Kondratyev 	mtx_init(&sc->io_lock, "IG4 I/O lock", NULL, MTX_SPIN);
1039db7caa2eSVladimir Kondratyev 	sx_init(&sc->call_lock, "IG4 call lock");
1040db7caa2eSVladimir Kondratyev 
1041db7caa2eSVladimir Kondratyev 	ig4iic_get_config(sc);
1042db7caa2eSVladimir Kondratyev 
104381e81838SVladimir Kondratyev 	error = ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version));
1044db7caa2eSVladimir Kondratyev 	if (error)
1045db7caa2eSVladimir Kondratyev 		goto done;
10469c9d7fddSAlexander Motin 	ig4iic_get_fifo(sc);
1047db7caa2eSVladimir Kondratyev 
1048448897d3SAndriy Gapon 	sc->iicbus = device_add_child(sc->dev, "iicbus", -1);
1049448897d3SAndriy Gapon 	if (sc->iicbus == NULL) {
1050448897d3SAndriy Gapon 		device_printf(sc->dev, "iicbus driver not found\n");
105171d51719SMichael Gmelin 		error = ENXIO;
105271d51719SMichael Gmelin 		goto done;
105371d51719SMichael Gmelin 	}
105471d51719SMichael Gmelin 
1055bf9c3c58SVladimir Kondratyev 	if (set_controller(sc, IG4_I2C_ENABLE)) {
105671d51719SMichael Gmelin 		device_printf(sc->dev, "controller error during attach-2\n");
1057bf9c3c58SVladimir Kondratyev 		error = ENXIO;
1058bf9c3c58SVladimir Kondratyev 		goto done;
1059bf9c3c58SVladimir Kondratyev 	}
1060edcf6a9fSVladimir Kondratyev 	if (set_controller(sc, 0)) {
1061edcf6a9fSVladimir Kondratyev 		device_printf(sc->dev, "controller error during attach-3\n");
1062edcf6a9fSVladimir Kondratyev 		error = ENXIO;
1063edcf6a9fSVladimir Kondratyev 		goto done;
1064edcf6a9fSVladimir Kondratyev 	}
106571d51719SMichael Gmelin 	error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
1066371d37d8SVladimir Kondratyev 			       ig4iic_intr, NULL, sc, &sc->intr_handle);
106771d51719SMichael Gmelin 	if (error) {
106871d51719SMichael Gmelin 		device_printf(sc->dev,
106971d51719SMichael Gmelin 			      "Unable to setup irq: error %d\n", error);
107071d51719SMichael Gmelin 	}
107171d51719SMichael Gmelin 
107271d51719SMichael Gmelin 	error = bus_generic_attach(sc->dev);
107371d51719SMichael Gmelin 	if (error) {
107471d51719SMichael Gmelin 		device_printf(sc->dev,
107571d51719SMichael Gmelin 			      "failed to attach child: error %d\n", error);
107671d51719SMichael Gmelin 	}
10776777ccd9SVladimir Kondratyev 
10786777ccd9SVladimir Kondratyev done:
10796777ccd9SVladimir Kondratyev 	return (error);
108071d51719SMichael Gmelin }
108171d51719SMichael Gmelin 
108271d51719SMichael Gmelin int
ig4iic_detach(ig4iic_softc_t * sc)108371d51719SMichael Gmelin ig4iic_detach(ig4iic_softc_t *sc)
108471d51719SMichael Gmelin {
108571d51719SMichael Gmelin 	int error;
108671d51719SMichael Gmelin 
108771d51719SMichael Gmelin 	if (device_is_attached(sc->dev)) {
108871d51719SMichael Gmelin 		error = bus_generic_detach(sc->dev);
108971d51719SMichael Gmelin 		if (error)
109071d51719SMichael Gmelin 			return (error);
109171d51719SMichael Gmelin 	}
1092448897d3SAndriy Gapon 	if (sc->iicbus)
1093448897d3SAndriy Gapon 		device_delete_child(sc->dev, sc->iicbus);
109471d51719SMichael Gmelin 	if (sc->intr_handle)
109571d51719SMichael Gmelin 		bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
109671d51719SMichael Gmelin 
10974cd6abddSMichael Gmelin 	sx_xlock(&sc->call_lock);
109871d51719SMichael Gmelin 
1099448897d3SAndriy Gapon 	sc->iicbus = NULL;
110071d51719SMichael Gmelin 	sc->intr_handle = NULL;
110171d51719SMichael Gmelin 	reg_write(sc, IG4_REG_INTR_MASK, 0);
110271d51719SMichael Gmelin 	set_controller(sc, 0);
110371d51719SMichael Gmelin 
11044cd6abddSMichael Gmelin 	sx_xunlock(&sc->call_lock);
11055c5bcb1dSOleksandr Tymoshenko 
11065c5bcb1dSOleksandr Tymoshenko 	mtx_destroy(&sc->io_lock);
11075c5bcb1dSOleksandr Tymoshenko 	sx_destroy(&sc->call_lock);
11085c5bcb1dSOleksandr Tymoshenko 
110971d51719SMichael Gmelin 	return (0);
111071d51719SMichael Gmelin }
111171d51719SMichael Gmelin 
1112db7caa2eSVladimir Kondratyev int
ig4iic_suspend(ig4iic_softc_t * sc)1113db7caa2eSVladimir Kondratyev ig4iic_suspend(ig4iic_softc_t *sc)
1114db7caa2eSVladimir Kondratyev {
1115db7caa2eSVladimir Kondratyev 	int error;
1116db7caa2eSVladimir Kondratyev 
1117db7caa2eSVladimir Kondratyev 	/* suspend all children */
1118db7caa2eSVladimir Kondratyev 	error = bus_generic_suspend(sc->dev);
1119db7caa2eSVladimir Kondratyev 
1120db7caa2eSVladimir Kondratyev 	sx_xlock(&sc->call_lock);
1121db7caa2eSVladimir Kondratyev 	set_controller(sc, 0);
112281e81838SVladimir Kondratyev 	if (IG4_HAS_ADDREGS(sc->version)) {
1123db7caa2eSVladimir Kondratyev 		/*
1124db7caa2eSVladimir Kondratyev 		 * Place the device in the idle state, just to be safe
1125db7caa2eSVladimir Kondratyev 		 */
1126db7caa2eSVladimir Kondratyev 		reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE);
1127db7caa2eSVladimir Kondratyev 		/*
1128db7caa2eSVladimir Kondratyev 		 * Controller can become dysfunctional if I2C lines are pulled
1129db7caa2eSVladimir Kondratyev 		 * down when suspend procedure turns off power to I2C device.
1130db7caa2eSVladimir Kondratyev 		 * Place device in the reset state to avoid this.
1131db7caa2eSVladimir Kondratyev 		 */
1132db7caa2eSVladimir Kondratyev 		reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
1133db7caa2eSVladimir Kondratyev 	}
1134db7caa2eSVladimir Kondratyev 	sx_xunlock(&sc->call_lock);
1135db7caa2eSVladimir Kondratyev 
1136db7caa2eSVladimir Kondratyev 	return (error);
1137db7caa2eSVladimir Kondratyev }
1138db7caa2eSVladimir Kondratyev 
ig4iic_resume(ig4iic_softc_t * sc)1139db7caa2eSVladimir Kondratyev int ig4iic_resume(ig4iic_softc_t *sc)
1140db7caa2eSVladimir Kondratyev {
1141db7caa2eSVladimir Kondratyev 	int error;
1142db7caa2eSVladimir Kondratyev 
1143db7caa2eSVladimir Kondratyev 	sx_xlock(&sc->call_lock);
114481e81838SVladimir Kondratyev 	if (ig4iic_set_config(sc, IG4_HAS_ADDREGS(sc->version)))
1145db7caa2eSVladimir Kondratyev 		device_printf(sc->dev, "controller error during resume\n");
1146db7caa2eSVladimir Kondratyev 	sx_xunlock(&sc->call_lock);
1147db7caa2eSVladimir Kondratyev 
1148db7caa2eSVladimir Kondratyev 	error = bus_generic_resume(sc->dev);
1149db7caa2eSVladimir Kondratyev 
1150db7caa2eSVladimir Kondratyev 	return (error);
1151db7caa2eSVladimir Kondratyev }
1152db7caa2eSVladimir Kondratyev 
115371d51719SMichael Gmelin /*
11544cd6abddSMichael Gmelin  * Interrupt Operation, see ig4_var.h for locking semantics.
115571d51719SMichael Gmelin  */
1156371d37d8SVladimir Kondratyev static int
ig4iic_intr(void * cookie)115771d51719SMichael Gmelin ig4iic_intr(void *cookie)
115871d51719SMichael Gmelin {
115971d51719SMichael Gmelin 	ig4iic_softc_t *sc = cookie;
1160371d37d8SVladimir Kondratyev 	int retval = FILTER_STRAY;
116171d51719SMichael Gmelin 
1162371d37d8SVladimir Kondratyev 	mtx_lock_spin(&sc->io_lock);
11630a6b1b56SVladimir Kondratyev 	/* Ignore stray interrupts */
11640a6b1b56SVladimir Kondratyev 	if (sc->intr_mask != 0 && reg_read(sc, IG4_REG_INTR_STAT) != 0) {
1165e3a56ba2SVladimir Kondratyev 		/* Interrupt bits are cleared in wait_intr() loop */
116669fac7c3SVladimir Kondratyev 		ig4iic_set_intr_mask(sc, 0);
116771d51719SMichael Gmelin 		wakeup(sc);
1168371d37d8SVladimir Kondratyev 		retval = FILTER_HANDLED;
11690a6b1b56SVladimir Kondratyev 	}
1170371d37d8SVladimir Kondratyev 	mtx_unlock_spin(&sc->io_lock);
1171371d37d8SVladimir Kondratyev 
1172371d37d8SVladimir Kondratyev 	return (retval);
117371d51719SMichael Gmelin }
117471d51719SMichael Gmelin 
117571d51719SMichael Gmelin #define REGDUMP(sc, reg)	\
117671d51719SMichael Gmelin 	device_printf(sc->dev, "  %-23s %08x\n", #reg, reg_read(sc, reg))
117771d51719SMichael Gmelin 
117871d51719SMichael Gmelin static void
ig4iic_dump(ig4iic_softc_t * sc)117971d51719SMichael Gmelin ig4iic_dump(ig4iic_softc_t *sc)
118071d51719SMichael Gmelin {
118171d51719SMichael Gmelin 	device_printf(sc->dev, "ig4iic register dump:\n");
118271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_CTL);
118371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TAR_ADD);
118471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_HCNT);
118571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SS_SCL_LCNT);
118671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_HCNT);
118771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_FS_SCL_LCNT);
118871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_STAT);
118971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_INTR_MASK);
119071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RAW_INTR_STAT);
119171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RX_TL);
119271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_TL);
119371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_EN);
119471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_I2C_STA);
119571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TXFLR);
119671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_RXFLR);
119771d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_HOLD);
119871d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE);
119971d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SLV_DATA_NACK);
120071d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_CTRL);
120171d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_TDLR);
120271d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_DMA_RDLR);
120371d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_SDA_SETUP);
120471d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_ENABLE_STATUS);
120571d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_COMP_PARAM1);
120671d51719SMichael Gmelin 	REGDUMP(sc, IG4_REG_COMP_VER);
1207b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_ATOM) {
120871d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_COMP_TYPE);
120971d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_CLK_PARMS);
1210b3e8ee5dSOleksandr Tymoshenko 	}
1211b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
1212b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_HSW);
121371d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_GENERAL);
1214b3e8ee5dSOleksandr Tymoshenko 	} else if (sc->version == IG4_SKYLAKE) {
1215b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_RESETS_SKL);
1216b3e8ee5dSOleksandr Tymoshenko 	}
1217b3e8ee5dSOleksandr Tymoshenko 	if (sc->version == IG4_HASWELL) {
121871d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_SW_LTR_VALUE);
121971d51719SMichael Gmelin 		REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE);
122081e81838SVladimir Kondratyev 	} else if (IG4_HAS_ADDREGS(sc->version)) {
1221b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_ACTIVE_LTR_VALUE);
1222b3e8ee5dSOleksandr Tymoshenko 		REGDUMP(sc, IG4_REG_IDLE_LTR_VALUE);
1223b3e8ee5dSOleksandr Tymoshenko 	}
122471d51719SMichael Gmelin }
122571d51719SMichael Gmelin #undef REGDUMP
122671d51719SMichael Gmelin 
1227676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, NULL, NULL);
12289b7938dcSVladimir Kondratyev #ifdef DEV_ACPI
1229676ea8e1SJohn Baldwin DRIVER_MODULE(acpi_iicbus, ig4iic, acpi_iicbus_driver, NULL, NULL);
12309b7938dcSVladimir Kondratyev #endif
1231984ed3e4SVladimir Kondratyev MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
1232984ed3e4SVladimir Kondratyev MODULE_VERSION(ig4iic, 1);
1233