xref: /freebsd-src/sys/arm/nvidia/tegra_i2c.c (revision b196276c20b577b364372f1aa1a646b9ce34bf5c)
1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun  * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun  * All rights reserved.
4ef2ee5d0SMichal Meloun  *
5ef2ee5d0SMichal Meloun  * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun  * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun  * are met:
8ef2ee5d0SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun  *    documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun  *
14ef2ee5d0SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun  * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun  */
26ef2ee5d0SMichal Meloun 
27ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28ef2ee5d0SMichal Meloun /*
29ef2ee5d0SMichal Meloun  * I2C driver for Tegra SoCs.
30ef2ee5d0SMichal Meloun  */
31ef2ee5d0SMichal Meloun #include <sys/param.h>
32ef2ee5d0SMichal Meloun #include <sys/systm.h>
33ef2ee5d0SMichal Meloun #include <sys/bus.h>
34ef2ee5d0SMichal Meloun #include <sys/kernel.h>
35ef2ee5d0SMichal Meloun #include <sys/limits.h>
36ef2ee5d0SMichal Meloun #include <sys/module.h>
37ef2ee5d0SMichal Meloun #include <sys/resource.h>
38ef2ee5d0SMichal Meloun 
39ef2ee5d0SMichal Meloun #include <machine/bus.h>
40ef2ee5d0SMichal Meloun #include <machine/resource.h>
41ef2ee5d0SMichal Meloun #include <sys/rman.h>
42ef2ee5d0SMichal Meloun 
43ef2ee5d0SMichal Meloun #include <sys/lock.h>
44ef2ee5d0SMichal Meloun #include <sys/mutex.h>
45ef2ee5d0SMichal Meloun 
46be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
471f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
48ef2ee5d0SMichal Meloun #include <dev/iicbus/iiconf.h>
49ef2ee5d0SMichal Meloun #include <dev/iicbus/iicbus.h>
50ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
51ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
52ef2ee5d0SMichal Meloun 
53ef2ee5d0SMichal Meloun #include "iicbus_if.h"
54ef2ee5d0SMichal Meloun 
55ef2ee5d0SMichal Meloun #define	I2C_CNFG				0x000
56ef2ee5d0SMichal Meloun #define	 I2C_CNFG_MSTR_CLR_BUS_ON_TIMEOUT		(1 << 15)
57ef2ee5d0SMichal Meloun #define	 I2C_CNFG_DEBOUNCE_CNT(x)			(((x) & 0x07) << 12)
58ef2ee5d0SMichal Meloun #define	 I2C_CNFG_NEW_MASTER_FSM			(1 << 11)
59ef2ee5d0SMichal Meloun #define	 I2C_CNFG_PACKET_MODE_EN			(1 << 10)
60ef2ee5d0SMichal Meloun #define	 I2C_CNFG_SEND					(1 <<  9)
61ef2ee5d0SMichal Meloun #define	 I2C_CNFG_NOACK					(1 <<  8)
62ef2ee5d0SMichal Meloun #define	 I2C_CNFG_CMD2					(1 <<  7)
63ef2ee5d0SMichal Meloun #define	 I2C_CNFG_CMD1					(1 <<  6)
64ef2ee5d0SMichal Meloun #define	 I2C_CNFG_START					(1 <<  5)
65ef2ee5d0SMichal Meloun #define	 I2C_CNFG_SLV2					(1 <<  4)
66ef2ee5d0SMichal Meloun #define	 I2C_CNFG_LENGTH_SHIFT				1
67ef2ee5d0SMichal Meloun #define	 I2C_CNFG_LENGTH_MASK				0x7
68ef2ee5d0SMichal Meloun #define	 I2C_CNFG_A_MOD					(1 <<  0)
69ef2ee5d0SMichal Meloun 
70ef2ee5d0SMichal Meloun #define	I2C_CMD_ADDR0				0x004
71ef2ee5d0SMichal Meloun #define	I2C_CMD_ADDR1				0x008
72ef2ee5d0SMichal Meloun #define	I2C_CMD_DATA1				0x00c
73ef2ee5d0SMichal Meloun #define	I2C_CMD_DATA2				0x010
74ef2ee5d0SMichal Meloun #define	I2C_STATUS				0x01c
75ef2ee5d0SMichal Meloun #define	I2C_SL_CNFG				0x020
76ef2ee5d0SMichal Meloun #define	I2C_SL_RCVD				0x024
77ef2ee5d0SMichal Meloun #define	I2C_SL_STATUS				0x028
78ef2ee5d0SMichal Meloun #define	I2C_SL_ADDR1				0x02c
79ef2ee5d0SMichal Meloun #define	I2C_SL_ADDR2				0x030
80ef2ee5d0SMichal Meloun #define	I2C_TLOW_SEXT				0x034
81ef2ee5d0SMichal Meloun #define	I2C_SL_DELAY_COUNT			0x03c
82ef2ee5d0SMichal Meloun #define	I2C_SL_INT_MASK				0x040
83ef2ee5d0SMichal Meloun #define	I2C_SL_INT_SOURCE			0x044
84ef2ee5d0SMichal Meloun #define	I2C_SL_INT_SET				0x048
85ef2ee5d0SMichal Meloun #define	I2C_TX_PACKET_FIFO			0x050
86ef2ee5d0SMichal Meloun #define	I2C_RX_FIFO				0x054
87ef2ee5d0SMichal Meloun #define	I2C_PACKET_TRANSFER_STATUS		0x058
88ef2ee5d0SMichal Meloun #define	I2C_FIFO_CONTROL			0x05c
89ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG(x)		(((x) & 0x07) << 13)
90ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_SLV_RX_FIFO_TRIG(x)		(((x) & 0x07) << 10)
91ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_SLV_TX_FIFO_FLUSH		(1 <<  9)
92ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_SLV_RX_FIFO_FLUSH		(1 <<  8)
93ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_TX_FIFO_TRIG(x)		(((x) & 0x07) << 5)
94ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_RX_FIFO_TRIG(x)		(((x) & 0x07) << 2)
95ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_TX_FIFO_FLUSH			(1 << 1)
96ef2ee5d0SMichal Meloun #define	 I2C_FIFO_CONTROL_RX_FIFO_FLUSH			(1 << 0)
97ef2ee5d0SMichal Meloun 
98ef2ee5d0SMichal Meloun #define	I2C_FIFO_STATUS				0x060
99ef2ee5d0SMichal Meloun #define	 I2C_FIFO_STATUS_SLV_XFER_ERR_REASON		(1 << 25)
100ef2ee5d0SMichal Meloun #define	 I2C_FIFO_STATUS_TX_FIFO_SLV_EMPTY_CNT(x)	(((x) >> 20) & 0xF)
101ef2ee5d0SMichal Meloun #define	 I2C_FIFO_STATUS_RX_FIFO_SLV_FULL_CNT(x)	(((x) >> 16) & 0xF)
102ef2ee5d0SMichal Meloun #define	 I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(x)		(((x) >>  4) & 0xF)
103ef2ee5d0SMichal Meloun #define	 I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(x)		(((x) >>  0) & 0xF)
104ef2ee5d0SMichal Meloun 
105ef2ee5d0SMichal Meloun #define	I2C_INTERRUPT_MASK_REGISTER		0x064
106ef2ee5d0SMichal Meloun #define	I2C_INTERRUPT_STATUS_REGISTER		0x068
107ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_ACK_WITHHELD			(1 << 28)
108ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_RD2WR				(1 << 27)
109ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_WR2RD				(1 << 26)
110ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_PKT_XFER_ERR			(1 << 25)
111ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_TX_BUFFER_REQ			(1 << 24)
112ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_RX_BUFFER_FILLED			(1 << 23)
113ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_PACKET_XFER_COMPLETE		(1 << 22)
114ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_TFIFO_OVF				(1 << 21)
115ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_RFIFO_UNF				(1 << 20)
116ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_TFIFO_DATA_REQ			(1 << 17)
117ef2ee5d0SMichal Meloun #define	 I2C_INT_SLV_RFIFO_DATA_REQ			(1 << 16)
118ef2ee5d0SMichal Meloun #define	 I2C_INT_BUS_CLEAR_DONE				(1 << 11)
119ef2ee5d0SMichal Meloun #define	 I2C_INT_TLOW_MEXT_TIMEOUT			(1 << 10)
120ef2ee5d0SMichal Meloun #define	 I2C_INT_TLOW_SEXT_TIMEOUT			(1 <<  9)
121ef2ee5d0SMichal Meloun #define	 I2C_INT_TIMEOUT				(1 <<  8)
122ef2ee5d0SMichal Meloun #define	 I2C_INT_PACKET_XFER_COMPLETE			(1 <<  7)
123ef2ee5d0SMichal Meloun #define	 I2C_INT_ALL_PACKETS_XFER_COMPLETE		(1 <<  6)
124ef2ee5d0SMichal Meloun #define	 I2C_INT_TFIFO_OVR				(1 <<  5)
125ef2ee5d0SMichal Meloun #define	 I2C_INT_RFIFO_UNF				(1 <<  4)
126ef2ee5d0SMichal Meloun #define	 I2C_INT_NOACK					(1 <<  3)
127ef2ee5d0SMichal Meloun #define	 I2C_INT_ARB_LOST				(1 <<  2)
128ef2ee5d0SMichal Meloun #define	 I2C_INT_TFIFO_DATA_REQ				(1 <<  1)
129ef2ee5d0SMichal Meloun #define	 I2C_INT_RFIFO_DATA_REQ				(1 <<  0)
130ef2ee5d0SMichal Meloun #define	 I2C_ERROR_MASK		(I2C_INT_ARB_LOST | I2C_INT_NOACK |	 \
131ef2ee5d0SMichal Meloun 				I2C_INT_RFIFO_UNF | I2C_INT_TFIFO_OVR)
132ef2ee5d0SMichal Meloun 
133ef2ee5d0SMichal Meloun #define	I2C_CLK_DIVISOR				0x06c
134ef2ee5d0SMichal Meloun #define	 I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT		16
135ef2ee5d0SMichal Meloun #define	 I2C_CLK_DIVISOR_STD_FAST_MODE_MASK		0xffff
136ef2ee5d0SMichal Meloun #define	 I2C_CLK_DIVISOR_HSMODE_SHIFT			0
137ef2ee5d0SMichal Meloun #define	 I2C_CLK_DIVISOR_HSMODE_MASK			0xffff
138ef2ee5d0SMichal Meloun #define	I2C_INTERRUPT_SOURCE_REGISTER		0x070
139ef2ee5d0SMichal Meloun #define	I2C_INTERRUPT_SET_REGISTER		0x074
140ef2ee5d0SMichal Meloun #define	I2C_SLV_TX_PACKET_FIFO			0x07c
141ef2ee5d0SMichal Meloun #define	I2C_SLV_PACKET_STATUS			0x080
142ef2ee5d0SMichal Meloun #define	I2C_BUS_CLEAR_CONFIG			0x084
143ef2ee5d0SMichal Meloun #define	 I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(x)	(((x) & 0xFF) << 16)
144ef2ee5d0SMichal Meloun #define	 I2C_BUS_CLEAR_CONFIG_BC_STOP_COND		(1 << 2)
145ef2ee5d0SMichal Meloun #define	 I2C_BUS_CLEAR_CONFIG_BC_TERMINATE		(1 << 1)
146ef2ee5d0SMichal Meloun #define	 I2C_BUS_CLEAR_CONFIG_BC_ENABLE			(1 << 0)
147ef2ee5d0SMichal Meloun 
148ef2ee5d0SMichal Meloun #define	I2C_BUS_CLEAR_STATUS			0x088
149ef2ee5d0SMichal Meloun #define	 I2C_BUS_CLEAR_STATUS_BC_STATUS			(1 << 0)
150ef2ee5d0SMichal Meloun 
151ef2ee5d0SMichal Meloun #define	I2C_CONFIG_LOAD				0x08c
152ef2ee5d0SMichal Meloun #define	 I2C_CONFIG_LOAD_TIMEOUT_CONFIG_LOAD		(1 << 2)
153ef2ee5d0SMichal Meloun #define	 I2C_CONFIG_LOAD_SLV_CONFIG_LOAD		(1 << 1)
154ef2ee5d0SMichal Meloun #define	 I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD		(1 << 0)
155ef2ee5d0SMichal Meloun 
156ef2ee5d0SMichal Meloun #define	I2C_INTERFACE_TIMING_0			0x094
157ef2ee5d0SMichal Meloun #define	I2C_INTERFACE_TIMING_1			0x098
158ef2ee5d0SMichal Meloun #define	I2C_HS_INTERFACE_TIMING_0		0x09c
159ef2ee5d0SMichal Meloun #define	I2C_HS_INTERFACE_TIMING_1		0x0a0
160ef2ee5d0SMichal Meloun 
161ef2ee5d0SMichal Meloun /* Protocol header 0 */
162ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_HEADER_SIZE_SHIFT	28
163ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_HEADER_SIZE_MASK		0x3
164ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_PACKET_ID_SHIFT		16
165ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_PACKET_ID_MASK		0xff
166ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_CONT_ID_SHIFT		12
167ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_CONT_ID_MASK		0xf
168ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_PROTOCOL_I2C		(1 << 4)
169ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_TYPE_SHIFT		0
170ef2ee5d0SMichal Meloun #define	PACKET_HEADER0_TYPE_MASK		0x7
171ef2ee5d0SMichal Meloun 
172ef2ee5d0SMichal Meloun /* I2C header */
173ef2ee5d0SMichal Meloun #define	I2C_HEADER_HIGHSPEED_MODE		(1 << 22)
174ef2ee5d0SMichal Meloun #define	I2C_HEADER_CONT_ON_NAK			(1 << 21)
175ef2ee5d0SMichal Meloun #define	I2C_HEADER_SEND_START_BYTE		(1 << 20)
176ef2ee5d0SMichal Meloun #define	I2C_HEADER_READ				(1 << 19)
177ef2ee5d0SMichal Meloun #define	I2C_HEADER_10BIT_ADDR			(1 << 18)
178ef2ee5d0SMichal Meloun #define	I2C_HEADER_IE_ENABLE			(1 << 17)
179ef2ee5d0SMichal Meloun #define	I2C_HEADER_REPEAT_START			(1 << 16)
180ef2ee5d0SMichal Meloun #define	I2C_HEADER_CONTINUE_XFER		(1 << 15)
181ef2ee5d0SMichal Meloun #define	I2C_HEADER_MASTER_ADDR_SHIFT		12
182ef2ee5d0SMichal Meloun #define	I2C_HEADER_MASTER_ADDR_MASK		0x7
183ef2ee5d0SMichal Meloun #define	I2C_HEADER_SLAVE_ADDR_SHIFT		0
184ef2ee5d0SMichal Meloun #define	I2C_HEADER_SLAVE_ADDR_MASK		0x3ff
185ef2ee5d0SMichal Meloun 
186ef2ee5d0SMichal Meloun #define	I2C_CLK_DIVISOR_STD_FAST_MODE		0x19
187ef2ee5d0SMichal Meloun #define	I2C_CLK_MULTIPLIER_STD_FAST_MODE	8
188ef2ee5d0SMichal Meloun 
189ef2ee5d0SMichal Meloun #define	I2C_REQUEST_TIMEOUT			(5 * hz)
190ef2ee5d0SMichal Meloun 
191ef2ee5d0SMichal Meloun #define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
192ef2ee5d0SMichal Meloun #define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
193ef2ee5d0SMichal Meloun 
194ef2ee5d0SMichal Meloun #define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
195ef2ee5d0SMichal Meloun #define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
196ef2ee5d0SMichal Meloun #define	SLEEP(_sc, timeout)						\
197ef2ee5d0SMichal Meloun 	mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", timeout);
198ef2ee5d0SMichal Meloun #define	LOCK_INIT(_sc)							\
199ef2ee5d0SMichal Meloun 	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_i2c", MTX_DEF)
200ef2ee5d0SMichal Meloun #define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
201ef2ee5d0SMichal Meloun #define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
202ef2ee5d0SMichal Meloun #define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
203ef2ee5d0SMichal Meloun 
204ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = {
205ef2ee5d0SMichal Meloun 	{"nvidia,tegra124-i2c",	1},
206b9cbd68dSMichal Meloun 	{"nvidia,tegra210-i2c",	1},
207ef2ee5d0SMichal Meloun 	{NULL,			0}
208ef2ee5d0SMichal Meloun };
209ef2ee5d0SMichal Meloun enum tegra_i2c_xfer_type {
210ef2ee5d0SMichal Meloun 	XFER_STOP, 		/* Send stop condition after xfer */
211ef2ee5d0SMichal Meloun 	XFER_REPEAT_START,	/* Send repeated start after xfer */
212ef2ee5d0SMichal Meloun 	XFER_CONTINUE		/* Don't send nothing */
213ef2ee5d0SMichal Meloun } ;
214ef2ee5d0SMichal Meloun 
215ef2ee5d0SMichal Meloun struct tegra_i2c_softc {
216ef2ee5d0SMichal Meloun 	device_t		dev;
217ef2ee5d0SMichal Meloun 	struct mtx		mtx;
218ef2ee5d0SMichal Meloun 
219ef2ee5d0SMichal Meloun 	struct resource		*mem_res;
220ef2ee5d0SMichal Meloun 	struct resource		*irq_res;
221ef2ee5d0SMichal Meloun 	void			*irq_h;
222ef2ee5d0SMichal Meloun 
223ef2ee5d0SMichal Meloun 	device_t		iicbus;
224ef2ee5d0SMichal Meloun 	clk_t			clk;
225ef2ee5d0SMichal Meloun 	hwreset_t		reset;
226ef2ee5d0SMichal Meloun 	uint32_t		core_freq;
227ef2ee5d0SMichal Meloun 	uint32_t		bus_freq;
228ef2ee5d0SMichal Meloun 	int			bus_inuse;
229ef2ee5d0SMichal Meloun 
230ef2ee5d0SMichal Meloun 	struct iic_msg		*msg;
231ef2ee5d0SMichal Meloun 	int			msg_idx;
232ef2ee5d0SMichal Meloun 	uint32_t		bus_err;
233ef2ee5d0SMichal Meloun 	int			done;
234ef2ee5d0SMichal Meloun };
235ef2ee5d0SMichal Meloun 
236ef2ee5d0SMichal Meloun static int
237ef2ee5d0SMichal Meloun tegra_i2c_flush_fifo(struct tegra_i2c_softc *sc)
238ef2ee5d0SMichal Meloun {
239ef2ee5d0SMichal Meloun 	int timeout;
240ef2ee5d0SMichal Meloun 	uint32_t reg;
241ef2ee5d0SMichal Meloun 
242ef2ee5d0SMichal Meloun 	reg = RD4(sc, I2C_FIFO_CONTROL);
243ef2ee5d0SMichal Meloun 	reg |= I2C_FIFO_CONTROL_TX_FIFO_FLUSH | I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
244ef2ee5d0SMichal Meloun 	WR4(sc, I2C_FIFO_CONTROL, reg);
245ef2ee5d0SMichal Meloun 
246ef2ee5d0SMichal Meloun 	timeout = 10;
247ef2ee5d0SMichal Meloun 	while (timeout > 0) {
248ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_FIFO_CONTROL);
249ef2ee5d0SMichal Meloun 		reg &= I2C_FIFO_CONTROL_TX_FIFO_FLUSH |
250ef2ee5d0SMichal Meloun 		    I2C_FIFO_CONTROL_RX_FIFO_FLUSH;
251ef2ee5d0SMichal Meloun 		if (reg == 0)
252ef2ee5d0SMichal Meloun 			break;
253ef2ee5d0SMichal Meloun 		DELAY(10);
254ef2ee5d0SMichal Meloun 	}
255ef2ee5d0SMichal Meloun 	if (timeout <= 0) {
256ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "FIFO flush timedout\n");
257ef2ee5d0SMichal Meloun 		return (ETIMEDOUT);
258ef2ee5d0SMichal Meloun 	}
259ef2ee5d0SMichal Meloun 	return (0);
260ef2ee5d0SMichal Meloun }
261ef2ee5d0SMichal Meloun 
262ef2ee5d0SMichal Meloun static void
263ef2ee5d0SMichal Meloun tegra_i2c_setup_clk(struct tegra_i2c_softc *sc, int clk_freq)
264ef2ee5d0SMichal Meloun {
265ef2ee5d0SMichal Meloun 	int div;
266ef2ee5d0SMichal Meloun 
267ef2ee5d0SMichal Meloun 	div = ((sc->core_freq  / clk_freq)  / 10) - 1;
268ef2ee5d0SMichal Meloun 	if ((sc->core_freq / (10 * (div + 1)))  > clk_freq)
269ef2ee5d0SMichal Meloun 		div++;
270ef2ee5d0SMichal Meloun 	if (div > 65535)
271ef2ee5d0SMichal Meloun 		div = 65535;
272ef2ee5d0SMichal Meloun 	WR4(sc, I2C_CLK_DIVISOR,
273ef2ee5d0SMichal Meloun 	    (1 << I2C_CLK_DIVISOR_HSMODE_SHIFT) |
274ef2ee5d0SMichal Meloun 	    (div << I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT));
275ef2ee5d0SMichal Meloun }
276ef2ee5d0SMichal Meloun 
277ef2ee5d0SMichal Meloun static void
278ef2ee5d0SMichal Meloun tegra_i2c_bus_clear(struct tegra_i2c_softc *sc)
279ef2ee5d0SMichal Meloun {
280ef2ee5d0SMichal Meloun 	int timeout;
281ef2ee5d0SMichal Meloun 	uint32_t reg, status;
282ef2ee5d0SMichal Meloun 
283ef2ee5d0SMichal Meloun 	WR4(sc, I2C_BUS_CLEAR_CONFIG,
284ef2ee5d0SMichal Meloun 	    I2C_BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD(18) |
285ef2ee5d0SMichal Meloun 	    I2C_BUS_CLEAR_CONFIG_BC_STOP_COND |
286ef2ee5d0SMichal Meloun 	    I2C_BUS_CLEAR_CONFIG_BC_TERMINATE);
287ef2ee5d0SMichal Meloun 
288ef2ee5d0SMichal Meloun 	WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
289ef2ee5d0SMichal Meloun 	for (timeout = 1000; timeout > 0; timeout--) {
290ef2ee5d0SMichal Meloun 		if (RD4(sc, I2C_CONFIG_LOAD) == 0)
291ef2ee5d0SMichal Meloun 			break;
292ef2ee5d0SMichal Meloun 		DELAY(10);
293ef2ee5d0SMichal Meloun 	}
294ef2ee5d0SMichal Meloun 	if (timeout <= 0)
295ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "config load timeouted\n");
296ef2ee5d0SMichal Meloun 	reg = RD4(sc, I2C_BUS_CLEAR_CONFIG);
297ef2ee5d0SMichal Meloun 	reg |= I2C_BUS_CLEAR_CONFIG_BC_ENABLE;
298ef2ee5d0SMichal Meloun 	WR4(sc, I2C_BUS_CLEAR_CONFIG,reg);
299ef2ee5d0SMichal Meloun 
300ef2ee5d0SMichal Meloun 	for (timeout = 1000; timeout > 0; timeout--) {
301ef2ee5d0SMichal Meloun 		if ((RD4(sc, I2C_BUS_CLEAR_CONFIG) &
302ef2ee5d0SMichal Meloun 		    I2C_BUS_CLEAR_CONFIG_BC_ENABLE) == 0)
303ef2ee5d0SMichal Meloun 			break;
304ef2ee5d0SMichal Meloun 		DELAY(10);
305ef2ee5d0SMichal Meloun 	}
306ef2ee5d0SMichal Meloun 	if (timeout <= 0)
307ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "bus clear timeouted\n");
308ef2ee5d0SMichal Meloun 
309ef2ee5d0SMichal Meloun 	status = RD4(sc, I2C_BUS_CLEAR_STATUS);
310ef2ee5d0SMichal Meloun 	if ((status & I2C_BUS_CLEAR_STATUS_BC_STATUS) == 0)
311ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "bus clear failed\n");
312ef2ee5d0SMichal Meloun }
313ef2ee5d0SMichal Meloun 
314ef2ee5d0SMichal Meloun static int
315ef2ee5d0SMichal Meloun tegra_i2c_hw_init(struct tegra_i2c_softc *sc)
316ef2ee5d0SMichal Meloun {
317ef2ee5d0SMichal Meloun 	int rv, timeout;
318ef2ee5d0SMichal Meloun 
319ef2ee5d0SMichal Meloun 	/* Reset the core. */
320ef2ee5d0SMichal Meloun 	rv = hwreset_assert(sc->reset);
321ef2ee5d0SMichal Meloun 	if (rv != 0) {
322ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Cannot assert reset\n");
323ef2ee5d0SMichal Meloun 		return (rv);
324ef2ee5d0SMichal Meloun 	}
325ef2ee5d0SMichal Meloun 	DELAY(10);
326ef2ee5d0SMichal Meloun 	rv = hwreset_deassert(sc->reset);
327ef2ee5d0SMichal Meloun 	if (rv != 0) {
328ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Cannot clear reset\n");
329ef2ee5d0SMichal Meloun 		return (rv);
330ef2ee5d0SMichal Meloun 	}
331ef2ee5d0SMichal Meloun 
332ef2ee5d0SMichal Meloun 	WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
333ef2ee5d0SMichal Meloun 	WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
334ef2ee5d0SMichal Meloun 	WR4(sc, I2C_CNFG, I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
335ef2ee5d0SMichal Meloun 	    I2C_CNFG_DEBOUNCE_CNT(2));
336ef2ee5d0SMichal Meloun 
337ef2ee5d0SMichal Meloun 	tegra_i2c_setup_clk(sc, sc->bus_freq);
338ef2ee5d0SMichal Meloun 
339ef2ee5d0SMichal Meloun 	WR4(sc, I2C_FIFO_CONTROL, I2C_FIFO_CONTROL_TX_FIFO_TRIG(7) |
340ef2ee5d0SMichal Meloun 	    I2C_FIFO_CONTROL_RX_FIFO_TRIG(0));
341ef2ee5d0SMichal Meloun 
342ef2ee5d0SMichal Meloun 	WR4(sc, I2C_CONFIG_LOAD, I2C_CONFIG_LOAD_MSTR_CONFIG_LOAD);
343ef2ee5d0SMichal Meloun 	for (timeout = 1000; timeout > 0; timeout--) {
344ef2ee5d0SMichal Meloun 		if (RD4(sc, I2C_CONFIG_LOAD) == 0)
345ef2ee5d0SMichal Meloun 			break;
346ef2ee5d0SMichal Meloun 		DELAY(10);
347ef2ee5d0SMichal Meloun 	}
348ef2ee5d0SMichal Meloun 	if (timeout <= 0)
349ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "config load timeouted\n");
350ef2ee5d0SMichal Meloun 
351ef2ee5d0SMichal Meloun 	tegra_i2c_bus_clear(sc);
352ef2ee5d0SMichal Meloun 	return (0);
353ef2ee5d0SMichal Meloun }
354ef2ee5d0SMichal Meloun 
355ef2ee5d0SMichal Meloun static int
356ef2ee5d0SMichal Meloun tegra_i2c_tx(struct tegra_i2c_softc *sc)
357ef2ee5d0SMichal Meloun {
358ef2ee5d0SMichal Meloun 	uint32_t reg;
359ef2ee5d0SMichal Meloun 	int cnt, i;
360ef2ee5d0SMichal Meloun 
361ef2ee5d0SMichal Meloun 	if (sc->msg_idx >= sc->msg->len)
362ef2ee5d0SMichal Meloun 		panic("Invalid call to tegra_i2c_tx\n");
363ef2ee5d0SMichal Meloun 
364ef2ee5d0SMichal Meloun 	while(sc->msg_idx < sc->msg->len) {
365ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_FIFO_STATUS);
366ef2ee5d0SMichal Meloun 		if (I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT(reg) == 0)
367ef2ee5d0SMichal Meloun 			break;
368ef2ee5d0SMichal Meloun 		cnt = min(4, sc->msg->len - sc->msg_idx);
369ef2ee5d0SMichal Meloun 		reg = 0;
370ef2ee5d0SMichal Meloun 		for (i = 0;  i < cnt; i++) {
371ef2ee5d0SMichal Meloun 			reg |=  sc->msg->buf[sc->msg_idx] << (i * 8);
372ef2ee5d0SMichal Meloun 			sc->msg_idx++;
373ef2ee5d0SMichal Meloun 		}
374ef2ee5d0SMichal Meloun 		WR4(sc, I2C_TX_PACKET_FIFO, reg);
375ef2ee5d0SMichal Meloun 	}
376ef2ee5d0SMichal Meloun 	if (sc->msg_idx >= sc->msg->len)
377ef2ee5d0SMichal Meloun 		return (0);
378ef2ee5d0SMichal Meloun 	return (sc->msg->len - sc->msg_idx - 1);
379ef2ee5d0SMichal Meloun }
380ef2ee5d0SMichal Meloun 
381ef2ee5d0SMichal Meloun static int
382ef2ee5d0SMichal Meloun tegra_i2c_rx(struct tegra_i2c_softc *sc)
383ef2ee5d0SMichal Meloun {
384ef2ee5d0SMichal Meloun 	uint32_t reg;
385ef2ee5d0SMichal Meloun 	int cnt, i;
386ef2ee5d0SMichal Meloun 
387ef2ee5d0SMichal Meloun 	if (sc->msg_idx >= sc->msg->len)
388ef2ee5d0SMichal Meloun 		panic("Invalid call to tegra_i2c_rx\n");
389ef2ee5d0SMichal Meloun 
390ef2ee5d0SMichal Meloun 	while(sc->msg_idx < sc->msg->len) {
391ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_FIFO_STATUS);
392ef2ee5d0SMichal Meloun 		if (I2C_FIFO_STATUS_RX_FIFO_FULL_CNT(reg) == 0)
393ef2ee5d0SMichal Meloun 			break;
394ef2ee5d0SMichal Meloun 		cnt = min(4, sc->msg->len - sc->msg_idx);
395ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_RX_FIFO);
396ef2ee5d0SMichal Meloun 		for (i = 0;  i < cnt; i++) {
397ef2ee5d0SMichal Meloun 			sc->msg->buf[sc->msg_idx] = (reg >> (i * 8)) & 0xFF;
398ef2ee5d0SMichal Meloun 			sc->msg_idx++;
399ef2ee5d0SMichal Meloun 		}
400ef2ee5d0SMichal Meloun 	}
401ef2ee5d0SMichal Meloun 
402ef2ee5d0SMichal Meloun 	if (sc->msg_idx >= sc->msg->len)
403ef2ee5d0SMichal Meloun 		return (0);
404ef2ee5d0SMichal Meloun 	return (sc->msg->len - sc->msg_idx - 1);
405ef2ee5d0SMichal Meloun }
406ef2ee5d0SMichal Meloun 
407ef2ee5d0SMichal Meloun static void
408ef2ee5d0SMichal Meloun tegra_i2c_intr(void *arg)
409ef2ee5d0SMichal Meloun {
410ef2ee5d0SMichal Meloun 	struct tegra_i2c_softc *sc;
411ef2ee5d0SMichal Meloun 	uint32_t status, reg;
412ef2ee5d0SMichal Meloun 	int rv;
413ef2ee5d0SMichal Meloun 
414ef2ee5d0SMichal Meloun 	sc = (struct tegra_i2c_softc *)arg;
415ef2ee5d0SMichal Meloun 
416ef2ee5d0SMichal Meloun 	LOCK(sc);
417ef2ee5d0SMichal Meloun 	status = RD4(sc, I2C_INTERRUPT_SOURCE_REGISTER);
418ef2ee5d0SMichal Meloun 	if (sc->msg == NULL) {
419ef2ee5d0SMichal Meloun 		/* Unexpected interrupt - disable FIFOs, clear reset. */
420ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
421ef2ee5d0SMichal Meloun 		reg &= ~I2C_INT_TFIFO_DATA_REQ;
422ef2ee5d0SMichal Meloun 		reg &= ~I2C_INT_RFIFO_DATA_REQ;
423ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
424ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
425ef2ee5d0SMichal Meloun 		UNLOCK(sc);
426ef2ee5d0SMichal Meloun 		return;
427ef2ee5d0SMichal Meloun 	}
428ef2ee5d0SMichal Meloun 
429ef2ee5d0SMichal Meloun 	if ((status & I2C_ERROR_MASK) != 0) {
430ef2ee5d0SMichal Meloun 		if (status & I2C_INT_NOACK)
431ef2ee5d0SMichal Meloun 			sc->bus_err = IIC_ENOACK;
432ef2ee5d0SMichal Meloun 		if (status & I2C_INT_ARB_LOST)
433ef2ee5d0SMichal Meloun 			sc->bus_err = IIC_EBUSERR;
434ef2ee5d0SMichal Meloun 		if ((status & I2C_INT_TFIFO_OVR) ||
435ef2ee5d0SMichal Meloun 		    (status & I2C_INT_RFIFO_UNF))
436ef2ee5d0SMichal Meloun 			sc->bus_err = IIC_EBUSERR;
437ef2ee5d0SMichal Meloun 		sc->done = 1;
438ef2ee5d0SMichal Meloun 	} else if ((status & I2C_INT_RFIFO_DATA_REQ) &&
439ef2ee5d0SMichal Meloun 	    (sc->msg != NULL) && (sc->msg->flags & IIC_M_RD)) {
440ef2ee5d0SMichal Meloun 		rv = tegra_i2c_rx(sc);
441ef2ee5d0SMichal Meloun 		if (rv == 0) {
442ef2ee5d0SMichal Meloun 			reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
443ef2ee5d0SMichal Meloun 			reg &= ~I2C_INT_RFIFO_DATA_REQ;
444ef2ee5d0SMichal Meloun 			WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
445ef2ee5d0SMichal Meloun 		}
446ef2ee5d0SMichal Meloun 	} else if ((status & I2C_INT_TFIFO_DATA_REQ) &&
447ef2ee5d0SMichal Meloun 	    (sc->msg != NULL) && !(sc->msg->flags & IIC_M_RD)) {
448ef2ee5d0SMichal Meloun 		rv = tegra_i2c_tx(sc);
449ef2ee5d0SMichal Meloun 		if (rv == 0) {
450ef2ee5d0SMichal Meloun 			reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
451ef2ee5d0SMichal Meloun 			reg &= ~I2C_INT_TFIFO_DATA_REQ;
452ef2ee5d0SMichal Meloun 			WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
453ef2ee5d0SMichal Meloun 		}
454ef2ee5d0SMichal Meloun 	} else if ((status & I2C_INT_RFIFO_DATA_REQ) ||
455ef2ee5d0SMichal Meloun 		    (status & I2C_INT_TFIFO_DATA_REQ)) {
456ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Unexpected data interrupt: 0x%08X\n",
457ef2ee5d0SMichal Meloun 		    status);
458ef2ee5d0SMichal Meloun 		reg = RD4(sc, I2C_INTERRUPT_MASK_REGISTER);
459ef2ee5d0SMichal Meloun 		reg &= ~I2C_INT_TFIFO_DATA_REQ;
460ef2ee5d0SMichal Meloun 		reg &= ~I2C_INT_RFIFO_DATA_REQ;
461ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, reg);
462ef2ee5d0SMichal Meloun 	}
463ef2ee5d0SMichal Meloun 	if (status & I2C_INT_PACKET_XFER_COMPLETE)
464ef2ee5d0SMichal Meloun 		sc->done = 1;
465ef2ee5d0SMichal Meloun 	WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, status);
466ef2ee5d0SMichal Meloun 	if (sc->done) {
467ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
468ef2ee5d0SMichal Meloun 		wakeup(&(sc->done));
469ef2ee5d0SMichal Meloun 	}
470ef2ee5d0SMichal Meloun 	UNLOCK(sc);
471ef2ee5d0SMichal Meloun }
472ef2ee5d0SMichal Meloun 
473ef2ee5d0SMichal Meloun static void
474ef2ee5d0SMichal Meloun tegra_i2c_start_msg(struct tegra_i2c_softc *sc, struct iic_msg *msg,
475ef2ee5d0SMichal Meloun     enum tegra_i2c_xfer_type xtype)
476ef2ee5d0SMichal Meloun {
477ef2ee5d0SMichal Meloun 	uint32_t tmp, mask;
478ef2ee5d0SMichal Meloun 
479ef2ee5d0SMichal Meloun 	/* Packet header. */
480ef2ee5d0SMichal Meloun 	tmp =  (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
481ef2ee5d0SMichal Meloun 	   PACKET_HEADER0_PROTOCOL_I2C |
482ef2ee5d0SMichal Meloun 	   (1 << PACKET_HEADER0_CONT_ID_SHIFT) |
483ef2ee5d0SMichal Meloun 	   (1 << PACKET_HEADER0_PACKET_ID_SHIFT);
484ef2ee5d0SMichal Meloun 	WR4(sc, I2C_TX_PACKET_FIFO, tmp);
485ef2ee5d0SMichal Meloun 
486ef2ee5d0SMichal Meloun 	/* Packet size. */
487ef2ee5d0SMichal Meloun 	WR4(sc, I2C_TX_PACKET_FIFO, msg->len - 1);
488ef2ee5d0SMichal Meloun 
489ef2ee5d0SMichal Meloun 	/* I2C header. */
490ef2ee5d0SMichal Meloun 	tmp = I2C_HEADER_IE_ENABLE;
491ef2ee5d0SMichal Meloun 	if (xtype == XFER_CONTINUE)
492ef2ee5d0SMichal Meloun 		tmp |= I2C_HEADER_CONTINUE_XFER;
493ef2ee5d0SMichal Meloun 	else if (xtype == XFER_REPEAT_START)
494ef2ee5d0SMichal Meloun 		tmp |= I2C_HEADER_REPEAT_START;
495ef2ee5d0SMichal Meloun 	tmp |= msg->slave << I2C_HEADER_SLAVE_ADDR_SHIFT;
496ef2ee5d0SMichal Meloun 	if (msg->flags & IIC_M_RD) {
497ef2ee5d0SMichal Meloun 		tmp |= I2C_HEADER_READ;
498ef2ee5d0SMichal Meloun 		tmp |= 1 << I2C_HEADER_SLAVE_ADDR_SHIFT;
499ef2ee5d0SMichal Meloun 	} else
500ef2ee5d0SMichal Meloun 		tmp &= ~(1 << I2C_HEADER_SLAVE_ADDR_SHIFT);
501ef2ee5d0SMichal Meloun 
502ef2ee5d0SMichal Meloun 	WR4(sc, I2C_TX_PACKET_FIFO, tmp);
503ef2ee5d0SMichal Meloun 
504ef2ee5d0SMichal Meloun 	/* Interrupt mask. */
505ef2ee5d0SMichal Meloun 	mask = I2C_INT_NOACK | I2C_INT_ARB_LOST | I2C_INT_PACKET_XFER_COMPLETE;
506ef2ee5d0SMichal Meloun 	if (msg->flags & IIC_M_RD)
507ef2ee5d0SMichal Meloun 		mask |= I2C_INT_RFIFO_DATA_REQ;
508ef2ee5d0SMichal Meloun 	else
509ef2ee5d0SMichal Meloun 		mask |= I2C_INT_TFIFO_DATA_REQ;
510ef2ee5d0SMichal Meloun 	WR4(sc, I2C_INTERRUPT_MASK_REGISTER, mask);
511ef2ee5d0SMichal Meloun }
512ef2ee5d0SMichal Meloun 
513ef2ee5d0SMichal Meloun static int
514ef2ee5d0SMichal Meloun tegra_i2c_poll(struct tegra_i2c_softc *sc)
515ef2ee5d0SMichal Meloun {
516ef2ee5d0SMichal Meloun 	int timeout;
517ef2ee5d0SMichal Meloun 
518ef2ee5d0SMichal Meloun 	for(timeout = 10000; timeout > 0; timeout--)  {
519ef2ee5d0SMichal Meloun 		UNLOCK(sc);
520ef2ee5d0SMichal Meloun 		tegra_i2c_intr(sc);
521ef2ee5d0SMichal Meloun 		LOCK(sc);
522ef2ee5d0SMichal Meloun 		if (sc->done != 0)
523ef2ee5d0SMichal Meloun 			 break;
524ef2ee5d0SMichal Meloun 		DELAY(1);
525ef2ee5d0SMichal Meloun 	}
526ef2ee5d0SMichal Meloun 	if (timeout <= 0)
527ef2ee5d0SMichal Meloun 		return (ETIMEDOUT);
528ef2ee5d0SMichal Meloun 	return (0);
529ef2ee5d0SMichal Meloun }
530ef2ee5d0SMichal Meloun 
531ef2ee5d0SMichal Meloun static int
532ef2ee5d0SMichal Meloun tegra_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
533ef2ee5d0SMichal Meloun {
534ef2ee5d0SMichal Meloun 	int rv, i;
535ef2ee5d0SMichal Meloun 	struct tegra_i2c_softc *sc;
536ef2ee5d0SMichal Meloun 	enum tegra_i2c_xfer_type xtype;
537ef2ee5d0SMichal Meloun 
538ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
539ef2ee5d0SMichal Meloun 	LOCK(sc);
540ef2ee5d0SMichal Meloun 
541ef2ee5d0SMichal Meloun 	/* Get the bus. */
542ef2ee5d0SMichal Meloun 	while (sc->bus_inuse == 1)
543ef2ee5d0SMichal Meloun 		SLEEP(sc,  0);
544ef2ee5d0SMichal Meloun 	sc->bus_inuse = 1;
545ef2ee5d0SMichal Meloun 
546ef2ee5d0SMichal Meloun 	rv = 0;
547ef2ee5d0SMichal Meloun 	for (i = 0; i < nmsgs; i++) {
548ef2ee5d0SMichal Meloun 		sc->msg = &msgs[i];
549ef2ee5d0SMichal Meloun 		sc->msg_idx = 0;
550ef2ee5d0SMichal Meloun 		sc->bus_err = 0;
551ef2ee5d0SMichal Meloun 		sc->done = 0;
552ef2ee5d0SMichal Meloun 		/* Check for valid parameters. */
553ef2ee5d0SMichal Meloun 		if (sc->msg == NULL || sc->msg->buf == NULL ||
554ef2ee5d0SMichal Meloun 		    sc->msg->len == 0) {
555ef2ee5d0SMichal Meloun 			rv = EINVAL;
556ef2ee5d0SMichal Meloun 			break;
557ef2ee5d0SMichal Meloun 		}
558ef2ee5d0SMichal Meloun 
559ef2ee5d0SMichal Meloun 		/* Get flags for next transfer. */
560ef2ee5d0SMichal Meloun 		if (i == (nmsgs - 1)) {
561ef2ee5d0SMichal Meloun 			if (msgs[i].flags & IIC_M_NOSTOP)
562ef2ee5d0SMichal Meloun 				xtype = XFER_CONTINUE;
563ef2ee5d0SMichal Meloun 			else
564ef2ee5d0SMichal Meloun 				xtype = XFER_STOP;
565ef2ee5d0SMichal Meloun 		} else {
566ef2ee5d0SMichal Meloun 			if (msgs[i + 1].flags & IIC_M_NOSTART)
567ef2ee5d0SMichal Meloun 				xtype = XFER_CONTINUE;
568ef2ee5d0SMichal Meloun 			else
569ef2ee5d0SMichal Meloun 				xtype = XFER_REPEAT_START;
570ef2ee5d0SMichal Meloun 		}
571ef2ee5d0SMichal Meloun 		tegra_i2c_start_msg(sc, sc->msg, xtype);
572ef2ee5d0SMichal Meloun 		if (cold)
573ef2ee5d0SMichal Meloun 			rv = tegra_i2c_poll(sc);
574ef2ee5d0SMichal Meloun 		else
575ef2ee5d0SMichal Meloun 			rv = msleep(&sc->done, &sc->mtx, PZERO, "iic",
576ef2ee5d0SMichal Meloun 			    I2C_REQUEST_TIMEOUT);
577ef2ee5d0SMichal Meloun 
578ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_MASK_REGISTER, 0);
579ef2ee5d0SMichal Meloun 		WR4(sc, I2C_INTERRUPT_STATUS_REGISTER, 0xFFFFFFFF);
580ef2ee5d0SMichal Meloun 		if (rv == 0)
581ef2ee5d0SMichal Meloun 			rv = sc->bus_err;
582ef2ee5d0SMichal Meloun 		if (rv != 0)
583ef2ee5d0SMichal Meloun 			break;
584ef2ee5d0SMichal Meloun 	}
585ef2ee5d0SMichal Meloun 
586ef2ee5d0SMichal Meloun 	if (rv != 0) {
587ef2ee5d0SMichal Meloun 		tegra_i2c_hw_init(sc);
588ef2ee5d0SMichal Meloun 		tegra_i2c_flush_fifo(sc);
589ef2ee5d0SMichal Meloun 	}
590ef2ee5d0SMichal Meloun 
591ef2ee5d0SMichal Meloun 	sc->msg = NULL;
592ef2ee5d0SMichal Meloun 	sc->msg_idx = 0;
593ef2ee5d0SMichal Meloun 	sc->bus_err = 0;
594ef2ee5d0SMichal Meloun 	sc->done = 0;
595ef2ee5d0SMichal Meloun 
596ef2ee5d0SMichal Meloun 	/* Wake up the processes that are waiting for the bus. */
597ef2ee5d0SMichal Meloun 	sc->bus_inuse = 0;
598ef2ee5d0SMichal Meloun 	wakeup(sc);
599ef2ee5d0SMichal Meloun 	UNLOCK(sc);
600ef2ee5d0SMichal Meloun 
601ef2ee5d0SMichal Meloun 	return (rv);
602ef2ee5d0SMichal Meloun }
603ef2ee5d0SMichal Meloun 
604ef2ee5d0SMichal Meloun static int
605ef2ee5d0SMichal Meloun tegra_i2c_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
606ef2ee5d0SMichal Meloun {
607ef2ee5d0SMichal Meloun 	struct tegra_i2c_softc *sc;
608ef2ee5d0SMichal Meloun 	int busfreq;
609ef2ee5d0SMichal Meloun 
610ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
611ef2ee5d0SMichal Meloun 	busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
612ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
613ef2ee5d0SMichal Meloun 	LOCK(sc);
614ef2ee5d0SMichal Meloun 	tegra_i2c_setup_clk(sc, busfreq);
615ef2ee5d0SMichal Meloun 	UNLOCK(sc);
616ef2ee5d0SMichal Meloun 	return (0);
617ef2ee5d0SMichal Meloun }
618ef2ee5d0SMichal Meloun 
619ef2ee5d0SMichal Meloun static int
620ef2ee5d0SMichal Meloun tegra_i2c_probe(device_t dev)
621ef2ee5d0SMichal Meloun {
622ef2ee5d0SMichal Meloun 	if (!ofw_bus_status_okay(dev))
623ef2ee5d0SMichal Meloun 		return (ENXIO);
624ef2ee5d0SMichal Meloun 
625ef2ee5d0SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
626ef2ee5d0SMichal Meloun 		return (ENXIO);
627ef2ee5d0SMichal Meloun 
628ef2ee5d0SMichal Meloun 	return (BUS_PROBE_DEFAULT);
629ef2ee5d0SMichal Meloun }
630ef2ee5d0SMichal Meloun 
631ef2ee5d0SMichal Meloun static int
632ef2ee5d0SMichal Meloun tegra_i2c_attach(device_t dev)
633ef2ee5d0SMichal Meloun {
634ef2ee5d0SMichal Meloun 	int rv, rid;
635ef2ee5d0SMichal Meloun 	phandle_t node;
636ef2ee5d0SMichal Meloun 	struct tegra_i2c_softc *sc;
637ef2ee5d0SMichal Meloun 	uint64_t freq;
638ef2ee5d0SMichal Meloun 
639ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
640ef2ee5d0SMichal Meloun 	sc->dev = dev;
641ef2ee5d0SMichal Meloun 	node = ofw_bus_get_node(dev);
642ef2ee5d0SMichal Meloun 
643ef2ee5d0SMichal Meloun 	LOCK_INIT(sc);
644ef2ee5d0SMichal Meloun 
645ef2ee5d0SMichal Meloun 	/* Get the memory resource for the register mapping. */
646ef2ee5d0SMichal Meloun 	rid = 0;
647ef2ee5d0SMichal Meloun 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
648ef2ee5d0SMichal Meloun 	    RF_ACTIVE);
649ef2ee5d0SMichal Meloun 	if (sc->mem_res == NULL) {
650ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot map registers.\n");
651ef2ee5d0SMichal Meloun 		rv = ENXIO;
652ef2ee5d0SMichal Meloun 		goto fail;
653ef2ee5d0SMichal Meloun 	}
654ef2ee5d0SMichal Meloun 
655ef2ee5d0SMichal Meloun 	/* Allocate our IRQ resource. */
656ef2ee5d0SMichal Meloun 	rid = 0;
657ef2ee5d0SMichal Meloun 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
658ef2ee5d0SMichal Meloun 	    RF_ACTIVE);
659ef2ee5d0SMichal Meloun 	if (sc->irq_res == NULL) {
660ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot allocate interrupt.\n");
661ef2ee5d0SMichal Meloun 		rv = ENXIO;
662ef2ee5d0SMichal Meloun 		goto fail;
663ef2ee5d0SMichal Meloun 	}
664ef2ee5d0SMichal Meloun 
665ef2ee5d0SMichal Meloun 	/* FDT resources. */
666dac93553SMichal Meloun 	rv = clk_get_by_ofw_name(dev, 0, "div-clk", &sc->clk);
667ef2ee5d0SMichal Meloun 	if (rv != 0) {
668ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot get i2c clock: %d\n", rv);
669ef2ee5d0SMichal Meloun 		goto fail;
670ef2ee5d0SMichal Meloun 	}
671dac93553SMichal Meloun 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "i2c", &sc->reset);
672ef2ee5d0SMichal Meloun 	if (rv != 0) {
673ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Cannot get i2c reset\n");
674ef2ee5d0SMichal Meloun 		return (ENXIO);
675ef2ee5d0SMichal Meloun 	}
676ef2ee5d0SMichal Meloun 	rv = OF_getencprop(node, "clock-frequency", &sc->bus_freq,
677ef2ee5d0SMichal Meloun 	    sizeof(sc->bus_freq));
678ef2ee5d0SMichal Meloun 	if (rv != sizeof(sc->bus_freq)) {
679ef2ee5d0SMichal Meloun 		sc->bus_freq = 100000;
680ef2ee5d0SMichal Meloun 	}
681ef2ee5d0SMichal Meloun 
682ef2ee5d0SMichal Meloun 	/* Request maximum frequency for I2C block 136MHz (408MHz / 3). */
683ef2ee5d0SMichal Meloun 	rv = clk_set_freq(sc->clk, 136000000, CLK_SET_ROUND_DOWN);
684ef2ee5d0SMichal Meloun 	if (rv != 0) {
685ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot set clock frequency\n");
686ef2ee5d0SMichal Meloun 		goto fail;
687ef2ee5d0SMichal Meloun 	}
688ef2ee5d0SMichal Meloun 	rv = clk_get_freq(sc->clk, &freq);
689ef2ee5d0SMichal Meloun 	if (rv != 0) {
690ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot get clock frequency\n");
691ef2ee5d0SMichal Meloun 		goto fail;
692ef2ee5d0SMichal Meloun 	}
693ef2ee5d0SMichal Meloun 	sc->core_freq = (uint32_t)freq;
694ef2ee5d0SMichal Meloun 
695ef2ee5d0SMichal Meloun 	rv = clk_enable(sc->clk);
696ef2ee5d0SMichal Meloun 	if (rv != 0) {
697ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot enable clock: %d\n", rv);
698ef2ee5d0SMichal Meloun 		goto fail;
699ef2ee5d0SMichal Meloun 	}
700ef2ee5d0SMichal Meloun 
701ef2ee5d0SMichal Meloun 	/* Init hardware. */
702ef2ee5d0SMichal Meloun 	rv = tegra_i2c_hw_init(sc);
703ef2ee5d0SMichal Meloun 	if (rv) {
704ef2ee5d0SMichal Meloun 		device_printf(dev, "tegra_i2c_activate failed\n");
705ef2ee5d0SMichal Meloun 		goto fail;
706ef2ee5d0SMichal Meloun 	}
707ef2ee5d0SMichal Meloun 
708ef2ee5d0SMichal Meloun 	/* Setup interrupt. */
709ef2ee5d0SMichal Meloun 	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
710ef2ee5d0SMichal Meloun 	    NULL, tegra_i2c_intr, sc, &sc->irq_h);
711ef2ee5d0SMichal Meloun 	if (rv) {
712ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot setup interrupt.\n");
713ef2ee5d0SMichal Meloun 		goto fail;
714ef2ee5d0SMichal Meloun 	}
715ef2ee5d0SMichal Meloun 
716ef2ee5d0SMichal Meloun 	/* Attach the iicbus. */
7175b56413dSWarner Losh 	sc->iicbus = device_add_child(dev, "iicbus", DEVICE_UNIT_ANY);
718ef2ee5d0SMichal Meloun 	if (sc->iicbus == NULL) {
719ef2ee5d0SMichal Meloun 		device_printf(dev, "Could not allocate iicbus instance.\n");
720ef2ee5d0SMichal Meloun 		rv = ENXIO;
721ef2ee5d0SMichal Meloun 		goto fail;
722ef2ee5d0SMichal Meloun 	}
723ef2ee5d0SMichal Meloun 
724ef2ee5d0SMichal Meloun 	/* Probe and attach the iicbus. */
725*18250ec6SJohn Baldwin 	bus_attach_children(dev);
726*18250ec6SJohn Baldwin 	return (0);
727ef2ee5d0SMichal Meloun 
728ef2ee5d0SMichal Meloun fail:
729ef2ee5d0SMichal Meloun 	if (sc->irq_h != NULL)
730ef2ee5d0SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
731ef2ee5d0SMichal Meloun 	if (sc->irq_res != NULL)
732ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
733ef2ee5d0SMichal Meloun 	if (sc->mem_res != NULL)
734ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
735ef2ee5d0SMichal Meloun 	LOCK_DESTROY(sc);
736ef2ee5d0SMichal Meloun 
737ef2ee5d0SMichal Meloun 	return (rv);
738ef2ee5d0SMichal Meloun }
739ef2ee5d0SMichal Meloun 
740ef2ee5d0SMichal Meloun static int
741ef2ee5d0SMichal Meloun tegra_i2c_detach(device_t dev)
742ef2ee5d0SMichal Meloun {
743ef2ee5d0SMichal Meloun 	struct tegra_i2c_softc *sc;
744d412c076SJohn Baldwin 	int error;
745d412c076SJohn Baldwin 
746d412c076SJohn Baldwin 	error = bus_generic_detach(dev);
747d412c076SJohn Baldwin 	if (error != 0)
748d412c076SJohn Baldwin 		return (error);
749ef2ee5d0SMichal Meloun 
750ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
751ef2ee5d0SMichal Meloun 	tegra_i2c_hw_init(sc);
752ef2ee5d0SMichal Meloun 	if (sc->irq_h != NULL)
753ef2ee5d0SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
754ef2ee5d0SMichal Meloun 	if (sc->irq_res != NULL)
755ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
756ef2ee5d0SMichal Meloun 	if (sc->mem_res != NULL)
757ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
758ef2ee5d0SMichal Meloun 
759ef2ee5d0SMichal Meloun 	LOCK_DESTROY(sc);
760d412c076SJohn Baldwin 	return (0);
761ef2ee5d0SMichal Meloun }
762ef2ee5d0SMichal Meloun 
763ef2ee5d0SMichal Meloun static phandle_t
764ef2ee5d0SMichal Meloun tegra_i2c_get_node(device_t bus, device_t dev)
765ef2ee5d0SMichal Meloun {
766ef2ee5d0SMichal Meloun 
767ef2ee5d0SMichal Meloun 	/* Share controller node with iibus device. */
768ef2ee5d0SMichal Meloun 	return (ofw_bus_get_node(bus));
769ef2ee5d0SMichal Meloun }
770ef2ee5d0SMichal Meloun 
771ef2ee5d0SMichal Meloun static device_method_t tegra_i2c_methods[] = {
772ef2ee5d0SMichal Meloun 	/* Device interface */
773ef2ee5d0SMichal Meloun 	DEVMETHOD(device_probe,		tegra_i2c_probe),
774ef2ee5d0SMichal Meloun 	DEVMETHOD(device_attach,	tegra_i2c_attach),
775ef2ee5d0SMichal Meloun 	DEVMETHOD(device_detach,	tegra_i2c_detach),
776ef2ee5d0SMichal Meloun 
777ef2ee5d0SMichal Meloun 	/* Bus interface */
778ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
779ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
780ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
781ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
782ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
783ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
784ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
785ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
786ef2ee5d0SMichal Meloun 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
787ef2ee5d0SMichal Meloun 
788ef2ee5d0SMichal Meloun 	/* OFW methods */
789ef2ee5d0SMichal Meloun 	DEVMETHOD(ofw_bus_get_node,	tegra_i2c_get_node),
790ef2ee5d0SMichal Meloun 
791ef2ee5d0SMichal Meloun 	/* iicbus interface */
792ef2ee5d0SMichal Meloun 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
793ef2ee5d0SMichal Meloun 	DEVMETHOD(iicbus_reset,		tegra_i2c_iicbus_reset),
794ef2ee5d0SMichal Meloun 	DEVMETHOD(iicbus_transfer,	tegra_i2c_transfer),
795ef2ee5d0SMichal Meloun 
796ef2ee5d0SMichal Meloun 	DEVMETHOD_END
797ef2ee5d0SMichal Meloun };
798ef2ee5d0SMichal Meloun 
7994bda238aSMichal Meloun static DEFINE_CLASS_0(iichb, tegra_i2c_driver, tegra_i2c_methods,
8004bda238aSMichal Meloun     sizeof(struct tegra_i2c_softc));
801289f133bSJohn Baldwin EARLY_DRIVER_MODULE(tegra_iic, simplebus, tegra_i2c_driver, NULL, NULL, 73);
802