From: Zhaoyifeng <z...@rock-chips.com>

SFC stands for Serial Flash Controller on some
rockchip platforms such as RV1108/RK3128.

This patch add support for Standard,Dual,Quad
mode.

Signed-off-by: Zhaoyifeng <z...@rock-chips.com>
Signed-off-by: Andy Yan <andy....@rock-chips.com>
---

 drivers/spi/Kconfig        |   8 +
 drivers/spi/Makefile       |   1 +
 drivers/spi/rockchip_sfc.c | 402 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/rockchip_sfc.h |  82 +++++++++
 4 files changed, 493 insertions(+)
 create mode 100644 drivers/spi/rockchip_sfc.c
 create mode 100644 drivers/spi/rockchip_sfc.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8a8e8e4..52c4993 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -99,6 +99,14 @@ config ROCKCHIP_SPI
          This uses driver model and requires a device tree binding to
          operate.
 
+config ROCKCHIP_SFC
+       bool "Rockchip SFC driver"
+       help
+         Enable the Rockchip SFC driver, used to access SPI NOR flash
+         on Rockchip SoCs.
+         This uses driver model and requires a device tree binding to
+         operate.
+
 config SANDBOX_SPI
        bool "Sandbox SPI driver"
        depends on SANDBOX && DM
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9f8b86d..831dfb4 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_MXS_SPI) += mxs_spi.o
 obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
 obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
 obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
+obj-$(CONFIG_ROCKCHIP_SFC) += rk_sfc.o
 obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
 obj-$(CONFIG_SH_SPI) += sh_spi.o
 obj-$(CONFIG_SH_QSPI) += sh_qspi.o
