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