diff --git a/sys/arm64/rockchip/rk_fspi.c b/sys/arm64/rockchip/rk_fspi.c new file mode 100644 --- /dev/null +++ b/sys/arm64/rockchip/rk_fspi.c @@ -0,0 +1,902 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "spibus_if.h" + +#define FSPI_CTRL0 0 +#define FSPI_CTRL0_SHIFTPHASE 2 +#define FSPI_CTRL0_CMDB 8 +#define FSPI_CTRL0_ADRB 10 +#define FSPI_CTRL0_DATB 12 + +/* Interrupt mask */ +#define FSPI_IMR 0x4 +#define FSPI_IMR_RXFM (1 << 0) +#define FSPI_IMR_RXUM (1 << 1) +#define FSPI_IMR_TXOM (1 << 2) +#define FSPI_IMR_TXEM (1 << 3) +#define FSPI_IMR_TRANSM (1 << 4) +#define FSPI_IMR_AHBM (1 << 5) +#define FSPI_IMR_NSPIM (1 << 6) +#define FSPI_IMR_DMAM (1 << 7) + +/* Interrupt clear */ +#define FSPI_ICLR 0x8 +#define FSPI_ICLR_RXFC (1 << 0) +#define FSPI_ICLR_RXUC (1 << 1) +#define FSPI_ICLR_TXOC (1 << 2) +#define FSPI_ICLR_TXEC (1 << 3) +#define FSPI_ICLR_TRANSC (1 << 4) +#define FSPI_ICLR_AHBC (1 << 5) +#define FSPI_ICLR_NSPIC (1 << 6) +#define FSPI_ICLR_DMAC (1 << 7) + +/* FIFO threshold level */ +#define FSPI_FLTR 0xc +#define FSPI_FLTR_TX_SHIFT 0 +#define FSPI_FLTR_TX_MASK 0x1f +#define FSPI_FLTR_RX_SHIFT 8 +#define FSPI_FLTR_RX_MASK 0x1f + +/* Reset FSM and FIFO */ +#define FSPI_RCVR 0x10 +#define FSPI_RCVR_RESET (1 << 0) + +/* Enhanced mode */ +#define FSPI_AX0 0x14 + +/* Address Bit number */ +#define FSPI_ABIT0 0x18 + +/* Interrupt status */ +#define FSPI_ISR 0x1c +#define FSPI_ISR_RXFS (1 << 0) +#define FSPI_ISR_RXUS (1 << 1) +#define FSPI_ISR_TXOS (1 << 2) +#define FSPI_ISR_TXES (1 << 3) +#define FSPI_ISR_TRANSS (1 << 4) +#define FSPI_ISR_AHBS (1 << 5) +#define FSPI_ISR_NSPIS (1 << 6) +#define FSPI_ISR_DMAS (1 << 7) + +/* FIFO status */ +#define FSPI_FSR 0x20 +#define FSPI_FSR_TXFS (1 << 0) +#define FSPI_FSR_TXES (1 << 1) +#define FSPI_FSR_RXES (1 << 2) +#define FSPI_FSR_RXFS (1 << 3) +#define FSPI_FSR_TX_MASK (0x1f << 8) +#define FSPI_FSR_TS_SHIFT 8 +#define FSPI_FSR_RX_MASK (0x1f << 16) +#define FSPI_FSR_RX_SHIFBT 16 + +/* FSM status */ +#define FSPI_SR 0x24 +#define FSPI_SR_IDLE 0x0 +#define FSPI_SR_BUSY 0x1 + +/* Raw interrupt status */ +#define FSPI_RISR 0x28 +#define FSPI_RISR_RXFS (1 << 0) +#define FSPI_RISR_RXUS (1 << 1) +#define FSPI_RISR_TXOS (1 << 2) +#define FSPI_RISR_TXES (1 << 3) +#define FSPI_RISR_TRANSS (1 << 4) +#define FSPI_RISR_AHBS (1 << 5) +#define FSPI_RISR_NSPIS (1 << 6) +#define FSPI_RISR_DMAS (1 << 7) + +/* Version */ +#define FSPI_VER 0x2C +#define FSPI_VER_3 0x3 +#define FSPI_VER_4 0x4 +#define FSPI_VER_5 0x5 + +/* Delay line controller resiter */ +#define FSPI_DLL_CTRL0 0x3C +#define FSPI_DLL_CTRL0_SCLK_SRC (1 << 15) +#define FSPI_DLL_CTRL0_DLL_MASK_V4 0xFFU +#define FSPI_DLL_CTRL0_DLL_MASK_V5 0x1FFU + +/* Master trigger */ +#define FSPI_DMATR 0x80 +#define FSPI_DMATR_TRIG 1 + +/* Src or Dst addr for master */ +#define FSPI_DMA_ADDR 0x84 + +/* Length control register extension 32GB */ +#define FSPI_LEN_CTRL 0x88 +#define FSPI_LEN_CTRL_SRC 1 +#define FSPI_LEN_EXT 0x8C + +/* Command */ +#define FSPI_CMD 0x100 +#define FSPI_CMD_SHIFT 0 +#define FSPI_DUMMY_SHIFT 8 +#define FSPI_CMD_DIR_WR 12 +#define FSPI_CMD_DIR_IN 0 +#define FSPI_CMD_DIR_OUT 1 +#define FSPI_CMD_ADDR_SHIFT 14 +#define FSPI_CMD_ADDR_0 0 +#define FSPI_CMD_ADDR_24 1 +#define FSPI_CMD_ADDR_32 2 +#define FSPI_CMD_ADDR_NN 3 +#define FSPI_CMD_TRB_SHIFT 16 +#define FSPI_CMD_CS_SHIFT 30 + +/* Address */ +#define FSPI_ADDR 0x104 + +/* Data */ +#define FSPI_DATA 0x108 +#define FSPI_MAX_CS_NUM 4 +#define FSPI_MAX_IOSIZE_V3 (512 * 31) +#define FSPI_MAX_IOSIZE_V4 (1024 * 1024) + +#define FSPI_DMA_MIN_THR (0x40) + +#define FSPI_CLK_MAX_FREQ (150 * 1000 * 1000) + +#define RK_FSPI_XFER_OUT 0 +#define RK_FSPI_XFER_IN 1 + +#define CMD_WRITE_ENABLE 0x06 +#define CMD_WRITE_DISABLE 0x04 +#define CMD_READ_IDENT 0x9F +#define CMD_READ_STATUS 0x05 +#define CMD_WRITE_STATUS 0x01 +#define CMD_READ 0x03 +#define CMD_FAST_READ 0x0B +#define CMD_READ_DUAL_IO 0xBB +#define CMD_READ_QUAD_OUTPUT 0x6B +#define CMD_PAGE_PROGRAM 0x02 +#define CMD_SECTOR_ERASE 0xD8 +#define CMD_BULK_ERASE 0xC7 +#define CMD_BLOCK_4K_ERASE 0x20 +#define CMD_BLOCK_32K_ERASE 0x52 +#define CMD_ENTER_4B_MODE 0xB7 +#define CMD_EXIT_4B_MODE 0xE9 + +struct rk_fspi_conf { + uint8_t cmd, dir, addr_len, dummy_bytes; + uint32_t xfer_len, addr; + }; + +static struct ofw_compat_data compat_data[] = { + { "rockchip,sfc", 1 }, + { NULL, 0 } +}; + +static struct resource_spec rk_fspi_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +struct rk_fspi_softc { + device_t dev; + device_t spibus; + struct resource *res[2]; + struct mtx mtx; + clk_t clk_ahb; + clk_t clk_sfc; + uint32_t version; + uint32_t max_io_size; + void * intrhand; + bus_dmamap_t dmamap; + void *dmaaddr; + bus_addr_t physaddr; + bus_dma_tag_t dmat; + unsigned dmasize; + bool use_dma; +}; + +#define RK_FSPI_LOCK(sc) mtx_lock(&(sc)->mtx) +#define RK_FSPI_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define RK_FSPI_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define RK_FSPI_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) + +#if 0 + #define dprintf(dev, format, arg...) device_printf(dev, "%s: " format, __func__, arg) +#else + #define dprintf(dev, format, arg...) +#endif + +static int rk_fspi_probe(device_t dev); +static int rk_fspi_attach(device_t dev); +static int rk_fspi_detach(device_t dev); +static void rk_fspi_intr(void *arg); + + +static void +fspi_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct rk_fspi_softc *sc = arg; + if (error != 0) + return; + + sc->physaddr = segs[0].ds_addr; +} + +static int +rk_fspi_reset(struct rk_fspi_softc *sc) +{ + int error, i; + uint32_t status; + + RK_FSPI_WRITE_4(sc, FSPI_RCVR, FSPI_RCVR_RESET); + + for(i = 0; i < 10000;i++) { + status = RK_FSPI_READ_4(sc, FSPI_RCVR); + if(!(status & FSPI_RCVR_RESET)) break; + DELAY(20); + } + + error = status & FSPI_RCVR_RESET; + if (error) + device_printf(sc->dev, "FSPI reset did not finish\n"); + + /* Clear the masked interrupt from RISR */ + RK_FSPI_WRITE_4(sc, FSPI_ICLR, 0xFFFFFFFF); + + dprintf(sc->dev, "fspi reset %s\n", ""); + + return error; +} + +static uint32_t +rk_fspi_get_version(struct rk_fspi_softc *sc) +{ + return (RK_FSPI_READ_4(sc, FSPI_VER) & 0xffff); +} + +static uint32_t +rk_fspi_get_max_iosize(struct rk_fspi_softc *sc) +{ + return sc->version < 4 ? FSPI_MAX_IOSIZE_V3 : FSPI_MAX_IOSIZE_V4; +} + + +static void +rk_fspi_irq_mask(struct rk_fspi_softc *sc, uint32_t mask) +{ + uint32_t reg; + + /* Disable transfer finish interrupt */ + reg = RK_FSPI_READ_4(sc, FSPI_IMR); + reg |= mask; + RK_FSPI_WRITE_4(sc, FSPI_IMR, reg); +} + +static void +rk_fspi_irq_unmask(struct rk_fspi_softc *sc, uint32_t mask) +{ + uint32_t reg; + + /* Enable transfer complete interrupt */ + reg = RK_FSPI_READ_4(sc, FSPI_IMR); + reg &= ~mask; + RK_FSPI_WRITE_4(sc, FSPI_IMR, reg); +} + +static int +rk_fspi_init(struct rk_fspi_softc *sc) +{ + RK_FSPI_WRITE_4(sc, FSPI_CTRL0, 0); + RK_FSPI_WRITE_4(sc, FSPI_ICLR, 0xFFFFFFFF); + rk_fspi_irq_mask(sc, 0xFFFFFFFF); + if (rk_fspi_get_version(sc) >= FSPI_VER_4) + RK_FSPI_WRITE_4(sc, FSPI_LEN_CTRL, FSPI_LEN_CTRL_SRC); + + return 0; +} + +static int +rk_fspi_xfer_setup(struct rk_fspi_softc *sc, struct rk_fspi_conf *op, uint32_t cs) +{ + uint32_t ctrl = 0, cmd = 0; + + /* set CMD */ + cmd = op->cmd; + + /* set ADDR */ + if (op->addr_len) { + if (op->addr_len == 4) { + cmd |= FSPI_CMD_ADDR_32 << FSPI_CMD_ADDR_SHIFT; + } else if (op->addr_len == 3) { + cmd |= FSPI_CMD_ADDR_24 << FSPI_CMD_ADDR_SHIFT; + } else { + cmd |= FSPI_CMD_ADDR_NN << FSPI_CMD_ADDR_SHIFT; + RK_FSPI_WRITE_4(sc, FSPI_ABIT0, op->addr_len * 8 - 1); + } + + } + + /* set DUMMY */ + if (op->dummy_bytes) { + cmd |= op->dummy_bytes * 8 << FSPI_DUMMY_SHIFT; + } + + /* set DATA */ + if (sc->version >= FSPI_VER_4) /* Clear it if no data to transfer */ + RK_FSPI_WRITE_4(sc, FSPI_LEN_EXT, op->xfer_len); + else + cmd |= op->xfer_len << FSPI_CMD_TRB_SHIFT; + + if (op->xfer_len) { + if (op->dir == RK_FSPI_XFER_OUT) + cmd |= FSPI_CMD_DIR_OUT << FSPI_CMD_DIR_WR; + + } + if (!op->xfer_len && op->addr_len) + cmd |= FSPI_CMD_DIR_OUT << FSPI_CMD_DIR_WR; + + /* set the Controller */ + ctrl |= FSPI_CTRL0_SHIFTPHASE; + cmd |= cs << FSPI_CMD_CS_SHIFT; + + dprintf(sc->dev, "fspi addr-len =%x[x%d] dummy-len=%x[x%d]\n", + op->addr_len, 1, + op->dummy_bytes, 1); + dprintf(sc->dev, "fspi ctrl=%x cmd=%x addr=%x len=%x\n", + ctrl, cmd, op->addr, op->xfer_len); + + RK_FSPI_WRITE_4(sc, FSPI_CTRL0, ctrl); + RK_FSPI_WRITE_4(sc, FSPI_CMD, cmd); + + if (op->addr_len) + RK_FSPI_WRITE_4(sc, FSPI_ADDR, op->addr); + + return (0); +} + +static int +rk_fspi_xfer_done(struct rk_fspi_softc *sc, uint32_t timeout_us) +{ + int ret = 0, cycles =0; + uint32_t status; + sbintime_t sbt_end = sbinuptime() + 4294 * timeout_us; + + while(1) { + cycles++; + status = RK_FSPI_READ_4(sc, FSPI_SR); + if(!(status & FSPI_SR_BUSY)) break; + if(getsbinuptime() > sbt_end) { + ret = 1; + break; + } + } + if (ret) { + device_printf(sc->dev, "wait fspi idle timeout, cycles=%d\n", cycles); + rk_fspi_reset(sc); + + ret = EIO; + } + + return (ret); +} +static int +rk_fspi_wait_txfifo_ready(struct rk_fspi_softc *sc, uint32_t timeout_us, uint32_t *level) +{ + int ret = 0, cycles = 0; + uint32_t status; + sbintime_t sbt_end = sbinuptime() + 4294 * timeout_us; + while(1) { + cycles++; + status = RK_FSPI_READ_4(sc, FSPI_FSR); + if(status & FSPI_FSR_TX_MASK) break; + if(getsbinuptime() > sbt_end) { + ret = 1; + break; + } + } + + if (ret) { + device_printf(sc->dev, "fspi wait tx fifo timeout, cycles=%d\n", cycles); + + return (ETIMEDOUT); + } + + *level = (status & FSPI_FSR_TX_MASK) >> FSPI_FSR_TS_SHIFT; + return (0); +} + +static int +rk_fspi_wait_rxfifo_ready(struct rk_fspi_softc *sc, uint32_t timeout_us, uint32_t *level) +{ + int ret = 0, cycles = 0; + uint32_t status; + sbintime_t sbt_end = sbinuptime() + 4294 * timeout_us; + + while(1) { + cycles++; + status = RK_FSPI_READ_4(sc, FSPI_FSR); + if(status & FSPI_FSR_RX_MASK) break; + if(getsbinuptime() > sbt_end) { + ret = 1; + break; + } + } + if (ret) { + device_printf(sc->dev, "fspi wait rx fifo timeout, cycles=%d\n", cycles); + + return (ETIMEDOUT); + } + + *level = (status & FSPI_FSR_RX_MASK) >> FSPI_FSR_RX_SHIFBT; + return (0); +} + +static int +rk_fspi_write_fifo(struct rk_fspi_softc *sc, const uint8_t *buf, int len) +{ + uint8_t bytes = len & 0x3; + uint32_t dwords; + int tx_level, error; + uint32_t write_words; + uint32_t tmp = 0; + + dwords = len >> 2; + while (dwords) { + error = rk_fspi_wait_txfifo_ready(sc, 1000, &tx_level); + if (error) + return (-error); + write_words =tx_level < dwords ? tx_level : dwords; + while(write_words) { + RK_FSPI_WRITE_4(sc, FSPI_DATA, *((const uint32_t *)buf)); + buf += 4; + write_words--; + dwords--; + } + } + + /* write the rest non word aligned bytes */ + if (bytes) { + error = rk_fspi_wait_txfifo_ready(sc, 1000, &tx_level); + if (error) + return (-error); + memcpy(&tmp, buf, bytes); + RK_FSPI_WRITE_4(sc, FSPI_DATA, tmp); + } + + return (len); +} + +static int +rk_fspi_read_fifo(struct rk_fspi_softc *sc, uint8_t *buf, int len) +{ + uint8_t bytes = len & 0x3; + uint32_t dwords; + uint8_t read_words; + int rx_level; + int tmp, error; + + /* word aligned access only */ + dwords = len >> 2; + while (dwords) { + error = rk_fspi_wait_rxfifo_ready(sc, 10000, &rx_level); + if (error) + return (-error); + read_words = rx_level < dwords ? rx_level : dwords; + while(read_words) { + *((uint32_t *)buf) = RK_FSPI_READ_4(sc, FSPI_DATA); + buf += 4; + read_words--; + dwords--; + } + } + + /* read the rest non word aligned bytes */ + if (bytes) { + error = rk_fspi_wait_rxfifo_ready(sc, 10000, &rx_level); + if (error) + return (-error); + tmp = RK_FSPI_READ_4(sc, FSPI_DATA); + memcpy(buf, &tmp, bytes); + } + + return (len); +} + + +static int +rk_fspi_xfer_pio(struct rk_fspi_softc *sc, void *buffer, uint8_t dir, uint32_t len) +{ + + dprintf(sc->dev, "fspi xfer_pio dir=%d len=%x\n", dir, len); + + if (dir == RK_FSPI_XFER_OUT) + return rk_fspi_write_fifo(sc, buffer, len); + else + return rk_fspi_read_fifo(sc, buffer, len); +} + +static int +rk_fspi_do_dma(struct rk_fspi_softc *sc, size_t len) +{ + RK_FSPI_WRITE_4(sc, FSPI_ICLR, 0xffffffff); + RK_FSPI_WRITE_4(sc, FSPI_DMA_ADDR, sc->physaddr); + RK_FSPI_WRITE_4(sc, FSPI_DMATR, FSPI_DMATR_TRIG); + return len; +} + +static int +rk_fspi_xfer_dma(struct rk_fspi_softc *sc, void *buffer, uint8_t dir, uint32_t len) +{ + int ret, err; + + dprintf(sc->dev, "fspi xfer_dma dir=%d len=%x\n", dir, len); + + if (dir == RK_FSPI_XFER_OUT) { + memcpy(sc->dmaaddr, buffer, len); + } + rk_fspi_irq_unmask(sc, FSPI_IMR_DMAM); + ret = rk_fspi_do_dma(sc, len); + err = msleep(sc, &sc->mtx, 0, "rk_fspi", hz); + if(err) { + device_printf(sc->dev, "DMA transfer timed out\n"); + return (-err); + } + rk_fspi_irq_mask(sc, FSPI_IMR_DMAM); + if (dir == RK_FSPI_XFER_IN) { + memcpy(buffer, sc->dmaaddr, len); + rk_fspi_irq_mask(sc, FSPI_IMR_DMAM); + } + + return (ret); +} + +static int +rk_fspi_xfer(struct rk_fspi_softc *sc, void *buffer, uint8_t dir, uint32_t len) +{ + if(sc->use_dma && len > FSPI_DMA_MIN_THR) { + return rk_fspi_xfer_dma(sc, buffer, dir, len); + } else { + return rk_fspi_xfer_pio(sc, buffer, dir, len); + } +} + +static int +rk_fspi_setup_dma(device_t dev) +{ + struct rk_fspi_softc *sc; + int error; + + sc = device_get_softc(dev); + + sc->dmasize = sc->max_io_size; + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), + 4, sc->dmasize, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->dmasize, 1, /* maxsize, nsegs */ + sc->dmasize, 0, /* maxsegsize, flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->dmat); + + if (error != 0) { + device_printf(dev, "cannot create DMA tag\n"); + goto done; + } + + error = bus_dmamem_alloc(sc->dmat, &sc->dmaaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->dmamap); + if (error != 0) { + device_printf(sc->dev, "cannot allocate DMA buffer\n"); + goto done; + } + + error = bus_dmamap_load(sc->dmat, sc->dmamap, sc->dmaaddr, + sc->dmasize, fspi_dmamap_cb, sc, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->dev, "cannot load DMA map\n"); + goto done; + } +done: + return (error); +} + +static int +rk_fspi_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Rockchip Flexible SPI Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +rk_fspi_attach(device_t dev) +{ + struct rk_fspi_softc *sc; + int error; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + if (bus_alloc_resources(dev, rk_fspi_spec, sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + error = ENXIO; + goto fail; + } + + if (bus_setup_intr(dev, sc->res[1], + INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_fspi_intr, sc, + &sc->intrhand)) { + bus_release_resources(dev, rk_fspi_spec, sc->res); + device_printf(dev, "cannot setup interrupt handler\n"); + return (ENXIO); + } + + /* Activate the module clock. */ + error = clk_get_by_ofw_name(dev, 0, "hclk_sfc", &sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get sfc ahb clock\n"); + goto fail; + } + error = clk_get_by_ofw_name(dev, 0, "clk_sfc", &sc->clk_sfc); + if (error != 0) { + device_printf(dev, "cannot get clk_sfc clock\n"); + goto fail; + } + error = clk_enable(sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + goto fail; + } + error = clk_enable(sc->clk_sfc); + if (error != 0) { + device_printf(dev, "cannot enable sfc clock\n"); + goto fail; + } + + rk_fspi_init(sc); + sc->version = rk_fspi_get_version(sc); + sc->max_io_size = rk_fspi_get_max_iosize(sc); + + node = ofw_bus_get_node(dev); + if (!OF_hasprop(node, "rockchip,sfc-no-dma")) { + error = rk_fspi_setup_dma(dev); + if(error) { + device_printf(dev, "setup dma failed, using PIO\n"); + } else { + sc->use_dma = 1; + } + + } + device_printf(dev, "Rockchip FSPI version %d\n", sc->version); + + sc->spibus = device_add_child(dev, "spibus", -1); + + return (bus_generic_attach(dev)); + +fail: + rk_fspi_detach(dev); + return (error); +} + +static int +rk_fspi_detach(device_t dev) +{ + struct rk_fspi_softc *sc; + + sc = device_get_softc(dev); + + bus_generic_detach(sc->dev); + + if(sc->dmamap) { + bus_dmamap_unload(sc->dmat, sc->dmamap); + } + if(sc->dmaaddr) { + bus_dmamem_free(sc->dmat, sc->dmaaddr, sc->dmamap); + } + if(sc->dmat) { + bus_dma_tag_destroy(sc->dmat); + } + if (sc->spibus != NULL) + device_delete_child(dev, sc->spibus); + + if (sc->clk_sfc != NULL) + clk_release(sc->clk_sfc); + if (sc->clk_ahb) + clk_release(sc->clk_ahb); + + if (sc->intrhand != NULL) + bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); + + bus_release_resources(dev, rk_fspi_spec, sc->res); + mtx_destroy(&sc->mtx); + + return (0); +} + +static void +rk_fspi_intr(void *arg) +{ + struct rk_fspi_softc *sc; + uint32_t reg; + + sc = arg; + + RK_FSPI_LOCK(sc); + reg = RK_FSPI_READ_4(sc, FSPI_RISR); + RK_FSPI_WRITE_4(sc, FSPI_ICLR, reg); + if(reg & FSPI_RISR_DMAS) { + wakeup(sc); + } + RK_FSPI_UNLOCK(sc); +} + +static phandle_t +rk_fspi_get_node(device_t bus, device_t dev) +{ + + return ofw_bus_get_node(bus); +} + + + + +static uint32_t +mk_addr(uint8_t *b, uint8_t len) +{ +KASSERT((len == 3) || (len == 4), ("Invalid fspi address length")); + if(len == 3) + return (b[0] << 16) | (b[1] << 8) | b[2]; + else /* len == 4*/ + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +} + +static int +rk_fspi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + struct rk_fspi_softc *sc; + uint32_t cs, mode, clock, ret; + void *tbuf; + uint8_t *tx_cmd = (uint8_t *) cmd->tx_cmd; + struct rk_fspi_conf op; + + memset(&op, 0, sizeof(op)); + + op.cmd = tx_cmd[0]; + op.dir = RK_FSPI_XFER_OUT; + + int err = 0; + + sc = device_get_softc(dev); + spibus_get_cs(child, &cs); + spibus_get_clock(child, &clock); + spibus_get_mode(child, &mode); + + op.xfer_len = cmd->tx_data_sz; + if(op.xfer_len > sc->max_io_size) { + device_printf(sc->dev, + "Error: transfer size (%u)> max allowed (%u)", + op.xfer_len, sc->max_io_size); + return EIO; + } + tbuf = cmd->tx_data; + RK_FSPI_LOCK(sc); + err = 0; + dprintf(sc->dev, "CMDXX %d %d %d\n", tx_cmd[0], cmd->tx_cmd_sz, cmd->tx_data_sz); + if(cmd->tx_cmd_sz != 1) { + switch (op.cmd) { + + case CMD_READ_STATUS: + tbuf = (char *)cmd->rx_cmd + 1; + op.dir = RK_FSPI_XFER_IN; + op.xfer_len = 1; + break; + + case CMD_READ_IDENT: + op.xfer_len = 3; + op.dir = RK_FSPI_XFER_IN; + tbuf = ((char *)cmd->rx_cmd) + 1; + break; + case CMD_BLOCK_4K_ERASE: + case CMD_BLOCK_32K_ERASE: + case CMD_SECTOR_ERASE: + op.xfer_len = 0; + op.addr_len = cmd->tx_cmd_sz - 1; + op.addr = mk_addr(tx_cmd + 1, op.addr_len); + dprintf(dev, "ADDR DUMP cmd=%d %d %d %d %d\n", tx_cmd[0], tx_cmd[1], tx_cmd[2], tx_cmd[3], tx_cmd[4]); + dprintf(dev, "ADDR FMT a=%d l=%d\n",op.addr, op.addr_len); + break; + case CMD_FAST_READ: + op.dummy_bytes = 1; + op.dir = RK_FSPI_XFER_IN; + op.addr_len = cmd->tx_cmd_sz - 2; + op.addr = mk_addr(tx_cmd + 1, op.addr_len); + dprintf(dev, "ADDR DUMP cmd=%d %d %d %d %d\n", tx_cmd[0], tx_cmd[1], tx_cmd[2], tx_cmd[3], tx_cmd[4]); + dprintf(dev, "ADDR FMT a=%d l=%d\n",op.addr, op.addr_len); + break; + case CMD_PAGE_PROGRAM: + op.addr_len = cmd->tx_cmd_sz - 1; + op.addr = mk_addr(tx_cmd + 1, op.addr_len); + dprintf(dev, "ADDR DUMP cmd=%d %d %d %d %d\n", tx_cmd[0], tx_cmd[1], tx_cmd[2], tx_cmd[3], tx_cmd[4]); + dprintf(dev, "ADDR FMT a=%d l=%d\n",op.addr, op.addr_len); + break; + default: + device_printf(dev, "unknown cmd=%d tx_cmd_sz=%d\n", op.cmd, cmd->tx_cmd_sz); + break; + } + } + rk_fspi_xfer_setup(sc, &op, cs); + if(op.xfer_len) { + ret = rk_fspi_xfer(sc, tbuf, op.dir, op.xfer_len); + if(ret < 0) { + err = (-ret); + goto out; + } + if(ret != op.xfer_len) { + device_printf(sc->dev,"Short transfer want=%u done=%u\n", op.xfer_len, ret); + err = EIO; + } + } +out: + RK_FSPI_UNLOCK(sc); + if(!err) + err = rk_fspi_xfer_done(sc, 100000); + return (err); + +} + +static device_method_t rk_fspi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rk_fspi_probe), + DEVMETHOD(device_attach, rk_fspi_attach), + DEVMETHOD(device_detach, rk_fspi_detach), + + /* spibus_if */ + DEVMETHOD(spibus_transfer, rk_fspi_transfer), + + /* ofw_bus_if */ + DEVMETHOD(ofw_bus_get_node, rk_fspi_get_node), + + DEVMETHOD_END +}; + +static driver_t rk_fspi_driver = { + "rk_fspi", + rk_fspi_methods, + sizeof(struct rk_fspi_softc), +}; + +DRIVER_MODULE(rk_fspi, simplebus, rk_fspi_driver, 0, 0); +DRIVER_MODULE(ofw_spibus, rk_fspi, ofw_spibus_driver, 0, 0); +MODULE_DEPEND(rk_fspi, ofw_spibus, 1, 1, 1); +OFWBUS_PNP_INFO(compat_data);