Skip to content

Instantly share code, notes, and snippets.

@macromorgan
Created May 14, 2021 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save macromorgan/868d748be8e2d779b56646458dc99cd1 to your computer and use it in GitHub Desktop.
Save macromorgan/868d748be8e2d779b56646458dc99cd1 to your computer and use it in GitHub Desktop.
Patch to add Rockchip SFC to Mainline U-Boot for OGA
From a9c0bbfa6ee0c759805cfe47301394cbe1456c41 Mon Sep 17 00:00:00 2001
From: Chris Morgan <macromorgan@hotmail.com>
Date: Thu, 13 May 2021 21:39:54 -0500
Subject: [PATCH] Add Rockchip SFC Driver and create devicetree bindings for
it.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
arch/arm/dts/px30.dtsi | 31 +
arch/arm/dts/rk3326-odroid-go2-u-boot.dtsi | 10 +-
arch/arm/dts/rk3326-odroid-go2.dts | 20 +
arch/arm/mach-rockchip/px30/px30.c | 64 ++
configs/odroid-go2-spi_defconfig | 118 ++++
drivers/mtd/spi/Kconfig | 6 +
drivers/mtd/spi/spi-nor-ids.c | 8 +
drivers/spi/Kconfig | 8 +
drivers/spi/Makefile | 1 +
drivers/spi/rockchip_sfc.c | 667 +++++++++++++++++++++
10 files changed, 932 insertions(+), 1 deletion(-)
create mode 100644 configs/odroid-go2-spi_defconfig
create mode 100644 drivers/spi/rockchip_sfc.c
diff --git a/arch/arm/dts/px30.dtsi b/arch/arm/dts/px30.dtsi
index b6c79e7ed3..96ec5a7b92 100644
--- a/arch/arm/dts/px30.dtsi
+++ b/arch/arm/dts/px30.dtsi
@@ -960,6 +960,17 @@
status = "disabled";
};
+ sfc: sfc@ff3a0000 {
+ compatible = "rockchip,sfc";
+ reg = <0x0 0xff3a0000 0x0 0x4000>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru SCLK_SFC>, <&cru HCLK_SFC>;
+ clock-names = "clk_sfc", "hclk_sfc";
+ pinctrl-names = "default";
+ pinctrl-0 = <&sfc_clk &sfc_cs &sfc_bus>;
+ status = "disabled";
+ };
+
gpu: gpu@ff400000 {
compatible = "rockchip,px30-mali", "arm,mali-bifrost";
reg = <0x0 0xff400000 0x0 0x4000>;
@@ -1926,6 +1937,26 @@
};
};
+ serial_flash {
+ sfc_bus: sfc-bus {
+ rockchip,pins =
+ <1 RK_PA0 3 &pcfg_pull_none>,
+ <1 RK_PA1 3 &pcfg_pull_none>,
+ <1 RK_PA2 3 &pcfg_pull_none>,
+ <1 RK_PA3 3 &pcfg_pull_none>;
+ };
+
+ sfc_cs: sfc-cs {
+ rockchip,pins =
+ <1 RK_PA4 3 &pcfg_pull_none>;
+ };
+
+ sfc_clk: sfc-clk {
+ rockchip,pins =
+ <1 RK_PB1 3 &pcfg_pull_none>;
+ };
+ };
+
lcdc {
lcdc_rgb_dclk_pin: lcdc-rgb-dclk-pin {
rockchip,pins =
diff --git a/arch/arm/dts/rk3326-odroid-go2-u-boot.dtsi b/arch/arm/dts/rk3326-odroid-go2-u-boot.dtsi
index 00767d2abd..b5686f8f4d 100644
--- a/arch/arm/dts/rk3326-odroid-go2-u-boot.dtsi
+++ b/arch/arm/dts/rk3326-odroid-go2-u-boot.dtsi
@@ -5,7 +5,7 @@
/ {
chosen {
- u-boot,spl-boot-order = &sdmmc;
+ u-boot,spl-boot-order = &spi_flash, &sdmmc;
};
};
@@ -57,6 +57,14 @@
u-boot,spl-fifo-mode;
};
+&sfc {
+ u-boot,dm-pre-reloc;
+};
+
+&spi_flash {
+ u-boot,dm-pre-reloc;
+};
+
&uart1 {
clock-frequency = <24000000>;
u-boot,dm-pre-reloc;
diff --git a/arch/arm/dts/rk3326-odroid-go2.dts b/arch/arm/dts/rk3326-odroid-go2.dts
index 8cd4688c49..2e5752ef0b 100644
--- a/arch/arm/dts/rk3326-odroid-go2.dts
+++ b/arch/arm/dts/rk3326-odroid-go2.dts
@@ -48,6 +48,14 @@
};
};
+ aliases {
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+ serial1 = &uart1;
+ serial2 = &uart2;
+ spi0 = &sfc;
+ };
+
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
@@ -617,6 +625,18 @@
status = "okay";
};
+&sfc {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ spi_flash: xt25f128b@0 {
+ reg = <0>;
+ compatible = "xtx,xt25f128b","jedec,spi-nor";
+ spi-max-frequency = <108000000>;
+ };
+};
+
&tsadc {
status = "okay";
};
diff --git a/arch/arm/mach-rockchip/px30/px30.c b/arch/arm/mach-rockchip/px30/px30.c
index 6fcef63c1b..8674e815da 100644
--- a/arch/arm/mach-rockchip/px30/px30.c
+++ b/arch/arm/mach-rockchip/px30/px30.c
@@ -51,6 +51,57 @@ struct mm_region *mem_map = px30_mem_map;
#define QOS_PRIORITY_LEVEL(h, l) ((((h) & 3) << 8) | ((l) & 3))
+/* GRF_GPIO1AL_IOMUX */
+enum {
+ GPIO1A3_SHIFT = 12,
+ GPIO1A3_MASK = 0xf << GPIO1A3_SHIFT,
+ GPIO1A3_GPIO = 0,
+ GPIO1A3_FLASH_D3,
+ GPIO1A3_EMMC_D3,
+ GPIO1A3_SFC_SIO3,
+
+ GPIO1A2_SHIFT = 8,
+ GPIO1A2_MASK = 0xf << GPIO1A2_SHIFT,
+ GPIO1A2_GPIO = 0,
+ GPIO1A2_FLASH_D2,
+ GPIO1A2_EMMC_D2,
+ GPIO1A2_SFC_SIO2,
+
+ GPIO1A1_SHIFT = 4,
+ GPIO1A1_MASK = 0xf << GPIO1A1_SHIFT,
+ GPIO1A1_GPIO = 0,
+ GPIO1A1_FLASH_D1,
+ GPIO1A1_EMMC_D1,
+ GPIO1A1_SFC_SIO1,
+
+ GPIO1A0_SHIFT = 0,
+ GPIO1A0_MASK = 0xf << GPIO1A0_SHIFT,
+ GPIO1A0_GPIO = 0,
+ GPIO1A0_FLASH_D0,
+ GPIO1A0_EMMC_D0,
+ GPIO1A0_SFC_SIO0,
+};
+
+/* GRF_GPIO1AH_IOMUX */
+enum {
+ GPIO1A4_SHIFT = 0,
+ GPIO1A4_MASK = 0xf << GPIO1A4_SHIFT,
+ GPIO1A4_GPIO = 0,
+ GPIO1A4_FLASH_D4,
+ GPIO1A4_EMMC_D4,
+ GPIO1A4_SFC_CSN0,
+};
+
+/* GRF_GPIO1BL_IOMUX */
+enum {
+ GPIO1B1_SHIFT = 4,
+ GPIO1B1_MASK = 0xf << GPIO1B1_SHIFT,
+ GPIO1B1_GPIO = 0,
+ GPIO1B1_FLASH_RDY,
+ GPIO1B1_EMMC_CLKOUT,
+ GPIO1B1_SFC_CLK,
+};
+
/* GRF_GPIO1BH_IOMUX */
enum {
GPIO1B7_SHIFT = 12,
@@ -193,6 +244,19 @@ int arch_cpu_init(void)
GPIO1D4_SDMMC_D2 << GPIO1D4_SHIFT);
#endif
+#ifdef CONFIG_ROCKCHIP_SFC
+ rk_clrsetreg(&grf->gpio1al_iomux,
+ GPIO1A3_MASK | GPIO1A2_MASK | GPIO1A1_MASK | GPIO1A0_MASK,
+ GPIO1A3_SFC_SIO3 << GPIO1A3_SHIFT |
+ GPIO1A2_SFC_SIO2 << GPIO1A2_SHIFT |
+ GPIO1A1_SFC_SIO1 << GPIO1A1_SHIFT |
+ GPIO1A0_SFC_SIO0 << GPIO1A0_SHIFT);
+ rk_clrsetreg(&grf->gpio1ah_iomux, GPIO1A4_MASK,
+ GPIO1A4_SFC_CSN0 << GPIO1A4_SHIFT);
+ rk_clrsetreg(&grf->gpio1bl_iomux, GPIO1B1_MASK,
+ GPIO1B1_SFC_CLK << GPIO1B1_SHIFT);
+#endif
+
#endif
/* Enable PD_VO (default disable at reset) */
diff --git a/configs/odroid-go2-spi_defconfig b/configs/odroid-go2-spi_defconfig
new file mode 100644
index 0000000000..f6e42930d0
--- /dev/null
+++ b/configs/odroid-go2-spi_defconfig
@@ -0,0 +1,118 @@
+CONFIG_ARM=y
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_SYS_TEXT_BASE=0x00200000
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x4000
+CONFIG_ENV_OFFSET=0x4000
+CONFIG_SPL_TEXT_BASE=0x00000000
+CONFIG_ROCKCHIP_PX30=y
+CONFIG_TARGET_ODROID_GO2=y
+CONFIG_DEBUG_UART_CHANNEL=1
+CONFIG_TPL_LIBGENERIC_SUPPORT=y
+CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
+CONFIG_SPL_STACK_R_ADDR=0x600000
+CONFIG_DEBUG_UART_BASE=0xFF160000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_DEFAULT_DEVICE_TREE="rk3326-odroid-go2"
+CONFIG_DEBUG_UART=y
+CONFIG_TPL_SYS_MALLOC_F_LEN=0x600
+# CONFIG_ANDROID_BOOT_IMAGE is not set
+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_FIT_BEST_MATCH=y
+CONFIG_SPL_LOAD_FIT=y
+CONFIG_DEFAULT_FDT_FILE="rockchip/rk3326-odroid-go2.dtb"
+# CONFIG_CONSOLE_MUX is not set
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_MISC_INIT_R=y
+CONFIG_SPL_BOOTROM_SUPPORT=y
+# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set
+CONFIG_SPL_STACK_R=y
+# CONFIG_TPL_BANNER_PRINT is not set
+CONFIG_SPL_CRC32_SUPPORT=y
+CONFIG_SPL_I2C_SUPPORT=y
+CONFIG_SPL_SPI_SUPPORT=y
+CONFIG_SPL_SPI_FLASH_SUPPORT=y
+CONFIG_SPL_SPI_FLASH_TINY=y
+CONFIG_SPL_SPI_LOAD=y
+CONFIG_SYS_SPI_U_BOOT_OFFS=0x200000
+CONFIG_SPL_POWER_SUPPORT=y
+CONFIG_SPL_ATF=y
+# CONFIG_TPL_FRAMEWORK is not set
+# CONFIG_CMD_BOOTD is not set
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_IMI is not set
+# CONFIG_CMD_XIMG is not set
+# CONFIG_CMD_LZMADEC is not set
+# CONFIG_CMD_UNZIP is not set
+CONFIG_CMD_GPT=y
+# CONFIG_CMD_LOADB is not set
+# CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_USB=y
+CONFIG_CMD_USB_MASS_STORAGE=y
+# CONFIG_CMD_ITEST is not set
+# CONFIG_CMD_SLEEP is not set
+# CONFIG_SPL_DOS_PARTITION is not set
+# CONFIG_ISO_PARTITION is not set
+CONFIG_EFI_PARTITION_ENTRIES_NUMBERS=64
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_OF_LIVE=y
+CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_REGMAP=y
+CONFIG_SPL_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_SPL_SYSCON=y
+CONFIG_CLK=y
+CONFIG_SPL_CLK=y
+CONFIG_FASTBOOT_BUF_ADDR=0x800800
+CONFIG_FASTBOOT_BUF_SIZE=0x04000000
+CONFIG_ROCKCHIP_GPIO=y
+CONFIG_SYS_I2C_ROCKCHIP=y
+CONFIG_SPI_FLASH_XTX=y
+CONFIG_MISC=y
+CONFIG_ROCKCHIP_OTP=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_PHY_REALTEK=y
+CONFIG_DM_ETH=y
+CONFIG_ETH_DESIGNWARE=y
+CONFIG_GMAC_ROCKCHIP=y
+CONFIG_PINCTRL=y
+CONFIG_SPL_PINCTRL=y
+CONFIG_DM_PMIC=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_REGULATOR_PWM=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_REGULATOR_RK8XX=y
+CONFIG_PWM_ROCKCHIP=y
+CONFIG_RAM=y
+CONFIG_SPL_RAM=y
+CONFIG_TPL_RAM=y
+CONFIG_ROCKCHIP_SDRAM_COMMON=y
+CONFIG_DM_RESET=y
+# CONFIG_SPECIFY_CONSOLE_INDEX is not set
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_DEBUG_UART_SKIP_INIT=y
+CONFIG_SOUND=y
+CONFIG_SYSRESET=y
+CONFIG_ROCKCHIP_SFC=y
+CONFIG_OPTEE=y
+CONFIG_DM_THERMAL=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_GENERIC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DWC2_OTG=y
+CONFIG_DM_VIDEO=y
+CONFIG_DISPLAY=y
+CONFIG_LCD=y
+CONFIG_SPL_TINY_MEMSET=y
+CONFIG_TPL_TINY_MEMSET=y
+CONFIG_LZO=y
+CONFIG_ERRNO_STR=y
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index f8db8e5213..8c797d1e03 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -162,6 +162,12 @@ config SPI_FLASH_XMC
Add support for various XMC (Wuhan Xinxin Semiconductor
Manufacturing Corp.) SPI flash chips (XM25xxx)
+config SPI_FLASH_XTX
+ bool "XTX SPI flash support"
+ help
+ Add support for various XTX (XTX Technology Limited)
+ SPI flash chips (XT25xxx).
+
endif
config SPI_FLASH_USE_4K_SECTORS
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 2b57797954..78166f408e 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -336,6 +336,14 @@ const struct flash_info spi_nor_ids[] = {
/* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
{ INFO("XM25QH64A", 0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("XM25QH128A", 0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+#endif
+#ifdef CONFIG_SPI_FLASH_XTX
+ /* XTX Technology (Shenzhen) Limited */
+ {
+ INFO("xt25f128b", 0x0b4018, 0, 64 * 1024, 256,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
#endif
{ },
};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 1494c91763..bef36f2931 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -312,6 +312,14 @@ config RENESAS_RPC_SPI
on Renesas RCar Gen3 SoCs. 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 for SPI NOR flash. This device is
+ a limited purpose SPI controller for driving NOR flash on certain
+ Rockchip SoCs. This uses driver model and requires a device tree
+ binding to operate.
+
config ROCKCHIP_SPI
bool "Rockchip SPI driver"
help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index cfe4fae1d4..f02e84b5f1 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
obj-$(CONFIG_PL022_SPI) += pl022_spi.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
+obj-$(CONFIG_ROCKCHIP_SFC) += rockchip_sfc.o
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
diff --git a/drivers/spi/rockchip_sfc.c b/drivers/spi/rockchip_sfc.c
new file mode 100644
index 0000000000..d8f14fee6b
--- /dev/null
+++ b/drivers/spi/rockchip_sfc.c
@@ -0,0 +1,667 @@
+/*
+ * SFC driver for rockchip
+ *
+ * (C) Copyright 2017 Rockchip Electronics Co., Ltd
+ * Yifeng.zhao, Software Engineering, <zhao0116@gmail.com>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <bouncebuf.h>
+#include <clk.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <spi.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef SFC_DEBUG
+#define SFC_DBG printf
+#else
+#define SFC_DBG(args...)
+#endif
+
+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 ver;
+ u32 reserved[20];
+ u32 dmatr;
+ u32 dmaaddr;
+ u32 len_ctrl;
+ u32 len_ext;
+ u32 reserved1[28];
+ 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_SHIFT 8
+#define SFC_CMD_WIDTH_MASK GENMASK(9, 8)
+#define SFC_DATA_SHIFT_NEGETIVE BIT(1)
+
+/*SFC_CMD*/
+#define SFC_DUMMY_BITS_SHIFT 8
+#define SFC_RW_SHIFT 12
+#define SFC_WR 1
+#define SFC_RD 0
+#define SFC_ADDR_BITS_SHIFT 14
+#define SFC_ADDR_BITS_MASK GENMASK(15, 14)
+#define SFC_ADDR_0BITS 0
+#define SFC_ADDR_24BITS 1
+#define SFC_ADDR_32BITS 2
+#define SFC_ADDR_XBITS 3
+#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_VER3 (512 * 31)
+#define SFC_MAX_TRB_VER3 (1024 << 3)
+#define SFC_MAX_TRB_VER4 (0xFFFFFFFF)
+
+#define SFC_MAX_RATE (100 * 1000 * 1000)
+#define SFC_DEFAULT_RATE (80 * 1000 * 1000)
+#define SFC_MIN_RATE (10 * 1000 * 1000)
+
+#define SFC_VER_3 0x3
+#define SFC_VER_4 0x4
+
+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 max_iosize;
+ u32 cmd;
+ u32 addr;
+ u8 addr_bits;
+ u8 addr_xbits_ext;
+ u8 dummy_bits;
+ u8 rw;
+ u32 trb;
+ bool fifo_mode;
+};
+
+static int rockchip_sfc_ofdata_to_platdata(struct udevice *bus)
+{
+ struct rockchip_sfc_platdata *plat = dev_get_plat(bus);
+ struct rockchip_sfc *sfc = dev_get_priv(bus);
+ int ret;
+
+ plat->base = dev_read_addr(bus);
+ ret = clk_get_by_index(bus, 0, &sfc->clk);
+ if (ret < 0) {
+ printf("Could not get clock for %s: %d\n", bus->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+u32 rockchip_sfc_get_version(struct rockchip_sfc *sfc)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+
+ return (u32)(readl(&regs->ver) & 0xFFFF);
+}
+
+static int rockchip_sfc_probe(struct udevice *bus)
+{
+ struct rockchip_sfc_platdata *plat = dev_get_plat(bus);
+ struct rockchip_sfc *sfc = dev_get_priv(bus);
+ struct rockchip_sfc_reg *regs;
+ struct dm_spi_bus *dm_spi_bus;
+
+ dm_spi_bus = bus->uclass_priv_;
+ dm_spi_bus->max_hz = plat->frequency;
+ sfc->regbase = (struct rockchip_sfc_reg *)plat->base;
+ sfc->max_freq = SFC_MAX_RATE;
+ sfc->speed_hz = SFC_DEFAULT_RATE;
+ clk_set_rate(&sfc->clk, sfc->speed_hz);
+
+ regs = sfc->regbase;
+ if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) {
+ sfc->max_iosize = SFC_MAX_TRB_VER4;
+ writel(1, &regs->len_ctrl);
+ } else {
+ sfc->max_iosize = SFC_MAX_TRB_VER3;
+ }
+
+/* DMA causes A-TF to fail, disabling it in SPL stage */
+#ifdef CONFIG_SPL_BUILD
+ sfc->fifo_mode = 1;
+#endif
+ 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;
+}
+
+/* The SFC_CTRL register is a global control register,
+ * when the controller is in busy state(SFC_SR),
+ * SFC_CTRL cannot be set.
+ */
+static int rockchip_sfc_wait_idle(struct rockchip_sfc *sfc,
+ u32 timeout_ms)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+ unsigned long tbase = get_timer(0);
+ u32 sr, fsr;
+
+ while (1) {
+ sr = readl(&regs->sr);
+ fsr = readl(&regs->fsr);
+ if ((fsr & SFC_TX_EMPTY) &&
+ (fsr & SFC_RX_EMPTY) &&
+ !(sr & SFC_BUSY))
+ break;
+ if (get_timer(tbase) > timeout_ms) {
+ printf("waite sfc idle timeout(sr:0x%08x fsr:0x%08x)\n",
+ sr, fsr);
+ rockchip_sfc_reset(sfc);
+ return -ETIMEDOUT;
+ }
+ udelay(100);
+ }
+
+ return 0;
+}
+
+static u8 rockchip_sfc_get_if_type(struct rockchip_sfc *sfc)
+{
+ int type = IF_TYPE_STD;
+
+ if (sfc->rw == 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, u32 trb)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+ u32 val;
+ u8 data_width = IF_TYPE_STD;
+
+ rockchip_sfc_wait_idle(sfc, 10);
+
+ if (sfc->addr_bits == SFC_ADDR_24BITS ||
+ sfc->addr_bits == SFC_ADDR_32BITS)
+ data_width = rockchip_sfc_get_if_type(sfc);
+
+ SFC_DBG("--- sfc.addr_bit %x\n", sfc->addr_bits);
+ if (sfc->addr_bits == SFC_ADDR_XBITS)
+ writel(sfc->addr_xbits_ext - 1, &regs->abit);
+
+ if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) {
+ SFC_DBG("--- sfc.len_ext %x\n", trb);
+ writel(trb, &regs->len_ext);
+ }
+
+ val = 0x02;
+ val |= (data_width << SFC_DATA_WIDTH_SHIFT);
+
+ SFC_DBG("--- sfc.ctrl %x\n", val);
+ writel(val, &regs->ctrl);
+
+ val = sfc->cmd;
+ val |= (trb & 0x3fff) << SFC_TRB_SHIFT;
+ val |= sfc->rw << SFC_RW_SHIFT;
+ val |= sfc->addr_bits << SFC_ADDR_BITS_SHIFT;
+ val |= sfc->dummy_bits << SFC_DUMMY_BITS_SHIFT;
+
+ SFC_DBG("--- sfc.cmd %x\n", val);
+ writel(val, &regs->cmd);
+
+ if (sfc->addr_bits & SFC_ADDR_XBITS) {
+ SFC_DBG("--- sfc.addr %x\n", sfc->addr);
+ writel(sfc->addr, &regs->addr);
+ }
+}
+
+static int rockchip_sfc_dma_xfer(struct rockchip_sfc *sfc, void *buffer,
+ size_t trb)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+ struct bounce_buffer bb;
+ unsigned int bb_flags;
+ int timeout = trb * 1000;
+ int ret = 0;
+ int risr;
+ unsigned long tbase;
+
+ if (sfc->rw == SFC_WR)
+ bb_flags = GEN_BB_READ;
+ else
+ bb_flags = GEN_BB_WRITE;
+
+ ret = bounce_buffer_start(&bb, buffer, trb, bb_flags);
+ if (ret)
+ return ret;
+
+ rockchip_sfc_setup_xfer(sfc, bb.len_aligned);
+
+ writel(0xFFFFFFFF, &regs->iclr);
+ writel((unsigned long)bb.bounce_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);
+
+ bounce_buffer_stop(&bb);
+
+ return ret;
+}
+
+static int rockchip_sfc_wait_fifo_ready(struct rockchip_sfc *sfc, int rw,
+ 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 (rw == SFC_WR)
+ level = (fsr & SFC_TXLV_MASK) >> SFC_TXLV_SHIFT;
+ else
+ level = (fsr & SFC_RXLV_MASK) >> SFC_RXLV_SHIFT;
+ if (get_timer(tbase) > timeout) {
+ printf("SFC FIFO Timeout\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ } while (!level);
+
+ return level;
+}
+
+static int rockchip_sfc_write_fifo(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+ u32 bytes = len & 0x3;
+ u32 words = len >> 2;
+ int tx_level = 0;
+ u32 val = 0;
+ u8 count;
+
+ while (words) {
+ tx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_WR, 1000);
+ if (tx_level <= 0)
+ return tx_level;
+ count = min(words, (u32)tx_level);
+ writesl(&regs->data, buf, count);
+ buf += count;
+ words -= count;
+ }
+
+ /* handle the last non 4byte aligned bytes */
+ if (bytes) {
+ tx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_WR, 1000);
+ if (tx_level <= 0)
+ return tx_level;
+ memcpy(&val, buf, bytes);
+ writel(val, &regs->data);
+ }
+
+ return 0;
+}
+
+static int rockchip_sfc_read_fifo(struct rockchip_sfc *sfc, u32 *buf, u32 len)
+{
+ struct rockchip_sfc_reg *regs = sfc->regbase;
+ u32 bytes = len & 0x3;
+ u32 words = len >> 2;
+ int rx_level = 0;
+ u32 count;
+ u32 val;
+
+ while (words) {
+ rx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_RD, 1000);
+ if (rx_level <= 0)
+ return rx_level;
+ count = min(words, (u32)rx_level);
+ readsl(&regs->data, buf, count);
+ buf += count;
+ words -= count;
+ }
+
+ /* handle the last non 4 bytes aligned bytes */
+ if (bytes) {
+ rx_level = rockchip_sfc_wait_fifo_ready(sfc, SFC_RD, 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, void *buf, u32 len)
+{
+ int ret = 0;
+
+ rockchip_sfc_setup_xfer(sfc, len);
+
+ if (len) {
+ if (sfc->rw == SFC_WR)
+ ret = rockchip_sfc_write_fifo(sfc, (u32 *)buf, len);
+ else
+ ret = rockchip_sfc_read_fifo(sfc, (u32 *)buf, len);
+ }
+
+ return ret;
+}
+
+static int rockchip_sfc_read(struct rockchip_sfc *sfc, u32 offset,
+ void *buf, size_t len)
+{
+ u32 dma_trans;
+ u32 pio_trans;
+ u32 trb;
+ u32 bytes;
+ int ret;
+
+ if (sfc->fifo_mode) {
+ pio_trans = len;
+ while (pio_trans) {
+ trb = min_t(size_t, pio_trans, sfc->max_iosize);
+ ret = rockchip_sfc_pio_xfer(sfc, buf, trb);
+ if (ret < 0)
+ return ret;
+ pio_trans -= trb;
+ sfc->addr += (trb << 8);
+ buf += trb;
+ }
+
+ } else {
+ if (len >= ARCH_DMA_MINALIGN) {
+ bytes = len & (ARCH_DMA_MINALIGN - 1);
+ dma_trans = len - bytes;
+ } else {
+ dma_trans = 0;
+ bytes = len;
+ }
+
+ while (dma_trans) {
+ trb = min_t(size_t, dma_trans, sfc->max_iosize);
+ ret = rockchip_sfc_dma_xfer(sfc, buf, trb);
+ if (ret < 0)
+ return ret;
+ dma_trans -= trb;
+ sfc->addr += (trb << 8);
+ buf += trb;
+ }
+
+ /*
+ * transfer the last non dma aligned byte by pio mode
+ */
+ if (bytes)
+ ret = rockchip_sfc_pio_xfer(sfc, buf, bytes);
+ }
+
+ return 0;
+}
+
+static int rockchip_sfc_write(struct rockchip_sfc *sfc, u32 offset,
+ void *buf, size_t len)
+{
+ if (len > sfc->max_iosize) {
+ printf("out of the max sfc trb");
+ return -EINVAL;
+ }
+
+ if (len && !(len & (ARCH_DMA_MINALIGN - 1)) && !sfc->fifo_mode)
+ return rockchip_sfc_dma_xfer(sfc, buf, len);
+ else
+ return rockchip_sfc_pio_xfer(sfc, buf, len);
+
+ return 0;
+}
+
+static int rockchip_sfc_do_xfer(struct rockchip_sfc *sfc, void *buf, size_t len)
+{
+ if (sfc->rw)
+ return rockchip_sfc_write(sfc, sfc->addr, buf, len);
+ else
+ return rockchip_sfc_read(sfc, sfc->addr, buf, len);
+}
+
+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;
+ void *data_buf;
+ int ret = 0;
+
+ if (flags & SPI_XFER_BEGIN) {
+ sfc->cmd = pcmd[0];
+ switch (len) {
+ case 6: /* Nor >16MB 0x6b dummy op */
+ sfc->addr_bits = SFC_ADDR_32BITS;
+ sfc->dummy_bits = 8;
+ sfc->addr = pcmd[4] | (pcmd[3] << 8) | (pcmd[2] << 16) | (pcmd[1] << 24);
+ break;
+ case 5: /* Nor <=16MB 0x6b dummy op, Nor >16MB no dummy op */
+ if (sfc->cmd == 0x6b) {
+ sfc->addr_bits = SFC_ADDR_24BITS;
+ sfc->dummy_bits = 8;
+ sfc->addr = pcmd[3] | (pcmd[2] << 8) | (pcmd[1] << 16);
+ } else {
+ sfc->addr_bits = SFC_ADDR_32BITS;
+ sfc->dummy_bits = 0;
+ sfc->addr = pcmd[4] | (pcmd[3] << 8) | (pcmd[2] << 16) | (pcmd[1] << 24);
+ }
+ break;
+ case 4: /* Nand erase and read, Nor <=16MB no dummy op */
+ sfc->addr_bits = SFC_ADDR_24BITS;
+ sfc->dummy_bits = 0;
+ sfc->addr = pcmd[3] | (pcmd[2] << 8) | (pcmd[1] << 16);
+ break;
+ case 3: /* Nand prog, */
+ sfc->addr_bits = SFC_ADDR_XBITS;
+ sfc->addr_xbits_ext = 16;
+ sfc->dummy_bits = 0;
+ sfc->addr = pcmd[2] | pcmd[1] << 8;
+ break;
+ case 2: /* Nand read/write feature */
+ sfc->addr_bits = SFC_ADDR_XBITS;
+ sfc->addr_xbits_ext = 8;
+ sfc->dummy_bits = 0;
+ sfc->addr = pcmd[1];
+ break;
+ default: /* Nand/Nor Read/Write status */
+ sfc->addr_bits = SFC_ADDR_0BITS;
+ sfc->dummy_bits = 0;
+ sfc->addr = 0;
+ break;
+ }
+ SFC_DBG("%s %d %x %d %d %x\n", __func__, len, sfc->cmd,
+ sfc->addr_bits, sfc->dummy_bits, sfc->addr);
+ }
+ if (flags & SPI_XFER_END) {
+ if (din) {
+ sfc->rw = SFC_RD;
+ data_buf = din;
+ } else {
+ sfc->rw = SFC_WR;
+ data_buf = (void *)dout;
+ }
+
+ if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) {
+ len = 0;
+ data_buf = NULL;
+ }
+
+ if (sfc->cmd == 0x9f && len == 4) {
+ /* SPI Nand read id */
+ sfc->addr_bits = SFC_ADDR_XBITS;
+ sfc->addr_xbits_ext = 8;
+ sfc->dummy_bits = 0;
+ sfc->addr = 0;
+ ((u8 *)data_buf)[0] = 0xff;
+ ret = rockchip_sfc_do_xfer(sfc, &((u8 *)data_buf)[1], 3);
+ } else {
+ ret = rockchip_sfc_do_xfer(sfc, data_buf, 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;
+ clk_set_rate(&sfc->clk, sfc->speed_hz);
+ SFC_DBG("%s clk= %ld\n", __func__, clk_get_rate(&sfc->clk));
+
+ 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,
+ .of_to_plat = rockchip_sfc_ofdata_to_platdata,
+ .plat_auto = sizeof(struct rockchip_sfc_platdata),
+ .priv_auto = sizeof(struct rockchip_sfc),
+ .probe = rockchip_sfc_probe,
+};
--
2.25.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment