Page MenuHomeFreeBSD

D42318.diff
No OneTemporary

D42318.diff

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 <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+
+#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);

File Metadata

Mime Type
text/plain
Expires
Thu, May 14, 10:24 PM (12 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33064623
Default Alt Text
D42318.diff (21 KB)

Event Timeline