Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F156474227
D42318.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D42318.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D42318: Support for rockchip serial flash controller
Attached
Detach File
Event Timeline
Log In to Comment