[U-Boot] [PATCH v2 2/2] driver: spi: add rockchip sfc support
Andy Yan
andy.yan at rock-chips.com
Wed Aug 23 07:58:33 UTC 2017
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 at 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 at 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 at 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
More information about the U-Boot
mailing list