Module Name: src Committed By: martin Date: Wed Nov 20 16:49:58 UTC 2019
Modified Files: src/sys/arch/arm/rockchip [netbsd-9]: files.rockchip rk3399_cru.c src/sys/arch/evbarm/conf [netbsd-9]: GENERIC64 Added Files: src/sys/arch/arm/rockchip [netbsd-9]: rk_spi.c Log Message: Pull up following revision(s) (requested by tnn in ticket #458): sys/arch/arm/rockchip/rk3399_cru.c: revision 1.9 sys/arch/arm/rockchip/rk_spi.c: revision 1.1 sys/arch/evbarm/conf/GENERIC64: revision 1.104 sys/arch/arm/rockchip/files.rockchip: revision 1.20 rk3399_cru: add definitions for SPI clocks rk_spi: Rockchip SPI driver Match only on RK3399 for now, but should work on RK3328 as well with the proper CRU support. If you can, please test and enable for RK3328. rkspi* at fdt? To generate a diff of this commit: cvs rdiff -u -r1.19.2.1 -r1.19.2.2 src/sys/arch/arm/rockchip/files.rockchip cvs rdiff -u -r1.8.4.1 -r1.8.4.2 src/sys/arch/arm/rockchip/rk3399_cru.c cvs rdiff -u -r0 -r1.3.2.2 src/sys/arch/arm/rockchip/rk_spi.c cvs rdiff -u -r1.103.2.5 -r1.103.2.6 src/sys/arch/evbarm/conf/GENERIC64 Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/rockchip/files.rockchip diff -u src/sys/arch/arm/rockchip/files.rockchip:1.19.2.1 src/sys/arch/arm/rockchip/files.rockchip:1.19.2.2 --- src/sys/arch/arm/rockchip/files.rockchip:1.19.2.1 Sat Nov 16 16:48:25 2019 +++ src/sys/arch/arm/rockchip/files.rockchip Wed Nov 20 16:49:58 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.rockchip,v 1.19.2.1 2019/11/16 16:48:25 martin Exp $ +# $NetBSD: files.rockchip,v 1.19.2.2 2019/11/20 16:49:58 martin Exp $ # # Configuration info for Rockchip family SoCs # @@ -42,6 +42,11 @@ device rkiic: i2cbus, i2cexec attach rkiic at fdt with rk_i2c file arch/arm/rockchip/rk_i2c.c rk_i2c +# SPI +device rkspi: spibus +attach rkspi at fdt with rk_spi +file arch/arm/rockchip/rk_spi.c rk_spi + # USB PHY device rkusb { } attach rkusb at fdt with rk_usb Index: src/sys/arch/arm/rockchip/rk3399_cru.c diff -u src/sys/arch/arm/rockchip/rk3399_cru.c:1.8.4.1 src/sys/arch/arm/rockchip/rk3399_cru.c:1.8.4.2 --- src/sys/arch/arm/rockchip/rk3399_cru.c:1.8.4.1 Sat Nov 16 16:48:25 2019 +++ src/sys/arch/arm/rockchip/rk3399_cru.c Wed Nov 20 16:49:58 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: rk3399_cru.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $ */ +/* $NetBSD: rk3399_cru.c,v 1.8.4.2 2019/11/20 16:49:58 martin Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: rk3399_cru.c,v 1.8.4.1 2019/11/16 16:48:25 martin Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rk3399_cru.c,v 1.8.4.2 2019/11/20 16:49:58 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -771,6 +771,50 @@ static struct rk_cru_clk rk3399_cru_clks RK_GATE(RK3399_PCLK_I2C2, "pclk_rki2c2", "pclk_perilp1", CLKGATE_CON(22), 9), RK_GATE(RK3399_PCLK_I2C3, "pclk_rki2c3", "pclk_perilp1", CLKGATE_CON(22), 10), + /* + * SPI + */ + RK_COMPOSITE(RK3399_SCLK_SPI0, "clk_spi0", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(59), /* muxdiv_reg */ + __BIT(7), /* mux_mask */ + __BITS(6,0), /* div_mask */ + CLKGATE_CON(9), /* gate_reg */ + __BIT(12), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_SCLK_SPI1, "clk_spi1", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(59), /* muxdiv_reg */ + __BIT(15), /* mux_mask */ + __BITS(14,8), /* div_mask */ + CLKGATE_CON(9), /* gate_reg */ + __BIT(13), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_SCLK_SPI2, "clk_spi2", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(60), /* muxdiv_reg */ + __BIT(7), /* mux_mask */ + __BITS(6,0), /* div_mask */ + CLKGATE_CON(9), /* gate_reg */ + __BIT(14), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_SCLK_SPI4, "clk_spi4", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(60), /* muxdiv_reg */ + __BIT(15), /* mux_mask */ + __BITS(14,8), /* div_mask */ + CLKGATE_CON(9), /* gate_reg */ + __BIT(15), /* gate_mask */ + 0), + RK_COMPOSITE(RK3399_SCLK_SPI5, "clk_spi5", mux_pll_src_cpll_gpll_parents, + CLKSEL_CON(58), /* muxdiv_reg */ + __BIT(15), /* mux_mask */ + __BITS(14,8), /* div_mask */ + CLKGATE_CON(13), /* gate_reg */ + __BIT(13), /* gate_mask */ + 0), + RK_GATE(RK3399_PCLK_SPI0, "pclk_rkspi0", "pclk_perilp1", CLKGATE_CON(23), 10), + RK_GATE(RK3399_PCLK_SPI1, "pclk_rkspi1", "pclk_perilp1", CLKGATE_CON(23), 11), + RK_GATE(RK3399_PCLK_SPI2, "pclk_rkspi2", "pclk_perilp1", CLKGATE_CON(23), 12), + RK_GATE(RK3399_PCLK_SPI4, "pclk_rkspi4", "pclk_perilp1", CLKGATE_CON(23), 13), + RK_GATE(RK3399_PCLK_SPI5, "pclk_rkspi5", "hclk_perilp1", CLKGATE_CON(34), 5), + /* PCIe */ RK_GATE(RK3399_ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLKGATE_CON(20), 2), RK_GATE(RK3399_ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLKGATE_CON(20), 10), Index: src/sys/arch/evbarm/conf/GENERIC64 diff -u src/sys/arch/evbarm/conf/GENERIC64:1.103.2.5 src/sys/arch/evbarm/conf/GENERIC64:1.103.2.6 --- src/sys/arch/evbarm/conf/GENERIC64:1.103.2.5 Mon Nov 18 19:36:03 2019 +++ src/sys/arch/evbarm/conf/GENERIC64 Wed Nov 20 16:49:58 2019 @@ -1,5 +1,5 @@ # -# $NetBSD: GENERIC64,v 1.103.2.5 2019/11/18 19:36:03 martin Exp $ +# $NetBSD: GENERIC64,v 1.103.2.6 2019/11/20 16:49:58 martin Exp $ # # GENERIC ARM (aarch64) kernel # @@ -402,6 +402,7 @@ ld* at nvme? nsid ? # SPI controllers bcmspi* at fdt? # Broadcom BCM283x SPI +rkspi* at fdt? # Rockchip SPI sun6ispi* at fdt? # Allwinner SPI spi* at spibus? Added files: Index: src/sys/arch/arm/rockchip/rk_spi.c diff -u /dev/null src/sys/arch/arm/rockchip/rk_spi.c:1.3.2.2 --- /dev/null Wed Nov 20 16:49:58 2019 +++ src/sys/arch/arm/rockchip/rk_spi.c Wed Nov 20 16:49:58 2019 @@ -0,0 +1,517 @@ +/* $NetBSD: rk_spi.c,v 1.3.2.2 2019/11/20 16:49:58 martin Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tobias Nygren. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rk_spi.c,v 1.3.2.2 2019/11/20 16:49:58 martin Exp $"); + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/intr.h> +#include <sys/kernel.h> +#include <sys/bitops.h> +#include <dev/spi/spivar.h> +#include <dev/fdt/fdtvar.h> +#include <arm/fdt/arm_fdtvar.h> + +#define SPI_CTRLR0 0x00 +#define SPI_CTRLR0_MTM __BIT(21) +#define SPI_CTRLR0_OPM __BIT(20) +#define SPI_CTRLR0_XFM __BITS(19, 18) +#define SPI_CTRLR0_FRF __BITS(17, 16) +#define SPI_CTRLR0_RSD __BITS(15, 14) +#define SPI_CTRLR0_BHT __BIT(13) +#define SPI_CTRLR0_FBM __BIT(12) +#define SPI_CTRLR0_EM __BIT(11) +#define SPI_CTRLR0_RW __BIT(10) +#define SPI_CTRLR0_CSM __BITS(9, 8) +#define SPI_CTRLR0_SCPOL __BIT(7) +#define SPI_CTRLR0_SCPH __BIT(6) +#define SPI_CTRLR0_CFS __BITS(5, 2) +#define SPI_CTRLR0_DFS __BITS(1, 0) +#define SPI_CTRLR0_DFS_4BIT 0x0 +#define SPI_CTRLR0_DFS_8BIT 0x1 +#define SPI_CTRLR0_DFS_16BIT 0x2 + +#define SPI_CTRLR1 0x04 +#define SPI_CTRLR1_NDM __BITS(15, 0) + +#define SPI_ENR 0x08 +#define SPI_ENR_ENR __BIT(0) + +#define SPI_SER 0x0c +#define SPI_SER_SER1 __BIT(1) +#define SPI_SER_SER0 __BIT(0) + +#define SPI_BAUDR 0x10 +#define SPI_BAUDR_BAUDR __BITS(15, 0) + +#define SPI_TXFTLR 0x14 +#define SPI_TXFTLR_TXFLTR __BITS(4, 0) + +#define SPI_RXFTLR 0x18 +#define SPI_RXFLTR_RXFLTR __BITS(4, 0) + +#define SPI_TXFLR 0x1c +#define SPI_TXFLR_TXFLR __BITS(5, 0) + +#define SPI_RXFLR 0x20 +#define SPI_RXFLR_RXFLR __BITS(5, 0) + +#define SPI_SR 0x24 +#define SPI_SR_RFF __BIT(4) +#define SPI_SR_RFE __BIT(3) +#define SPI_SR_TFE __BIT(2) +#define SPI_SR_TFF __BIT(1) +#define SPI_SR_BSF __BIT(0) + +#define SPI_IPR 0x28 +#define SPI_IPR_IPR __BIT(0) + +#define SPI_IMR 0x2c +#define SPI_IMR_RFFIM __BIT(4) +#define SPI_IMR_RFOIM __BIT(3) +#define SPI_IMR_RFUIM __BIT(2) +#define SPI_IMR_TFOIM __BIT(1) +#define SPI_IMR_TFEIM __BIT(0) + +#define SPI_ISR 0x30 +#define SPI_ISR_RFFIS __BIT(4) +#define SPI_ISR_RFOIS __BIT(3) +#define SPI_ISR_RFUIS __BIT(2) +#define SPI_ISR_TFOIS __BIT(1) +#define SPI_ISR_TFEIS __BIT(0) + +#define SPI_RISR 0x34 +#define SPI_RISR_RFFRIS __BIT(4) +#define SPI_RISR_RFORIS __BIT(3) +#define SPI_RISR_RFURIS __BIT(2) +#define SPI_RISR_TFORIS __BIT(1) +#define SPI_RISR_TFERIS __BIT(0) + +#define SPI_ICR 0x38 +#define SPI_ICR_CTFOI __BIT(3) +#define SPI_ICR_CRFOI __BIT(2) +#define SPI_ICR_CRFUI __BIT(1) +#define SPI_ICR_CCI __BIT(0) +#define SPI_ICR_ALL __BITS(3, 0) + +#define SPI_DMACR 0x3c +#define SPI_DMACR_TDE __BIT(1) +#define SPI_DMACR_RDE __BIT(0) + +#define SPI_DMATDLR 0x40 +#define SPI_DMATDLR_TDL __BITS(4, 0) + +#define SPI_DMARDLR 0x44 +#define SPI_DMARDLR_RDL __BITS(4, 0) + +#define SPI_TXDR 0x400 +#define SPI_TXDR_TXDR __BITS(15, 0) + +#define SPI_RXDR 0x800 +#define SPI_RXDR_RXDR __BITS(15, 0) + +#define SPI_FIFOLEN 32 + +static const char * const compatible[] = { +#if 0 /* should work on RK3328 but untested */ + "rockchip,rk3066-spi", + "rockchip,rk3328-spi", +#endif + "rockchip,rk3399-spi", + NULL +}; + +struct rk_spi_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + void *sc_ih; + u_int sc_spi_freq; + struct spi_controller sc_spi; + SIMPLEQ_HEAD(,spi_transfer) sc_q; + struct spi_transfer *sc_transfer; + struct spi_chunk *sc_rchunk, *sc_wchunk; + volatile bool sc_running; +}; + +#define SPIREG_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define SPIREG_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int rk_spi_match(device_t, cfdata_t, void *); +static void rk_spi_attach(device_t, device_t, void *); + +static int rk_spi_configure(void *, int, int, int); +static int rk_spi_transfer(void *, struct spi_transfer *); + +static void rk_spi_txfifo_fill(struct rk_spi_softc * const, size_t); +static void rk_spi_rxfifo_drain(struct rk_spi_softc * const, size_t); +static void rk_spi_rxtx(struct rk_spi_softc * const); +static void rk_spi_set_interrupt_mask(struct rk_spi_softc * const); +static void rk_spi_start(struct rk_spi_softc * const); +static int rk_spi_intr(void *); + +CFATTACH_DECL_NEW(rk_spi, sizeof(struct rk_spi_softc), + rk_spi_match, rk_spi_attach, NULL, NULL); + +static int +rk_spi_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +rk_spi_attach(device_t parent, device_t self, void *aux) +{ + struct rk_spi_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + struct clk *sclk, *pclk; + char intrstr[128]; + struct spibus_attach_args sba; + + sc->sc_dev = self; + sc->sc_bst = faa->faa_bst; + SIMPLEQ_INIT(&sc->sc_q); + + if ((sclk = fdtbus_clock_get(phandle, "spiclk")) == NULL + || clk_enable(sclk) != 0) { + aprint_error(": couldn't enable sclk\n"); + return; + } + + if ((pclk = fdtbus_clock_get(phandle, "apb_pclk")) == NULL + || clk_enable(pclk) != 0) { + aprint_error(": couldn't enable pclk\n"); + return; + } + + sc->sc_spi_freq = clk_get_rate(sclk); + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 + || bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + SPIREG_WRITE(sc, SPI_ENR, 0); + SPIREG_WRITE(sc, SPI_IMR, 0); + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, rk_spi_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error(": unable to establish interrupt\n"); + return; + } + + aprint_naive("\n"); + aprint_normal(": SPI\n"); + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + + sc->sc_spi.sct_cookie = sc; + sc->sc_spi.sct_configure = rk_spi_configure; + sc->sc_spi.sct_transfer = rk_spi_transfer; + sc->sc_spi.sct_nslaves = 2; + + sba.sba_controller = &sc->sc_spi; + + (void) config_found_ia(self, "spibus", &sba, spibus_print); +} + +static int +rk_spi_configure(void *cookie, int slave, int mode, int speed) +{ + struct rk_spi_softc * const sc = cookie; + uint32_t ctrlr0; + uint16_t divider; + + divider = (sc->sc_spi_freq / speed) & ~1; + if (divider < 2) + return EINVAL; + + if (slave >= sc->sc_spi.sct_nslaves) + return EINVAL; + + ctrlr0 = SPI_CTRLR0_BHT | __SHIFTIN(SPI_CTRLR0_DFS_8BIT, SPI_CTRLR0_DFS); + + switch (mode) { + case SPI_MODE_0: + ctrlr0 |= 0; + break; + case SPI_MODE_1: + ctrlr0 |= SPI_CTRLR0_SCPH; + break; + case SPI_MODE_2: + ctrlr0 |= SPI_CTRLR0_SCPOL; + break; + case SPI_MODE_3: + ctrlr0 |= SPI_CTRLR0_SCPH | SPI_CTRLR0_SCPOL; + break; + default: + return EINVAL; + } + + SPIREG_WRITE(sc, SPI_ENR, 0); + SPIREG_WRITE(sc, SPI_SER, 0); + SPIREG_WRITE(sc, SPI_CTRLR0, ctrlr0); + SPIREG_WRITE(sc, SPI_BAUDR, divider); + + SPIREG_WRITE(sc, SPI_DMACR, 0); + SPIREG_WRITE(sc, SPI_DMATDLR, 0); + SPIREG_WRITE(sc, SPI_DMARDLR, 0); + + SPIREG_WRITE(sc, SPI_IPR, 0); + SPIREG_WRITE(sc, SPI_IMR, 0); + SPIREG_WRITE(sc, SPI_ICR, SPI_ICR_ALL); + + SPIREG_WRITE(sc, SPI_ENR, 1); + + return 0; +} + +static int +rk_spi_transfer(void *cookie, struct spi_transfer *st) +{ + struct rk_spi_softc * const sc = cookie; + int s; + + s = splbio(); + spi_transq_enqueue(&sc->sc_q, st); + if (sc->sc_running == false) { + rk_spi_start(sc); + } + splx(s); + + return 0; +} + +static void +rk_spi_txfifo_fill(struct rk_spi_softc * const sc, size_t maxlen) +{ + struct spi_chunk *chunk = sc->sc_wchunk; + size_t len; + uint8_t b; + + if (chunk == NULL) + return; + + len = MIN(maxlen, chunk->chunk_wresid); + chunk->chunk_wresid -= len; + while (len--) { + if (chunk->chunk_wptr) { + b = *chunk->chunk_wptr++; + } else { + b = 0; + } + bus_space_write_1(sc->sc_bst, sc->sc_bsh, SPI_TXDR, b); + } + if (sc->sc_wchunk->chunk_wresid == 0) { + sc->sc_wchunk = sc->sc_wchunk->chunk_next; + } +} + +static void +rk_spi_rxfifo_drain(struct rk_spi_softc * const sc, size_t maxlen) +{ + struct spi_chunk *chunk = sc->sc_rchunk; + size_t len; + uint8_t b; + + if (chunk == NULL) + return; + + len = MIN(maxlen, chunk->chunk_rresid); + chunk->chunk_rresid -= len; + + while (len--) { + b = bus_space_read_1(sc->sc_bst, sc->sc_bsh, SPI_RXDR); + if (chunk->chunk_rptr) { + *chunk->chunk_rptr++ = b; + } + } + if (sc->sc_rchunk->chunk_rresid == 0) { + sc->sc_rchunk = sc->sc_rchunk->chunk_next; + } +} + +static void +rk_spi_rxtx(struct rk_spi_softc * const sc) +{ + bool again; + uint32_t reg; + size_t avail; + + /* Service both FIFOs until no more progress can be made. */ + again = true; + while (again) { + again = false; + reg = SPIREG_READ(sc, SPI_RXFLR); + avail = __SHIFTOUT(reg, SPI_RXFLR_RXFLR); + if (avail > 0) { + KASSERT(sc->sc_rchunk != NULL); + rk_spi_rxfifo_drain(sc, avail); + again = true; + } + reg = SPIREG_READ(sc, SPI_TXFLR); + avail = SPI_FIFOLEN - __SHIFTOUT(reg, SPI_TXFLR_TXFLR); + if (avail > 0 && sc->sc_wchunk != NULL) { + rk_spi_txfifo_fill(sc, avail); + again = true; + } + } +} + +static void +rk_spi_set_interrupt_mask(struct rk_spi_softc * const sc) +{ + uint32_t imr = SPI_IMR_RFOIM | SPI_IMR_RFUIM | SPI_IMR_TFOIM; + int len; + + /* + * Delay rx interrupts until the FIFO has the # of bytes we'd + * ideally like to receive, or FIFO is half full. + */ + len = sc->sc_rchunk != NULL + ? MIN(sc->sc_rchunk->chunk_rresid, SPI_FIFOLEN / 2) : 0; + if (len > 0) { + SPIREG_WRITE(sc, SPI_RXFTLR, len - 1); + imr |= SPI_IMR_RFFIM; + } + + /* + * Delay tx interrupts until the FIFO can accept the # of bytes we'd + * ideally like to transmit, or the FIFO is half empty. + */ + len = sc->sc_wchunk != NULL + ? MIN(sc->sc_wchunk->chunk_wresid, SPI_FIFOLEN / 2) : 0; + if (len > 0) { + SPIREG_WRITE(sc, SPI_TXFTLR, SPI_FIFOLEN - len); + imr |= SPI_IMR_TFEIM; + } + + /* If xfer is done, then interrupt as soon as the tx fifo is empty. */ + if (!ISSET(imr, (SPI_IMR_RFFIM | SPI_IMR_TFEIM))) { + SPIREG_WRITE(sc, SPI_TXFTLR, 0); + imr |= SPI_IMR_TFEIM; + } + + SPIREG_WRITE(sc, SPI_IMR, imr); +} + +static void +rk_spi_start(struct rk_spi_softc * const sc) +{ + struct spi_transfer *st; + + while ((st = spi_transq_first(&sc->sc_q)) != NULL) { + spi_transq_dequeue(&sc->sc_q); + KASSERT(sc->sc_transfer == NULL); + sc->sc_transfer = st; + sc->sc_rchunk = sc->sc_wchunk = st->st_chunks; + sc->sc_running = true; + + KASSERT(st->st_slave < sc->sc_spi.sct_nslaves); + SPIREG_WRITE(sc, SPI_SER, 1 << st->st_slave); + + rk_spi_rxtx(sc); + rk_spi_set_interrupt_mask(sc); + + if (!cold) + return; + + for (;;) { + (void) rk_spi_intr(sc); + if (ISSET(st->st_flags, SPI_F_DONE)) + break; + } + } + sc->sc_running = false; +} + +static int +rk_spi_intr(void *cookie) +{ + struct rk_spi_softc * const sc = cookie; + struct spi_transfer *st; + uint32_t isr; + uint32_t sr; + uint32_t icr = SPI_ICR_CCI; + + isr = SPIREG_READ(sc, SPI_ISR); + if (!isr) + return 0; + + if (ISSET(isr, SPI_ISR_RFOIS)) { + device_printf(sc->sc_dev, "RXFIFO overflow\n"); + icr |= SPI_ICR_CRFOI; + } + if (ISSET(isr, SPI_ISR_RFUIS)) { + device_printf(sc->sc_dev, "RXFIFO underflow\n"); + icr |= SPI_ICR_CRFUI; + } + if (ISSET(isr, SPI_ISR_TFOIS)) { + device_printf(sc->sc_dev, "TXFIFO overflow\n"); + icr |= SPI_ICR_CTFOI; + } + + rk_spi_rxtx(sc); + + if (sc->sc_rchunk == NULL && sc->sc_wchunk == NULL) { + do { + sr = SPIREG_READ(sc, SPI_SR); + } while (ISSET(sr, SPI_SR_BSF)); + SPIREG_WRITE(sc, SPI_IMR, 0); + SPIREG_WRITE(sc, SPI_SER, 0); + st = sc->sc_transfer; + sc->sc_transfer = NULL; + KASSERT(st != NULL); + spi_done(st, 0); + sc->sc_running = false; + } else { + rk_spi_set_interrupt_mask(sc); + } + + SPIREG_WRITE(sc, SPI_ICR, icr); + + return 1; +}