1310645Sgonzo/*-
2310645Sgonzo * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3310645Sgonzo * All rights reserved.
4310645Sgonzo *
5310645Sgonzo * Redistribution and use in source and binary forms, with or without
6310645Sgonzo * modification, are permitted provided that the following conditions
7310645Sgonzo * are met:
8310645Sgonzo * 1. Redistributions of source code must retain the above copyright
9310645Sgonzo *    notice, this list of conditions and the following disclaimer.
10310645Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11310645Sgonzo *    notice, this list of conditions and the following disclaimer in the
12310645Sgonzo *    documentation and/or other materials provided with the distribution.
13310645Sgonzo *
14310645Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15310645Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16310645Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17310645Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18310645Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19310645Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20310645Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21310645Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22310645Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23310645Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24310645Sgonzo * SUCH DAMAGE.
25310645Sgonzo */
26310645Sgonzo
27310645Sgonzo#include <sys/cdefs.h>
28310645Sgonzo__FBSDID("$FreeBSD: stable/11/sys/dev/intel/spi.c 338693 2018-09-15 13:07:43Z kib $");
29310645Sgonzo
30310645Sgonzo#include "opt_acpi.h"
31310645Sgonzo
32310645Sgonzo#include <sys/param.h>
33310645Sgonzo#include <sys/bus.h>
34310645Sgonzo#include <sys/kernel.h>
35310645Sgonzo#include <sys/module.h>
36310645Sgonzo#include <sys/proc.h>
37310645Sgonzo#include <sys/rman.h>
38310645Sgonzo
39310645Sgonzo#include <machine/bus.h>
40310645Sgonzo#include <machine/resource.h>
41310645Sgonzo
42310645Sgonzo#include <dev/spibus/spi.h>
43310645Sgonzo#include <dev/spibus/spibusvar.h>
44310645Sgonzo
45310645Sgonzo#include <contrib/dev/acpica/include/acpi.h>
46310645Sgonzo#include <contrib/dev/acpica/include/accommon.h>
47310645Sgonzo
48310645Sgonzo#include <dev/acpica/acpivar.h>
49310645Sgonzo
50310645Sgonzo#include "spibus_if.h"
51310645Sgonzo
52310645Sgonzo/**
53310645Sgonzo *	Macros for driver mutex locking
54310645Sgonzo */
55310645Sgonzo#define	INTELSPI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
56310645Sgonzo#define	INTELSPI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
57310645Sgonzo#define	INTELSPI_LOCK_INIT(_sc)		\
58310645Sgonzo	mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
59310645Sgonzo	    "intelspi", MTX_DEF)
60310645Sgonzo#define	INTELSPI_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
61310645Sgonzo#define	INTELSPI_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
62310645Sgonzo#define	INTELSPI_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
63310645Sgonzo
64310645Sgonzo#define INTELSPI_WRITE(_sc, _off, _val)		\
65310645Sgonzo    bus_write_4((_sc)->sc_mem_res, (_off), (_val))
66310645Sgonzo#define INTELSPI_READ(_sc, _off)			\
67310645Sgonzo    bus_read_4((_sc)->sc_mem_res, (_off))
68310645Sgonzo
69310645Sgonzo#define	INTELSPI_BUSY		0x1
70310645Sgonzo#define	TX_FIFO_THRESHOLD	2
71310645Sgonzo#define	RX_FIFO_THRESHOLD	2
72310645Sgonzo#define	CLOCK_DIV_10MHZ		5
73310645Sgonzo#define	DATA_SIZE_8BITS		8
74310645Sgonzo
75310645Sgonzo#define	CS_LOW		0
76310645Sgonzo#define	CS_HIGH		1
77310645Sgonzo
78310645Sgonzo#define	INTELSPI_SSPREG_SSCR0	 	0x0
79310645Sgonzo#define	 SSCR0_SCR(n)				(((n) - 1) << 8)
80310645Sgonzo#define	 SSCR0_SSE				(1 << 7)
81310645Sgonzo#define	 SSCR0_FRF_SPI				(0 << 4)
82310645Sgonzo#define	 SSCR0_DSS(n)				(((n) - 1) << 0)
83310645Sgonzo#define	INTELSPI_SSPREG_SSCR1	 	0x4
84310645Sgonzo#define	 SSCR1_TINTE				(1 << 19)
85310645Sgonzo#define	 SSCR1_RFT(n)				(((n) - 1) << 10)
86310645Sgonzo#define	 SSCR1_RFT_MASK				(0xf << 10)
87310645Sgonzo#define	 SSCR1_TFT(n)				(((n) - 1) << 6)
88310645Sgonzo#define	 SSCR1_SPI_SPH				(1 << 4)
89310645Sgonzo#define	 SSCR1_SPI_SPO				(1 << 3)
90310645Sgonzo#define	 SSCR1_MODE_MASK				(SSCR1_SPI_SPO | SSCR1_SPI_SPH)
91310645Sgonzo#define	 SSCR1_MODE_0				(0)
92310645Sgonzo#define	 SSCR1_MODE_1				(SSCR1_SPI_SPH)
93310645Sgonzo#define	 SSCR1_MODE_2				(SSCR1_SPI_SPO)
94310645Sgonzo#define	 SSCR1_MODE_3				(SSCR1_SPI_SPO | SSCR1_SPI_SPH)
95310645Sgonzo#define	 SSCR1_TIE				(1 << 1)
96310645Sgonzo#define	 SSCR1_RIE				(1 << 0)
97310645Sgonzo#define	INTELSPI_SSPREG_SSSR	 	0x8
98310645Sgonzo#define	 SSSR_RFL_MASK				(0xf << 12)
99310645Sgonzo#define	 SSSR_TFL_MASK				(0xf << 8)
100310645Sgonzo#define	 SSSR_RNE				(1 << 3)
101310645Sgonzo#define	 SSSR_TNF				(1 << 2)
102310645Sgonzo#define	INTELSPI_SSPREG_SSITR	 	0xC
103310645Sgonzo#define	INTELSPI_SSPREG_SSDR	 	0x10
104310645Sgonzo#define	INTELSPI_SSPREG_SSTO	 	0x28
105310645Sgonzo#define	INTELSPI_SSPREG_SSPSP	 	0x2C
106310645Sgonzo#define	INTELSPI_SSPREG_SSTSA	 	0x30
107310645Sgonzo#define	INTELSPI_SSPREG_SSRSA	 	0x34
108310645Sgonzo#define	INTELSPI_SSPREG_SSTSS	 	0x38
109310645Sgonzo#define	INTELSPI_SSPREG_SSACD	 	0x3C
110310645Sgonzo#define	INTELSPI_SSPREG_ITF	 	0x40
111310645Sgonzo#define	INTELSPI_SSPREG_SITF	 	0x44
112310645Sgonzo#define	INTELSPI_SSPREG_SIRF	 	0x48
113310645Sgonzo#define	INTELSPI_SSPREG_PRV_CLOCK_PARAMS	0x400
114310645Sgonzo#define	INTELSPI_SSPREG_RESETS	 	0x404
115310645Sgonzo#define	INTELSPI_SSPREG_GENERAL	 	0x408
116310645Sgonzo#define	INTELSPI_SSPREG_SSP_REG	 	0x40C
117310645Sgonzo#define	INTELSPI_SSPREG_SPI_CS_CTRL	 0x418
118310645Sgonzo#define	 SPI_CS_CTRL_CS_MASK			(3)
119310645Sgonzo#define	 SPI_CS_CTRL_SW_MODE			(1 << 0)
120310645Sgonzo#define	 SPI_CS_CTRL_HW_MODE			(1 << 0)
121310645Sgonzo#define	 SPI_CS_CTRL_CS_HIGH			(1 << 1)
122310645Sgonzo#define	 SPI_CS_CTRL_CS_LOW			(0 << 1)
123310645Sgonzo
124310645Sgonzostruct intelspi_softc {
125310645Sgonzo	ACPI_HANDLE		sc_handle;
126310645Sgonzo	device_t		sc_dev;
127310645Sgonzo	struct mtx		sc_mtx;
128310645Sgonzo	int			sc_mem_rid;
129310645Sgonzo	struct resource		*sc_mem_res;
130310645Sgonzo	int			sc_irq_rid;
131310645Sgonzo	struct resource		*sc_irq_res;
132310645Sgonzo	void			*sc_irq_ih;
133310645Sgonzo	struct spi_command	*sc_cmd;
134310645Sgonzo	uint32_t		sc_len;
135310645Sgonzo	uint32_t		sc_read;
136310645Sgonzo	uint32_t		sc_flags;
137310645Sgonzo	uint32_t		sc_written;
138310645Sgonzo};
139310645Sgonzo
140310645Sgonzostatic int	intelspi_probe(device_t dev);
141310645Sgonzostatic int	intelspi_attach(device_t dev);
142310645Sgonzostatic int	intelspi_detach(device_t dev);
143310645Sgonzostatic void	intelspi_intr(void *);
144310645Sgonzo
145310645Sgonzostatic int
146310645Sgonzointelspi_txfifo_full(struct intelspi_softc *sc)
147310645Sgonzo{
148310645Sgonzo	uint32_t sssr;
149310645Sgonzo
150310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
151310645Sgonzo
152310645Sgonzo	sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR);
153310645Sgonzo	if (sssr & SSSR_TNF)
154310645Sgonzo		return (0);
155310645Sgonzo
156310645Sgonzo	return (1);
157310645Sgonzo}
158310645Sgonzo
159310645Sgonzostatic int
160310645Sgonzointelspi_rxfifo_empty(struct intelspi_softc *sc)
161310645Sgonzo{
162310645Sgonzo	uint32_t sssr;
163310645Sgonzo
164310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
165310645Sgonzo
166310645Sgonzo	sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR);
167310645Sgonzo	if (sssr & SSSR_RNE)
168310645Sgonzo		return (0);
169310645Sgonzo	else
170310645Sgonzo		return (1);
171310645Sgonzo}
172310645Sgonzo
173310645Sgonzostatic void
174310645Sgonzointelspi_fill_tx_fifo(struct intelspi_softc *sc)
175310645Sgonzo{
176310645Sgonzo	struct spi_command *cmd;
177310645Sgonzo	uint32_t written;
178310645Sgonzo	uint8_t *data;
179310645Sgonzo
180310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
181310645Sgonzo
182310645Sgonzo	cmd = sc->sc_cmd;
183310645Sgonzo	while (sc->sc_written < sc->sc_len &&
184310645Sgonzo	    !intelspi_txfifo_full(sc)) {
185310645Sgonzo		data = (uint8_t *)cmd->tx_cmd;
186310645Sgonzo		written = sc->sc_written++;
187310645Sgonzo
188310645Sgonzo		if (written >= cmd->tx_cmd_sz) {
189310645Sgonzo			data = (uint8_t *)cmd->tx_data;
190310645Sgonzo			written -= cmd->tx_cmd_sz;
191310645Sgonzo		}
192310645Sgonzo		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSDR, data[written]);
193310645Sgonzo	}
194310645Sgonzo}
195310645Sgonzo
196310645Sgonzostatic void
197310645Sgonzointelspi_drain_rx_fifo(struct intelspi_softc *sc)
198310645Sgonzo{
199310645Sgonzo	struct spi_command *cmd;
200310645Sgonzo	uint32_t  read;
201310645Sgonzo	uint8_t *data;
202310645Sgonzo
203310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
204310645Sgonzo
205310645Sgonzo	cmd = sc->sc_cmd;
206310645Sgonzo	while (sc->sc_read < sc->sc_len &&
207310645Sgonzo	    !intelspi_rxfifo_empty(sc)) {
208310645Sgonzo		data = (uint8_t *)cmd->rx_cmd;
209310645Sgonzo		read = sc->sc_read++;
210310645Sgonzo		if (read >= cmd->rx_cmd_sz) {
211310645Sgonzo			data = (uint8_t *)cmd->rx_data;
212310645Sgonzo			read -= cmd->rx_cmd_sz;
213310645Sgonzo		}
214310645Sgonzo		data[read] = INTELSPI_READ(sc, INTELSPI_SSPREG_SSDR) & 0xff;
215310645Sgonzo	}
216310645Sgonzo}
217310645Sgonzo
218310645Sgonzostatic int
219310645Sgonzointelspi_transaction_done(struct intelspi_softc *sc)
220310645Sgonzo{
221310645Sgonzo	int txfifo_empty;
222310645Sgonzo	uint32_t sssr;
223310645Sgonzo
224310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
225310645Sgonzo
226310645Sgonzo	if (sc->sc_written != sc->sc_len ||
227310645Sgonzo	    sc->sc_read != sc->sc_len)
228310645Sgonzo		return (0);
229310645Sgonzo
230310645Sgonzo	sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR);
231310645Sgonzo	txfifo_empty = ((sssr & SSSR_TFL_MASK) == 0) &&
232310645Sgonzo		(sssr & SSSR_TNF);
233310645Sgonzo
234310645Sgonzo	if (txfifo_empty && !(sssr & SSSR_RNE))
235310645Sgonzo		return (1);
236310645Sgonzo
237310645Sgonzo	return (0);
238310645Sgonzo}
239310645Sgonzo
240310645Sgonzostatic int
241310645Sgonzointelspi_transact(struct intelspi_softc *sc)
242310645Sgonzo{
243310645Sgonzo
244310645Sgonzo	INTELSPI_ASSERT_LOCKED(sc);
245310645Sgonzo
246310645Sgonzo	/* TX - Fill up the FIFO. */
247310645Sgonzo	intelspi_fill_tx_fifo(sc);
248310645Sgonzo
249310645Sgonzo	/* RX - Drain the FIFO. */
250310645Sgonzo	intelspi_drain_rx_fifo(sc);
251310645Sgonzo
252310645Sgonzo	/* Check for end of transfer. */
253310645Sgonzo	return intelspi_transaction_done(sc);
254310645Sgonzo}
255310645Sgonzo
256310645Sgonzostatic void
257310645Sgonzointelspi_intr(void *arg)
258310645Sgonzo{
259310645Sgonzo	struct intelspi_softc *sc;
260310645Sgonzo	uint32_t reg;
261310645Sgonzo
262310645Sgonzo	sc = (struct intelspi_softc *)arg;
263310645Sgonzo
264310645Sgonzo	INTELSPI_LOCK(sc);
265310645Sgonzo	if ((sc->sc_flags & INTELSPI_BUSY) == 0) {
266310645Sgonzo		INTELSPI_UNLOCK(sc);
267310645Sgonzo		return;
268310645Sgonzo	}
269310645Sgonzo
270310645Sgonzo	/* Check if SSP if off */
271310645Sgonzo	reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR);
272310645Sgonzo	if (reg == 0xffffffffU) {
273310645Sgonzo		INTELSPI_UNLOCK(sc);
274310645Sgonzo		return;
275310645Sgonzo	}
276310645Sgonzo
277310645Sgonzo	/* Check for end of transfer. */
278310645Sgonzo	if (intelspi_transact(sc)) {
279310645Sgonzo		/* Disable interrupts */
280310645Sgonzo		reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
281310645Sgonzo		reg &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
282310645Sgonzo		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg);
283310645Sgonzo		wakeup(sc->sc_dev);
284310645Sgonzo	}
285310645Sgonzo
286310645Sgonzo	INTELSPI_UNLOCK(sc);
287310645Sgonzo}
288310645Sgonzo
289310645Sgonzostatic void
290310645Sgonzointelspi_init(struct intelspi_softc *sc)
291310645Sgonzo{
292310645Sgonzo	uint32_t reg;
293310645Sgonzo
294310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0);
295310645Sgonzo
296310645Sgonzo	/* Manual CS control */
297310645Sgonzo	reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL);
298310645Sgonzo	reg &= ~(SPI_CS_CTRL_CS_MASK);
299310645Sgonzo	reg |= (SPI_CS_CTRL_SW_MODE | SPI_CS_CTRL_CS_HIGH);
300310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg);
301310645Sgonzo
302310645Sgonzo	/* Set TX/RX FIFO IRQ threshold levels */
303310645Sgonzo	reg = SSCR1_TFT(TX_FIFO_THRESHOLD) | SSCR1_RFT(RX_FIFO_THRESHOLD);
304310645Sgonzo	/*
305310645Sgonzo	 * Set SPI mode. This should be part of transaction or sysctl
306310645Sgonzo	 */
307310645Sgonzo	reg |= SSCR1_MODE_0;
308310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg);
309310645Sgonzo
310310645Sgonzo	/*
311310645Sgonzo	 * Parent clock on Minowboard Turbot is 50MHz
312310645Sgonzo	 * divide it by 5 to set to more or less reasonable
313310645Sgonzo	 * value. But this should be part of transaction config
314310645Sgonzo	 * or sysctl
315310645Sgonzo	 */
316310645Sgonzo	reg = SSCR0_SCR(CLOCK_DIV_10MHZ);
317310645Sgonzo	/* Put SSP in SPI mode */
318310645Sgonzo	reg |= SSCR0_FRF_SPI;
319310645Sgonzo	/* Data size */
320310645Sgonzo	reg |= SSCR0_DSS(DATA_SIZE_8BITS);
321310645Sgonzo	/* Enable SSP */
322310645Sgonzo	reg |= SSCR0_SSE;
323310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, reg);
324310645Sgonzo}
325310645Sgonzo
326310645Sgonzostatic void
327310645Sgonzointelspi_set_cs(struct intelspi_softc *sc, int level)
328310645Sgonzo{
329310645Sgonzo	uint32_t reg;
330310645Sgonzo
331310645Sgonzo	reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL);
332310645Sgonzo	reg &= ~(SPI_CS_CTRL_CS_MASK);
333310645Sgonzo	reg |= SPI_CS_CTRL_SW_MODE;
334310645Sgonzo
335310645Sgonzo	if (level == CS_HIGH)
336310645Sgonzo		reg |= SPI_CS_CTRL_CS_HIGH;
337310645Sgonzo	else
338310645Sgonzo		reg |= SPI_CS_CTRL_CS_LOW;
339310645Sgonzo
340310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg);
341310645Sgonzo}
342310645Sgonzo
343310645Sgonzostatic int
344310645Sgonzointelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
345310645Sgonzo{
346310645Sgonzo	struct intelspi_softc *sc;
347310645Sgonzo	int err;
348310645Sgonzo	uint32_t sscr1;
349310645Sgonzo
350310645Sgonzo	sc = device_get_softc(dev);
351310645Sgonzo	err = 0;
352310645Sgonzo
353310645Sgonzo	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
354310645Sgonzo	    ("TX/RX command sizes should be equal"));
355310645Sgonzo	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
356310645Sgonzo	    ("TX/RX data sizes should be equal"));
357310645Sgonzo
358310645Sgonzo	INTELSPI_LOCK(sc);
359310645Sgonzo
360310645Sgonzo	/* If the controller is in use wait until it is available. */
361310645Sgonzo	while (sc->sc_flags & INTELSPI_BUSY) {
362310645Sgonzo		err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0);
363310645Sgonzo		if (err == EINTR) {
364310645Sgonzo			INTELSPI_UNLOCK(sc);
365310645Sgonzo			return (err);
366310645Sgonzo		}
367310645Sgonzo	}
368310645Sgonzo
369310645Sgonzo	/* Now we have control over SPI controller. */
370310645Sgonzo	sc->sc_flags = INTELSPI_BUSY;
371310645Sgonzo
372310645Sgonzo	/* Save a pointer to the SPI command. */
373310645Sgonzo	sc->sc_cmd = cmd;
374310645Sgonzo	sc->sc_read = 0;
375310645Sgonzo	sc->sc_written = 0;
376310645Sgonzo	sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
377310645Sgonzo
378310645Sgonzo	/* Enable CS */
379310645Sgonzo	intelspi_set_cs(sc, CS_LOW);
380310645Sgonzo	/* Transfer as much as possible to FIFOs */
381310645Sgonzo	if (!intelspi_transact(sc)) {
382310645Sgonzo		/* If FIFO is not large enough - enable interrupts */
383310645Sgonzo		sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
384310645Sgonzo		sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
385310645Sgonzo		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1);
386310645Sgonzo
387310645Sgonzo		/* and wait for transaction to complete */
388310645Sgonzo		err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2);
389310645Sgonzo	}
390310645Sgonzo
391310645Sgonzo	/* de-asser CS */
392310645Sgonzo	intelspi_set_cs(sc, CS_HIGH);
393310645Sgonzo
394310645Sgonzo	/* Clear transaction details */
395310645Sgonzo	sc->sc_cmd = NULL;
396310645Sgonzo	sc->sc_read = 0;
397310645Sgonzo	sc->sc_written = 0;
398310645Sgonzo	sc->sc_len = 0;
399310645Sgonzo
400310645Sgonzo	/* Make sure the SPI engine and interrupts are disabled. */
401310645Sgonzo	sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
402310645Sgonzo	sscr1 &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
403310645Sgonzo	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1);
404310645Sgonzo
405310645Sgonzo	/* Release the controller and wakeup the next thread waiting for it. */
406310645Sgonzo	sc->sc_flags = 0;
407310645Sgonzo	wakeup_one(dev);
408310645Sgonzo	INTELSPI_UNLOCK(sc);
409310645Sgonzo
410310645Sgonzo	/*
411310645Sgonzo	 * Check for transfer timeout.  The SPI controller doesn't
412310645Sgonzo	 * return errors.
413310645Sgonzo	 */
414310645Sgonzo	if (err == EWOULDBLOCK) {
415310645Sgonzo		device_printf(sc->sc_dev, "transfer timeout\n");
416310645Sgonzo		err = EIO;
417310645Sgonzo	}
418310645Sgonzo
419310645Sgonzo	return (err);
420310645Sgonzo}
421310645Sgonzo
422310645Sgonzostatic int
423310645Sgonzointelspi_probe(device_t dev)
424310645Sgonzo{
425310645Sgonzo	static char *gpio_ids[] = { "80860F0E", NULL };
426310645Sgonzo
427310645Sgonzo	if (acpi_disabled("spi") ||
428310645Sgonzo	    ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids) == NULL)
429310645Sgonzo	return (ENXIO);
430310645Sgonzo
431310645Sgonzo	device_set_desc(dev, "Intel SPI Controller");
432310645Sgonzo	return (0);
433310645Sgonzo}
434310645Sgonzo
435310645Sgonzostatic int
436310645Sgonzointelspi_attach(device_t dev)
437310645Sgonzo{
438310645Sgonzo	struct intelspi_softc	*sc;
439310645Sgonzo
440310645Sgonzo	sc = device_get_softc(dev);
441310645Sgonzo	sc->sc_dev = dev;
442310645Sgonzo	sc->sc_handle = acpi_get_handle(dev);
443310645Sgonzo
444310645Sgonzo	INTELSPI_LOCK_INIT(sc);
445310645Sgonzo
446310645Sgonzo	sc->sc_mem_rid = 0;
447310645Sgonzo	sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev,
448310645Sgonzo	    SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE);
449310645Sgonzo	if (sc->sc_mem_res == NULL) {
450310645Sgonzo		device_printf(dev, "can't allocate memory resource\n");
451310645Sgonzo		goto error;
452310645Sgonzo	}
453310645Sgonzo
454310645Sgonzo	sc->sc_irq_rid = 0;
455310645Sgonzo	sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev,
456310645Sgonzo	    SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE);
457310645Sgonzo	if (sc->sc_irq_res == NULL) {
458310645Sgonzo		device_printf(dev, "can't allocate IRQ resource\n");
459310645Sgonzo		goto error;
460310645Sgonzo	}
461310645Sgonzo
462310645Sgonzo	/* Hook up our interrupt handler. */
463310645Sgonzo	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
464310645Sgonzo	    NULL, intelspi_intr, sc, &sc->sc_irq_ih)) {
465310645Sgonzo		device_printf(dev, "cannot setup the interrupt handler\n");
466310645Sgonzo		goto error;
467310645Sgonzo	}
468310645Sgonzo
469310645Sgonzo	intelspi_init(sc);
470310645Sgonzo
471310645Sgonzo	device_add_child(dev, "spibus", -1);
472310645Sgonzo
473310645Sgonzo	return (bus_generic_attach(dev));
474310645Sgonzo
475310645Sgonzoerror:
476310645Sgonzo	INTELSPI_LOCK_DESTROY(sc);
477310645Sgonzo
478310645Sgonzo	if (sc->sc_mem_res != NULL)
479310645Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY,
480310645Sgonzo		    sc->sc_mem_rid, sc->sc_mem_res);
481310645Sgonzo
482310645Sgonzo	if (sc->sc_irq_res != NULL)
483338678Smarkj		bus_release_resource(dev, SYS_RES_IRQ,
484310645Sgonzo		    sc->sc_irq_rid, sc->sc_irq_res);
485310645Sgonzo
486310645Sgonzo	return (ENXIO);
487310645Sgonzo}
488310645Sgonzo
489310645Sgonzostatic int
490310645Sgonzointelspi_detach(device_t dev)
491310645Sgonzo{
492310645Sgonzo	struct intelspi_softc	*sc;
493310645Sgonzo
494310645Sgonzo	sc = device_get_softc(dev);
495310645Sgonzo
496310645Sgonzo	INTELSPI_LOCK_DESTROY(sc);
497310645Sgonzo
498310645Sgonzo	if (sc->sc_irq_ih)
499310645Sgonzo		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih);
500310645Sgonzo
501310645Sgonzo	if (sc->sc_mem_res != NULL)
502310645Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY,
503310645Sgonzo		    sc->sc_mem_rid, sc->sc_mem_res);
504310645Sgonzo
505310645Sgonzo	if (sc->sc_irq_res != NULL)
506338678Smarkj		bus_release_resource(dev, SYS_RES_IRQ,
507310645Sgonzo		    sc->sc_irq_rid, sc->sc_irq_res);
508310645Sgonzo
509338693Skib	return (bus_generic_detach(dev));
510310645Sgonzo}
511310645Sgonzo
512310645Sgonzostatic device_method_t intelspi_methods[] = {
513310645Sgonzo	/* Device interface */
514310645Sgonzo	DEVMETHOD(device_probe, intelspi_probe),
515310645Sgonzo	DEVMETHOD(device_attach, intelspi_attach),
516310645Sgonzo	DEVMETHOD(device_detach, intelspi_detach),
517310645Sgonzo
518310645Sgonzo	/* SPI interface */
519310645Sgonzo	DEVMETHOD(spibus_transfer,	intelspi_transfer),
520310645Sgonzo
521310645Sgonzo	DEVMETHOD_END
522310645Sgonzo};
523310645Sgonzo
524310645Sgonzostatic driver_t intelspi_driver = {
525310645Sgonzo	"spi",
526310645Sgonzo	intelspi_methods,
527310645Sgonzo	sizeof(struct intelspi_softc),
528310645Sgonzo};
529310645Sgonzo
530310645Sgonzostatic devclass_t intelspi_devclass;
531310645SgonzoDRIVER_MODULE(intelspi, acpi, intelspi_driver, intelspi_devclass, 0, 0);
532310645SgonzoMODULE_DEPEND(intelspi, acpi, 1, 1, 1);
533310645SgonzoMODULE_DEPEND(intelspi, spibus, 1, 1, 1);
534