
ping
On 2017年08月08日 09:43, Andy Yan wrote:
SFC stands for Serial Flash Controller on some rockchip platforms such as RV1108/RK3128.
This patch add support for it with Standard,Dual,Quad mode.
Signed-off-by: Andy Yan andy.yan@rock-chips.com
Changes in v2:
fix the wrong reference in Makefile, which should be rockchip_sfc.o not rk_sfc.o
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..052c608 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) += rockchip_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, zhao0116@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, ®s->rcvr);
- do {
rcvr = readl(®s->rcvr);
if (get_timer(tbase) > 1000) {
debug("sfc reset timeout\n");
ret = -ETIMEDOUT;
break;
}
udelay(1);
- } while (rcvr);
- writel(0xFFFFFFFF, ®s->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(®s->fsr);
- u32 sr = readl(®s->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, ®s->ctrl);
- writel(sfc->cmd, ®s->cmd);
- if (sfc->cmd & SFC_ADDR_XBITS)
writel(sfc->addr, ®s->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, ®s->iclr);
- writel((u32)buffer, ®s->dmaaddr);
- writel(SFC_DMA_START, ®s->dmatr);
- tbase = get_timer(0);
- do {
udelay(1);
risr = readl(®s->risr);
if (get_timer(tbase) > timeout) {
debug("dma timeout\n");
ret = -ETIMEDOUT;
break;
}
- } while (!(risr & TRANS_FINISH_INT));
- writel(0xFFFFFFFF, ®s->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(®s->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(®s->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, ®s->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(®s->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(®s->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, zhao0116@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