xref: /freebsd/sys/arm/xilinx/zy7_spi.c (revision 814bd1ed438f7dfc5bedcb1f3e772a46fe7026bb)
1 /*-
2  * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/sysctl.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 #include <sys/resource.h>
41 #include <sys/rman.h>
42 #include <sys/uio.h>
43 
44 #include <machine/bus.h>
45 #include <machine/resource.h>
46 #include <machine/stdarg.h>
47 
48 #include <dev/fdt/fdt_common.h>
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 
52 #include <dev/spibus/spi.h>
53 #include <dev/spibus/spibusvar.h>
54 
55 #include "spibus_if.h"
56 
57 static struct ofw_compat_data compat_data[] = {
58 	{"xlnx,zy7_spi",		1},
59 	{"xlnx,zynq-spi-1.0",		1},
60 	{"cdns,spi-r1p6",		1},
61 	{NULL,				0}
62 };
63 
64 struct zy7_spi_softc {
65 	device_t		dev;
66 	device_t		child;
67 	struct mtx		sc_mtx;
68 	struct resource		*mem_res;
69 	struct resource		*irq_res;
70 	void			*intrhandle;
71 
72 	uint32_t		cfg_reg_shadow;
73 	uint32_t		spi_clock;
74 	uint32_t		ref_clock;
75 	unsigned int		spi_clk_real_freq;
76 	unsigned int		rx_overflows;
77 	unsigned int		tx_underflows;
78 	unsigned int		interrupts;
79 	unsigned int		stray_ints;
80 	struct spi_command	*cmd;
81 	int			tx_bytes;	/* tx_cmd_sz + tx_data_sz */
82 	int			tx_bytes_sent;
83 	int			rx_bytes;	/* rx_cmd_sz + rx_data_sz */
84 	int			rx_bytes_rcvd;
85 	int			busy;
86 };
87 
88 #define ZY7_SPI_DEFAULT_SPI_CLOCK	50000000
89 
90 #define SPI_SC_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
91 #define	SPI_SC_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
92 #define SPI_SC_LOCK_INIT(sc) \
93 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	NULL, MTX_DEF)
94 #define SPI_SC_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
95 #define SPI_SC_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->sc_mtx, MA_OWNED)
96 
97 #define RD4(sc, off)		(bus_read_4((sc)->mem_res, (off)))
98 #define WR4(sc, off, val)	(bus_write_4((sc)->mem_res, (off), (val)))
99 
100 /*
101  * SPI device registers.
102  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
103  * (v1.12.1) December 6, 2017.  Xilinx doc UG585.
104  */
105 #define ZY7_SPI_CONFIG_REG		0x0000
106 #define   ZY7_SPI_CONFIG_MODEFAIL_GEN_EN	(1 << 17)
107 #define   ZY7_SPI_CONFIG_MAN_STRT		(1 << 16)
108 #define   ZY7_SPI_CONFIG_MAN_STRT_EN		(1 << 15)
109 #define   ZY7_SPI_CONFIG_MAN_CS			(1 << 14)
110 #define   ZY7_SPI_CONFIG_CS_MASK		(0xf << 10)
111 #define   ZY7_SPI_CONFIG_CS(x)			((0xf ^ (1 << (x))) << 10)
112 #define   ZY7_SPI_CONFIG_PERI_SEL		(1 << 9)
113 #define   ZY7_SPI_CONFIG_REF_CLK		(1 << 8)
114 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV_MASK	(7 << 3)
115 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV_SHIFT	3
116 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV(x)	((x) << 3) /* divide by 2<<x */
117 #define   ZY7_SPI_CONFIG_CLK_PH			(1 << 2)   /* clock phase */
118 #define   ZY7_SPI_CONFIG_CLK_POL		(1 << 1)   /* clock polatiry */
119 #define   ZY7_SPI_CONFIG_MODE_SEL		(1 << 0)   /* master enable */
120 
121 #define ZY7_SPI_INTR_STAT_REG		0x0004
122 #define ZY7_SPI_INTR_EN_REG		0x0008
123 #define ZY7_SPI_INTR_DIS_REG		0x000c
124 #define ZY7_SPI_INTR_MASK_REG		0x0010
125 #define   ZY7_SPI_INTR_TX_FIFO_UNDERFLOW	(1 << 6)
126 #define   ZY7_SPI_INTR_RX_FIFO_FULL		(1 << 5)
127 #define   ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY	(1 << 4)
128 #define   ZY7_SPI_INTR_TX_FIFO_FULL		(1 << 3)
129 #define   ZY7_SPI_INTR_TX_FIFO_NOT_FULL		(1 << 2)
130 #define   ZY7_SPI_INTR_MODE_FAULT		(1 << 1)
131 #define   ZY7_SPI_INTR_RX_OVERFLOW		(1 << 0)
132 
133 #define ZY7_SPI_EN_REG			0x0014
134 #define   ZY7_SPI_ENABLE		(1 << 0)
135 
136 #define ZY7_SPI_DELAY_CTRL_REG		0x0018
137 #define   ZY7_SPI_DELAY_CTRL_BTWN_MASK		(0xff << 16)
138 #define   ZY7_SPI_DELAY_CTRL_BTWN_SHIFT		16
139 #define   ZY7_SPI_DELAY_CTRL_AFTER_MASK		(0xff << 8)
140 #define   ZY7_SPI_DELAY_CTRL_AFTER_SHIFT	8
141 #define   ZY7_SPI_DELAY_CTRL_INIT_MASK		(0xff << 0)
142 #define   ZY7_SPI_DELAY_CTRL_INIT_SHIFT		0
143 
144 #define ZY7_SPI_TX_DATA_REG		0x001c
145 #define ZY7_SPI_RX_DATA_REG		0x0020
146 
147 #define ZY7_SPI_SLV_IDLE_COUNT_REG	0x0024
148 
149 #define ZY7_SPI_TX_THRESH_REG		0x0028
150 #define ZY7_SPI_RX_THRESH_REG		0x002c
151 
152 /* Fill hardware fifo with command and data bytes. */
153 static void
154 zy7_spi_write_fifo(struct zy7_spi_softc *sc, int nbytes)
155 {
156 	uint8_t byte;
157 
158 	while (nbytes > 0) {
159 		if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz)
160 			/* Writing command. */
161 			byte = *((uint8_t *)sc->cmd->tx_cmd +
162 				 sc->tx_bytes_sent);
163 		else
164 			/* Writing data. */
165 			byte = *((uint8_t *)sc->cmd->tx_data +
166 				 (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz));
167 
168 		WR4(sc, ZY7_SPI_TX_DATA_REG, (uint32_t)byte);
169 
170 		sc->tx_bytes_sent++;
171 		nbytes--;
172 	}
173 }
174 
175 /* Read hardware fifo data into command response and data buffers. */
176 static void
177 zy7_spi_read_fifo(struct zy7_spi_softc *sc)
178 {
179 	uint8_t byte;
180 
181 	do {
182 		byte = RD4(sc, ZY7_SPI_RX_DATA_REG) & 0xff;
183 
184 		if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz)
185 			/* Reading command. */
186 			*((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd) =
187 			    byte;
188 		else
189 			/* Reading data. */
190 			*((uint8_t *)sc->cmd->rx_data +
191 			    (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz)) =
192 			    byte;
193 
194 		sc->rx_bytes_rcvd++;
195 
196 	} while (sc->rx_bytes_rcvd < sc->rx_bytes &&
197 	    (RD4(sc, ZY7_SPI_INTR_STAT_REG) &
198 		ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
199 }
200 
201 /* End a transfer early by draining rx fifo and disabling interrupts. */
202 static void
203 zy7_spi_abort_transfer(struct zy7_spi_softc *sc)
204 {
205 	/* Drain receive fifo. */
206 	while ((RD4(sc, ZY7_SPI_INTR_STAT_REG) &
207 		ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
208 		(void)RD4(sc, ZY7_SPI_RX_DATA_REG);
209 
210 	/* Shut down interrupts. */
211 	WR4(sc, ZY7_SPI_INTR_DIS_REG,
212 	    ZY7_SPI_INTR_RX_OVERFLOW |
213 	    ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
214 	    ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
215 }
216 
217 static void
218 zy7_spi_intr(void *arg)
219 {
220 	struct zy7_spi_softc *sc = (struct zy7_spi_softc *)arg;
221 	uint32_t istatus;
222 
223 	SPI_SC_LOCK(sc);
224 
225 	sc->interrupts++;
226 
227 	istatus = RD4(sc, ZY7_SPI_INTR_STAT_REG);
228 
229 	/* Stray interrupts can happen if a transfer gets interrupted. */
230 	if (!sc->busy) {
231 		sc->stray_ints++;
232 		SPI_SC_UNLOCK(sc);
233 		return;
234 	}
235 
236 	if ((istatus & ZY7_SPI_INTR_RX_OVERFLOW) != 0) {
237 		device_printf(sc->dev, "rx fifo overflow!\n");
238 		sc->rx_overflows++;
239 
240 		/* Clear status bit. */
241 		WR4(sc, ZY7_SPI_INTR_STAT_REG,
242 		    ZY7_SPI_INTR_RX_OVERFLOW);
243 	}
244 
245 	/* Empty receive fifo before any more transmit data is sent. */
246 	if (sc->rx_bytes_rcvd < sc->rx_bytes &&
247 	    (istatus & ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
248 		zy7_spi_read_fifo(sc);
249 		if (sc->rx_bytes_rcvd == sc->rx_bytes)
250 			/* Disable receive interrupts. */
251 			WR4(sc, ZY7_SPI_INTR_DIS_REG,
252 			    ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
253 			    ZY7_SPI_INTR_RX_OVERFLOW);
254 	}
255 
256 	/* Count tx underflows.  They probably shouldn't happen. */
257 	if ((istatus & ZY7_SPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
258 		sc->tx_underflows++;
259 
260 		/* Clear status bit. */
261 		WR4(sc, ZY7_SPI_INTR_STAT_REG,
262 		    ZY7_SPI_INTR_TX_FIFO_UNDERFLOW);
263 	}
264 
265 	/* Fill transmit fifo. */
266 	if (sc->tx_bytes_sent < sc->tx_bytes &&
267 	    (istatus & ZY7_SPI_INTR_TX_FIFO_NOT_FULL) != 0) {
268 		zy7_spi_write_fifo(sc, MIN(96, sc->tx_bytes -
269 			sc->tx_bytes_sent));
270 
271 		if (sc->tx_bytes_sent == sc->tx_bytes) {
272 			/* Disable transmit FIFO interrupt, enable receive
273 			 * FIFO interrupt.
274 			 */
275 			WR4(sc, ZY7_SPI_INTR_DIS_REG,
276 			    ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
277 			WR4(sc, ZY7_SPI_INTR_EN_REG,
278 			    ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY);
279 		}
280 	}
281 
282 	/* Finished with transfer? */
283 	if (sc->tx_bytes_sent == sc->tx_bytes &&
284 	    sc->rx_bytes_rcvd == sc->rx_bytes) {
285 		/* De-assert CS. */
286 		sc->cfg_reg_shadow &=
287 		    ~(ZY7_SPI_CONFIG_CLK_PH | ZY7_SPI_CONFIG_CLK_POL);
288 		sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS_MASK;
289 		WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
290 
291 		wakeup(sc->dev);
292 	}
293 
294 	SPI_SC_UNLOCK(sc);
295 }
296 
297 /* Initialize hardware. */
298 static int
299 zy7_spi_init_hw(struct zy7_spi_softc *sc)
300 {
301 	uint32_t baud_div;
302 
303 	/* Find best clock divider. Divide by 2 not supported. */
304 	baud_div = 1;
305 	while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
306 	    baud_div < 8)
307 		baud_div++;
308 	if (baud_div >= 8) {
309 		device_printf(sc->dev, "cannot configure clock divider: ref=%d"
310 		    " spi=%d.\n", sc->ref_clock, sc->spi_clock);
311 		return (EINVAL);
312 	}
313 	sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
314 
315 	/* Set up configuration register. */
316 	sc->cfg_reg_shadow =
317 	    ZY7_SPI_CONFIG_MAN_CS |
318 	    ZY7_SPI_CONFIG_CS_MASK |
319 	    ZY7_SPI_CONFIG_BAUD_RATE_DIV(baud_div) |
320 	    ZY7_SPI_CONFIG_MODE_SEL;
321 	WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
322 
323 	/* Set thresholds. */
324 	WR4(sc, ZY7_SPI_TX_THRESH_REG, 32);
325 	WR4(sc, ZY7_SPI_RX_THRESH_REG, 1);
326 
327 	/* Clear and disable all interrupts. */
328 	WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
329 	WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
330 
331 	/* Enable SPI. */
332 	WR4(sc, ZY7_SPI_EN_REG, ZY7_SPI_ENABLE);
333 
334 	return (0);
335 }
336 
337 static void
338 zy7_spi_add_sysctls(device_t dev)
339 {
340 	struct zy7_spi_softc *sc = device_get_softc(dev);
341 	struct sysctl_ctx_list *ctx;
342 	struct sysctl_oid_list *child;
343 
344 	ctx = device_get_sysctl_ctx(dev);
345 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
346 
347 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
348 	    &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
349 
350 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
351 	    &sc->rx_overflows, 0, "RX FIFO overflow events");
352 
353 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
354 	    &sc->tx_underflows, 0, "TX FIFO underflow events");
355 
356 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
357 	    &sc->interrupts, 0, "interrupt calls");
358 
359 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
360 	    &sc->stray_ints, 0, "stray interrupts");
361 }
362 
363 static int
364 zy7_spi_probe(device_t dev)
365 {
366 
367 	if (!ofw_bus_status_okay(dev))
368 		return (ENXIO);
369 
370 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
371 		return (ENXIO);
372 
373 	device_set_desc(dev, "Zynq SPI Controller");
374 
375 	return (BUS_PROBE_DEFAULT);
376 }
377 
378 static int zy7_spi_detach(device_t);
379 
380 static int
381 zy7_spi_attach(device_t dev)
382 {
383 	struct zy7_spi_softc *sc;
384 	int rid, err;
385 	phandle_t node;
386 	pcell_t cell;
387 
388 	sc = device_get_softc(dev);
389 	sc->dev = dev;
390 
391 	SPI_SC_LOCK_INIT(sc);
392 
393 	/* Get ref-clock and spi-clock properties. */
394 	node = ofw_bus_get_node(dev);
395 	if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
396 		sc->ref_clock = fdt32_to_cpu(cell);
397 	else {
398 		device_printf(dev, "must have ref-clock property\n");
399 		return (ENXIO);
400 	}
401 	if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
402 		sc->spi_clock = fdt32_to_cpu(cell);
403 	else
404 		sc->spi_clock = ZY7_SPI_DEFAULT_SPI_CLOCK;
405 
406 	/* Get memory resource. */
407 	rid = 0;
408 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
409 	    RF_ACTIVE);
410 	if (sc->mem_res == NULL) {
411 		device_printf(dev, "could not allocate memory resources.\n");
412 		zy7_spi_detach(dev);
413 		return (ENOMEM);
414 	}
415 
416 	/* Allocate IRQ. */
417 	rid = 0;
418 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
419 	    RF_ACTIVE);
420 	if (sc->irq_res == NULL) {
421 		device_printf(dev, "could not allocate IRQ resource.\n");
422 		zy7_spi_detach(dev);
423 		return (ENOMEM);
424 	}
425 
426 	/* Activate the interrupt. */
427 	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
428 	    NULL, zy7_spi_intr, sc, &sc->intrhandle);
429 	if (err) {
430 		device_printf(dev, "could not setup IRQ.\n");
431 		zy7_spi_detach(dev);
432 		return (err);
433 	}
434 
435 	/* Configure the device. */
436 	err = zy7_spi_init_hw(sc);
437 	if (err) {
438 		zy7_spi_detach(dev);
439 		return (err);
440 	}
441 
442 	sc->child = device_add_child(dev, "spibus", -1);
443 
444 	zy7_spi_add_sysctls(dev);
445 
446 	/* Attach spibus driver as a child later when interrupts work. */
447 	config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
448 
449 	return (0);
450 }
451 
452 static int
453 zy7_spi_detach(device_t dev)
454 {
455 	struct zy7_spi_softc *sc = device_get_softc(dev);
456 
457 	if (device_is_attached(dev))
458 		bus_generic_detach(dev);
459 
460 	/* Delete child bus. */
461 	if (sc->child)
462 		device_delete_child(dev, sc->child);
463 
464 	/* Disable hardware. */
465 	if (sc->mem_res != NULL) {
466 		/* Disable SPI. */
467 		WR4(sc, ZY7_SPI_EN_REG, 0);
468 
469 		/* Clear and disable all interrupts. */
470 		WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
471 		WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
472 	}
473 
474 	/* Teardown and release interrupt. */
475 	if (sc->irq_res != NULL) {
476 		if (sc->intrhandle)
477 			bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
478 		bus_release_resource(dev, SYS_RES_IRQ,
479 		    rman_get_rid(sc->irq_res), sc->irq_res);
480 	}
481 
482 	/* Release memory resource. */
483 	if (sc->mem_res != NULL)
484 		bus_release_resource(dev, SYS_RES_MEMORY,
485 		    rman_get_rid(sc->mem_res), sc->mem_res);
486 
487 	SPI_SC_LOCK_DESTROY(sc);
488 
489 	return (0);
490 }
491 
492 static phandle_t
493 zy7_spi_get_node(device_t bus, device_t dev)
494 {
495 
496 	return (ofw_bus_get_node(bus));
497 }
498 
499 static int
500 zy7_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
501 {
502 	struct zy7_spi_softc *sc = device_get_softc(dev);
503 	uint32_t cs;
504 	uint32_t mode;
505 	int err = 0;
506 
507 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
508 	    ("TX/RX command sizes should be equal"));
509 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
510 	    ("TX/RX data sizes should be equal"));
511 
512 	/* Get chip select and mode for this child. */
513 	spibus_get_cs(child, &cs);
514 	cs &= ~SPIBUS_CS_HIGH;
515 	if (cs > 2) {
516 		device_printf(dev, "Invalid chip select %d requested by %s",
517 		    cs, device_get_nameunit(child));
518 		return (EINVAL);
519 	}
520 	spibus_get_mode(child, &mode);
521 
522 	SPI_SC_LOCK(sc);
523 
524 	/* Wait for controller available. */
525 	while (sc->busy != 0) {
526 		err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi0", 0);
527 		if (err) {
528 			SPI_SC_UNLOCK(sc);
529 			return (err);
530 		}
531 	}
532 
533 	/* Start transfer. */
534 	sc->busy = 1;
535 	sc->cmd = cmd;
536 	sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
537 	sc->tx_bytes_sent = 0;
538 	sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
539 	sc->rx_bytes_rcvd = 0;
540 
541 	/* Enable interrupts.  zy7_spi_intr() will handle transfer. */
542 	WR4(sc, ZY7_SPI_INTR_EN_REG,
543 	    ZY7_SPI_INTR_TX_FIFO_NOT_FULL |
544 	    ZY7_SPI_INTR_RX_OVERFLOW);
545 
546 	/* Handle polarity and phase. */
547 	if (mode == SPIBUS_MODE_CPHA || mode == SPIBUS_MODE_CPOL_CPHA)
548 		sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_PH;
549 	if (mode == SPIBUS_MODE_CPOL || mode == SPIBUS_MODE_CPOL_CPHA)
550 		sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_POL;
551 
552 	/* Assert CS. */
553 	sc->cfg_reg_shadow &= ~ZY7_SPI_CONFIG_CS_MASK;
554 	sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS(cs);
555 	WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
556 
557 	/* Wait for completion. */
558 	err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi1", hz * 2);
559 	if (err)
560 		zy7_spi_abort_transfer(sc);
561 
562 	/* Release controller. */
563 	sc->busy = 0;
564 	wakeup_one(dev);
565 
566 	SPI_SC_UNLOCK(sc);
567 
568 	return (err);
569 }
570 
571 static device_method_t zy7_spi_methods[] = {
572 	/* Device interface */
573 	DEVMETHOD(device_probe,		zy7_spi_probe),
574 	DEVMETHOD(device_attach,	zy7_spi_attach),
575 	DEVMETHOD(device_detach,	zy7_spi_detach),
576 
577 	/* SPI interface */
578 	DEVMETHOD(spibus_transfer,	zy7_spi_transfer),
579 
580 	/* ofw_bus interface */
581 	DEVMETHOD(ofw_bus_get_node,	zy7_spi_get_node),
582 
583 	DEVMETHOD_END
584 };
585 
586 static driver_t zy7_spi_driver = {
587 	"zy7_spi",
588 	zy7_spi_methods,
589 	sizeof(struct zy7_spi_softc),
590 };
591 
592 DRIVER_MODULE(zy7_spi, simplebus, zy7_spi_driver, 0, 0);
593 DRIVER_MODULE(ofw_spibus, zy7_spi, ofw_spibus_driver, 0, 0);
594 SIMPLEBUS_PNP_INFO(compat_data);
595 MODULE_DEPEND(zy7_spi, ofw_spibus, 1, 1, 1);
596