Module Name: src Committed By: tnn Date: Sat Aug 3 13:28:43 UTC 2019
Modified Files: src/sys/arch/arm/sunxi: files.sunxi Added Files: src/sys/arch/arm/sunxi: sun4i_spi.c sun4i_spireg.h Log Message: sun4i_spi: spi(4) driver for Allwinner A10/A20 platform To generate a diff of this commit: cvs rdiff -u -r1.65 -r1.66 src/sys/arch/arm/sunxi/files.sunxi cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/sunxi/sun4i_spi.c \ src/sys/arch/arm/sunxi/sun4i_spireg.h 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/sunxi/files.sunxi diff -u src/sys/arch/arm/sunxi/files.sunxi:1.65 src/sys/arch/arm/sunxi/files.sunxi:1.66 --- src/sys/arch/arm/sunxi/files.sunxi:1.65 Fri Jun 14 21:26:36 2019 +++ src/sys/arch/arm/sunxi/files.sunxi Sat Aug 3 13:28:42 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.sunxi,v 1.65 2019/06/14 21:26:36 tnn Exp $ +# $NetBSD: files.sunxi,v 1.66 2019/08/03 13:28:42 tnn Exp $ # # Configuration info for Allwinner sunxi family SoCs # @@ -306,7 +306,10 @@ device sunxinand: nandbus attach sunxinand at fdt with sunxi_nand file arch/arm/sunxi/sunxi_nand.c sunxi_nand -# SPI Controller (sun6i-compatible) +# SPI Controller +device sun4ispi: spibus +attach sun4ispi at fdt with sun4i_spi +file arch/arm/sunxi/sun4i_spi.c sun4i_spi device sun6ispi: spibus attach sun6ispi at fdt with sun6i_spi file arch/arm/sunxi/sun6i_spi.c sun6i_spi Added files: Index: src/sys/arch/arm/sunxi/sun4i_spi.c diff -u /dev/null src/sys/arch/arm/sunxi/sun4i_spi.c:1.1 --- /dev/null Sat Aug 3 13:28:43 2019 +++ src/sys/arch/arm/sunxi/sun4i_spi.c Sat Aug 3 13:28:42 2019 @@ -0,0 +1,417 @@ +/* $NetBSD: sun4i_spi.c,v 1.1 2019/08/03 13:28:42 tnn Exp $ */ + +/* + * Copyright (c) 2019 Tobias Nygren + * Copyright (c) 2018 Jonathan A. Kollasch + * All rights reserved. + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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: sun4i_spi.c,v 1.1 2019/08/03 13:28:42 tnn 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 <arm/sunxi/sun4i_spireg.h> +#include <dev/fdt/fdtvar.h> + +static const char * const compatible[] = { + "allwinner,sun4i-a10-spi", + NULL +}; + +struct sun4ispi_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + void *sc_intrh; + struct spi_controller sc_spi; + SIMPLEQ_HEAD(,spi_transfer) sc_q; + struct spi_transfer *sc_transfer; + struct spi_chunk *sc_rchunk, *sc_wchunk; + uint32_t sc_CTL; + u_int sc_modclkrate; + 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 sun4ispi_match(device_t, cfdata_t, void *); +static void sun4ispi_attach(device_t, device_t, void *); + +static int sun4ispi_configure(void *, int, int, int); +static int sun4ispi_transfer(void *, struct spi_transfer *); + +static void sun4ispi_txfifo_fill(struct sun4ispi_softc * const, size_t); +static void sun4ispi_rxfifo_drain(struct sun4ispi_softc * const, size_t); +static void sun4ispi_rxtx(struct sun4ispi_softc *); +static void sun4ispi_set_interrupt_mask(struct sun4ispi_softc * const); +static void sun4ispi_start(struct sun4ispi_softc * const); +static int sun4ispi_intr(void *); + +CFATTACH_DECL_NEW(sun4i_spi, sizeof(struct sun4ispi_softc), + sun4ispi_match, sun4ispi_attach, NULL, NULL); + +static int +sun4ispi_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 +sun4ispi_attach(device_t parent, device_t self, void *aux) +{ + struct sun4ispi_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 *clk, *modclk; + char intrstr[128]; + struct spibus_attach_args sba; + + sc->sc_dev = self; + sc->sc_bst = faa->faa_bst; + SIMPLEQ_INIT(&sc->sc_q); + + if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL + || clk_enable(clk) != 0) { + aprint_error(": couldn't enable clock\n"); + return; + } + + if ((modclk = fdtbus_clock_get(phandle, "mod")) == NULL + || clk_set_rate(modclk, clk_get_rate(clk)) != 0 + || clk_enable(modclk) != 0) { + aprint_error(": couldn't enable module clock\n"); + return; + } + sc->sc_modclkrate = clk_get_rate(modclk); + + 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_CTL, SPI_CTL_SSPOL | SPI_CTL_RF_RST + | SPI_CTL_TF_RST | SPI_CTL_MODE); + SPIREG_WRITE(sc, SPI_DMACTL, 0); + SPIREG_WRITE(sc, SPI_WAIT, 0); + SPIREG_WRITE(sc, SPI_INTCTL, 0); + SPIREG_WRITE(sc, SPI_INT_STA, ~0); + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + + sc->sc_intrh = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, sun4ispi_intr, sc); + if (sc->sc_intrh == 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 = sun4ispi_configure; + sc->sc_spi.sct_transfer = sun4ispi_transfer; + (void) of_getprop_uint32(phandle, "num-cs", &sc->sc_spi.sct_nslaves); + sba.sba_controller = &sc->sc_spi; + + (void) config_found_ia(self, "spibus", &sba, spibus_print); +} + +static int +sun4ispi_configure(void *cookie, int slave, int mode, int speed) +{ + struct sun4ispi_softc * const sc = cookie; + uint32_t ctl, cctl; + uint32_t minfreq, maxfreq; + + minfreq = sc->sc_modclkrate >> 16; + maxfreq = sc->sc_modclkrate >> 1; + + if (speed <= 0 || speed < minfreq || speed > maxfreq) + return EINVAL; + + if (slave >= sc->sc_spi.sct_nslaves) + return EINVAL; + + ctl = SPI_CTL_SDM | SPI_CTL_TP_EN | SPI_CTL_SSPOL | SPI_CTL_MODE | SPI_CTL_EN; + + switch (mode) { + case SPI_MODE_0: + ctl |= 0; + break; + case SPI_MODE_1: + ctl |= SPI_CTL_PHA; + break; + case SPI_MODE_2: + ctl |= SPI_CTL_POL; + break; + case SPI_MODE_3: + ctl |= SPI_CTL_PHA | SPI_CTL_POL; + break; + default: + return EINVAL; + } + + if (speed < sc->sc_modclkrate / 512) { + for (cctl = 0; cctl <= __SHIFTOUT_MASK(SPI_CCTL_CDR1); cctl++) { + if ((sc->sc_modclkrate / (1 << cctl)) <= speed) + goto cdr1_found; + } + return EINVAL; +cdr1_found: + cctl = __SHIFTIN(cctl, SPI_CCTL_CDR1); + } else { + cctl = howmany(sc->sc_modclkrate, 2 * speed) - 1; + cctl = SPI_CCTL_DRS|__SHIFTIN(cctl, SPI_CCTL_CDR2); + } + + device_printf(sc->sc_dev, "ctl 0x%x, cctl 0x%x, CLK %uHz, SCLK %uHz\n", + ctl, cctl, sc->sc_modclkrate, + (cctl & SPI_CCTL_DRS) + ? (sc->sc_modclkrate / (u_int)(2 * (__SHIFTOUT(cctl, SPI_CCTL_CDR2) + 1))) + : (sc->sc_modclkrate >> (__SHIFTOUT(cctl, SPI_CCTL_CDR1) + 1)) + ); + + sc->sc_CTL = ctl; + SPIREG_WRITE(sc, SPI_CTL, (ctl | SPI_CTL_RF_RST | SPI_CTL_TF_RST) & ~SPI_CTL_EN); + SPIREG_WRITE(sc, SPI_CCTL, cctl); + SPIREG_WRITE(sc, SPI_CTL, ctl); + + return 0; +} + +static int +sun4ispi_transfer(void *cookie, struct spi_transfer *st) +{ + struct sun4ispi_softc * const sc = cookie; + int s; + + s = splbio(); + spi_transq_enqueue(&sc->sc_q, st); + if (sc->sc_running == false) { + sun4ispi_start(sc); + } + splx(s); + + return 0; +} + +static void +sun4ispi_txfifo_fill(struct sun4ispi_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_TXDATA, b); + } + if (sc->sc_wchunk->chunk_wresid == 0) { + sc->sc_wchunk = sc->sc_wchunk->chunk_next; + } +} + +static void +sun4ispi_rxfifo_drain(struct sun4ispi_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_RXDATA); + 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 +sun4ispi_rxtx(struct sun4ispi_softc * const sc) +{ + bool again; + size_t rxavail, txavail; + uint32_t fsr; + + /* service both FIFOs until no more progress can be made */ + again = true; + while (again) { + again = false; + fsr = SPIREG_READ(sc, SPI_FIFO_STA); + rxavail = __SHIFTOUT(fsr, SPI_FIFO_STA_RF_CNT); + txavail = 64 - __SHIFTOUT(fsr, SPI_FIFO_STA_TF_CNT); + if (rxavail > 0) { + KASSERT(sc->sc_rchunk != NULL); + sun4ispi_rxfifo_drain(sc, rxavail); + again = true; + } + if (txavail > 0 && sc->sc_wchunk != NULL) { + sun4ispi_txfifo_fill(sc, txavail); + again = true; + } + } +} + +static void +sun4ispi_set_interrupt_mask(struct sun4ispi_softc * const sc) +{ + uint32_t intctl; + + intctl = SPI_INTCTL_TX_INT_EN; + intctl |= SPI_INTCTL_RF_OF_INT_EN; + intctl |= SPI_INTCTL_TF_UR_INT_EN; + + if (sc->sc_rchunk) { + if (sc->sc_rchunk->chunk_rresid >= 32) { + intctl |= SPI_INTCTL_RF_HALF_FU_INT_EN; + } else { + intctl |= SPI_INTCTL_RF_RDY_INT_EN; + } + } + if (sc->sc_wchunk) { + intctl |= SPI_INTCTL_TF_HALF_EMP_INT_EN; + } + SPIREG_WRITE(sc, SPI_INTCTL, intctl); +} + +static void +sun4ispi_start(struct sun4ispi_softc * const sc) +{ + struct spi_transfer *st; + uint32_t ctl; + int s; + struct spi_chunk *chunk; + size_t burstcount; + + 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; + + burstcount = 0; + for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) { + burstcount += chunk->chunk_count; + } + KASSERT(burstcount <= SPI_BC_BC); + SPIREG_WRITE(sc, SPI_BC, __SHIFTIN(burstcount, SPI_BC_BC)); + SPIREG_WRITE(sc, SPI_TC, __SHIFTIN(burstcount, SPI_TC_WTC)); + + sun4ispi_rxtx(sc); + sun4ispi_set_interrupt_mask(sc); + + KASSERT(st->st_slave < sc->sc_spi.sct_nslaves); + ctl = sc->sc_CTL | __SHIFTIN(st->st_slave, SPI_CTL_SS) | SPI_CTL_XCH; + SPIREG_WRITE(sc, SPI_CTL, ctl); + + if (!cold) + return; + + s = splbio(); + for (;;) { + (void) sun4ispi_intr(sc); + if (ISSET(st->st_flags, SPI_F_DONE)) + break; + } + splx(s); + } + sc->sc_running = false; +} + +static int +sun4ispi_intr(void *cookie) +{ + struct sun4ispi_softc * const sc = cookie; + struct spi_transfer *st; + uint32_t isr; + + isr = SPIREG_READ(sc, SPI_INT_STA); + if (!isr) + return 0; + + if (ISSET(isr, SPI_INT_STA_RO)) { + device_printf(sc->sc_dev, "RXFIFO overflow\n"); + } + if (ISSET(isr, SPI_INT_STA_TU)) { + device_printf(sc->sc_dev, "TXFIFO underrun\n"); + } + + sun4ispi_rxtx(sc); + + if (ISSET(isr, SPI_INT_STA_TC)) { + SPIREG_WRITE(sc, SPI_INTCTL, 0); + KASSERT(sc->sc_rchunk == NULL); + KASSERT(sc->sc_wchunk == NULL); + st = sc->sc_transfer; + sc->sc_transfer = NULL; + KASSERT(st != NULL); + spi_done(st, 0); + sc->sc_running = false; + } else { + sun4ispi_set_interrupt_mask(sc); + } + SPIREG_WRITE(sc, SPI_INT_STA, isr); + + return 1; +} Index: src/sys/arch/arm/sunxi/sun4i_spireg.h diff -u /dev/null src/sys/arch/arm/sunxi/sun4i_spireg.h:1.1 --- /dev/null Sat Aug 3 13:28:43 2019 +++ src/sys/arch/arm/sunxi/sun4i_spireg.h Sat Aug 3 13:28:42 2019 @@ -0,0 +1,127 @@ +/* $NetBSD: sun4i_spireg.h,v 1.1 2019/08/03 13:28:42 tnn Exp $ */ + +/* + * Copyright (c) 2019 Tobias Nygren + * All rights reserved. + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + */ + +#ifndef _SUNXI_SUN4I_SPIREG_H_ +#define _SUNXI_SUN4I_SPIREG_H_ + +#include <sys/cdefs.h> + +#define SPI_RXDATA 0x00 + +#define SPI_TXDATA 0x04 + +#define SPI_CTL 0x08 +#define SPI_CTL_SDM __BIT(20) +#define SPI_CTL_SDC __BIT(19) +#define SPI_CTL_TP_EN __BIT(18) +#define SPI_CTL_SS_LEVEL __BIT(17) +#define SPI_CTL_SS_CTRL __BIT(16) +#define SPI_CTL_DHB __BIT(15) +#define SPI_CTL_DDB __BIT(14) +#define SPI_CTL_SS __BITS(13, 12) +#define SPI_CTL_RPSM __BIT(11) +#define SPI_CTL_XCH __BIT(10) +#define SPI_CTL_RF_RST __BIT(9) +#define SPI_CTL_TF_RST __BIT(8) +#define SPI_CTL_SSCTL __BIT(7) +#define SPI_CTL_LMTF __BIT(6) +#define SPI_CTL_DMAMC __BIT(5) +#define SPI_CTL_SSPOL __BIT(4) +#define SPI_CTL_POL __BIT(3) +#define SPI_CTL_PHA __BIT(2) +#define SPI_CTL_MODE __BIT(1) +#define SPI_CTL_EN __BIT(0) + +#define SPI_INTCTL 0x0c +#define SPI_INTCTL_SS_INT_EN __BIT(17) +#define SPI_INTCTL_TX_INT_EN __BIT(16) +#define SPI_INTCTL_TF_UR_INT_EN __BIT(14) +#define SPI_INTCTL_TF_OF_INT_EN __BIT(13) +#define SPI_INTCTL_TF_E34_INT_EN __BIT(12) +#define SPI_INTCTL_TF_E14_INT_EN __BIT(11) +#define SPI_INTCTL_TF_FL_INT_EN __BIT(10) +#define SPI_INTCTL_TF_HALF_EMP_INT_EN __BIT(9) +#define SPI_INTCTL_TF_EMP_INT_EN __BIT(8) +#define SPI_INTCTL_RF_UR_INT_EN __BIT(6) +#define SPI_INTCTL_RF_OF_INT_EN __BIT(5) +#define SPI_INTCTL_RF_F34_INT_EN __BIT(4) +#define SPI_INTCTL_RF_F14_INT_EN __BIT(3) +#define SPI_INTCTL_RF_FU_INT_EN __BIT(2) +#define SPI_INTCTL_RF_HALF_FU_INT_EN __BIT(1) +#define SPI_INTCTL_RF_RDY_INT_EN __BIT(0) + +#define SPI_INT_STA 0x10 +#define SPI_INT_STA_INT_CBF __BIT(31) +#define SPI_INT_STA_SSI __BIT(17) +#define SPI_INT_STA_TC __BIT(16) +#define SPI_INT_STA_TU __BIT(14) +#define SPI_INT_STA_TO __BIT(13) +#define SPI_INT_STA_TE34 __BIT(12) +#define SPI_INT_STA_TE14 __BIT(11) +#define SPI_INT_STA_TF __BIT(10) +#define SPI_INT_STA_THE __BIT(9) +#define SPI_INT_STA_TE __BIT(8) +#define SPI_INT_STA_RU __BIT(6) +#define SPI_INT_STA_RO __BIT(5) +#define SPI_INT_STA_RF34 __BIT(4) +#define SPI_INT_STA_RF14 __BIT(3) +#define SPI_INT_STA_RF __BIT(2) +#define SPI_INT_STA_RHF __BIT(1) +#define SPI_INT_STA_RR __BIT(0) + +#define SPI_DMACTL 0x14 +#define SPI_DMACTL_TF_EMP34_DMA __BIT(12) +#define SPI_DMACTL_TF_EMP14_DMA __BIT(11) +#define SPI_DMACTL_TF_NF_DMA __BIT(10) +#define SPI_DMACTL_TF_HE_DMA __BIT(9) +#define SPI_DMACTL_TF_EMP_DMA __BIT(8) +#define SPI_DMACTL_RF_FU34_DMA __BIT(4) +#define SPI_DMACTL_RF_FU14_DMA __BIT(3) +#define SPI_DMACTL_RF_FU_DMA __BIT(2) +#define SPI_DMACTL_RF_HF_DMA __BIT(1) +#define SPI_DMACTL_RF_RDY_DMA __BIT(0) + +#define SPI_WAIT 0x18 +#define SPI_WAIT_WCC __BITS(15, 0) + +#define SPI_CCTL 0x1c +#define SPI_CCTL_DRS __BIT(12) +#define SPI_CCTL_CDR1 __BITS(11, 8) +#define SPI_CCTL_CDR2 __BITS(7, 0) + +#define SPI_BC 0x20 +#define SPI_BC_BC __BITS(23, 0) + +#define SPI_TC 0x24 +#define SPI_TC_WTC __BITS(23, 0) + +#define SPI_FIFO_STA 0x28 +#define SPI_FIFO_STA_TF_CNT __BITS(22, 16) +#define SPI_FIFO_STA_RF_CNT __BITS(6, 0) + +#endif /* _SUNXI_SUN4I_SPIREG_H_ */