xref: /freebsd/sys/arm/ti/ti_spi.c (revision 64d1a02e4e1a5f561d03b2bbc9c20dad255e79e8)
1cb18f3f3SLuiz Otavio O Souza /*-
2cb18f3f3SLuiz Otavio O Souza  * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
3cb18f3f3SLuiz Otavio O Souza  * All rights reserved.
4cb18f3f3SLuiz Otavio O Souza  *
5cb18f3f3SLuiz Otavio O Souza  * Redistribution and use in source and binary forms, with or without
6cb18f3f3SLuiz Otavio O Souza  * modification, are permitted provided that the following conditions
7cb18f3f3SLuiz Otavio O Souza  * are met:
8cb18f3f3SLuiz Otavio O Souza  * 1. Redistributions of source code must retain the above copyright
9cb18f3f3SLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer.
10cb18f3f3SLuiz Otavio O Souza  * 2. Redistributions in binary form must reproduce the above copyright
11cb18f3f3SLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer in the
12cb18f3f3SLuiz Otavio O Souza  *    documentation and/or other materials provided with the distribution.
13cb18f3f3SLuiz Otavio O Souza  *
14cb18f3f3SLuiz Otavio O Souza  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15cb18f3f3SLuiz Otavio O Souza  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16cb18f3f3SLuiz Otavio O Souza  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17cb18f3f3SLuiz Otavio O Souza  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18cb18f3f3SLuiz Otavio O Souza  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19cb18f3f3SLuiz Otavio O Souza  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20cb18f3f3SLuiz Otavio O Souza  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21cb18f3f3SLuiz Otavio O Souza  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22cb18f3f3SLuiz Otavio O Souza  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23cb18f3f3SLuiz Otavio O Souza  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24cb18f3f3SLuiz Otavio O Souza  * SUCH DAMAGE.
25cb18f3f3SLuiz Otavio O Souza  *
26cb18f3f3SLuiz Otavio O Souza  */
27fdafd315SWarner Losh 
28cb18f3f3SLuiz Otavio O Souza #include <sys/param.h>
29cb18f3f3SLuiz Otavio O Souza #include <sys/systm.h>
30cb18f3f3SLuiz Otavio O Souza #include <sys/bus.h>
31cb18f3f3SLuiz Otavio O Souza 
32cb18f3f3SLuiz Otavio O Souza #include <sys/kernel.h>
33cb18f3f3SLuiz Otavio O Souza #include <sys/module.h>
34cb18f3f3SLuiz Otavio O Souza #include <sys/rman.h>
35cb18f3f3SLuiz Otavio O Souza #include <sys/lock.h>
36cb18f3f3SLuiz Otavio O Souza #include <sys/mutex.h>
37cb18f3f3SLuiz Otavio O Souza #include <sys/sysctl.h>
38cb18f3f3SLuiz Otavio O Souza 
39cb18f3f3SLuiz Otavio O Souza #include <machine/bus.h>
40cb18f3f3SLuiz Otavio O Souza #include <machine/resource.h>
41cb18f3f3SLuiz Otavio O Souza #include <machine/intr.h>
42cb18f3f3SLuiz Otavio O Souza 
43cb18f3f3SLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h>
44cb18f3f3SLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h>
45cb18f3f3SLuiz Otavio O Souza 
46cb18f3f3SLuiz Otavio O Souza #include <dev/spibus/spi.h>
47cb18f3f3SLuiz Otavio O Souza #include <dev/spibus/spibusvar.h>
48cb18f3f3SLuiz Otavio O Souza 
490050ea24SMichal Meloun #include <arm/ti/ti_sysc.h>
50cb18f3f3SLuiz Otavio O Souza #include <arm/ti/ti_spireg.h>
51cb18f3f3SLuiz Otavio O Souza #include <arm/ti/ti_spivar.h>
52cb18f3f3SLuiz Otavio O Souza 
53cb18f3f3SLuiz Otavio O Souza #include "spibus_if.h"
54cb18f3f3SLuiz Otavio O Souza 
55cb18f3f3SLuiz Otavio O Souza static void ti_spi_intr(void *);
56cb18f3f3SLuiz Otavio O Souza static int ti_spi_detach(device_t);
57cb18f3f3SLuiz Otavio O Souza 
58cb18f3f3SLuiz Otavio O Souza #undef TI_SPI_DEBUG
59cb18f3f3SLuiz Otavio O Souza #ifdef TI_SPI_DEBUG
60cb18f3f3SLuiz Otavio O Souza #define	IRQSTATUSBITS							\
61cb18f3f3SLuiz Otavio O Souza 	"\020\1TX0_EMPTY\2TX0_UNDERFLOW\3RX0_FULL\4RX0_OVERFLOW"	\
62cb18f3f3SLuiz Otavio O Souza 	"\5TX1_EMPTY\6TX1_UNDERFLOW\7RX1_FULL\11TX2_EMPTY"		\
63cb18f3f3SLuiz Otavio O Souza 	"\12TX1_UNDERFLOW\13RX2_FULL\15TX3_EMPTY\16TX3_UNDERFLOW"	\
64cb18f3f3SLuiz Otavio O Souza 	"\17RX3_FULL\22EOW"
65cb18f3f3SLuiz Otavio O Souza #define	CONFBITS							\
66cb18f3f3SLuiz Otavio O Souza 	"\020\1PHA\2POL\7EPOL\17DMAW\20DMAR\21DPE0\22DPE1\23IS"		\
67cb18f3f3SLuiz Otavio O Souza 	"\24TURBO\25FORCE\30SBE\31SBPOL\34FFEW\35FFER\36CLKG"
68cb18f3f3SLuiz Otavio O Souza #define	STATBITS							\
69cb18f3f3SLuiz Otavio O Souza 	"\020\1RXS\2TXS\3EOT\4TXFFE\5TXFFF\6RXFFE\7RXFFFF"
70cb18f3f3SLuiz Otavio O Souza #define	MODULCTRLBITS							\
71cb18f3f3SLuiz Otavio O Souza 	"\020\1SINGLE\2NOSPIEN\3SLAVE\4SYST\10MOA\11FDAA"
72cb18f3f3SLuiz Otavio O Souza #define	CTRLBITS							\
73cb18f3f3SLuiz Otavio O Souza 	"\020\1ENABLED"
74cb18f3f3SLuiz Otavio O Souza 
75cb18f3f3SLuiz Otavio O Souza static void
ti_spi_printr(device_t dev)76cb18f3f3SLuiz Otavio O Souza ti_spi_printr(device_t dev)
77cb18f3f3SLuiz Otavio O Souza {
78cb18f3f3SLuiz Otavio O Souza 	int clk, conf, ctrl, div, i, j, wl;
79cb18f3f3SLuiz Otavio O Souza 	struct ti_spi_softc *sc;
80cb18f3f3SLuiz Otavio O Souza 	uint32_t reg;
81cb18f3f3SLuiz Otavio O Souza 
82cb18f3f3SLuiz Otavio O Souza 	sc = device_get_softc(dev);
83cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_SYSCONFIG);
84cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "SYSCONFIG: %#x\n", reg);
85cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_SYSSTATUS);
86cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "SYSSTATUS: %#x\n", reg);
87cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
88cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "IRQSTATUS: 0x%b\n", reg, IRQSTATUSBITS);
89cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
90cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "IRQENABLE: 0x%b\n", reg, IRQSTATUSBITS);
91cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_MODULCTRL);
92cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "MODULCTRL: 0x%b\n", reg, MODULCTRLBITS);
93cb18f3f3SLuiz Otavio O Souza 	for (i = 0; i < sc->sc_numcs; i++) {
94cb18f3f3SLuiz Otavio O Souza 		ctrl = TI_SPI_READ(sc, MCSPI_CTRL_CH(i));
95cb18f3f3SLuiz Otavio O Souza 		conf = TI_SPI_READ(sc, MCSPI_CONF_CH(i));
96cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "CH%dCONF: 0x%b\n", i, conf, CONFBITS);
97cb18f3f3SLuiz Otavio O Souza 		if (conf & MCSPI_CONF_CLKG) {
98cb18f3f3SLuiz Otavio O Souza 			div = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
99cb18f3f3SLuiz Otavio O Souza 			div |= ((ctrl >> MCSPI_CTRL_EXTCLK_SHIFT) & MCSPI_CTRL_EXTCLK_MSK) << 4;
100cb18f3f3SLuiz Otavio O Souza 		} else {
101cb18f3f3SLuiz Otavio O Souza 			div = 1;
102cb18f3f3SLuiz Otavio O Souza 			j = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
103cb18f3f3SLuiz Otavio O Souza 			while (j-- > 0)
104cb18f3f3SLuiz Otavio O Souza 				div <<= 1;
105cb18f3f3SLuiz Otavio O Souza 		}
106cb18f3f3SLuiz Otavio O Souza 		clk = TI_SPI_GCLK / div;
107cb18f3f3SLuiz Otavio O Souza 		wl = ((conf >> MCSPI_CONF_WL_SHIFT) & MCSPI_CONF_WL_MSK) + 1;
108cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "wordlen: %-2d clock: %d\n", wl, clk);
109cb18f3f3SLuiz Otavio O Souza 		reg = TI_SPI_READ(sc, MCSPI_STAT_CH(i));
110cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "CH%dSTAT: 0x%b\n", i, reg, STATBITS);
111cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "CH%dCTRL: 0x%b\n", i, ctrl, CTRLBITS);
112cb18f3f3SLuiz Otavio O Souza 	}
113cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_XFERLEVEL);
114cb18f3f3SLuiz Otavio O Souza 	device_printf(dev, "XFERLEVEL: %#x\n", reg);
115cb18f3f3SLuiz Otavio O Souza }
116cb18f3f3SLuiz Otavio O Souza #endif
117cb18f3f3SLuiz Otavio O Souza 
118cb18f3f3SLuiz Otavio O Souza static void
ti_spi_set_clock(struct ti_spi_softc * sc,int ch,int freq)119cb18f3f3SLuiz Otavio O Souza ti_spi_set_clock(struct ti_spi_softc *sc, int ch, int freq)
120cb18f3f3SLuiz Otavio O Souza {
121cb18f3f3SLuiz Otavio O Souza 	uint32_t clkdiv, conf, div, extclk, reg;
122cb18f3f3SLuiz Otavio O Souza 
123cb18f3f3SLuiz Otavio O Souza 	clkdiv = TI_SPI_GCLK / freq;
124cb18f3f3SLuiz Otavio O Souza 	if (clkdiv > MCSPI_EXTCLK_MSK) {
125cb18f3f3SLuiz Otavio O Souza 		extclk = 0;
126cb18f3f3SLuiz Otavio O Souza 		clkdiv = 0;
127cb18f3f3SLuiz Otavio O Souza 		div = 1;
128cb18f3f3SLuiz Otavio O Souza 		while (TI_SPI_GCLK / div > freq && clkdiv <= 0xf) {
129cb18f3f3SLuiz Otavio O Souza 			clkdiv++;
130cb18f3f3SLuiz Otavio O Souza 			div <<= 1;
131cb18f3f3SLuiz Otavio O Souza 		}
132cb18f3f3SLuiz Otavio O Souza 		conf = clkdiv << MCSPI_CONF_CLK_SHIFT;
133cb18f3f3SLuiz Otavio O Souza 	} else {
134cb18f3f3SLuiz Otavio O Souza 		extclk = clkdiv >> 4;
135cb18f3f3SLuiz Otavio O Souza 		clkdiv &= MCSPI_CONF_CLK_MSK;
136cb18f3f3SLuiz Otavio O Souza 		conf = MCSPI_CONF_CLKG | clkdiv << MCSPI_CONF_CLK_SHIFT;
137cb18f3f3SLuiz Otavio O Souza 	}
138cb18f3f3SLuiz Otavio O Souza 
139cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(ch));
140cb18f3f3SLuiz Otavio O Souza 	reg &= ~(MCSPI_CTRL_EXTCLK_MSK << MCSPI_CTRL_EXTCLK_SHIFT);
141cb18f3f3SLuiz Otavio O Souza 	reg |= extclk << MCSPI_CTRL_EXTCLK_SHIFT;
142cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(ch), reg);
143cb18f3f3SLuiz Otavio O Souza 
144cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(ch));
145cb18f3f3SLuiz Otavio O Souza 	reg &= ~(MCSPI_CONF_CLKG | MCSPI_CONF_CLK_MSK << MCSPI_CONF_CLK_SHIFT);
146cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CONF_CH(ch), reg | conf);
147cb18f3f3SLuiz Otavio O Souza }
148cb18f3f3SLuiz Otavio O Souza 
149cb18f3f3SLuiz Otavio O Souza static int
ti_spi_probe(device_t dev)150cb18f3f3SLuiz Otavio O Souza ti_spi_probe(device_t dev)
151cb18f3f3SLuiz Otavio O Souza {
152cb18f3f3SLuiz Otavio O Souza 
153cb18f3f3SLuiz Otavio O Souza 	if (!ofw_bus_status_okay(dev))
154cb18f3f3SLuiz Otavio O Souza 		return (ENXIO);
155cb18f3f3SLuiz Otavio O Souza 	if (!ofw_bus_is_compatible(dev, "ti,omap4-mcspi"))
156cb18f3f3SLuiz Otavio O Souza 		return (ENXIO);
157cb18f3f3SLuiz Otavio O Souza 
158cb18f3f3SLuiz Otavio O Souza 	device_set_desc(dev, "TI McSPI controller");
159cb18f3f3SLuiz Otavio O Souza 
160cb18f3f3SLuiz Otavio O Souza 	return (BUS_PROBE_DEFAULT);
161cb18f3f3SLuiz Otavio O Souza }
162cb18f3f3SLuiz Otavio O Souza 
163cb18f3f3SLuiz Otavio O Souza static int
ti_spi_attach(device_t dev)164cb18f3f3SLuiz Otavio O Souza ti_spi_attach(device_t dev)
165cb18f3f3SLuiz Otavio O Souza {
1660050ea24SMichal Meloun 	int err, i, rid, timeout;
167cb18f3f3SLuiz Otavio O Souza 	struct ti_spi_softc *sc;
168cb18f3f3SLuiz Otavio O Souza 	uint32_t rev;
169cb18f3f3SLuiz Otavio O Souza 
170cb18f3f3SLuiz Otavio O Souza 	sc = device_get_softc(dev);
171cb18f3f3SLuiz Otavio O Souza 	sc->sc_dev = dev;
172cb18f3f3SLuiz Otavio O Souza 
173cb18f3f3SLuiz Otavio O Souza 	/* Activate the McSPI module. */
1740050ea24SMichal Meloun 	err = ti_sysc_clock_enable(device_get_parent(dev));
175cb18f3f3SLuiz Otavio O Souza 	if (err) {
176cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "Error: failed to activate source clock\n");
177cb18f3f3SLuiz Otavio O Souza 		return (err);
178cb18f3f3SLuiz Otavio O Souza 	}
179cb18f3f3SLuiz Otavio O Souza 
180cb18f3f3SLuiz Otavio O Souza 	/* Get the number of available channels. */
181cb18f3f3SLuiz Otavio O Souza 	if ((OF_getencprop(ofw_bus_get_node(dev), "ti,spi-num-cs",
182cb18f3f3SLuiz Otavio O Souza 	    &sc->sc_numcs, sizeof(sc->sc_numcs))) <= 0) {
183cb18f3f3SLuiz Otavio O Souza 		sc->sc_numcs = 2;
184cb18f3f3SLuiz Otavio O Souza 	}
185cb18f3f3SLuiz Otavio O Souza 
186cb18f3f3SLuiz Otavio O Souza 	rid = 0;
187cb18f3f3SLuiz Otavio O Souza 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
188cb18f3f3SLuiz Otavio O Souza 	    RF_ACTIVE);
189cb18f3f3SLuiz Otavio O Souza 	if (!sc->sc_mem_res) {
190cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "cannot allocate memory window\n");
191cb18f3f3SLuiz Otavio O Souza 		return (ENXIO);
192cb18f3f3SLuiz Otavio O Souza 	}
193cb18f3f3SLuiz Otavio O Souza 
194cb18f3f3SLuiz Otavio O Souza 	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
195cb18f3f3SLuiz Otavio O Souza 	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
196cb18f3f3SLuiz Otavio O Souza 
197cb18f3f3SLuiz Otavio O Souza 	rid = 0;
198cb18f3f3SLuiz Otavio O Souza 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
199cb18f3f3SLuiz Otavio O Souza 	    RF_ACTIVE);
200cb18f3f3SLuiz Otavio O Souza 	if (!sc->sc_irq_res) {
201cb18f3f3SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
202cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "cannot allocate interrupt\n");
203cb18f3f3SLuiz Otavio O Souza 		return (ENXIO);
204cb18f3f3SLuiz Otavio O Souza 	}
205cb18f3f3SLuiz Otavio O Souza 
206cb18f3f3SLuiz Otavio O Souza 	/* Hook up our interrupt handler. */
207cb18f3f3SLuiz Otavio O Souza 	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
208cb18f3f3SLuiz Otavio O Souza 	    NULL, ti_spi_intr, sc, &sc->sc_intrhand)) {
209cb18f3f3SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
210cb18f3f3SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
211cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "cannot setup the interrupt handler\n");
212cb18f3f3SLuiz Otavio O Souza 		return (ENXIO);
213cb18f3f3SLuiz Otavio O Souza 	}
214cb18f3f3SLuiz Otavio O Souza 
215cb18f3f3SLuiz Otavio O Souza 	mtx_init(&sc->sc_mtx, "ti_spi", NULL, MTX_DEF);
216cb18f3f3SLuiz Otavio O Souza 
217cb18f3f3SLuiz Otavio O Souza 	/* Issue a softreset to the controller */
218cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
219cb18f3f3SLuiz Otavio O Souza 	timeout = 1000;
220cb18f3f3SLuiz Otavio O Souza 	while (!(TI_SPI_READ(sc, MCSPI_SYSSTATUS) &
221cb18f3f3SLuiz Otavio O Souza 	    MCSPI_SYSSTATUS_RESETDONE)) {
222cb18f3f3SLuiz Otavio O Souza 		if (--timeout == 0) {
223cb18f3f3SLuiz Otavio O Souza 			device_printf(dev,
224cb18f3f3SLuiz Otavio O Souza 			    "Error: Controller reset operation timed out\n");
225cb18f3f3SLuiz Otavio O Souza 			ti_spi_detach(dev);
226cb18f3f3SLuiz Otavio O Souza 			return (ENXIO);
227cb18f3f3SLuiz Otavio O Souza 		}
228cb18f3f3SLuiz Otavio O Souza 		DELAY(100);
229cb18f3f3SLuiz Otavio O Souza 	}
230cb18f3f3SLuiz Otavio O Souza 
231cb18f3f3SLuiz Otavio O Souza 	/* Print the McSPI module revision. */
2320050ea24SMichal Meloun 	rev = TI_SPI_READ(sc,
2330050ea24SMichal Meloun 	    ti_sysc_get_rev_address_offset_host(device_get_parent(dev)));
234cb18f3f3SLuiz Otavio O Souza 	device_printf(dev,
235cb18f3f3SLuiz Otavio O Souza 	    "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
236cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK,
237cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_FUNC_SHIFT) & MCSPI_REVISION_FUNC_MSK,
238cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_RTL_SHIFT) & MCSPI_REVISION_RTL_MSK,
239cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_MAJOR_SHIFT) & MCSPI_REVISION_MAJOR_MSK,
240cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_MINOR_SHIFT) & MCSPI_REVISION_MINOR_MSK,
241cb18f3f3SLuiz Otavio O Souza 	    (rev >> MCSPI_REVISION_CUSTOM_SHIFT) & MCSPI_REVISION_CUSTOM_MSK);
242cb18f3f3SLuiz Otavio O Souza 
243cb18f3f3SLuiz Otavio O Souza 	/* Set Master mode, single channel. */
244cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE);
245cb18f3f3SLuiz Otavio O Souza 
246cb18f3f3SLuiz Otavio O Souza 	/* Clear pending interrupts and disable interrupts. */
247cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0x0);
248cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
249cb18f3f3SLuiz Otavio O Souza 
250cb18f3f3SLuiz Otavio O Souza 	for (i = 0; i < sc->sc_numcs; i++) {
251cb18f3f3SLuiz Otavio O Souza 		/*
252cb18f3f3SLuiz Otavio O Souza 		 * Default to SPI mode 0, CS active low, 8 bits word length and
253cb18f3f3SLuiz Otavio O Souza 		 * 500kHz clock.
254cb18f3f3SLuiz Otavio O Souza 		 */
255cb18f3f3SLuiz Otavio O Souza 		TI_SPI_WRITE(sc, MCSPI_CONF_CH(i),
256cb18f3f3SLuiz Otavio O Souza 		    MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL |
257cb18f3f3SLuiz Otavio O Souza 		    (8 - 1) << MCSPI_CONF_WL_SHIFT);
258cb18f3f3SLuiz Otavio O Souza 		/* Set initial clock - 500kHz. */
259cb18f3f3SLuiz Otavio O Souza 		ti_spi_set_clock(sc, i, 500000);
260cb18f3f3SLuiz Otavio O Souza 	}
261cb18f3f3SLuiz Otavio O Souza 
262cb18f3f3SLuiz Otavio O Souza #ifdef	TI_SPI_DEBUG
263cb18f3f3SLuiz Otavio O Souza 	ti_spi_printr(dev);
264cb18f3f3SLuiz Otavio O Souza #endif
265cb18f3f3SLuiz Otavio O Souza 
2665b56413dSWarner Losh 	device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
26718250ec6SJohn Baldwin 	bus_attach_children(dev);
268cb18f3f3SLuiz Otavio O Souza 
26918250ec6SJohn Baldwin 	return (0);
270cb18f3f3SLuiz Otavio O Souza }
271cb18f3f3SLuiz Otavio O Souza 
272cb18f3f3SLuiz Otavio O Souza static int
ti_spi_detach(device_t dev)273cb18f3f3SLuiz Otavio O Souza ti_spi_detach(device_t dev)
274cb18f3f3SLuiz Otavio O Souza {
275cb18f3f3SLuiz Otavio O Souza 	struct ti_spi_softc *sc;
276*64d1a02eSJohn Baldwin 	int error;
277*64d1a02eSJohn Baldwin 
278*64d1a02eSJohn Baldwin 	error = bus_generic_detach(dev);
279*64d1a02eSJohn Baldwin 	if (error != 0)
280*64d1a02eSJohn Baldwin 		return (error);
281cb18f3f3SLuiz Otavio O Souza 
282cb18f3f3SLuiz Otavio O Souza 	sc = device_get_softc(dev);
283cb18f3f3SLuiz Otavio O Souza 
284cb18f3f3SLuiz Otavio O Souza 	/* Clear pending interrupts and disable interrupts. */
285cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0);
286cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
287cb18f3f3SLuiz Otavio O Souza 
288cb18f3f3SLuiz Otavio O Souza 	/* Reset controller. */
289cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
290cb18f3f3SLuiz Otavio O Souza 
291cb18f3f3SLuiz Otavio O Souza 	mtx_destroy(&sc->sc_mtx);
292cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_intrhand)
293cb18f3f3SLuiz Otavio O Souza 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
294cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_irq_res)
295cb18f3f3SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
296cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_mem_res)
297cb18f3f3SLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
298cb18f3f3SLuiz Otavio O Souza 
299cb18f3f3SLuiz Otavio O Souza 	return (0);
300cb18f3f3SLuiz Otavio O Souza }
301cb18f3f3SLuiz Otavio O Souza 
302cb18f3f3SLuiz Otavio O Souza static int
ti_spi_fill_fifo(struct ti_spi_softc * sc)303cb18f3f3SLuiz Otavio O Souza ti_spi_fill_fifo(struct ti_spi_softc *sc)
304cb18f3f3SLuiz Otavio O Souza {
305cb18f3f3SLuiz Otavio O Souza 	int bytes, timeout;
306cb18f3f3SLuiz Otavio O Souza 	struct spi_command *cmd;
307cb18f3f3SLuiz Otavio O Souza 	uint32_t written;
308cb18f3f3SLuiz Otavio O Souza 	uint8_t *data;
309cb18f3f3SLuiz Otavio O Souza 
310cb18f3f3SLuiz Otavio O Souza 	cmd = sc->sc_cmd;
311cb18f3f3SLuiz Otavio O Souza 	bytes = min(sc->sc_len - sc->sc_written, sc->sc_fifolvl);
312cb18f3f3SLuiz Otavio O Souza 	while (bytes-- > 0) {
313cb18f3f3SLuiz Otavio O Souza 		data = (uint8_t *)cmd->tx_cmd;
314cb18f3f3SLuiz Otavio O Souza 		written = sc->sc_written++;
315cb18f3f3SLuiz Otavio O Souza 		if (written >= cmd->tx_cmd_sz) {
316cb18f3f3SLuiz Otavio O Souza 			data = (uint8_t *)cmd->tx_data;
317cb18f3f3SLuiz Otavio O Souza 			written -= cmd->tx_cmd_sz;
318cb18f3f3SLuiz Otavio O Souza 		}
319cb18f3f3SLuiz Otavio O Souza 		if (sc->sc_fifolvl == 1) {
320cb18f3f3SLuiz Otavio O Souza 			/* FIFO disabled. */
321cb18f3f3SLuiz Otavio O Souza 			timeout = 1000;
322cb18f3f3SLuiz Otavio O Souza 			while (--timeout > 0 && (TI_SPI_READ(sc,
323cb18f3f3SLuiz Otavio O Souza 			    MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_TXS) == 0) {
324e5185f3eSLuiz Otavio O Souza 				DELAY(100);
325cb18f3f3SLuiz Otavio O Souza 			}
326cb18f3f3SLuiz Otavio O Souza 			if (timeout == 0)
327cb18f3f3SLuiz Otavio O Souza 				return (-1);
328cb18f3f3SLuiz Otavio O Souza 		}
329cb18f3f3SLuiz Otavio O Souza 		TI_SPI_WRITE(sc, MCSPI_TX_CH(sc->sc_cs), data[written]);
330cb18f3f3SLuiz Otavio O Souza 	}
331cb18f3f3SLuiz Otavio O Souza 
332cb18f3f3SLuiz Otavio O Souza 	return (0);
333cb18f3f3SLuiz Otavio O Souza }
334cb18f3f3SLuiz Otavio O Souza 
335cb18f3f3SLuiz Otavio O Souza static int
ti_spi_drain_fifo(struct ti_spi_softc * sc)336cb18f3f3SLuiz Otavio O Souza ti_spi_drain_fifo(struct ti_spi_softc *sc)
337cb18f3f3SLuiz Otavio O Souza {
338cb18f3f3SLuiz Otavio O Souza 	int bytes, timeout;
339cb18f3f3SLuiz Otavio O Souza 	struct spi_command *cmd;
340cb18f3f3SLuiz Otavio O Souza 	uint32_t read;
341cb18f3f3SLuiz Otavio O Souza 	uint8_t *data;
342cb18f3f3SLuiz Otavio O Souza 
343cb18f3f3SLuiz Otavio O Souza 	cmd = sc->sc_cmd;
344cb18f3f3SLuiz Otavio O Souza 	bytes = min(sc->sc_len - sc->sc_read, sc->sc_fifolvl);
345cb18f3f3SLuiz Otavio O Souza 	while (bytes-- > 0) {
346cb18f3f3SLuiz Otavio O Souza 		data = (uint8_t *)cmd->rx_cmd;
347cb18f3f3SLuiz Otavio O Souza 		read = sc->sc_read++;
348cb18f3f3SLuiz Otavio O Souza 		if (read >= cmd->rx_cmd_sz) {
349cb18f3f3SLuiz Otavio O Souza 			data = (uint8_t *)cmd->rx_data;
350cb18f3f3SLuiz Otavio O Souza 			read -= cmd->rx_cmd_sz;
351cb18f3f3SLuiz Otavio O Souza 		}
352cb18f3f3SLuiz Otavio O Souza 		if (sc->sc_fifolvl == 1) {
353cb18f3f3SLuiz Otavio O Souza 			/* FIFO disabled. */
354cb18f3f3SLuiz Otavio O Souza 			timeout = 1000;
355cb18f3f3SLuiz Otavio O Souza 			while (--timeout > 0 && (TI_SPI_READ(sc,
356cb18f3f3SLuiz Otavio O Souza 			    MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_RXS) == 0) {
357e5185f3eSLuiz Otavio O Souza 				DELAY(100);
358cb18f3f3SLuiz Otavio O Souza 			}
359cb18f3f3SLuiz Otavio O Souza 			if (timeout == 0)
360cb18f3f3SLuiz Otavio O Souza 				return (-1);
361cb18f3f3SLuiz Otavio O Souza 		}
362cb18f3f3SLuiz Otavio O Souza 		data[read] = TI_SPI_READ(sc, MCSPI_RX_CH(sc->sc_cs));
363cb18f3f3SLuiz Otavio O Souza 	}
364cb18f3f3SLuiz Otavio O Souza 
365cb18f3f3SLuiz Otavio O Souza 	return (0);
366cb18f3f3SLuiz Otavio O Souza }
367cb18f3f3SLuiz Otavio O Souza 
368cb18f3f3SLuiz Otavio O Souza static void
ti_spi_intr(void * arg)369cb18f3f3SLuiz Otavio O Souza ti_spi_intr(void *arg)
370cb18f3f3SLuiz Otavio O Souza {
371cb18f3f3SLuiz Otavio O Souza 	struct ti_spi_softc *sc;
372cb18f3f3SLuiz Otavio O Souza 	uint32_t status;
373cb18f3f3SLuiz Otavio O Souza 
374cb18f3f3SLuiz Otavio O Souza 	sc = (struct ti_spi_softc *)arg;
375cb18f3f3SLuiz Otavio O Souza 	TI_SPI_LOCK(sc);
376cb18f3f3SLuiz Otavio O Souza 	status = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
377cb18f3f3SLuiz Otavio O Souza 
378cb18f3f3SLuiz Otavio O Souza 	/*
379cb18f3f3SLuiz Otavio O Souza 	 * No new TX_empty or RX_full event will be asserted while the CPU has
380cb18f3f3SLuiz Otavio O Souza 	 * not performed the number of writes or reads defined by
381cb18f3f3SLuiz Otavio O Souza 	 * MCSPI_XFERLEVEL[AEL] and MCSPI_XFERLEVEL[AFL].  It is responsibility
382cb18f3f3SLuiz Otavio O Souza 	 * of CPU perform the right number of writes and reads.
383cb18f3f3SLuiz Otavio O Souza 	 */
384cb18f3f3SLuiz Otavio O Souza 	if (status & MCSPI_IRQ_TX0_EMPTY)
385cb18f3f3SLuiz Otavio O Souza 		ti_spi_fill_fifo(sc);
386cb18f3f3SLuiz Otavio O Souza 	if (status & MCSPI_IRQ_RX0_FULL)
387cb18f3f3SLuiz Otavio O Souza 		ti_spi_drain_fifo(sc);
388cb18f3f3SLuiz Otavio O Souza 
389cb18f3f3SLuiz Otavio O Souza 	/* Clear interrupt status. */
390cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, status);
391cb18f3f3SLuiz Otavio O Souza 
392cb18f3f3SLuiz Otavio O Souza 	/* Check for end of transfer. */
393cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) {
394cb18f3f3SLuiz Otavio O Souza 		sc->sc_flags |= TI_SPI_DONE;
395cb18f3f3SLuiz Otavio O Souza 		wakeup(sc->sc_dev);
396cb18f3f3SLuiz Otavio O Souza 	}
397cb18f3f3SLuiz Otavio O Souza 
398cb18f3f3SLuiz Otavio O Souza 	TI_SPI_UNLOCK(sc);
399cb18f3f3SLuiz Otavio O Souza }
400cb18f3f3SLuiz Otavio O Souza 
401cb18f3f3SLuiz Otavio O Souza static int
ti_spi_pio_transfer(struct ti_spi_softc * sc)402cb18f3f3SLuiz Otavio O Souza ti_spi_pio_transfer(struct ti_spi_softc *sc)
403cb18f3f3SLuiz Otavio O Souza {
404cb18f3f3SLuiz Otavio O Souza 
405cb18f3f3SLuiz Otavio O Souza 	while (sc->sc_len - sc->sc_written > 0) {
406cb18f3f3SLuiz Otavio O Souza 		if (ti_spi_fill_fifo(sc) == -1)
407cb18f3f3SLuiz Otavio O Souza 			return (EIO);
408cb18f3f3SLuiz Otavio O Souza 		if (ti_spi_drain_fifo(sc) == -1)
409cb18f3f3SLuiz Otavio O Souza 			return (EIO);
410cb18f3f3SLuiz Otavio O Souza 	}
411cb18f3f3SLuiz Otavio O Souza 
412cb18f3f3SLuiz Otavio O Souza 	return (0);
413cb18f3f3SLuiz Otavio O Souza }
414cb18f3f3SLuiz Otavio O Souza 
415cb18f3f3SLuiz Otavio O Souza static int
ti_spi_gcd(int a,int b)416cb18f3f3SLuiz Otavio O Souza ti_spi_gcd(int a, int b)
417cb18f3f3SLuiz Otavio O Souza {
418cb18f3f3SLuiz Otavio O Souza 	int m;
419cb18f3f3SLuiz Otavio O Souza 
420cb18f3f3SLuiz Otavio O Souza 	while ((m = a % b) != 0) {
421cb18f3f3SLuiz Otavio O Souza 		a = b;
422cb18f3f3SLuiz Otavio O Souza 		b = m;
423cb18f3f3SLuiz Otavio O Souza 	}
424cb18f3f3SLuiz Otavio O Souza 
425cb18f3f3SLuiz Otavio O Souza 	return (b);
426cb18f3f3SLuiz Otavio O Souza }
427cb18f3f3SLuiz Otavio O Souza 
428cb18f3f3SLuiz Otavio O Souza static int
ti_spi_transfer(device_t dev,device_t child,struct spi_command * cmd)429cb18f3f3SLuiz Otavio O Souza ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
430cb18f3f3SLuiz Otavio O Souza {
431718860e4SEmmanuel Vadot 	int err;
432cb18f3f3SLuiz Otavio O Souza 	struct ti_spi_softc *sc;
433584e3185SIan Lepore 	uint32_t clockhz, cs, mode, reg;
434cb18f3f3SLuiz Otavio O Souza 
435cb18f3f3SLuiz Otavio O Souza 	sc = device_get_softc(dev);
436cb18f3f3SLuiz Otavio O Souza 
437cb18f3f3SLuiz Otavio O Souza 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
438cb18f3f3SLuiz Otavio O Souza 	    ("TX/RX command sizes should be equal"));
439cb18f3f3SLuiz Otavio O Souza 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
440cb18f3f3SLuiz Otavio O Souza 	    ("TX/RX data sizes should be equal"));
441cb18f3f3SLuiz Otavio O Souza 
442cb18f3f3SLuiz Otavio O Souza 	/* Get the proper chip select for this child. */
443cb18f3f3SLuiz Otavio O Souza 	spibus_get_cs(child, &cs);
444584e3185SIan Lepore 	spibus_get_clock(child, &clockhz);
445584e3185SIan Lepore 	spibus_get_mode(child, &mode);
4467073d12cSEmmanuel Vadot 
4477073d12cSEmmanuel Vadot 	cs &= ~SPIBUS_CS_HIGH;
4487073d12cSEmmanuel Vadot 
449718860e4SEmmanuel Vadot 	if (cs > sc->sc_numcs) {
450cb18f3f3SLuiz Otavio O Souza 		device_printf(dev, "Invalid chip select %d requested by %s\n",
451cb18f3f3SLuiz Otavio O Souza 		    cs, device_get_nameunit(child));
452cb18f3f3SLuiz Otavio O Souza 		return (EINVAL);
453cb18f3f3SLuiz Otavio O Souza 	}
454cb18f3f3SLuiz Otavio O Souza 
455584e3185SIan Lepore 	if (mode > 3)
456584e3185SIan Lepore 	{
457584e3185SIan Lepore 	    device_printf(dev, "Invalid mode %d requested by %s\n", mode,
458584e3185SIan Lepore 		    device_get_nameunit(child));
459584e3185SIan Lepore 	    return (EINVAL);
460584e3185SIan Lepore 	}
461584e3185SIan Lepore 
462cb18f3f3SLuiz Otavio O Souza 	TI_SPI_LOCK(sc);
463cb18f3f3SLuiz Otavio O Souza 
464cb18f3f3SLuiz Otavio O Souza 	/* If the controller is in use wait until it is available. */
465cb18f3f3SLuiz Otavio O Souza 	while (sc->sc_flags & TI_SPI_BUSY)
466cb18f3f3SLuiz Otavio O Souza 		mtx_sleep(dev, &sc->sc_mtx, 0, "ti_spi", 0);
467cb18f3f3SLuiz Otavio O Souza 
468cb18f3f3SLuiz Otavio O Souza 	/* Now we have control over SPI controller. */
469cb18f3f3SLuiz Otavio O Souza 	sc->sc_flags = TI_SPI_BUSY;
470cb18f3f3SLuiz Otavio O Souza 
471cb18f3f3SLuiz Otavio O Souza 	/* Save the SPI command data. */
472cb18f3f3SLuiz Otavio O Souza 	sc->sc_cs = cs;
473cb18f3f3SLuiz Otavio O Souza 	sc->sc_cmd = cmd;
474cb18f3f3SLuiz Otavio O Souza 	sc->sc_read = 0;
475cb18f3f3SLuiz Otavio O Souza 	sc->sc_written = 0;
476cb18f3f3SLuiz Otavio O Souza 	sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
477cb18f3f3SLuiz Otavio O Souza 	sc->sc_fifolvl = ti_spi_gcd(sc->sc_len, TI_SPI_FIFOSZ);
478cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_fifolvl < 2 || sc->sc_len > 0xffff)
479cb18f3f3SLuiz Otavio O Souza 		sc->sc_fifolvl = 1;	/* FIFO disabled. */
480cb18f3f3SLuiz Otavio O Souza 	/* Disable FIFO for now. */
481cb18f3f3SLuiz Otavio O Souza 	sc->sc_fifolvl = 1;
482cb18f3f3SLuiz Otavio O Souza 
483584e3185SIan Lepore 	/* Set the bus frequency. */
484584e3185SIan Lepore 	ti_spi_set_clock(sc, sc->sc_cs, clockhz);
485cb18f3f3SLuiz Otavio O Souza 
486cb18f3f3SLuiz Otavio O Souza 	/* Disable the FIFO. */
487cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_XFERLEVEL, 0);
488cb18f3f3SLuiz Otavio O Souza 
489cb18f3f3SLuiz Otavio O Souza 	/* 8 bits word, d0 miso, d1 mosi, mode 0 and CS active low. */
490cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
491cb18f3f3SLuiz Otavio O Souza 	reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW | MCSPI_CONF_SBPOL |
492cb18f3f3SLuiz Otavio O Souza 	    MCSPI_CONF_SBE | MCSPI_CONF_TURBO | MCSPI_CONF_IS |
493cb18f3f3SLuiz Otavio O Souza 	    MCSPI_CONF_DPE1 | MCSPI_CONF_DPE0 | MCSPI_CONF_DMAR |
494cb18f3f3SLuiz Otavio O Souza 	    MCSPI_CONF_DMAW | MCSPI_CONF_EPOL);
495cb18f3f3SLuiz Otavio O Souza 	reg |= MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | MCSPI_CONF_WL8BITS;
496584e3185SIan Lepore 	reg |= mode; /* POL and PHA are the low bits, we can just OR-in mode */
497cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
498cb18f3f3SLuiz Otavio O Souza 
499cb18f3f3SLuiz Otavio O Souza #if 0
500cb18f3f3SLuiz Otavio O Souza 	/* Enable channel interrupts. */
501cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
502cb18f3f3SLuiz Otavio O Souza 	reg |= 0xf;
503cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
504cb18f3f3SLuiz Otavio O Souza #endif
505cb18f3f3SLuiz Otavio O Souza 
506cb18f3f3SLuiz Otavio O Souza 	/* Start the transfer. */
507cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
508cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg | MCSPI_CTRL_ENABLE);
509cb18f3f3SLuiz Otavio O Souza 
510cb18f3f3SLuiz Otavio O Souza 	/* Force CS on. */
511cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
512cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg |= MCSPI_CONF_FORCE);
513cb18f3f3SLuiz Otavio O Souza 
514cb18f3f3SLuiz Otavio O Souza 	err = 0;
515cb18f3f3SLuiz Otavio O Souza 	if (sc->sc_fifolvl == 1)
516cb18f3f3SLuiz Otavio O Souza 		err = ti_spi_pio_transfer(sc);
517cb18f3f3SLuiz Otavio O Souza 
518cb18f3f3SLuiz Otavio O Souza 	/* Force CS off. */
519cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
520cb18f3f3SLuiz Otavio O Souza 	reg &= ~MCSPI_CONF_FORCE;
521cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
522cb18f3f3SLuiz Otavio O Souza 
523cb18f3f3SLuiz Otavio O Souza 	/* Disable IRQs. */
524cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
525cb18f3f3SLuiz Otavio O Souza 	reg &= ~0xf;
526cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
527cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xf);
528cb18f3f3SLuiz Otavio O Souza 
529cb18f3f3SLuiz Otavio O Souza 	/* Disable the SPI channel. */
530cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
531cb18f3f3SLuiz Otavio O Souza 	reg &= ~MCSPI_CTRL_ENABLE;
532cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg);
533cb18f3f3SLuiz Otavio O Souza 
534cb18f3f3SLuiz Otavio O Souza 	/* Disable FIFO. */
535cb18f3f3SLuiz Otavio O Souza 	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
536cb18f3f3SLuiz Otavio O Souza 	reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW);
537cb18f3f3SLuiz Otavio O Souza 	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
538cb18f3f3SLuiz Otavio O Souza 
539cb18f3f3SLuiz Otavio O Souza 	/* Release the controller and wakeup the next thread waiting for it. */
540cb18f3f3SLuiz Otavio O Souza 	sc->sc_flags = 0;
541cb18f3f3SLuiz Otavio O Souza 	wakeup_one(dev);
542cb18f3f3SLuiz Otavio O Souza 	TI_SPI_UNLOCK(sc);
543cb18f3f3SLuiz Otavio O Souza 
544cb18f3f3SLuiz Otavio O Souza 	return (err);
545cb18f3f3SLuiz Otavio O Souza }
546cb18f3f3SLuiz Otavio O Souza 
547cb18f3f3SLuiz Otavio O Souza static phandle_t
ti_spi_get_node(device_t bus,device_t dev)548cb18f3f3SLuiz Otavio O Souza ti_spi_get_node(device_t bus, device_t dev)
549cb18f3f3SLuiz Otavio O Souza {
550cb18f3f3SLuiz Otavio O Souza 
551cb18f3f3SLuiz Otavio O Souza 	/* Share controller node with spibus. */
552cb18f3f3SLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
553cb18f3f3SLuiz Otavio O Souza }
554cb18f3f3SLuiz Otavio O Souza 
555cb18f3f3SLuiz Otavio O Souza static device_method_t ti_spi_methods[] = {
556cb18f3f3SLuiz Otavio O Souza 	/* Device interface */
557cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD(device_probe,		ti_spi_probe),
558cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD(device_attach,	ti_spi_attach),
559cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD(device_detach,	ti_spi_detach),
560cb18f3f3SLuiz Otavio O Souza 
561cb18f3f3SLuiz Otavio O Souza 	/* SPI interface */
562cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD(spibus_transfer,	ti_spi_transfer),
563cb18f3f3SLuiz Otavio O Souza 
564cb18f3f3SLuiz Otavio O Souza 	/* ofw_bus interface */
565cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node,	ti_spi_get_node),
566cb18f3f3SLuiz Otavio O Souza 
567cb18f3f3SLuiz Otavio O Souza 	DEVMETHOD_END
568cb18f3f3SLuiz Otavio O Souza };
569cb18f3f3SLuiz Otavio O Souza 
570cb18f3f3SLuiz Otavio O Souza static driver_t ti_spi_driver = {
571cb18f3f3SLuiz Otavio O Souza 	"spi",
572cb18f3f3SLuiz Otavio O Souza 	ti_spi_methods,
573cb18f3f3SLuiz Otavio O Souza 	sizeof(struct ti_spi_softc),
574cb18f3f3SLuiz Otavio O Souza };
575cb18f3f3SLuiz Otavio O Souza 
5768537e671SJohn Baldwin DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, 0, 0);
5770050ea24SMichal Meloun MODULE_DEPEND(ti_spi, ti_sysc, 1, 1, 1);
578