Created
May 14, 2021 02:42
-
-
Save macromorgan/868d748be8e2d779b56646458dc99cd1 to your computer and use it in GitHub Desktop.
Patch to add Rockchip SFC to Mainline U-Boot for OGA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From a9c0bbfa6ee0c759805cfe47301394cbe1456c41 Mon Sep 17 00:00:00 2001 | |
From: Chris Morgan <[email protected]> | |
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 <[email protected]> | |
--- | |
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, <[email protected]>. | |
+ * | |
+ * 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(®s->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, ®s->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, ®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; | |
+} | |
+ | |
+/* 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(®s->sr); | |
+ fsr = readl(®s->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, ®s->abit); | |
+ | |
+ if (rockchip_sfc_get_version(sfc) >= SFC_VER_4) { | |
+ SFC_DBG("--- sfc.len_ext %x\n", trb); | |
+ writel(trb, ®s->len_ext); | |
+ } | |
+ | |
+ val = 0x02; | |
+ val |= (data_width << SFC_DATA_WIDTH_SHIFT); | |
+ | |
+ SFC_DBG("--- sfc.ctrl %x\n", val); | |
+ writel(val, ®s->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, ®s->cmd); | |
+ | |
+ if (sfc->addr_bits & SFC_ADDR_XBITS) { | |
+ SFC_DBG("--- sfc.addr %x\n", sfc->addr); | |
+ writel(sfc->addr, ®s->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, ®s->iclr); | |
+ writel((unsigned long)bb.bounce_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); | |
+ | |
+ 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(®s->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(®s->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, ®s->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(®s->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(®s->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