diff mbox

[U-Boot,v2,2/2] driver: spi: add rockchip sfc support

Message ID 1502156619-13127-1-git-send-email-andy.yan@rock-chips.com
State Deferred
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Andy Yan Aug. 8, 2017, 1:43 a.m. UTC
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

Comments

Andy Yan Aug. 23, 2017, 7:58 a.m. UTC | #1
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, &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, <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
Jagan Teki Aug. 24, 2017, 6:58 a.m. UTC | #2
On Tue, Aug 8, 2017 at 7:13 AM, Andy Yan <andy.yan@rock-chips.com> 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.

What I found from the code and related kconfig, this sounds like
spi-nor flash driver try to write driver under mtd with
UCLASS_SPI_FLASH.

thanks!
Andy Yan Aug. 24, 2017, 7:23 a.m. UTC | #3
Hi Jagan:


On 2017年08月24日 14:58, Jagan Teki wrote:
> On Tue, Aug 8, 2017 at 7:13 AM, Andy Yan <andy.yan@rock-chips.com> 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.
> What I found from the code and related kconfig, this sounds like
> spi-nor flash driver try to write driver under mtd with
> UCLASS_SPI_FLASH.

     You mean I should write a UCLASS_SPI_FLASH 
U_BOOT_DRIVER(rockchip_sfc) which implement a dm_spi_flash_ops?
       I see you updated the spi-nor framework on your spi tree these 
days, How about I write the sfc driver based on your spi-nor framework?
> thanks!
Jagan Teki Aug. 24, 2017, 8:15 a.m. UTC | #4
On Thu, Aug 24, 2017 at 12:53 PM, Andy Yan <andy.yan@rock-chips.com> wrote:
> Hi Jagan:
>
>
> On 2017年08月24日 14:58, Jagan Teki wrote:
>>
>> On Tue, Aug 8, 2017 at 7:13 AM, Andy Yan <andy.yan@rock-chips.com> 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.
>>
>> What I found from the code and related kconfig, this sounds like
>> spi-nor flash driver try to write driver under mtd with
>> UCLASS_SPI_FLASH.
>
>
>     You mean I should write a UCLASS_SPI_FLASH U_BOOT_DRIVER(rockchip_sfc)
> which implement a dm_spi_flash_ops?

Yes, see sf_dataflash for example.

>       I see you updated the spi-nor framework on your spi tree these days,
> How about I write the sfc driver based on your spi-nor framework?

May be you can do that - certainly we need to write spi-nor, It is WIP
looking for suitable design for handling spi-nor chips through MTD.

thanks!
diff mbox

Patch

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, &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, <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