diff --git a/drivers/spi/rockchip_sfc.c b/drivers/spi/rockchip_sfc.c
new file mode 100644
index 0000000..4388e51
--- /dev/null
+++ b/drivers/spi/rockchip_sfc.c
@@ -0,0 +1,402 @@
+/*
+ * sfc driver for rockchip
+ *
+ * (C) Copyright 2008-2016 Rockchip Electronics
+ * Yifeng.zhao, Software Engineering, <zhao0...@gmail.com>.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <spi.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/periph.h>
+#include <dm/pinctrl.h>
+#include "rockchip_sfc.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum rockchip_sfc_if_type {
+       IF_TYPE_STD,
+       IF_TYPE_DUAL,
+       IF_TYPE_QUAD,
+};
+
+struct rockchip_sfc_platdata {
+       s32 frequency;
+       fdt_addr_t base;
+};
+
+struct rockchip_sfc {
+       struct rockchip_sfc_reg *regbase;
+       struct clk clk;
+       unsigned int max_freq;
+       unsigned int mode;
+       unsigned int speed_hz;
+       u32 cmd;
+       u32 addr;
+};
+
+static int rockchip_sfc_ofdata_to_platdata(struct udevice *bus)
+{
+       struct rockchip_sfc_platdata *plat = dev_get_platdata(bus);
+       struct rockchip_sfc *sfc = dev_get_priv(bus);
+       const void *blob = gd->fdt_blob;
+       int node = dev_of_offset(bus);
+       int subnode;
+       int ret;
+
+       plat->base = devfdt_get_addr(bus);
+
+       ret = clk_get_by_index(bus, 0, &sfc->clk);
+       if (ret < 0) {
+               debug("Could not get clock for %s: %d\n", bus->name, ret);
+               return ret;
+       }
+
+       subnode = fdt_first_subnode(blob, node);
+       if (subnode < 0) {
+               debug("Error: subnode with SPI flash config missing!\n");
+               return -ENODEV;
+       }
+
+       plat->frequency = fdtdec_get_int(blob, subnode, "spi-max-frequency",
+                                        100000000);
+
+       return 0;
+}
+
+static int rockchip_sfc_probe(struct udevice *bus)
+{
+       struct rockchip_sfc_platdata *plat = dev_get_platdata(bus);
+       struct rockchip_sfc *sfc = dev_get_priv(bus);
+       int ret;
+
+       sfc->regbase = (struct rockchip_sfc_reg *)plat->base;
+
+       sfc->max_freq = plat->frequency;
+
+       ret = clk_set_rate(&sfc->clk, sfc->max_freq);
+       if (ret < 0) {
+               debug("%s: Failed to set clock: %d\n", __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rockchip_sfc_reset(struct rockchip_sfc *sfc)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       int tbase = get_timer(0);
+       u32 rcvr;
+       int ret = 0;
+
+       writel(SFC_RESET, &regs->rcvr);
+       do {
+               rcvr = readl(&regs->rcvr);
+               if (get_timer(tbase) > 1000) {
+                       debug("sfc reset timeout\n");
+                       ret =  -ETIMEDOUT;
+                       break;
+               }
+               udelay(1);
+       } while (rcvr);
+
+       writel(0xFFFFFFFF, &regs->iclr);
+
+       debug("sfc reset\n");
+
+       return ret;
+}
+
+static u8 rockchip_sfc_get_if_type(struct rockchip_sfc *sfc)
+{
+       int type = IF_TYPE_STD;
+
+       if (sfc->cmd & SFC_WR) {
+               if (sfc->mode & SPI_TX_QUAD)
+                       type = IF_TYPE_QUAD;
+               else if (sfc->mode & SPI_TX_DUAL)
+                       type = IF_TYPE_DUAL;
+               else
+                       type = IF_TYPE_STD;
+       } else {
+               if (sfc->mode & SPI_RX_QUAD)
+                       type = IF_TYPE_QUAD;
+               else if (sfc->mode & SPI_RX_DUAL)
+                       type = IF_TYPE_DUAL;
+               else
+                       type = IF_TYPE_STD;
+       }
+
+       return type;
+}
+
+static void rockchip_sfc_setup_xfer(struct rockchip_sfc *sfc)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       u32 val = 0x02;
+       u32 fsr = readl(&regs->fsr);
+       u32 sr = readl(&regs->sr);
+       u8 data_width = IF_TYPE_STD;
+
+       if (!(fsr & SFC_TX_EMPTY) || !(fsr & SFC_RX_EMPTY) || (sr & SFC_BUSY))
+               rockchip_sfc_reset(sfc);
+
+       if (sfc->cmd & SFC_ADDR_XBITS)
+               data_width = rockchip_sfc_get_if_type(sfc);
+
+       val |= (data_width << SFC_DATA_WIDTH_SHIFT);
+
+       writel(val, &regs->ctrl);
+       writel(sfc->cmd, &regs->cmd);
+       if (sfc->cmd & SFC_ADDR_XBITS)
+               writel(sfc->addr, &regs->addr);
+}
+
+static int rockchip_sfc_do_dma_xfer(struct rockchip_sfc *sfc, u32 *buffer)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       int timeout = 1000;
+       int ret = 0;
+       int risr;
+       unsigned long tbase;
+
+       rockchip_sfc_setup_xfer(sfc);
+
+       writel(0xFFFFFFFF, &regs->iclr);
+       writel((u32)buffer, &regs->dmaaddr);
+       writel(SFC_DMA_START, &regs->dmatr);
+
+       tbase = get_timer(0);
+       do {
+               udelay(1);
+               risr = readl(&regs->risr);
+               if (get_timer(tbase) > timeout) {
+                       debug("dma timeout\n");
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+       } while (!(risr & TRANS_FINISH_INT));
+
+       writel(0xFFFFFFFF, &regs->iclr);
+
+       return ret;
+}
+
+static int rockchip_sfc_dma_xfer(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+       u32 trb;
+       u32 *p32_data = buf;
+       int ret = 0;
+
+       while (len) {
+               trb = min(len, (u32)SFC_MAX_TRB);
+               sfc->cmd |= (trb << SFC_TRB_SHIFT);
+               ret = rockchip_sfc_do_dma_xfer(sfc, p32_data);
+               if (ret < 0)
+                       break;
+               len -= trb;
+               sfc->addr += trb;
+               p32_data += (trb >> 2);
+       }
+
+       return ret;
+}
+
+static int rockchip_sfc_wait_fifo_ready(struct rockchip_sfc *sfc, int wr,
+                                       u32 timeout)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       unsigned long tbase = get_timer(0);
+       u8 level;
+       u32 fsr;
+
+       do {
+               fsr = readl(&regs->fsr);
+               if (wr)
+                       level = (fsr & SFC_TXLV_MASK) >> SFC_TXLV_SHIFT;
+               else
+                       level = (fsr & SFC_RXLV_MASK) >> SFC_RXLV_SHIFT;
+               if (get_timer(tbase) > timeout)
+                       return -ETIMEDOUT;
+               udelay(1);
+       } while (!level);
+
+       return level;
+}
+
+static int rockchip_sfc_write(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       u32 bytes = len & 0x3;
+       u32 words = len >> 2;
+       u32 tx_level = 0;
+       u32 val = 0;
+       u8 count;
+
+       while (words) {
+               tx_level = rockchip_sfc_wait_fifo_ready(sfc, 1, 1000);
+               if (tx_level <= 0)
+                       return tx_level;
+               count = min(words, tx_level);
+               writesl(&regs->data, buf, count);
+               buf += count;
+               words -= count;
+       }
+
+       /* handle the last none word aligned bytes */
+       if (bytes) {
+               tx_level = rockchip_sfc_wait_fifo_ready(sfc, 1, 1000);
+               if (tx_level <= 0)
+                       return tx_level;
+               memcpy(&val, buf, bytes);
+               writel(val, &regs->data);
+       }
+
+       return 0;
+}
+
+static int rockchip_sfc_read(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+       struct rockchip_sfc_reg *regs = sfc->regbase;
+       u32 bytes = len & 0x3;
+       u32 words = len >> 2;
+       u32 rx_level = 0;
+       u32 count;
+       u32 val;
+
+       while (words) {
+               rx_level = rockchip_sfc_wait_fifo_ready(sfc, 0, 1000);
+               if (rx_level <= 0)
+                       return rx_level;
+               count = min(words, rx_level);
+               readsl(&regs->data, buf, count);
+               buf += count;
+               words -= count;
+       }
+
+       /* handle the last none word aligned bytes */
+       if (bytes) {
+               rx_level = rockchip_sfc_wait_fifo_ready(sfc, 0, 1000);
+               if (rx_level <= 0)
+                       return rx_level;
+               val = readl(&regs->data);
+               memcpy(buf, &val, bytes);
+       }
+
+       return 0;
+}
+
+static int rockchip_sfc_pio_xfer(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+       int ret = 0;
+       int rw = sfc->cmd & SFC_WR;
+
+       sfc->cmd |= (len << SFC_TRB_SHIFT);
+       rockchip_sfc_setup_xfer(sfc);
+
+       if (len) {
+               if (rw)
+                       ret = rockchip_sfc_write(sfc, buf, len);
+               else
+                       ret = rockchip_sfc_read(sfc, buf, len);
+       }
+
+       return ret;
+}
+
+static int rockchip_sfc_do_xfer(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+       int ret = 0;
+
+       if (!(len & 0x03) && (len >= 4))
+               ret = rockchip_sfc_dma_xfer(sfc, buf, len);
+       else
+               ret = rockchip_sfc_pio_xfer(sfc, buf, len);
+
+       return ret;
+}
+
+static int rockchip_sfc_xfer(struct udevice *dev, unsigned int bitlen,
+                            const void *dout, void *din, unsigned long flags)
+{
+       struct udevice *bus = dev->parent;
+       struct rockchip_sfc *sfc = dev_get_priv(bus);
+       int len = bitlen >> 3;
+       u8 *pcmd = (u8 *)dout;
+       int ret = 0;
+
+       if (flags & SPI_XFER_BEGIN) {
+               sfc->cmd = pcmd[0];
+               if (len >= 4) {
+                       sfc->cmd |= SFC_ADDR_24BITS | (((len - 4) * 8) << 8);
+                       sfc->addr = pcmd[3] | (pcmd[2] << 8) | (pcmd[1] << 16);
+               }
+       }
+
+       if (flags == (SPI_XFER_BEGIN | SPI_XFER_END))
+               len = 0;
+
+       if (flags & SPI_XFER_END) {
+               if (dout && len)
+                       sfc->cmd |= SFC_WR;
+
+               if (din)
+                       ret = rockchip_sfc_do_xfer(sfc, (u32 *)din, len);
+               else if (dout)
+                       ret = rockchip_sfc_do_xfer(sfc, (u32 *)dout, len);
+       }
+
+       return ret;
+}
+
+static int rockchip_sfc_set_speed(struct udevice *bus, uint speed)
+{
+       struct rockchip_sfc *sfc = dev_get_priv(bus);
+
+       if (speed > sfc->max_freq)
+               speed = sfc->max_freq;
+
+       sfc->speed_hz = speed;
+
+       return 0;
+}
+
+static int rockchip_sfc_set_mode(struct udevice *bus, uint mode)
+{
+       struct rockchip_sfc *sfc = dev_get_priv(bus);
+
+       sfc->mode = mode;
+
+       return 0;
+}
+
+static const struct dm_spi_ops rockchip_sfc_ops = {
+       .xfer           = rockchip_sfc_xfer,
+       .set_speed      = rockchip_sfc_set_speed,
+       .set_mode       = rockchip_sfc_set_mode,
+};
+
+static const struct udevice_id rockchip_sfc_ids[] = {
+       { .compatible = "rockchip,sfc" },
+       { }
+};
+
+U_BOOT_DRIVER(rockchip_sfc_driver) = {
+       .name   = "rockchip_sfc",
+       .id     = UCLASS_SPI,
+       .of_match = rockchip_sfc_ids,
+       .ops    = &rockchip_sfc_ops,
+       .ofdata_to_platdata = rockchip_sfc_ofdata_to_platdata,
+       .platdata_auto_alloc_size = sizeof(struct rockchip_sfc_platdata),
+       .priv_auto_alloc_size = sizeof(struct rockchip_sfc),
+       .probe  = rockchip_sfc_probe,
+};
diff --git a/drivers/spi/rockchip_sfc.h b/drivers/spi/rockchip_sfc.h
new file mode 100644
index 0000000..b865056
--- /dev/null
+++ b/drivers/spi/rockchip_sfc.h
@@ -0,0 +1,82 @@
+/*
+ * sfc driver for rockchip
+ *
+ * (C) Copyright 2008-2016 Rockchip Electronics
+ * Yifeng.zhao, Software Engineering, <zhao0...@gmail.com>.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __RK_SFC_H
+#define __RK_SFC_H
+
+struct rockchip_sfc_reg {
+       u32 ctrl;
+       u32 imr;
+       u32 iclr;
+       u32 ftlr;
+       u32 rcvr;
+       u32 ax;
+       u32 abit;
+       u32 isr;
+       u32 fsr;
+       u32 sr;
+       u32 risr;
+       u32 reserved[21];
+       u32 dmatr;
+       u32 dmaaddr;
+       u32 reserved1[30];
+       u32 cmd;
+       u32 addr;
+       u32 data;
+};
+check_member(rockchip_sfc_reg, data, 0x108);
+
+/*SFC_CTRL*/
+#define SFC_DATA_WIDTH_SHIFT   12
+#define SFC_DATA_WIDTH_MASK    GENMASK(13, 12)
+#define SFC_ADDR_WIDTH_SHIFT   10
+#define SFC_ADDR_WIDTH_MASK    GENMASK(11, 10)
+#define SFC_CMD_WIDTH_SHIT     8
+#define SFC_CMD_WIDTH_MASK     GENMASK(9, 8)
+#define SFC_DATA_SHIFT_NEGETIVE        BIT(1)
+
+/*SFC_CMD*/
+#define SFC_WR                 BIT(12)
+#define SFC_ADDR_0BITS         (0 << 14)
+#define SFC_ADDR_24BITS                (1 << 14)
+#define SFC_ADDR_32BITS                (2 << 14)
+#define SFC_ADDR_XBITS         (3 << 14)
+#define SFC_TRB_SHIFT          (16)
+#define SFC_TRB_MASK           GENMASK(29, 16)
+
+/* Dma start trigger signal. Auto cleared after write */
+#define SFC_DMA_START          BIT(0)
+
+#define SFC_RESET              BIT(0)
+
+/*SFC_FSR*/
+#define SFC_RXLV_SHIFT         (16)
+#define SFC_RXLV_MASK          GENMASK(20, 16)
+#define SFC_TXLV_SHIFT         (8)
+#define SFC_TXLV_MASK          GENMASK(12, 8)
+#define SFC_RX_FULL            BIT(3)  /* rx fifo full */
+#define SFC_RX_EMPTY           BIT(2)  /* rx fifo empty */
+#define SFC_TX_EMPTY           BIT(1)  /* tx fifo empty */
+#define SFC_TX_FULL            BIT(0)  /* tx fifo full */
+
+#define SFC_BUSY               BIT(0)  /* sfc busy flag */
+
+/*SFC_RISR*/
+#define DMA_FINISH_INT         BIT(7)        /* dma interrupt */
+#define SPI_ERR_INT            BIT(6)        /* Nspi error interrupt */
+#define AHB_ERR_INT            BIT(5)        /* Ahb bus error interrupt */
+#define TRANS_FINISH_INT       BIT(4)        /* Transfer finish interrupt */
+#define TX_EMPTY_INT           BIT(3)        /* Tx fifo empty interrupt */
+#define TX_OF_INT              BIT(2)        /* Tx fifo overflow interrupt */
+#define RX_UF_INT              BIT(1)        /* Rx fifo underflow interrupt */
+#define RX_FULL_INT            BIT(0)        /* Rx fifo full interrupt */
+
+#define SFC_MAX_TRB            (1024 << 3)
+
+#endif
-- 
2.7.4


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to