2 * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/sysctl.h>
39 #include <sys/mutex.h>
40 #include <sys/resource.h>
44 #include <machine/bus.h>
45 #include <machine/resource.h>
46 #include <machine/stdarg.h>
48 #include <dev/fdt/fdt_common.h>
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
52 #include <dev/spibus/spi.h>
53 #include <dev/spibus/spibusvar.h>
55 #include "spibus_if.h"
57 static struct ofw_compat_data compat_data[] = {
59 {"xlnx,zynq-spi-1.0", 1},
64 struct zy7_spi_softc {
68 struct resource *mem_res;
69 struct resource *irq_res;
72 uint32_t cfg_reg_shadow;
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 */
83 int rx_bytes; /* rx_cmd_sz + rx_data_sz */
88 #define ZY7_SPI_DEFAULT_SPI_CLOCK 50000000
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)
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)))
101 * SPI device registers.
102 * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
103 * (v1.12.1) December 6, 2017. Xilinx doc UG585.
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 */
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)
133 #define ZY7_SPI_EN_REG 0x0014
134 #define ZY7_SPI_ENABLE (1 << 0)
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
144 #define ZY7_SPI_TX_DATA_REG 0x001c
145 #define ZY7_SPI_RX_DATA_REG 0x0020
147 #define ZY7_SPI_SLV_IDLE_COUNT_REG 0x0024
149 #define ZY7_SPI_TX_THRESH_REG 0x0028
150 #define ZY7_SPI_RX_THRESH_REG 0x002c
153 /* Fill hardware fifo with command and data bytes. */
155 zy7_spi_write_fifo(struct zy7_spi_softc *sc, int nbytes)
160 if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz)
161 /* Writing command. */
162 byte = *((uint8_t *)sc->cmd->tx_cmd +
166 byte = *((uint8_t *)sc->cmd->tx_data +
167 (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz));
169 WR4(sc, ZY7_SPI_TX_DATA_REG, (uint32_t)byte);
177 /* Read hardware fifo data into command response and data buffers. */
179 zy7_spi_read_fifo(struct zy7_spi_softc *sc)
184 byte = RD4(sc, ZY7_SPI_RX_DATA_REG) & 0xff;
186 if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz)
187 /* Reading command. */
188 *((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd) =
192 *((uint8_t *)sc->cmd->rx_data +
193 (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz)) =
198 } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
199 (RD4(sc, ZY7_SPI_INTR_STAT_REG) &
200 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
203 /* End a transfer early by draining rx fifo and disabling interrupts. */
205 zy7_spi_abort_transfer(struct zy7_spi_softc *sc)
207 /* Drain receive fifo. */
208 while ((RD4(sc, ZY7_SPI_INTR_STAT_REG) &
209 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
210 (void)RD4(sc, ZY7_SPI_RX_DATA_REG);
212 /* Shut down interrupts. */
213 WR4(sc, ZY7_SPI_INTR_DIS_REG,
214 ZY7_SPI_INTR_RX_OVERFLOW |
215 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
216 ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
221 zy7_spi_intr(void *arg)
223 struct zy7_spi_softc *sc = (struct zy7_spi_softc *)arg;
230 istatus = RD4(sc, ZY7_SPI_INTR_STAT_REG);
232 /* Stray interrupts can happen if a transfer gets interrupted. */
239 if ((istatus & ZY7_SPI_INTR_RX_OVERFLOW) != 0) {
240 device_printf(sc->dev, "rx fifo overflow!\n");
243 /* Clear status bit. */
244 WR4(sc, ZY7_SPI_INTR_STAT_REG,
245 ZY7_SPI_INTR_RX_OVERFLOW);
248 /* Empty receive fifo before any more transmit data is sent. */
249 if (sc->rx_bytes_rcvd < sc->rx_bytes &&
250 (istatus & ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
251 zy7_spi_read_fifo(sc);
252 if (sc->rx_bytes_rcvd == sc->rx_bytes)
253 /* Disable receive interrupts. */
254 WR4(sc, ZY7_SPI_INTR_DIS_REG,
255 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
256 ZY7_SPI_INTR_RX_OVERFLOW);
259 /* Count tx underflows. They probably shouldn't happen. */
260 if ((istatus & ZY7_SPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
263 /* Clear status bit. */
264 WR4(sc, ZY7_SPI_INTR_STAT_REG,
265 ZY7_SPI_INTR_TX_FIFO_UNDERFLOW);
268 /* Fill transmit fifo. */
269 if (sc->tx_bytes_sent < sc->tx_bytes &&
270 (istatus & ZY7_SPI_INTR_TX_FIFO_NOT_FULL) != 0) {
271 zy7_spi_write_fifo(sc, MIN(96, sc->tx_bytes -
274 if (sc->tx_bytes_sent == sc->tx_bytes) {
275 /* Disable transmit FIFO interrupt, enable receive
278 WR4(sc, ZY7_SPI_INTR_DIS_REG,
279 ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
280 WR4(sc, ZY7_SPI_INTR_EN_REG,
281 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY);
285 /* Finished with transfer? */
286 if (sc->tx_bytes_sent == sc->tx_bytes &&
287 sc->rx_bytes_rcvd == sc->rx_bytes) {
290 sc->cfg_reg_shadow &=
291 ~(ZY7_SPI_CONFIG_CLK_PH | ZY7_SPI_CONFIG_CLK_POL);
292 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS_MASK;
293 WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
301 /* Initialize hardware. */
303 zy7_spi_init_hw(struct zy7_spi_softc *sc)
307 /* Find best clock divider. Divide by 2 not supported. */
309 while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
313 device_printf(sc->dev, "cannot configure clock divider: ref=%d"
314 " spi=%d.\n", sc->ref_clock, sc->spi_clock);
317 sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
319 /* Set up configuration register. */
321 ZY7_SPI_CONFIG_MAN_CS |
322 ZY7_SPI_CONFIG_CS_MASK |
323 ZY7_SPI_CONFIG_BAUD_RATE_DIV(baud_div) |
324 ZY7_SPI_CONFIG_MODE_SEL;
325 WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
327 /* Set thresholds. */
328 WR4(sc, ZY7_SPI_TX_THRESH_REG, 32);
329 WR4(sc, ZY7_SPI_RX_THRESH_REG, 1);
331 /* Clear and disable all interrupts. */
332 WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
333 WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
336 WR4(sc, ZY7_SPI_EN_REG, ZY7_SPI_ENABLE);
343 zy7_spi_add_sysctls(device_t dev)
345 struct zy7_spi_softc *sc = device_get_softc(dev);
346 struct sysctl_ctx_list *ctx;
347 struct sysctl_oid_list *child;
349 ctx = device_get_sysctl_ctx(dev);
350 child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
352 SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
353 &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
355 SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
356 &sc->rx_overflows, 0, "RX FIFO overflow events");
358 SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
359 &sc->tx_underflows, 0, "TX FIFO underflow events");
361 SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
362 &sc->interrupts, 0, "interrupt calls");
364 SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
365 &sc->stray_ints, 0, "stray interrupts");
370 zy7_spi_probe(device_t dev)
373 if (!ofw_bus_status_okay(dev))
376 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
379 device_set_desc(dev, "Zynq SPI Controller");
381 return (BUS_PROBE_DEFAULT);
385 static int zy7_spi_detach(device_t);
388 zy7_spi_attach(device_t dev)
390 struct zy7_spi_softc *sc;
395 sc = device_get_softc(dev);
398 SPI_SC_LOCK_INIT(sc);
400 /* Get ref-clock and spi-clock properties. */
401 node = ofw_bus_get_node(dev);
402 if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
403 sc->ref_clock = fdt32_to_cpu(cell);
405 device_printf(dev, "must have ref-clock property\n");
408 if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
409 sc->spi_clock = fdt32_to_cpu(cell);
411 sc->spi_clock = ZY7_SPI_DEFAULT_SPI_CLOCK;
413 /* Get memory resource. */
415 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
417 if (sc->mem_res == NULL) {
418 device_printf(dev, "could not allocate memory resources.\n");
425 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
427 if (sc->irq_res == NULL) {
428 device_printf(dev, "could not allocate IRQ resource.\n");
433 /* Activate the interrupt. */
434 err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
435 NULL, zy7_spi_intr, sc, &sc->intrhandle);
437 device_printf(dev, "could not setup IRQ.\n");
442 /* Configure the device. */
443 err = zy7_spi_init_hw(sc);
449 sc->child = device_add_child(dev, "spibus", -1);
451 zy7_spi_add_sysctls(dev);
453 /* Attach spibus driver as a child later when interrupts work. */
454 config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
460 zy7_spi_detach(device_t dev)
462 struct zy7_spi_softc *sc = device_get_softc(dev);
464 if (device_is_attached(dev))
465 bus_generic_detach(dev);
467 /* Delete child bus. */
469 device_delete_child(dev, sc->child);
471 /* Disable hardware. */
472 if (sc->mem_res != NULL) {
474 WR4(sc, ZY7_SPI_EN_REG, 0);
476 /* Clear and disable all interrupts. */
477 WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
478 WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
481 /* Teardown and release interrupt. */
482 if (sc->irq_res != NULL) {
484 bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
485 bus_release_resource(dev, SYS_RES_IRQ,
486 rman_get_rid(sc->irq_res), sc->irq_res);
489 /* Release memory resource. */
490 if (sc->mem_res != NULL)
491 bus_release_resource(dev, SYS_RES_MEMORY,
492 rman_get_rid(sc->mem_res), sc->mem_res);
494 SPI_SC_LOCK_DESTROY(sc);
501 zy7_spi_get_node(device_t bus, device_t dev)
504 return (ofw_bus_get_node(bus));
509 zy7_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
511 struct zy7_spi_softc *sc = device_get_softc(dev);
516 KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
517 ("TX/RX command sizes should be equal"));
518 KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
519 ("TX/RX data sizes should be equal"));
521 /* Get chip select and mode for this child. */
522 spibus_get_cs(child, &cs);
523 cs &= ~SPIBUS_CS_HIGH;
525 device_printf(dev, "Invalid chip select %d requested by %s",
526 cs, device_get_nameunit(child));
529 spibus_get_mode(child, &mode);
533 /* Wait for controller available. */
534 while (sc->busy != 0) {
535 err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi0", 0);
542 /* Start transfer. */
545 sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
546 sc->tx_bytes_sent = 0;
547 sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
548 sc->rx_bytes_rcvd = 0;
550 /* Enable interrupts. zy7_spi_intr() will handle transfer. */
551 WR4(sc, ZY7_SPI_INTR_EN_REG,
552 ZY7_SPI_INTR_TX_FIFO_NOT_FULL |
553 ZY7_SPI_INTR_RX_OVERFLOW);
555 /* Handle polarity and phase. */
556 if (mode == SPIBUS_MODE_CPHA || mode == SPIBUS_MODE_CPOL_CPHA)
557 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_PH;
558 if (mode == SPIBUS_MODE_CPOL || mode == SPIBUS_MODE_CPOL_CPHA)
559 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_POL;
562 sc->cfg_reg_shadow &= ~ZY7_SPI_CONFIG_CS_MASK;
563 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS(cs);
564 WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
566 /* Wait for completion. */
567 err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi1", hz * 2);
569 zy7_spi_abort_transfer(sc);
571 /* Release controller. */
580 static device_method_t zy7_spi_methods[] = {
581 /* Device interface */
582 DEVMETHOD(device_probe, zy7_spi_probe),
583 DEVMETHOD(device_attach, zy7_spi_attach),
584 DEVMETHOD(device_detach, zy7_spi_detach),
587 DEVMETHOD(spibus_transfer, zy7_spi_transfer),
589 /* ofw_bus interface */
590 DEVMETHOD(ofw_bus_get_node, zy7_spi_get_node),
596 static driver_t zy7_spi_driver = {
599 sizeof(struct zy7_spi_softc),
601 static devclass_t zy7_spi_devclass;
603 DRIVER_MODULE(zy7_spi, simplebus, zy7_spi_driver, zy7_spi_devclass, 0, 0);
604 DRIVER_MODULE(ofw_spibus, zy7_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
605 SIMPLEBUS_PNP_INFO(compat_data);
606 MODULE_DEPEND(zy7_spi, ofw_spibus, 1, 1, 1);