[PATCH 2/7] pistachio: Copy kernel 4.14 to 5.4

Hauke Mehrtens hauke at hauke-m.de
Sat Aug 15 14:06:51 EDT 2020


This only copies the patches and configuration without doing any
modification to the files.

Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
 target/linux/pistachio/config-5.4             | 354 +++++++++
 ...ine-img-mdc-Handle-early-status-read.patch |  68 ++
 ...mg-spfi-Implement-dual-and-quad-mode.patch | 198 +++++
 ...-device-select-bits-for-SPFI-port-st.patch |  27 +
 ...-device-0-configuration-for-all-devi.patch |  64 ++
 ...i-RX-maximum-burst-size-for-DMA-is-8.patch |  59 ++
 ...g-spfi-finish-every-transfer-cleanly.patch | 120 +++
 ...y-programming-min-delta-up-to-10-tim.patch |  66 ++
 ...istachio-Fix-wrong-SDHost-card-speed.patch |  49 ++
 ...-img-marduk-switch-mmc-to-1-bit-mode.patch |  47 ++
 ...or-support-mtd-name-from-device-tree.patch |  34 +
 ...ength-of-ID-before-reading-bits-per-.patch |  33 +
 ...JEDEC-manufacturer-ID-for-Gigadevice.patch |  35 +
 ...413-mtd-Introduce-SPI-NAND-framework.patch | 707 ++++++++++++++++++
 ...mtd-spi-nand-Support-Gigadevice-GD5F.patch | 524 +++++++++++++
 ...TS-img-marduk-add-nor-partition-name.patch |  20 +
 ...S-img-marduk-add-nand-device-support.patch |  30 +
 17 files changed, 2435 insertions(+)
 create mode 100644 target/linux/pistachio/config-5.4
 create mode 100644 target/linux/pistachio/patches-5.4/101-dmaengine-img-mdc-Handle-early-status-read.patch
 create mode 100644 target/linux/pistachio/patches-5.4/102-spi-img-spfi-Implement-dual-and-quad-mode.patch
 create mode 100644 target/linux/pistachio/patches-5.4/103-spi-img-spfi-set-device-select-bits-for-SPFI-port-st.patch
 create mode 100644 target/linux/pistachio/patches-5.4/104-spi-img-spfi-use-device-0-configuration-for-all-devi.patch
 create mode 100644 target/linux/pistachio/patches-5.4/105-spi-img-spfi-RX-maximum-burst-size-for-DMA-is-8.patch
 create mode 100644 target/linux/pistachio/patches-5.4/106-spi-img-spfi-finish-every-transfer-cleanly.patch
 create mode 100644 target/linux/pistachio/patches-5.4/107-clockevents-Retry-programming-min-delta-up-to-10-tim.patch
 create mode 100644 target/linux/pistachio/patches-5.4/108-clk-pistachio-Fix-wrong-SDHost-card-speed.patch
 create mode 100644 target/linux/pistachio/patches-5.4/109-MIPS-DTS-img-marduk-switch-mmc-to-1-bit-mode.patch
 create mode 100644 target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch
 create mode 100644 target/linux/pistachio/patches-5.4/411-mtd-nand-Check-length-of-ID-before-reading-bits-per-.patch
 create mode 100644 target/linux/pistachio/patches-5.4/412-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch
 create mode 100644 target/linux/pistachio/patches-5.4/413-mtd-Introduce-SPI-NAND-framework.patch
 create mode 100644 target/linux/pistachio/patches-5.4/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch
 create mode 100644 target/linux/pistachio/patches-5.4/901-MIPS-DTS-img-marduk-add-nor-partition-name.patch
 create mode 100644 target/linux/pistachio/patches-5.4/902-MIPS-DTS-img-marduk-add-nand-device-support.patch

diff --git a/target/linux/pistachio/config-5.4 b/target/linux/pistachio/config-5.4
new file mode 100644
index 000000000000..8b24b68a051f
--- /dev/null
+++ b/target/linux/pistachio/config-5.4
@@ -0,0 +1,354 @@
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+# CONFIG_ARCH_HAS_GCOV_PROFILE_ALL is not set
+# CONFIG_ARCH_HAS_SG_CHAIN is not set
+# CONFIG_ARCH_HAS_STRICT_KERNEL_RWX is not set
+# CONFIG_ARCH_HAS_STRICT_MODULE_RWX is not set
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
+CONFIG_ARCH_MMAP_RND_BITS_MAX=15
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
+# CONFIG_ARCH_OPTIONAL_KERNEL_RWX is not set
+# CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
+CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_SCSI_REQUEST=y
+CONFIG_BOARD_SCACHE=y
+CONFIG_BOOT_ELF32=y
+CONFIG_CEVT_R4K=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_MIPS_GIC=y
+CONFIG_CLKSRC_PISTACHIO=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+# CONFIG_COMMON_CLK_BOSTON is not set
+CONFIG_CONNECTOR=y
+CONFIG_CPU_GENERIC_DUMP_TLB=y
+CONFIG_CPU_HAS_PREFETCH=y
+CONFIG_CPU_HAS_RIXI=y
+CONFIG_CPU_HAS_SYNC=y
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_CPU_MIPS32=y
+CONFIG_CPU_MIPS32_R2=y
+CONFIG_CPU_MIPSR2=y
+CONFIG_CPU_MIPSR2_IRQ_EI=y
+CONFIG_CPU_MIPSR2_IRQ_VI=y
+CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_R4K_CACHE_TLB=y
+CONFIG_CPU_R4K_FPU=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
+CONFIG_CPU_SUPPORTS_HIGHMEM=y
+CONFIG_CPU_SUPPORTS_MSA=y
+CONFIG_CRC16=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRYPTO_ACOMP2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_CSRC_R4K=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_NONCOHERENT=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DTC=y
+# CONFIG_DWMAC_DWC_QOS_ETH is not set
+CONFIG_DWMAC_GENERIC=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_EARLY_PRINTK_8250=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_FIXED_PHY=y
+CONFIG_FS_MBCACHE=y
+CONFIG_FS_POSIX_ACL=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_CPU_AUTOPROBE=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
+CONFIG_GENERIC_IRQ_IPI=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_PINCTRL_GROUPS=y
+CONFIG_GENERIC_PINMUX_FUNCTIONS=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+# CONFIG_GRO_CELLS is not set
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDWARE_WATCHPOINTS=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+# CONFIG_HAVE_ARCH_BITREVERSE is not set
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_CBPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_COPY_THREAD_TLS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_KVM=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_IMG=y
+CONFIG_IMGPDC_WDT=y
+CONFIG_IMG_MDC_DMA=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_MIPS_CPU=y
+CONFIG_IRQ_WORK=y
+CONFIG_JBD2=y
+CONFIG_LEDS_PWM=y
+CONFIG_LIBFDT=y
+CONFIG_LKDTM=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MACH_PISTACHIO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MICREL_PHY=y
+CONFIG_MIPS=y
+CONFIG_MIPS_ASID_BITS=8
+CONFIG_MIPS_ASID_SHIFT=0
+CONFIG_MIPS_CLOCK_VSYSCALL=y
+CONFIG_MIPS_CM=y
+# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set
+CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER=y
+# CONFIG_MIPS_CMDLINE_FROM_DTB is not set
+CONFIG_MIPS_CPC=y
+CONFIG_MIPS_CPS=y
+# CONFIG_MIPS_CPS_CPUIDLE is not set
+# CONFIG_MIPS_CPS_NS16550 is not set
+CONFIG_MIPS_CPS_PM=y
+CONFIG_MIPS_CPU_SCACHE=y
+# CONFIG_MIPS_ELF_APPENDED_DTB is not set
+CONFIG_MIPS_EXTERNAL_TIMER=y
+CONFIG_MIPS_GIC=y
+# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+# CONFIG_MIPS_MACHINE is not set
+CONFIG_MIPS_MT=y
+CONFIG_MIPS_MT_FPAFF=y
+CONFIG_MIPS_MT_SMP=y
+CONFIG_MIPS_NO_APPENDED_DTB=y
+CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y
+# CONFIG_MIPS_RAW_APPENDED_DTB is not set
+CONFIG_MIPS_SPRAM=y
+# CONFIG_MIPS_VPE_LOADER is not set
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_DW=y
+# CONFIG_MMC_DW_EXYNOS is not set
+# CONFIG_MMC_DW_K3 is not set
+CONFIG_MMC_DW_PLTFM=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_MTD_SPI_NAND_DEVICES=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS_LIMIT=16384
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_MTD_UBI_FASTMAP=y
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_NAMESPACES=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_NS=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NLS=y
+CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
+CONFIG_NO_HZ=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+# CONFIG_NO_IOPORT_MAP is not set
+CONFIG_NR_CPUS=4
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_NET=y
+CONFIG_PADATA=y
+CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PGTABLE_LEVELS=2
+# CONFIG_PGTABLE_MAPPING is not set
+CONFIG_PHYLIB=y
+CONFIG_PHY_PISTACHIO_USB=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_PISTACHIO=y
+CONFIG_PISTACHIO_GPTIMER_CLKSRC=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_EVENTS=y
+CONFIG_PROFILING=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_PWM=y
+CONFIG_PWM_IMG=y
+CONFIG_PWM_SYSFS=y
+CONFIG_QUEUED_RWLOCKS=y
+CONFIG_QUEUED_SPINLOCKS=y
+CONFIG_RATIONAL=y
+CONFIG_RCU_NEED_SEGCBLIST=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGMAP_SPI=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_PISTACHIO=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_INFO=y
+CONFIG_SCSI=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SERIAL_8250_DW=y
+# CONFIG_SERIAL_8250_FSL is not set
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SG_POOL=y
+CONFIG_SMP=y
+CONFIG_SMP_UP=y
+CONFIG_SPI=y
+CONFIG_SPI_IMG_SPFI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SRAM=y
+CONFIG_SRCU=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_SWPHY=y
+CONFIG_SYNC_R4K=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_SYS_HAS_CPU_MIPS32_R2=y
+CONFIG_SYS_HAS_EARLY_PRINTK=y
+CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
+CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
+CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y
+CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
+CONFIG_SYS_SUPPORTS_MIPS_CPS=y
+CONFIG_SYS_SUPPORTS_MULTITHREADING=y
+CONFIG_SYS_SUPPORTS_RELOCATABLE=y
+CONFIG_SYS_SUPPORTS_SCHED_SMT=y
+CONFIG_SYS_SUPPORTS_SMP=y
+CONFIG_SYS_SUPPORTS_ZBOOT=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_OF=y
+CONFIG_TIMER_PROBE=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TREE_RCU=y
+CONFIG_TREE_SRCU=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_DWC2_DUAL_ROLE=y
+# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_HCD_PLATFORM is not set
+CONFIG_USB_GADGET=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USER_NS=y
+CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
+CONFIG_USE_OF=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_WEAK_ORDERING=y
+CONFIG_XPS=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZSMALLOC=y
+# CONFIG_ZSMALLOC_STAT is not set
diff --git a/target/linux/pistachio/patches-5.4/101-dmaengine-img-mdc-Handle-early-status-read.patch b/target/linux/pistachio/patches-5.4/101-dmaengine-img-mdc-Handle-early-status-read.patch
new file mode 100644
index 000000000000..92293f879607
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/101-dmaengine-img-mdc-Handle-early-status-read.patch
@@ -0,0 +1,68 @@
+From a2dd154377c9aa6ddda00d39b8c7c334e4fa16ff Mon Sep 17 00:00:00 2001
+From: Damien Horsley <damien.horsley at imgtec.com>
+Date: Tue, 22 Mar 2016 12:46:09 +0000
+Subject: dmaengine: img-mdc: Handle early status read
+
+It is possible that mdc_tx_status may be called before the first
+node has been read from memory.
+
+In this case, the residue value stored in the register is undefined.
+Return the transfer size instead.
+
+Signed-off-by: Damien Horsley <damien.horsley at imgtec.com>
+---
+ drivers/dma/img-mdc-dma.c | 40 ++++++++++++++++++++++++----------------
+ 1 file changed, 24 insertions(+), 16 deletions(-)
+
+--- a/drivers/dma/img-mdc-dma.c
++++ b/drivers/dma/img-mdc-dma.c
+@@ -620,25 +620,33 @@ static enum dma_status mdc_tx_status(str
+ 			(MDC_CMDS_PROCESSED_CMDS_DONE_MASK + 1);
+ 
+ 		/*
+-		 * If the command loaded event hasn't been processed yet, then
+-		 * the difference above includes an extra command.
++		 * If the first node has not yet been read from memory,
++		 * the residue register value is undefined
+ 		 */
+-		if (!mdesc->cmd_loaded)
+-			cmds--;
+-		else
+-			cmds += mdesc->list_cmds_done;
+-
+-		bytes = mdesc->list_xfer_size;
+-		ldesc = mdesc->list;
+-		for (i = 0; i < cmds; i++) {
+-			bytes -= ldesc->xfer_size + 1;
+-			ldesc = ldesc->next_desc;
+-		}
+-		if (ldesc) {
+-			if (residue != MDC_TRANSFER_SIZE_MASK)
+-				bytes -= ldesc->xfer_size - residue;
++		if (!mdesc->cmd_loaded && !cmds) {
++			bytes = mdesc->list_xfer_size;
++		} else {
++			/*
++			 * If the command loaded event hasn't been processed yet, then
++			 * the difference above includes an extra command.
++			 */
++			if (!mdesc->cmd_loaded)
++				cmds--;
+ 			else
++				cmds += mdesc->list_cmds_done;
++
++			bytes = mdesc->list_xfer_size;
++			ldesc = mdesc->list;
++			for (i = 0; i < cmds; i++) {
+ 				bytes -= ldesc->xfer_size + 1;
++				ldesc = ldesc->next_desc;
++			}
++			if (ldesc) {
++				if (residue != MDC_TRANSFER_SIZE_MASK)
++					bytes -= ldesc->xfer_size - residue;
++				else
++					bytes -= ldesc->xfer_size + 1;
++			}
+ 		}
+ 	}
+ 	spin_unlock_irqrestore(&mchan->vc.lock, flags);
diff --git a/target/linux/pistachio/patches-5.4/102-spi-img-spfi-Implement-dual-and-quad-mode.patch b/target/linux/pistachio/patches-5.4/102-spi-img-spfi-Implement-dual-and-quad-mode.patch
new file mode 100644
index 000000000000..15a5d3c806fc
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/102-spi-img-spfi-Implement-dual-and-quad-mode.patch
@@ -0,0 +1,198 @@
+From cd2a6af51553d38072cd31699b58d16ca6176ef5 Mon Sep 17 00:00:00 2001
+From: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Date: Thu, 2 Feb 2017 16:46:14 +0000
+Subject: spi: img-spfi: Implement dual and quad mode
+
+For dual and quad modes to work the SPFI controller needs
+to have information about command/address/dummy bytes in the
+transaction register. This information is not relevant for
+single mode, and therefore it can have any value in the
+allowed range. Therefore, for any read or write transfers of less
+than 8 bytes (cmd = 1 byte, addr up to 7 bytes), SPFI will be
+configured, but not enabled (unless it is the last transfer in
+the queue). The transfer will be enabled by the subsequent tranfer.
+A pending transfer is determined by the content of the transaction
+register: if command part is set and tsize is not.
+
+This way we ensure that for dual and quad transactions
+the command request size will apear in the command/address part
+of the transaction register, while the data size will be in
+tsize, all data being sent/received in the same transaction (as
+set up in the transaction register).
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Signed-off-by: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+---
+ drivers/spi/spi-img-spfi.c | 96 ++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 85 insertions(+), 11 deletions(-)
+
+--- a/drivers/spi/spi-img-spfi.c
++++ b/drivers/spi/spi-img-spfi.c
+@@ -40,7 +40,8 @@
+ #define SPFI_CONTROL_SOFT_RESET			BIT(11)
+ #define SPFI_CONTROL_SEND_DMA			BIT(10)
+ #define SPFI_CONTROL_GET_DMA			BIT(9)
+-#define SPFI_CONTROL_SE			BIT(8)
++#define SPFI_CONTROL_SE				BIT(8)
++#define SPFI_CONTROL_TX_RX			BIT(1)
+ #define SPFI_CONTROL_TMODE_SHIFT		5
+ #define SPFI_CONTROL_TMODE_MASK			0x7
+ #define SPFI_CONTROL_TMODE_SINGLE		0
+@@ -51,6 +52,10 @@
+ #define SPFI_TRANSACTION			0x18
+ #define SPFI_TRANSACTION_TSIZE_SHIFT		16
+ #define SPFI_TRANSACTION_TSIZE_MASK		0xffff
++#define SPFI_TRANSACTION_CMD_SHIFT		13
++#define SPFI_TRANSACTION_CMD_MASK		0x7
++#define SPFI_TRANSACTION_ADDR_SHIFT		10
++#define SPFI_TRANSACTION_ADDR_MASK		0x7
+ 
+ #define SPFI_PORT_STATE				0x1c
+ #define SPFI_PORT_STATE_DEV_SEL_SHIFT		20
+@@ -87,6 +92,7 @@
+  */
+ #define SPFI_32BIT_FIFO_SIZE			64
+ #define SPFI_8BIT_FIFO_SIZE			16
++#define SPFI_DATA_REQUEST_MAX_SIZE		8
+ 
+ struct img_spfi {
+ 	struct device *dev;
+@@ -103,6 +109,8 @@ struct img_spfi {
+ 	struct dma_chan *tx_ch;
+ 	bool tx_dma_busy;
+ 	bool rx_dma_busy;
++
++	bool complete;
+ };
+ 
+ struct img_spfi_device_data {
+@@ -123,9 +131,11 @@ static inline void spfi_start(struct img
+ {
+ 	u32 val;
+ 
+-	val = spfi_readl(spfi, SPFI_CONTROL);
+-	val |= SPFI_CONTROL_SPFI_EN;
+-	spfi_writel(spfi, val, SPFI_CONTROL);
++	if (spfi->complete) {
++		val = spfi_readl(spfi, SPFI_CONTROL);
++		val |= SPFI_CONTROL_SPFI_EN;
++		spfi_writel(spfi, val, SPFI_CONTROL);
++	}
+ }
+ 
+ static inline void spfi_reset(struct img_spfi *spfi)
+@@ -138,12 +148,21 @@ static int spfi_wait_all_done(struct img
+ {
+ 	unsigned long timeout = jiffies + msecs_to_jiffies(50);
+ 
++	if (!(spfi->complete))
++		return 0;
++
+ 	while (time_before(jiffies, timeout)) {
+ 		u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS);
+ 
+ 		if (status & SPFI_INTERRUPT_ALLDONETRIG) {
+ 			spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG,
+ 				    SPFI_INTERRUPT_CLEAR);
++			/*
++			 * Disable SPFI for it not to interfere with
++			 * pending transactions
++			 */
++			spfi_writel(spfi, spfi_readl(spfi, SPFI_CONTROL)
++			& ~SPFI_CONTROL_SPFI_EN, SPFI_CONTROL);
+ 			return 0;
+ 		}
+ 		cpu_relax();
+@@ -494,9 +513,32 @@ static void img_spfi_config(struct spi_m
+ 			    struct spi_transfer *xfer)
+ {
+ 	struct img_spfi *spfi = spi_master_get_devdata(spi->master);
+-	u32 val, div;
++	u32 val, div, transact;
++	bool is_pending;
+ 
+ 	/*
++	 * For read or write transfers of less than 8 bytes (cmd = 1 byte,
++	 * addr up to 7 bytes), SPFI will be configured, but not enabled
++	 * (unless it is the last transfer in the queue).The transfer will
++	 * be enabled by the subsequent transfer.
++	 * A pending transfer is determined by the content of the
++	 * transaction register: if command part is set and tsize
++	 * is not
++	 */
++	transact = spfi_readl(spfi, SPFI_TRANSACTION);
++	is_pending = ((transact >> SPFI_TRANSACTION_CMD_SHIFT) &
++			SPFI_TRANSACTION_CMD_MASK) &&
++			(!((transact >> SPFI_TRANSACTION_TSIZE_SHIFT) &
++			SPFI_TRANSACTION_TSIZE_MASK));
++
++	/* If there are no pending transactions it's OK to soft reset */
++	if (!is_pending) {
++		/* Start the transaction from a known (reset) state */
++		spfi_reset(spfi);
++	}
++
++	/*
++	 * Before anything else, set up parameters.
+ 	 * output = spfi_clk * (BITCLK / 512), where BITCLK must be a
+ 	 * power of 2 up to 128
+ 	 */
+@@ -509,20 +551,52 @@ static void img_spfi_config(struct spi_m
+ 	val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
+ 	spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
+ 
+-	spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT,
+-		    SPFI_TRANSACTION);
++	if (!list_is_last(&xfer->transfer_list, &master->cur_msg->transfers) &&
++		/*
++		 * For duplex mode (both the tx and rx buffers are !NULL) the
++		 * CMD, ADDR, and DUMMY byte parts of the transaction register
++		 * should always be 0 and therefore the pending transfer
++		 * technique cannot be used.
++		 */
++		(xfer->tx_buf) && (!xfer->rx_buf) &&
++		(xfer->len <= SPFI_DATA_REQUEST_MAX_SIZE) && !is_pending) {
++		transact = (1 & SPFI_TRANSACTION_CMD_MASK) <<
++			SPFI_TRANSACTION_CMD_SHIFT;
++		transact |= ((xfer->len - 1) & SPFI_TRANSACTION_ADDR_MASK) <<
++			SPFI_TRANSACTION_ADDR_SHIFT;
++		spfi->complete = false;
++	} else {
++		spfi->complete = true;
++		if (is_pending) {
++			/* Keep setup from pending transfer */
++			transact |= ((xfer->len & SPFI_TRANSACTION_TSIZE_MASK) <<
++				SPFI_TRANSACTION_TSIZE_SHIFT);
++		} else {
++			transact = ((xfer->len & SPFI_TRANSACTION_TSIZE_MASK) <<
++				SPFI_TRANSACTION_TSIZE_SHIFT);
++		}
++	}
++	spfi_writel(spfi, transact, SPFI_TRANSACTION);
+ 
+ 	val = spfi_readl(spfi, SPFI_CONTROL);
+ 	val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA);
+-	if (xfer->tx_buf)
++	/*
++	 * We set up send DMA for pending transfers also, as
++	 * those are always send transfers
++	 */
++	if ((xfer->tx_buf) || is_pending)
+ 		val |= SPFI_CONTROL_SEND_DMA;
+-	if (xfer->rx_buf)
++	if (xfer->tx_buf)
++		val |= SPFI_CONTROL_TX_RX;
++	if (xfer->rx_buf) {
+ 		val |= SPFI_CONTROL_GET_DMA;
++		val &= ~SPFI_CONTROL_TX_RX;
++	}
+ 	val &= ~(SPFI_CONTROL_TMODE_MASK << SPFI_CONTROL_TMODE_SHIFT);
+-	if (xfer->tx_nbits == SPI_NBITS_DUAL &&
++	if (xfer->tx_nbits == SPI_NBITS_DUAL ||
+ 	    xfer->rx_nbits == SPI_NBITS_DUAL)
+ 		val |= SPFI_CONTROL_TMODE_DUAL << SPFI_CONTROL_TMODE_SHIFT;
+-	else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
++	else if (xfer->tx_nbits == SPI_NBITS_QUAD ||
+ 		 xfer->rx_nbits == SPI_NBITS_QUAD)
+ 		val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
+ 	val |= SPFI_CONTROL_SE;
diff --git a/target/linux/pistachio/patches-5.4/103-spi-img-spfi-set-device-select-bits-for-SPFI-port-st.patch b/target/linux/pistachio/patches-5.4/103-spi-img-spfi-set-device-select-bits-for-SPFI-port-st.patch
new file mode 100644
index 000000000000..ba70348da9ab
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/103-spi-img-spfi-set-device-select-bits-for-SPFI-port-st.patch
@@ -0,0 +1,27 @@
+From 145f5369510b86cd55c659388a26a0cc267f8874 Mon Sep 17 00:00:00 2001
+From: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Date: Mon, 1 Feb 2016 10:58:08 +0000
+Subject: spi: img-spfi: set device select bits for SPFI port state
+
+Even if the chip select line is not controlled by the SPFI
+hardware, the device select bits need to be set to specify
+the chip select line in use for the hardware to know what
+parameters to use for the current transfer.
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+---
+ drivers/spi/spi-img-spfi.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/spi/spi-img-spfi.c
++++ b/drivers/spi/spi-img-spfi.c
+@@ -438,6 +438,9 @@ static int img_spfi_prepare(struct spi_m
+ 	u32 val;
+ 
+ 	val = spfi_readl(spfi, SPFI_PORT_STATE);
++	val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK <<
++		 SPFI_PORT_STATE_DEV_SEL_SHIFT);
++	val |= msg->spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT;
+ 	if (msg->spi->mode & SPI_CPHA)
+ 		val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
+ 	else
diff --git a/target/linux/pistachio/patches-5.4/104-spi-img-spfi-use-device-0-configuration-for-all-devi.patch b/target/linux/pistachio/patches-5.4/104-spi-img-spfi-use-device-0-configuration-for-all-devi.patch
new file mode 100644
index 000000000000..6c9e6b5a768f
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/104-spi-img-spfi-use-device-0-configuration-for-all-devi.patch
@@ -0,0 +1,64 @@
+From 905ee06a9966113fe51d6bad1819759cb30fd0bd Mon Sep 17 00:00:00 2001
+From: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Date: Tue, 9 Feb 2016 10:18:31 +0000
+Subject: spi: img-spfi: use device 0 configuration for all devices
+
+Given that we control the chip select line externally
+we can use only one parameter register (device 0 parameter
+register) and one set of configuration bits (port configuration
+bits for device 0) for all devices (all chip select lines).
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+---
+ drivers/spi/spi-img-spfi.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/spi/spi-img-spfi.c
++++ b/drivers/spi/spi-img-spfi.c
+@@ -437,18 +437,23 @@ static int img_spfi_prepare(struct spi_m
+ 	struct img_spfi *spfi = spi_master_get_devdata(master);
+ 	u32 val;
+ 
++	/*
++	 * The chip select line is controlled externally so
++	 * we can use the CS0 configuration for all devices
++	 */
+ 	val = spfi_readl(spfi, SPFI_PORT_STATE);
++
++	/* 0 for device selection */
+ 	val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK <<
+ 		 SPFI_PORT_STATE_DEV_SEL_SHIFT);
+-	val |= msg->spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT;
+ 	if (msg->spi->mode & SPI_CPHA)
+-		val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
++		val |= SPFI_PORT_STATE_CK_PHASE(0);
+ 	else
+-		val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
++		val &= ~SPFI_PORT_STATE_CK_PHASE(0);
+ 	if (msg->spi->mode & SPI_CPOL)
+-		val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
++		val |= SPFI_PORT_STATE_CK_POL(0);
+ 	else
+-		val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select);
++		val &= ~SPFI_PORT_STATE_CK_POL(0);
+ 	spfi_writel(spfi, val, SPFI_PORT_STATE);
+ 
+ 	return 0;
+@@ -548,11 +553,15 @@ static void img_spfi_config(struct spi_m
+ 	div = DIV_ROUND_UP(clk_get_rate(spfi->spfi_clk), xfer->speed_hz);
+ 	div = clamp(512 / (1 << get_count_order(div)), 1, 128);
+ 
+-	val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select));
++	/*
++	 * The chip select line is controlled externally so
++	 * we can use the CS0 parameters for all devices
++	 */
++	val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(0));
+ 	val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK <<
+ 		 SPFI_DEVICE_PARAMETER_BITCLK_SHIFT);
+ 	val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT;
+-	spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select));
++	spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(0));
+ 
+ 	if (!list_is_last(&xfer->transfer_list, &master->cur_msg->transfers) &&
+ 		/*
diff --git a/target/linux/pistachio/patches-5.4/105-spi-img-spfi-RX-maximum-burst-size-for-DMA-is-8.patch b/target/linux/pistachio/patches-5.4/105-spi-img-spfi-RX-maximum-burst-size-for-DMA-is-8.patch
new file mode 100644
index 000000000000..0067b0ea4ac0
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/105-spi-img-spfi-RX-maximum-burst-size-for-DMA-is-8.patch
@@ -0,0 +1,59 @@
+From 56466f505f58f44b69feb7eaed3b506842800456 Mon Sep 17 00:00:00 2001
+From: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Date: Tue, 1 Mar 2016 17:49:45 +0000
+Subject: spi: img-spfi: RX maximum burst size for DMA is 8
+
+The depth of the FIFOs is 16 bytes. The DMA request line is tied
+to the half full/empty (depending on the use of the TX or RX FIFO)
+threshold. For the TX FIFO, if you set a burst size of 8 (equal to
+half the depth) the first burst goes into FIFO without any issues,
+but due the latency involved (the time the data leaves  the DMA
+engine to the time it arrives at the FIFO), the DMA might trigger
+another burst of 8. But given that there is no space for 2 additonal
+bursts of 8, this would result in a failure. Therefore, we have to
+keep the burst size for TX to 4 to accomodate for an extra burst.
+
+For the read (RX) scenario, the DMA request line goes high when
+there is at least 8 entries in the FIFO (half full), and we can
+program the burst size to be 8 because the risk of accidental burst
+does not exist. The DMA engine will not trigger another read until
+the read data for all the burst it has sent out has been received.
+
+While here, move the burst size setting outside of the if/else branches
+as they have the same value for both 8 and 32 bit data widths.
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+---
+ drivers/spi/spi-img-spfi.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/spi/spi-img-spfi.c
++++ b/drivers/spi/spi-img-spfi.c
+@@ -346,12 +346,11 @@ static int img_spfi_start_dma(struct spi
+ 		if (xfer->len % 4 == 0) {
+ 			rxconf.src_addr = spfi->phys + SPFI_RX_32BIT_VALID_DATA;
+ 			rxconf.src_addr_width = 4;
+-			rxconf.src_maxburst = 4;
+ 		} else {
+ 			rxconf.src_addr = spfi->phys + SPFI_RX_8BIT_VALID_DATA;
+ 			rxconf.src_addr_width = 1;
+-			rxconf.src_maxburst = 4;
+ 		}
++		rxconf.src_maxburst = 8;
+ 		dmaengine_slave_config(spfi->rx_ch, &rxconf);
+ 
+ 		rxdesc = dmaengine_prep_slave_sg(spfi->rx_ch, xfer->rx_sg.sgl,
+@@ -370,12 +369,11 @@ static int img_spfi_start_dma(struct spi
+ 		if (xfer->len % 4 == 0) {
+ 			txconf.dst_addr = spfi->phys + SPFI_TX_32BIT_VALID_DATA;
+ 			txconf.dst_addr_width = 4;
+-			txconf.dst_maxburst = 4;
+ 		} else {
+ 			txconf.dst_addr = spfi->phys + SPFI_TX_8BIT_VALID_DATA;
+ 			txconf.dst_addr_width = 1;
+-			txconf.dst_maxburst = 4;
+ 		}
++		txconf.dst_maxburst = 4;
+ 		dmaengine_slave_config(spfi->tx_ch, &txconf);
+ 
+ 		txdesc = dmaengine_prep_slave_sg(spfi->tx_ch, xfer->tx_sg.sgl,
diff --git a/target/linux/pistachio/patches-5.4/106-spi-img-spfi-finish-every-transfer-cleanly.patch b/target/linux/pistachio/patches-5.4/106-spi-img-spfi-finish-every-transfer-cleanly.patch
new file mode 100644
index 000000000000..0f958314e3be
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/106-spi-img-spfi-finish-every-transfer-cleanly.patch
@@ -0,0 +1,120 @@
+From 5fcca3fd4b621d7b5bdeca18d36dfc6ca6cfe383 Mon Sep 17 00:00:00 2001
+From: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Date: Wed, 10 Aug 2016 11:42:26 +0100
+Subject: spi: img-spfi: finish every transfer cleanly
+
+Before this change, the interrupt status bit that signaled
+the end of a tranfers was cleared in the wait_all_done
+function. That functionality triggered issues for DMA
+duplex transactions where the wait function was called
+twice, in both the TX and RX callbacks.
+
+In order to fix the issue, clear all interrupt data bits
+at the end of a PIO transfer or at the end of both TX and RX
+duplex transfers, if the transfer is not a pending tranfer
+(command waiting for data). After that, the status register
+is checked for new incoming data or new data requests to be
+signaled. If SPFI finished cleanly, no new interrupt data
+bits should be set.
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+---
+ drivers/spi/spi-img-spfi.c | 49 +++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 35 insertions(+), 14 deletions(-)
+
+--- a/drivers/spi/spi-img-spfi.c
++++ b/drivers/spi/spi-img-spfi.c
+@@ -83,6 +83,14 @@
+ #define SPFI_INTERRUPT_SDE			BIT(1)
+ #define SPFI_INTERRUPT_SDTRIG			BIT(0)
+ 
++#define SPFI_INTERRUPT_DATA_BITS		(SPFI_INTERRUPT_SDHF |\
++						SPFI_INTERRUPT_SDFUL |\
++						SPFI_INTERRUPT_GDEX32BIT |\
++						SPFI_INTERRUPT_GDHF |\
++						SPFI_INTERRUPT_GDFUL |\
++						SPFI_INTERRUPT_ALLDONETRIG |\
++						SPFI_INTERRUPT_GDEX8BIT)
++
+ /*
+  * There are four parallel FIFOs of 16 bytes each.  The word buffer
+  * (*_32BIT_VALID_DATA) accesses all four FIFOs at once, resulting in an
+@@ -144,6 +152,23 @@ static inline void spfi_reset(struct img
+ 	spfi_writel(spfi, 0, SPFI_CONTROL);
+ }
+ 
++static inline void spfi_finish(struct img_spfi *spfi)
++{
++	if (!(spfi->complete))
++		return;
++
++	/* Clear data bits as all transfers(TX and RX) have finished */
++	spfi_writel(spfi, SPFI_INTERRUPT_DATA_BITS, SPFI_INTERRUPT_CLEAR);
++	if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & SPFI_INTERRUPT_DATA_BITS) {
++		dev_err(spfi->dev, "SPFI did not finish transfer cleanly.\n");
++		spfi_reset(spfi);
++	}
++	/* Disable SPFI for it not to interfere with pending transactions */
++	spfi_writel(spfi,
++		    spfi_readl(spfi, SPFI_CONTROL) & ~SPFI_CONTROL_SPFI_EN,
++		    SPFI_CONTROL);
++}
++
+ static int spfi_wait_all_done(struct img_spfi *spfi)
+ {
+ 	unsigned long timeout = jiffies + msecs_to_jiffies(50);
+@@ -152,19 +177,9 @@ static int spfi_wait_all_done(struct img
+ 		return 0;
+ 
+ 	while (time_before(jiffies, timeout)) {
+-		u32 status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS);
+-
+-		if (status & SPFI_INTERRUPT_ALLDONETRIG) {
+-			spfi_writel(spfi, SPFI_INTERRUPT_ALLDONETRIG,
+-				    SPFI_INTERRUPT_CLEAR);
+-			/*
+-			 * Disable SPFI for it not to interfere with
+-			 * pending transactions
+-			 */
+-			spfi_writel(spfi, spfi_readl(spfi, SPFI_CONTROL)
+-			& ~SPFI_CONTROL_SPFI_EN, SPFI_CONTROL);
++		if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) &
++		    SPFI_INTERRUPT_ALLDONETRIG)
+ 			return 0;
+-		}
+ 		cpu_relax();
+ 	}
+ 
+@@ -296,6 +311,8 @@ static int img_spfi_start_pio(struct spi
+ 	}
+ 
+ 	ret = spfi_wait_all_done(spfi);
++	spfi_finish(spfi);
++
+ 	if (ret < 0)
+ 		return ret;
+ 
+@@ -311,8 +328,10 @@ static void img_spfi_dma_rx_cb(void *dat
+ 
+ 	spin_lock_irqsave(&spfi->lock, flags);
+ 	spfi->rx_dma_busy = false;
+-	if (!spfi->tx_dma_busy)
++	if (!spfi->tx_dma_busy) {
++		spfi_finish(spfi);
+ 		spi_finalize_current_transfer(spfi->master);
++	}
+ 	spin_unlock_irqrestore(&spfi->lock, flags);
+ }
+ 
+@@ -325,8 +344,10 @@ static void img_spfi_dma_tx_cb(void *dat
+ 
+ 	spin_lock_irqsave(&spfi->lock, flags);
+ 	spfi->tx_dma_busy = false;
+-	if (!spfi->rx_dma_busy)
++	if (!spfi->rx_dma_busy) {
++		spfi_finish(spfi);
+ 		spi_finalize_current_transfer(spfi->master);
++	}
+ 	spin_unlock_irqrestore(&spfi->lock, flags);
+ }
+ 
diff --git a/target/linux/pistachio/patches-5.4/107-clockevents-Retry-programming-min-delta-up-to-10-tim.patch b/target/linux/pistachio/patches-5.4/107-clockevents-Retry-programming-min-delta-up-to-10-tim.patch
new file mode 100644
index 000000000000..857823b78398
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/107-clockevents-Retry-programming-min-delta-up-to-10-tim.patch
@@ -0,0 +1,66 @@
+From b46f8c74afdd30cd52bfdcc2231470a0bab04416 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan at imgtec.com>
+Date: Fri, 22 Apr 2016 18:22:45 +0100
+Subject: clockevents: Retry programming min delta up to 10 times
+
+Under virtualisation it is possible to get unexpected latency during a
+clockevent device's set_next_event() callback which can make it return
+-ETIME even for a delta based on min_delta_ns.
+
+The clockevents_program_min_delta() implementation for
+CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=n doesn't handle retries when this
+happens, nor does clockevents_program_event() or its callers when force
+is true (for example hrtimer_reprogram()). This can result in hangs
+until the clock event device does a full period.
+
+It isn't appropriate to use MIN_ADJUST in this case as occasional
+hypervisor induced high latency will cause min_delta_ns to quickly
+increase to the maximum.
+Instead, borrow the retry pattern from the MIN_ADJUST case, but without
+making adjustments. We retry up to 10 times before giving up.
+
+(picked https://patchwork.kernel.org/patch/8909491/)
+
+Signed-off-by: James Hogan <james.hogan at imgtec.com>
+---
+ kernel/time/clockevents.c | 26 +++++++++++++++++++-------
+ 1 file changed, 19 insertions(+), 7 deletions(-)
+
+--- a/kernel/time/clockevents.c
++++ b/kernel/time/clockevents.c
+@@ -281,16 +281,28 @@ static int clockevents_program_min_delta
+ {
+ 	unsigned long long clc;
+ 	int64_t delta;
++	int i;
+ 
+-	delta = dev->min_delta_ns;
+-	dev->next_event = ktime_add_ns(ktime_get(), delta);
++	for (i = 0;;) {
++		delta = dev->min_delta_ns;
++		dev->next_event = ktime_add_ns(ktime_get(), delta);
+ 
+-	if (clockevent_state_shutdown(dev))
+-		return 0;
++		if (clockevent_state_shutdown(dev))
++			return 0;
+ 
+-	dev->retries++;
+-	clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+-	return dev->set_next_event((unsigned long) clc, dev);
++		dev->retries++;
++		clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
++		if (dev->set_next_event((unsigned long) clc, dev) == 0)
++			return 0;
++
++		if (++i > 9) {
++			/*
++			 * We tried 10 times to program the device with the
++			 * given min_delta_ns. Get out of here.
++			 */
++			return -ETIME;
++		}
++	}
+ }
+ 
+ #endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
diff --git a/target/linux/pistachio/patches-5.4/108-clk-pistachio-Fix-wrong-SDHost-card-speed.patch b/target/linux/pistachio/patches-5.4/108-clk-pistachio-Fix-wrong-SDHost-card-speed.patch
new file mode 100644
index 000000000000..5329ad62d0b4
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/108-clk-pistachio-Fix-wrong-SDHost-card-speed.patch
@@ -0,0 +1,49 @@
+From 3642843a06025ec333d7e92580cf52cb8db2a652 Mon Sep 17 00:00:00 2001
+From: Govindraj Raja <Govindraj.Raja at imgtec.com>
+Date: Fri, 8 Jan 2016 16:36:07 +0000
+Subject: clk: pistachio: Fix wrong SDHost card speed
+
+The SDHost currently clocks the card 4x slower than it
+should do, because there is fixed divide by 4 in the
+sdhost wrapper that is not present in the clock tree.
+To model this add a fixed divide by 4 clock node in
+the SDHost clock path.
+
+This will ensure the right clock frequency is selected when
+the mmc driver tries to configure frequency on card insert.
+
+Signed-off-by: Govindraj Raja <Govindraj.Raja at imgtec.com>
+---
+ drivers/clk/pistachio/clk-pistachio.c     | 3 ++-
+ include/dt-bindings/clock/pistachio-clk.h | 1 +
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/pistachio/clk-pistachio.c
++++ b/drivers/clk/pistachio/clk-pistachio.c
+@@ -44,7 +44,7 @@ static struct pistachio_gate pistachio_g
+ 	GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div",
+ 	     0x104, 22),
+ 	GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23),
+-	GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24),
++	GATE(CLK_SD_HOST, "sd_host", "sd_host_div4", 0x104, 24),
+ 	GATE(CLK_BT, "bt", "bt_div", 0x104, 25),
+ 	GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26),
+ 	GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27),
+@@ -54,6 +54,7 @@ static struct pistachio_gate pistachio_g
+ static struct pistachio_fixed_factor pistachio_ffs[] __initdata = {
+ 	FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4),
+ 	FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8),
++	FIXED_FACTOR(CLK_SDHOST_DIV4, "sd_host_div4", "sd_host_div", 4),
+ };
+ 
+ static struct pistachio_div pistachio_divs[] __initdata = {
+--- a/include/dt-bindings/clock/pistachio-clk.h
++++ b/include/dt-bindings/clock/pistachio-clk.h
+@@ -21,6 +21,7 @@
+ /* Fixed-factor clocks */
+ #define CLK_WIFI_DIV4			16
+ #define CLK_WIFI_DIV8			17
++#define CLK_SDHOST_DIV4			18
+ 
+ /* Gate clocks */
+ #define CLK_MIPS			32
diff --git a/target/linux/pistachio/patches-5.4/109-MIPS-DTS-img-marduk-switch-mmc-to-1-bit-mode.patch b/target/linux/pistachio/patches-5.4/109-MIPS-DTS-img-marduk-switch-mmc-to-1-bit-mode.patch
new file mode 100644
index 000000000000..22fc42b98809
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/109-MIPS-DTS-img-marduk-switch-mmc-to-1-bit-mode.patch
@@ -0,0 +1,47 @@
+From 981c1d416af45eff207227aec106381ac23aac99 Mon Sep 17 00:00:00 2001
+From: Ian Pozella <Ian.Pozella at imgtec.com>
+Date: Mon, 20 Feb 2017 10:00:52 +0000
+Subject: MIPS: DTS: img: marduk: switch mmc to 1 bit mode
+
+The mmc block in Pistachio allows 1 to 8 data bits to be used.
+Marduk uses 4 bits allowing the upper 4 bits to be allocated
+to the Mikrobus ports. However these bits are still connected
+internally meaning the mmc block recieves signals on all data lines
+and seems the internal HW CRC checks get corrupted by this erroneous
+data.
+
+We cannot control what data is sent on these lines because they go
+to external ports. 1 bit mode does not exhibit the issue hence the
+safe default is to use this. If a user knows that in their use case
+they will not use the upper bits then they can set to 4 bit mode in
+order to improve performance.
+
+Also make sure that the upper 4 bits don't get allocated to the mmc
+driver (the default is to assign all 8 pins) so they can be allocated
+to other drivers. Allocating all 4 despite setting 1 bit mode as this
+matches what is there in hardware.
+
+Signed-off-by: Ian Pozella <Ian.Pozella at imgtec.com>
+---
+ arch/mips/boot/dts/img/pistachio_marduk.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/mips/boot/dts/img/pistachio_marduk.dts
++++ b/arch/mips/boot/dts/img/pistachio_marduk.dts
+@@ -120,7 +120,7 @@
+ 
+ &sdhost {
+ 	status = "okay";
+-	bus-width = <4>;
++	bus-width = <1>;
+ 	disable-wp;
+ };
+ 
+@@ -130,6 +130,7 @@
+ 
+ &pin_sdhost_data {
+ 	drive-strength = <2>;
++	pins = "mfio17", "mfio18", "mfio19", "mfio20";
+ };
+ 
+ &pwm {
diff --git a/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch b/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch
new file mode 100644
index 000000000000..065a0b77bcd9
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/401-mtd-nor-support-mtd-name-from-device-tree.patch
@@ -0,0 +1,34 @@
+From f32bc2aa01edcba2f2ed5db151cf183eac9ef919 Mon Sep 17 00:00:00 2001
+From: Abhimanyu Vishwakarma <Abhimanyu.Vishwakarma at imgtec.com>
+Date: Sat, 25 Feb 2017 16:42:50 +0000
+Subject: mtd: nor: support mtd name from device tree
+
+Signed-off-by: Abhimanyu Vishwakarma <Abhimanyu.Vishwakarma at imgtec.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -2671,6 +2671,7 @@ int spi_nor_scan(struct spi_nor *nor, co
+ 	struct device *dev = nor->dev;
+ 	struct mtd_info *mtd = &nor->mtd;
+ 	struct device_node *np = spi_nor_get_flash_node(nor);
++	const char __maybe_unused *of_mtd_name = NULL;
+ 	int ret;
+ 	int i;
+ 
+@@ -2746,7 +2747,12 @@ int spi_nor_scan(struct spi_nor *nor, co
+ 		spi_nor_wait_till_ready(nor);
+ 	}
+ 
+-	if (!mtd->name)
++#ifdef CONFIG_MTD_OF_PARTS
++	of_property_read_string(np, "linux,mtd-name", &of_mtd_name);
++#endif
++	if (of_mtd_name)
++		mtd->name = of_mtd_name;
++	else if (!mtd->name)
+ 		mtd->name = dev_name(dev);
+ 	mtd->priv = nor;
+ 	mtd->type = MTD_NORFLASH;
diff --git a/target/linux/pistachio/patches-5.4/411-mtd-nand-Check-length-of-ID-before-reading-bits-per-.patch b/target/linux/pistachio/patches-5.4/411-mtd-nand-Check-length-of-ID-before-reading-bits-per-.patch
new file mode 100644
index 000000000000..3311de645f11
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/411-mtd-nand-Check-length-of-ID-before-reading-bits-per-.patch
@@ -0,0 +1,33 @@
+From 42ebff638003be18fab503b37de4ad7853244e95 Mon Sep 17 00:00:00 2001
+From: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Date: Sat, 25 Feb 2017 15:58:22 +0000
+Subject: mtd: nand: Check length of ID before reading bits per cell
+
+The table-based NAND identification currently reads the number
+of bits per cell from the 3rd byte of the extended ID. This is done
+for the so-called 'full ID' devices; i.e. devices that have a known
+length ID.
+
+However, if the ID length is shorter than three, there's no 3rd byte,
+and so it's wrong to read the bits per cell from there. Fix this by
+adding a check for the ID length.
+
+(picked from http://lists.infradead.org/pipermail/linux-mtd/2014-December/056764.html)
+
+Signed-off-by: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+---
+ drivers/mtd/nand/nand_base.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -3803,7 +3803,8 @@ static bool find_full_id_nand(struct nan
+ 		mtd->erasesize = type->erasesize;
+ 		mtd->oobsize = type->oobsize;
+ 
+-		chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
++		if (type->id_len > 2)
++			chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ 		chip->chipsize = (uint64_t)type->chipsize << 20;
+ 		chip->options |= type->options;
+ 		chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
diff --git a/target/linux/pistachio/patches-5.4/412-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch b/target/linux/pistachio/patches-5.4/412-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch
new file mode 100644
index 000000000000..a967124c812f
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/412-mtd-nand-Add-JEDEC-manufacturer-ID-for-Gigadevice.patch
@@ -0,0 +1,35 @@
+From a4bc33b205fd9b1db862f1e45173dba57b0fa57f Mon Sep 17 00:00:00 2001
+From: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Date: Sat, 25 Feb 2017 15:43:09 +0000
+Subject: mtd: nand: Add JEDEC manufacturer ID for Gigadevice
+
+This commit adds Gigadevice to the list of manufacturer ID and name strings.
+
+(picked from http://lists.infradead.org/pipermail/linux-mtd/2014-December/056765.html)
+
+Signed-off-by: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+---
+ drivers/mtd/nand/nand_ids.c | 1 +
+ include/linux/mtd/rawnand.h    | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/mtd/nand/nand_ids.c
++++ b/drivers/mtd/nand/nand_ids.c
+@@ -184,6 +184,7 @@ static const struct nand_manufacturer na
+ 	{NAND_MFR_SANDISK, "SanDisk"},
+ 	{NAND_MFR_INTEL, "Intel"},
+ 	{NAND_MFR_ATO, "ATO"},
++	{NAND_MFR_GIGADEVICE, "Gigadevice"},
+ 	{NAND_MFR_WINBOND, "Winbond"},
+ };
+ 
+--- a/include/linux/mtd/rawnand.h
++++ b/include/linux/mtd/rawnand.h
+@@ -1014,6 +1014,7 @@ static inline void *nand_get_manufacture
+ #define NAND_MFR_SANDISK	0x45
+ #define NAND_MFR_INTEL		0x89
+ #define NAND_MFR_ATO		0x9b
++#define NAND_MFR_GIGADEVICE	0xc8
+ #define NAND_MFR_WINBOND	0xef
+ 
+ 
diff --git a/target/linux/pistachio/patches-5.4/413-mtd-Introduce-SPI-NAND-framework.patch b/target/linux/pistachio/patches-5.4/413-mtd-Introduce-SPI-NAND-framework.patch
new file mode 100644
index 000000000000..d1189eeb8c58
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/413-mtd-Introduce-SPI-NAND-framework.patch
@@ -0,0 +1,707 @@
+From 082a89a78e29b15008284df90441747cb742f149 Mon Sep 17 00:00:00 2001
+From: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Date: Tue, 2 Dec 2014 09:58:52 -0300
+Subject: mtd: Introduce SPI NAND framework
+
+Add a new framework, to support SPI NAND devices. The framework registers
+a NAND chip and handles the generic SPI NAND protocol, calling device-specific
+hooks for each SPI NAND command.
+
+The following is the stack design, from userspace to hardware. This commit
+adds the "SPI NAND core" layer.
+
+    Userspace
+  ------------------
+    MTD
+  ------------------
+    NAND core
+  ------------------
+    SPI NAND core
+  ------------------
+    SPI NAND device
+  ------------------
+    SPI core
+  ------------------
+    SPI master
+  ------------------
+    Hardware
+
+(based on http://lists.infradead.org/pipermail/linux-mtd/2014-December/056763.html)
+
+Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
+Signed-off-by: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Signed-off-by: Ian Pozella <Ian.Pozella at imgtec.com>
+---
+ drivers/mtd/Kconfig                  |   2 +
+ drivers/mtd/Makefile                 |   1 +
+ drivers/mtd/spi-nand/Kconfig         |   7 +
+ drivers/mtd/spi-nand/Makefile        |   1 +
+ drivers/mtd/spi-nand/spi-nand-base.c | 566 +++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spi-nand.h         |  54 ++++
+ 6 files changed, 631 insertions(+)
+ create mode 100644 drivers/mtd/spi-nand/Kconfig
+ create mode 100644 drivers/mtd/spi-nand/Makefile
+ create mode 100644 drivers/mtd/spi-nand/spi-nand-base.c
+ create mode 100644 include/linux/mtd/spi-nand.h
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -373,6 +373,8 @@ source "drivers/mtd/onenand/Kconfig"
+ 
+ source "drivers/mtd/lpddr/Kconfig"
+ 
++source "drivers/mtd/spi-nand/Kconfig"
++
+ source "drivers/mtd/spi-nor/Kconfig"
+ 
+ source "drivers/mtd/ubi/Kconfig"
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -37,6 +37,7 @@ inftl-objs		:= inftlcore.o inftlmount.o
+ 
+ obj-y		+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
+ 
++obj-$(CONFIG_MTD_SPI_NAND)	+= spi-nand/
+ obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
+ obj-$(CONFIG_MTD_UBI)		+= ubi/
+ 
+--- /dev/null
++++ b/drivers/mtd/spi-nand/Kconfig
+@@ -0,0 +1,7 @@
++menuconfig MTD_SPI_NAND
++	tristate "SPI NAND device support"
++	depends on MTD
++	select MTD_NAND
++	help
++	  This is the framework for the SPI NAND.
++
+--- /dev/null
++++ b/drivers/mtd/spi-nand/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_MTD_SPI_NAND)		+= spi-nand-base.o
+--- /dev/null
++++ b/drivers/mtd/spi-nand/spi-nand-base.c
+@@ -0,0 +1,566 @@
++/*
++ * Copyright (C) 2014 Imagination Technologies Ltd.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * Notes:
++ * 1. Erase and program operations need to call write_enable() first,
++ *    to clear the enable bit. This bit is cleared automatically after
++ *    the erase or program operation.
++ *
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mtd/rawnand.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/spi-nand.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++/* Registers common to all devices */
++#define SPI_NAND_LOCK_REG		0xa0
++#define SPI_NAND_PROT_UNLOCK_ALL	0x0
++
++#define SPI_NAND_FEATURE_REG		0xb0
++#define SPI_NAND_ECC_EN			BIT(4)
++#define SPI_NAND_QUAD_EN		BIT(0)
++
++#define SPI_NAND_STATUS_REG		0xc0
++#define SPI_NAND_STATUS_REG_ECC_MASK	0x3
++#define SPI_NAND_STATUS_REG_ECC_SHIFT	4
++#define SPI_NAND_STATUS_REG_PROG_FAIL	BIT(3)
++#define SPI_NAND_STATUS_REG_ERASE_FAIL	BIT(2)
++#define SPI_NAND_STATUS_REG_WREN	BIT(1)
++#define SPI_NAND_STATUS_REG_BUSY	BIT(0)
++
++#define SPI_NAND_CMD_BUF_LEN		8
++
++/* Rewind and fill the buffer with 0xff */
++static void spi_nand_clear_buffer(struct spi_nand *snand)
++{
++	snand->buf_start = 0;
++	memset(snand->data_buf, 0xff, snand->buf_size);
++}
++
++static int spi_nand_enable_ecc(struct spi_nand *snand)
++{
++	int ret;
++
++	ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++
++	snand->buf[0] |= SPI_NAND_ECC_EN;
++	ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++	snand->ecc = true;
++
++	return 0;
++}
++
++static int spi_nand_disable_ecc(struct spi_nand *snand)
++{
++	int ret;
++
++	ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++
++	snand->buf[0] &= ~SPI_NAND_ECC_EN;
++	ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++	snand->ecc = false;
++
++	return 0;
++}
++
++static int spi_nand_enable_quad(struct spi_nand *snand)
++{
++	int ret;
++
++	ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++
++	snand->buf[0] |= SPI_NAND_QUAD_EN;
++	ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++/*
++ * Wait until the status register busy bit is cleared.
++ * Returns a negatie errno on error or time out, and a non-negative status
++ * value if the device is ready.
++ */
++static int spi_nand_wait_till_ready(struct spi_nand *snand)
++{
++	unsigned long deadline = jiffies + msecs_to_jiffies(100);
++	bool timeout = false;
++	int ret;
++
++	/*
++	 * Perhaps we should set a different timeout for each
++	 * operation (reset, read, write, erase).
++	 */
++	while (!timeout) {
++		if (time_after_eq(jiffies, deadline))
++			timeout = true;
++
++		ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
++		if (ret < 0) {
++			dev_err(snand->dev, "error reading status register\n");
++			return ret;
++		} else if (!(snand->buf[0] & SPI_NAND_STATUS_REG_BUSY)) {
++			return snand->buf[0];
++		}
++
++		cond_resched();
++	}
++
++	dev_err(snand->dev, "operation timed out\n");
++
++	return -ETIMEDOUT;
++}
++
++static int spi_nand_reset(struct spi_nand *snand)
++{
++	int ret;
++
++	ret = snand->reset(snand);
++	if (ret < 0) {
++		dev_err(snand->dev, "reset command failed\n");
++		return ret;
++	}
++
++	/*
++	 * The NAND core won't wait after a device reset, so we need
++	 * to do that here.
++	 */
++	ret = spi_nand_wait_till_ready(snand);
++	if (ret < 0)
++		return ret;
++	return 0;
++}
++
++static int spi_nand_status(struct spi_nand *snand)
++{
++	int ret, status;
++
++	ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
++	if (ret < 0) {
++		dev_err(snand->dev, "error reading status register\n");
++		return ret;
++	}
++	status = snand->buf[0];
++
++	/* Convert this into standard NAND_STATUS values */
++	if (status & SPI_NAND_STATUS_REG_BUSY)
++		snand->buf[0] = 0;
++	else
++		snand->buf[0] = NAND_STATUS_READY;
++
++	if (status & SPI_NAND_STATUS_REG_PROG_FAIL ||
++	    status & SPI_NAND_STATUS_REG_ERASE_FAIL)
++		snand->buf[0] |= NAND_STATUS_FAIL;
++
++	/*
++	 * Since we unlock the entire device at initialization, unconditionally
++	 * set the WP bit to indicate it's not protected.
++	 */
++	snand->buf[0] |= NAND_STATUS_WP;
++	return 0;
++}
++
++static int spi_nand_erase(struct spi_nand *snand, int page_addr)
++{
++	int ret;
++
++	ret = snand->write_enable(snand);
++	if (ret < 0) {
++		dev_err(snand->dev, "write enable command failed\n");
++		return ret;
++	}
++
++	ret = snand->block_erase(snand, page_addr);
++	if (ret < 0) {
++		dev_err(snand->dev, "block erase command failed\n");
++		return ret;
++	}
++
++	return 0;
++}
++
++static int spi_nand_write(struct spi_nand *snand)
++{
++	int ret;
++
++	/* Enable quad mode */
++	ret = spi_nand_enable_quad(snand);
++	if (ret) {
++		dev_err(snand->dev, "error %d enabling quad mode\n", ret);
++		return ret;
++	}
++	/* Store the page to cache */
++	ret = snand->store_cache(snand, 0, snand->buf_size, snand->data_buf);
++	if (ret < 0) {
++		dev_err(snand->dev, "error %d storing page 0x%x to cache\n",
++			ret, snand->page_addr);
++		return ret;
++	}
++
++	ret = snand->write_enable(snand);
++	if (ret < 0) {
++		dev_err(snand->dev, "write enable command failed\n");
++		return ret;
++	}
++
++	/* Get page from the device cache into our internal buffer */
++	ret = snand->write_page(snand, snand->page_addr);
++	if (ret < 0) {
++		dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
++			ret, snand->page_addr);
++		return ret;
++	}
++
++	return 0;
++}
++
++static int spi_nand_read_id(struct spi_nand *snand)
++{
++	int ret;
++
++	ret = snand->read_id(snand, snand->data_buf);
++	if (ret < 0) {
++		dev_err(snand->dev, "error %d reading ID\n", ret);
++		return ret;
++	}
++	return 0;
++}
++
++static int spi_nand_read_page(struct spi_nand *snand, unsigned int page_addr,
++			      unsigned int page_offset, size_t length)
++{
++	unsigned int corrected = 0, ecc_error = 0;
++	int ret;
++
++	/* Load a page into the cache register */
++	ret = snand->load_page(snand, page_addr);
++	if (ret < 0) {
++		dev_err(snand->dev, "error %d loading page 0x%x to cache\n",
++			ret, page_addr);
++		return ret;
++	}
++
++	ret = spi_nand_wait_till_ready(snand);
++	if (ret < 0)
++		return ret;
++
++	if (snand->ecc) {
++		snand->get_ecc_status(ret, &corrected, &ecc_error);
++		snand->bitflips = corrected;
++
++		/*
++		 * If there's an ECC error, print a message and notify MTD
++		 * about it. Then complete the read, to load actual data on
++		 * the buffer (instead of the status result).
++		 */
++		if (ecc_error) {
++			dev_err(snand->dev,
++				"internal ECC error reading page 0x%x\n",
++				page_addr);
++			snand->nand_chip.mtd.ecc_stats.failed++;
++		} else {
++			snand->nand_chip.mtd.ecc_stats.corrected += corrected;
++		}
++	}
++
++	/* Enable quad mode */
++	ret = spi_nand_enable_quad(snand);
++	if (ret) {
++		dev_err(snand->dev, "error %d enabling quad mode\n", ret);
++		return ret;
++	}
++	/* Get page from the device cache into our internal buffer */
++	ret = snand->read_cache(snand, page_offset, length, snand->data_buf);
++	if (ret < 0) {
++		dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
++			ret, page_addr);
++		return ret;
++	}
++	return 0;
++}
++
++static u8 spi_nand_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct spi_nand *snand = nand_get_controller_data(chip);
++	char val = 0xff;
++
++	if (snand->buf_start < snand->buf_size)
++		val = snand->data_buf[snand->buf_start++];
++	return val;
++}
++
++static void spi_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct spi_nand *snand = nand_get_controller_data(chip);
++	size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
++
++	memcpy(snand->data_buf + snand->buf_start, buf, n);
++	snand->buf_start += n;
++}
++
++static void spi_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct spi_nand *snand = nand_get_controller_data(chip);
++	size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
++
++	memcpy(buf, snand->data_buf + snand->buf_start, n);
++	snand->buf_start += n;
++}
++
++static int spi_nand_write_page_hwecc(struct mtd_info *mtd,
++		struct nand_chip *chip, const uint8_t *buf, int oob_required,
++		int page)
++{
++	chip->write_buf(mtd, buf, mtd->writesize);
++	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
++
++	return 0;
++}
++
++static int spi_nand_read_page_hwecc(struct mtd_info *mtd,
++		struct nand_chip *chip, uint8_t *buf, int oob_required,
++		int page)
++{
++	struct spi_nand *snand = nand_get_controller_data(chip);
++
++	chip->read_buf(mtd, buf, mtd->writesize);
++	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
++
++	return snand->bitflips;
++}
++
++static int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
++{
++	struct spi_nand *snand = nand_get_controller_data(chip);
++	int ret;
++
++	ret = spi_nand_wait_till_ready(snand);
++
++	if (ret < 0) {
++		return NAND_STATUS_FAIL;
++	} else if (ret & SPI_NAND_STATUS_REG_PROG_FAIL) {
++		dev_err(snand->dev, "page program failed\n");
++		return NAND_STATUS_FAIL;
++	} else if (ret & SPI_NAND_STATUS_REG_ERASE_FAIL) {
++		dev_err(snand->dev, "block erase failed\n");
++		return NAND_STATUS_FAIL;
++	}
++
++	return NAND_STATUS_READY;
++}
++
++static void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
++			     int column, int page_addr)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct spi_nand *snand = nand_get_controller_data(chip);
++
++	/*
++	 * In case there's any unsupported command, let's make sure
++	 * we don't keep garbage around in the buffer.
++	 */
++	if (command != NAND_CMD_PAGEPROG) {
++		spi_nand_clear_buffer(snand);
++		snand->page_addr = 0;
++	}
++
++	switch (command) {
++	case NAND_CMD_READ0:
++		spi_nand_read_page(snand, page_addr, 0, mtd->writesize);
++		break;
++	case NAND_CMD_READOOB:
++		spi_nand_disable_ecc(snand);
++		spi_nand_read_page(snand, page_addr, mtd->writesize,
++				   mtd->oobsize);
++		spi_nand_enable_ecc(snand);
++		break;
++	case NAND_CMD_READID:
++		spi_nand_read_id(snand);
++		break;
++	case NAND_CMD_ERASE1:
++		spi_nand_erase(snand, page_addr);
++		break;
++	case NAND_CMD_ERASE2:
++		/* There's nothing to do here, as the erase is one-step */
++		break;
++	case NAND_CMD_SEQIN:
++		snand->buf_start = column;
++		snand->page_addr = page_addr;
++		break;
++	case NAND_CMD_PAGEPROG:
++		spi_nand_write(snand);
++		break;
++	case NAND_CMD_STATUS:
++		spi_nand_status(snand);
++		break;
++	case NAND_CMD_RESET:
++		spi_nand_reset(snand);
++		break;
++	default:
++		dev_err(&mtd->dev, "unknown command 0x%x\n", command);
++	}
++}
++
++static void spi_nand_select_chip(struct mtd_info *mtd, int chip)
++{
++	/* We need this to override the default */
++}
++
++int spi_nand_check(struct spi_nand *snand)
++{
++	if (!snand->dev)
++		return -ENODEV;
++	if (!snand->read_cache)
++		return -ENODEV;
++	if (!snand->load_page)
++		return -ENODEV;
++	if (!snand->store_cache)
++		return -ENODEV;
++	if (!snand->write_page)
++		return -ENODEV;
++	if (!snand->write_reg)
++		return -ENODEV;
++	if (!snand->read_reg)
++		return -ENODEV;
++	if (!snand->block_erase)
++		return -ENODEV;
++	if (!snand->reset)
++		return -ENODEV;
++	if (!snand->write_enable)
++		return -ENODEV;
++	if (!snand->write_disable)
++		return -ENODEV;
++	if (!snand->get_ecc_status)
++		return -ENODEV;
++	return 0;
++}
++
++int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids)
++{
++	struct nand_chip *chip = &snand->nand_chip;
++	struct mtd_info *mtd = nand_to_mtd(chip);
++	struct device_node *np = snand->dev->of_node;
++	const char __maybe_unused *of_mtd_name = NULL;
++	int ret;
++
++	/* Let's check all the hooks are in-place so we don't panic later */
++	ret = spi_nand_check(snand);
++	if (ret)
++		return ret;
++
++	nand_set_controller_data(chip, snand);
++	nand_set_flash_node(chip, np);
++	chip->read_buf	= spi_nand_read_buf;
++	chip->write_buf	= spi_nand_write_buf;
++	chip->read_byte	= spi_nand_read_byte;
++	chip->cmdfunc	= spi_nand_cmdfunc;
++	chip->waitfunc	= spi_nand_waitfunc;
++	chip->select_chip = spi_nand_select_chip;
++	chip->options |= NAND_NO_SUBPAGE_WRITE;
++	chip->bits_per_cell = 1;
++
++	mtd_set_ooblayout(mtd, snand->ooblayout);
++	chip->ecc.read_page	= spi_nand_read_page_hwecc;
++	chip->ecc.write_page	= spi_nand_write_page_hwecc;
++	chip->ecc.mode		= NAND_ECC_HW;
++
++	if (of_property_read_bool(np, "nand-on-flash-bbt"))
++		chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++#ifdef CONFIG_MTD_OF_PARTS
++	of_property_read_string(np, "linux,mtd-name", &of_mtd_name);
++#endif
++	if (of_mtd_name)
++		mtd->name = of_mtd_name;
++	else
++		mtd->name = snand->name;
++	mtd->owner = THIS_MODULE;
++
++	/* Allocate buffer to be used to read/write the internal registers */
++	snand->buf = kmalloc(SPI_NAND_CMD_BUF_LEN, GFP_KERNEL);
++	if (!snand->buf)
++		return -ENOMEM;
++
++	/* This is enabled at device power up but we'd better make sure */
++	ret = spi_nand_enable_ecc(snand);
++	if (ret)
++		return ret;
++
++	/* Preallocate buffer for flash identification (NAND_CMD_READID) */
++	snand->buf_size = SPI_NAND_CMD_BUF_LEN;
++	snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
++
++	ret = nand_scan_ident(mtd, 1, flash_ids);
++	if (ret)
++		return ret;
++
++	/*
++	 * SPI NAND has on-die ECC, which means we can correct as much as
++	 * we are required to. This must be done after identification of
++	 * the device.
++	 */
++	chip->ecc.strength = chip->ecc_strength_ds;
++	chip->ecc.size = chip->ecc_step_ds;
++
++	/*
++	 * Unlock all the device before calling nand_scan_tail. This is needed
++	 * in case the in-flash bad block table needs to be created.
++	 * We could override __nand_unlock(), but since it's not currently used
++	 * by the NAND core we call this explicitly.
++	 */
++	snand->buf[0] = SPI_NAND_PROT_UNLOCK_ALL;
++	ret = snand->write_reg(snand, SPI_NAND_LOCK_REG, snand->buf);
++	if (ret)
++		return ret;
++
++	/* Free the buffer and allocate a good one, to fit a page plus OOB */
++	kfree(snand->data_buf);
++
++	snand->buf_size = mtd->writesize + mtd->oobsize;
++	snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
++	if (!snand->data_buf)
++		return -ENOMEM;
++
++	ret = nand_scan_tail(mtd);
++	if (ret)
++		return ret;
++
++	return mtd_device_register(mtd, NULL, 0);
++}
++EXPORT_SYMBOL_GPL(spi_nand_register);
++
++void spi_nand_unregister(struct spi_nand *snand)
++{
++	kfree(snand->buf);
++	kfree(snand->data_buf);
++}
++EXPORT_SYMBOL_GPL(spi_nand_unregister);
++
++MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia at imgtec.com>");
++MODULE_DESCRIPTION("Framework for SPI NAND");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/include/linux/mtd/spi-nand.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (C) 2014 Imagination Technologies Ltd.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ */
++
++#ifndef __LINUX_MTD_SPI_NAND_H
++#define __LINUX_MTD_SPI_NAND_H
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/rawnand.h>
++
++struct spi_nand {
++	struct nand_chip	nand_chip;
++	struct device		*dev;
++	const char		*name;
++
++	u8			*buf, *data_buf;
++	size_t			buf_size;
++	off_t			buf_start;
++	unsigned int		page_addr;
++	unsigned int		bitflips;
++	bool			ecc;
++	struct mtd_ooblayout_ops *ooblayout;
++
++	int (*reset)(struct spi_nand *snand);
++	int (*read_id)(struct spi_nand *snand, u8 *buf);
++
++	int (*write_disable)(struct spi_nand *snand);
++	int (*write_enable)(struct spi_nand *snand);
++
++	int (*read_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);
++	int (*write_reg)(struct spi_nand *snand, u8 opcode, u8 *buf);
++	void (*get_ecc_status)(unsigned int status,
++			       unsigned int *corrected,
++			       unsigned int *ecc_errors);
++
++	int (*store_cache)(struct spi_nand *snand, unsigned int page_offset,
++			   size_t length, u8 *write_buf);
++	int (*write_page)(struct spi_nand *snand, unsigned int page_addr);
++	int (*load_page)(struct spi_nand *snand, unsigned int page_addr);
++	int (*read_cache)(struct spi_nand *snand, unsigned int page_offset,
++			  size_t length, u8 *read_buf);
++	int (*block_erase)(struct spi_nand *snand, unsigned int page_addr);
++
++	void *priv;
++};
++
++int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids);
++void spi_nand_unregister(struct spi_nand *snand);
++
++#endif
diff --git a/target/linux/pistachio/patches-5.4/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch b/target/linux/pistachio/patches-5.4/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch
new file mode 100644
index 000000000000..1f1461061ca8
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/414-mtd-spi-nand-Support-Gigadevice-GD5F.patch
@@ -0,0 +1,524 @@
+From 7723e59d483a883578115a73eb87eb7fff0ff724 Mon Sep 17 00:00:00 2001
+From: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Date: Tue, 28 Feb 2017 10:37:24 +0000
+Subject: mtd: spi-nand: Support Gigadevice GD5F
+
+This commit uses the recently introduced SPI NAND framework to support
+the Gigadevice GD5F serial NAND device.
+
+The current support includes:
+
+  * Page read and page program operations (using on-die ECC)
+  * Page out-of-band read
+  * Erase
+  * Reset
+  * Device status retrieval
+  * Device ID retrieval
+
+(based on http://lists.infradead.org/pipermail/linux-mtd/2014-December/056769.html)
+
+Signed-off-by: Ezequiel Garcia <ezequiel.garcia at imgtec.com>
+Signed-off-by: Ian Pozella <Ian.Pozella at imgtec.com>
+---
+ drivers/mtd/spi-nand/Kconfig           |  10 +
+ drivers/mtd/spi-nand/Makefile          |   1 +
+ drivers/mtd/spi-nand/spi-nand-device.c | 472 +++++++++++++++++++++++++++++++++
+ 3 files changed, 483 insertions(+)
+ create mode 100644 drivers/mtd/spi-nand/spi-nand-device.c
+
+--- a/drivers/mtd/spi-nand/Kconfig
++++ b/drivers/mtd/spi-nand/Kconfig
+@@ -5,3 +5,13 @@ menuconfig MTD_SPI_NAND
+ 	help
+ 	  This is the framework for the SPI NAND.
+ 
++if MTD_SPI_NAND
++
++config MTD_SPI_NAND_DEVICES
++	tristate "Support for SPI NAND devices"
++	default y
++	depends on MTD_SPI_NAND
++	help
++	  Select this option if you require support for SPI NAND devices.
++
++endif # MTD_SPI_NAND
+--- a/drivers/mtd/spi-nand/Makefile
++++ b/drivers/mtd/spi-nand/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_MTD_SPI_NAND)		+= spi-nand-base.o
++obj-$(CONFIG_MTD_SPI_NAND_DEVICES)     += spi-nand-device.o
+--- /dev/null
++++ b/drivers/mtd/spi-nand/spi-nand-device.c
+@@ -0,0 +1,472 @@
++/*
++ * Copyright (C) 2014 Imagination Technologies Ltd.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * Notes:
++ * 1. We avoid using a stack-allocated buffer for SPI messages. Using
++ *    a kmalloced buffer is probably better, given we shouldn't assume
++ *    any particular usage by SPI core.
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/spi-nand.h>
++#include <linux/sizes.h>
++#include <linux/spi/spi.h>
++
++/* SPI NAND commands */
++#define	SPI_NAND_WRITE_ENABLE		0x06
++#define	SPI_NAND_WRITE_DISABLE		0x04
++#define	SPI_NAND_GET_FEATURE		0x0f
++#define	SPI_NAND_SET_FEATURE		0x1f
++#define	SPI_NAND_PAGE_READ		0x13
++#define	SPI_NAND_READ_CACHE		0x03
++#define	SPI_NAND_FAST_READ_CACHE	0x0b
++#define	SPI_NAND_READ_CACHE_X2		0x3b
++#define	SPI_NAND_READ_CACHE_X4		0x6b
++#define	SPI_NAND_READ_CACHE_DUAL_IO	0xbb
++#define	SPI_NAND_READ_CACHE_QUAD_IO	0xeb
++#define	SPI_NAND_READ_ID		0x9f
++#define	SPI_NAND_PROGRAM_LOAD		0x02
++#define	SPI_NAND_PROGRAM_LOAD4		0x32
++#define	SPI_NAND_PROGRAM_EXEC		0x10
++#define	SPI_NAND_PROGRAM_LOAD_RANDOM	0x84
++#define	SPI_NAND_PROGRAM_LOAD_RANDOM4	0xc4
++#define	SPI_NAND_BLOCK_ERASE		0xd8
++#define	SPI_NAND_RESET			0xff
++
++#define SPI_NAND_GD5F_READID_LEN	2
++
++#define SPI_NAND_GD5F_ECC_MASK		(BIT(0) | BIT(1) | BIT(2))
++#define SPI_NAND_GD5F_ECC_UNCORR	(BIT(0) | BIT(1) | BIT(2))
++#define SPI_NAND_GD5F_ECC_SHIFT		4
++
++static int spi_nand_gd5f_ooblayout_256_ecc(struct mtd_info *mtd, int section,
++					struct mtd_oob_region *oobregion)
++{
++	if (section)
++		return -ERANGE;
++
++	oobregion->offset = 128;
++	oobregion->length = 128;
++
++	return 0;
++}
++
++static int spi_nand_gd5f_ooblayout_256_free(struct mtd_info *mtd, int section,
++					struct mtd_oob_region *oobregion)
++{
++	if (section)
++		return -ERANGE;
++
++	oobregion->offset = 1;
++	oobregion->length = 127;
++
++	return 0;
++}
++
++static const struct mtd_ooblayout_ops spi_nand_gd5f_oob_256_ops = {
++	.ecc = spi_nand_gd5f_ooblayout_256_ecc,
++	.free = spi_nand_gd5f_ooblayout_256_free,
++};
++
++static struct nand_flash_dev spi_nand_flash_ids[] = {
++	{
++		.name = "SPI NAND 512MiB 3,3V",
++		.id = { NAND_MFR_GIGADEVICE, 0xb4 },
++		.chipsize = 512,
++		.pagesize = SZ_4K,
++		.erasesize = SZ_256K,
++		.id_len = 2,
++		.oobsize = 256,
++		.ecc.strength_ds = 8,
++		.ecc.step_ds = 512,
++	},
++	{
++		.name = "SPI NAND 512MiB 1,8V",
++		.id = { NAND_MFR_GIGADEVICE, 0xa4 },
++		.chipsize = 512,
++		.pagesize = SZ_4K,
++		.erasesize = SZ_256K,
++		.id_len = 2,
++		.oobsize = 256,
++		.ecc.strength_ds = 8,
++		.ecc.step_ds = 512,
++	},
++};
++
++enum spi_nand_device_variant {
++	SPI_NAND_GENERIC,
++	SPI_NAND_GD5F,
++};
++
++struct spi_nand_device_cmd {
++
++	/*
++	 * Command and address. I/O errors have been observed if a
++	 * separate spi_transfer is used for command and address,
++	 * so keep them together.
++	 */
++	u32 n_cmd;
++	u8 cmd[5];
++
++	/* Tx data */
++	u32 n_tx;
++	u8 *tx_buf;
++
++	/* Rx data */
++	u32 n_rx;
++	u8 *rx_buf;
++	u8 rx_nbits;
++	u8 tx_nbits;
++};
++
++struct spi_nand_device {
++	struct spi_nand	spi_nand;
++	struct spi_device *spi;
++
++	struct spi_nand_device_cmd cmd;
++};
++
++static int spi_nand_send_command(struct spi_device *spi,
++				 struct spi_nand_device_cmd *cmd)
++{
++	struct spi_message message;
++	struct spi_transfer x[2];
++
++	if (!cmd->n_cmd) {
++		dev_err(&spi->dev, "cannot send an empty command\n");
++		return -EINVAL;
++	}
++
++	if (cmd->n_tx && cmd->n_rx) {
++		dev_err(&spi->dev, "cannot send and receive data at the same time\n");
++		return -EINVAL;
++	}
++
++	spi_message_init(&message);
++	memset(x, 0, sizeof(x));
++
++	/* Command and address */
++	x[0].len = cmd->n_cmd;
++	x[0].tx_buf = cmd->cmd;
++	x[0].tx_nbits = cmd->tx_nbits;
++	spi_message_add_tail(&x[0], &message);
++
++	/* Data to be transmitted */
++	if (cmd->n_tx) {
++		x[1].len = cmd->n_tx;
++		x[1].tx_buf = cmd->tx_buf;
++		x[1].tx_nbits = cmd->tx_nbits;
++		spi_message_add_tail(&x[1], &message);
++	}
++
++	/* Data to be received */
++	if (cmd->n_rx) {
++		x[1].len = cmd->n_rx;
++		x[1].rx_buf = cmd->rx_buf;
++		x[1].rx_nbits = cmd->rx_nbits;
++		spi_message_add_tail(&x[1], &message);
++	}
++
++	return spi_sync(spi, &message);
++}
++
++static int spi_nand_device_reset(struct spi_nand *snand)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 1;
++	cmd->cmd[0] = SPI_NAND_RESET;
++
++	dev_dbg(snand->dev, "%s\n", __func__);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_read_reg(struct spi_nand *snand, u8 opcode, u8 *buf)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 2;
++	cmd->cmd[0] = SPI_NAND_GET_FEATURE;
++	cmd->cmd[1] = opcode;
++	cmd->n_rx = 1;
++	cmd->rx_buf = buf;
++
++	dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_write_reg(struct spi_nand *snand, u8 opcode, u8 *buf)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 2;
++	cmd->cmd[0] = SPI_NAND_SET_FEATURE;
++	cmd->cmd[1] = opcode;
++	cmd->n_tx = 1;
++	cmd->tx_buf = buf;
++
++	dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_write_enable(struct spi_nand *snand)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 1;
++	cmd->cmd[0] = SPI_NAND_WRITE_ENABLE;
++
++	dev_dbg(snand->dev, "%s\n", __func__);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_write_disable(struct spi_nand *snand)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 1;
++	cmd->cmd[0] = SPI_NAND_WRITE_DISABLE;
++
++	dev_dbg(snand->dev, "%s\n", __func__);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_write_page(struct spi_nand *snand,
++				      unsigned int page_addr)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 4;
++	cmd->cmd[0] = SPI_NAND_PROGRAM_EXEC;
++	cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
++	cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
++	cmd->cmd[3] = (u8)(page_addr & 0xff);
++
++	dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_store_cache(struct spi_nand *snand,
++				       unsigned int page_offset, size_t length,
++				       u8 *write_buf)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++	struct spi_device *spi = snand_dev->spi;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 3;
++	cmd->cmd[0] = spi->mode & SPI_TX_QUAD ? SPI_NAND_PROGRAM_LOAD4 :
++			SPI_NAND_PROGRAM_LOAD;
++	cmd->cmd[1] = (u8)((page_offset & 0xff00) >> 8);
++	cmd->cmd[2] = (u8)(page_offset & 0xff);
++	cmd->n_tx = length;
++	cmd->tx_buf = write_buf;
++	cmd->tx_nbits = spi->mode & SPI_TX_QUAD ? 4 : 1;
++
++	dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_load_page(struct spi_nand *snand,
++				     unsigned int page_addr)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 4;
++	cmd->cmd[0] = SPI_NAND_PAGE_READ;
++	cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
++	cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
++	cmd->cmd[3] = (u8)(page_addr & 0xff);
++
++	dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_read_cache(struct spi_nand *snand,
++				      unsigned int page_offset, size_t length,
++				      u8 *read_buf)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++	struct spi_device *spi = snand_dev->spi;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	if ((spi->mode & SPI_RX_DUAL) || (spi->mode & SPI_RX_QUAD))
++		cmd->n_cmd = 5;
++	else
++		cmd->n_cmd = 4;
++	cmd->cmd[0] = (spi->mode & SPI_RX_QUAD) ? SPI_NAND_READ_CACHE_X4 :
++			((spi->mode & SPI_RX_DUAL) ? SPI_NAND_READ_CACHE_X2 :
++			SPI_NAND_READ_CACHE);
++	cmd->cmd[1] = 0; /* dummy byte */
++	cmd->cmd[2] = (u8)((page_offset & 0xff00) >> 8);
++	cmd->cmd[3] = (u8)(page_offset & 0xff);
++	cmd->cmd[4] = 0; /* dummy byte */
++	cmd->n_rx = length;
++	cmd->rx_buf = read_buf;
++	cmd->rx_nbits = (spi->mode & SPI_RX_QUAD) ? 4 :
++			((spi->mode & SPI_RX_DUAL) ? 2 : 1);
++
++	dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_device_block_erase(struct spi_nand *snand,
++				       unsigned int page_addr)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 4;
++	cmd->cmd[0] = SPI_NAND_BLOCK_ERASE;
++	cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
++	cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
++	cmd->cmd[3] = (u8)(page_addr & 0xff);
++
++	dev_dbg(snand->dev, "%s: block 0x%x\n", __func__, page_addr);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static int spi_nand_gd5f_read_id(struct spi_nand *snand, u8 *buf)
++{
++	struct spi_nand_device *snand_dev = snand->priv;
++	struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
++
++	memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
++	cmd->n_cmd = 1;
++	cmd->cmd[0] = SPI_NAND_READ_ID;
++	cmd->n_rx = SPI_NAND_GD5F_READID_LEN;
++	cmd->rx_buf = buf;
++
++	dev_dbg(snand->dev, "%s\n", __func__);
++
++	return spi_nand_send_command(snand_dev->spi, cmd);
++}
++
++static void spi_nand_gd5f_ecc_status(unsigned int status,
++				     unsigned int *corrected,
++				     unsigned int *ecc_error)
++{
++	unsigned int ecc_status = (status >> SPI_NAND_GD5F_ECC_SHIFT) &
++					     SPI_NAND_GD5F_ECC_MASK;
++
++	*ecc_error = (ecc_status == SPI_NAND_GD5F_ECC_UNCORR) ? 1 : 0;
++	if (*ecc_error == 0)
++		*corrected = (ecc_status > 1) ? (2 + ecc_status) : 0;
++}
++
++static int spi_nand_device_probe(struct spi_device *spi)
++{
++	enum spi_nand_device_variant variant;
++	struct spi_nand_device *priv;
++	struct spi_nand *snand;
++	int ret;
++
++	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	snand = &priv->spi_nand;
++
++	snand->read_cache = spi_nand_device_read_cache;
++	snand->load_page = spi_nand_device_load_page;
++	snand->store_cache = spi_nand_device_store_cache;
++	snand->write_page = spi_nand_device_write_page;
++	snand->write_reg = spi_nand_device_write_reg;
++	snand->read_reg = spi_nand_device_read_reg;
++	snand->block_erase = spi_nand_device_block_erase;
++	snand->reset = spi_nand_device_reset;
++	snand->write_enable = spi_nand_device_write_enable;
++	snand->write_disable = spi_nand_device_write_disable;
++	snand->dev = &spi->dev;
++	snand->priv = priv;
++
++	/* This'll mean we won't need to specify any specific compatible string
++	 * for a given device, and instead just support spi-nand.
++	 */
++	variant = spi_get_device_id(spi)->driver_data;
++	switch (variant) {
++	case SPI_NAND_GD5F:
++		snand->read_id = spi_nand_gd5f_read_id;
++		snand->get_ecc_status = spi_nand_gd5f_ecc_status;
++		snand->ooblayout = &spi_nand_gd5f_oob_256_ops;
++		break;
++	default:
++		dev_err(snand->dev, "unknown device\n");
++		return -ENODEV;
++	}
++
++	spi_set_drvdata(spi, snand);
++	priv->spi = spi;
++
++	ret = spi_nand_register(snand, spi_nand_flash_ids);
++	if (ret)
++		return ret;
++	return 0;
++}
++
++static int spi_nand_device_remove(struct spi_device *spi)
++{
++	struct spi_nand *snand = spi_get_drvdata(spi);
++
++	spi_nand_unregister(snand);
++
++	return 0;
++}
++
++const struct spi_device_id spi_nand_id_table[] = {
++	{ "spi-nand", SPI_NAND_GENERIC },
++	{ "gd5f", SPI_NAND_GD5F },
++	{ },
++};
++MODULE_DEVICE_TABLE(spi, spi_nand_id_table);
++
++static struct spi_driver spi_nand_device_driver = {
++	.driver = {
++		.name	= "spi_nand_device",
++		.owner	= THIS_MODULE,
++	},
++	.id_table = spi_nand_id_table,
++	.probe	= spi_nand_device_probe,
++	.remove	= spi_nand_device_remove,
++};
++module_spi_driver(spi_nand_device_driver);
++
++MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia at imgtec.com>");
++MODULE_DESCRIPTION("SPI NAND device support");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/pistachio/patches-5.4/901-MIPS-DTS-img-marduk-add-nor-partition-name.patch b/target/linux/pistachio/patches-5.4/901-MIPS-DTS-img-marduk-add-nor-partition-name.patch
new file mode 100644
index 000000000000..6af11eaa361c
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/901-MIPS-DTS-img-marduk-add-nor-partition-name.patch
@@ -0,0 +1,20 @@
+From b5f49b448cd3c7ab930f1a53c88f739a86071df8 Mon Sep 17 00:00:00 2001
+From: Ian Pozella <Ian.Pozella at imgtec.com>
+Date: Mon, 20 Feb 2017 10:38:07 +0000
+Subject: MIPS: DTS: img: marduk: add nor partition name
+
+Signed-off-by: Ian Pozella <Ian.Pozella at imgtec.com>
+---
+ arch/mips/boot/dts/img/pistachio_marduk.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/mips/boot/dts/img/pistachio_marduk.dts
++++ b/arch/mips/boot/dts/img/pistachio_marduk.dts
+@@ -90,6 +90,7 @@
+ 		compatible = "spansion,s25fl016k", "jedec,spi-nor";
+ 		reg = <0>;
+ 		spi-max-frequency = <50000000>;
++		linux,mtd-name = "spi-nor";
+ 	};
+ };
+ 
diff --git a/target/linux/pistachio/patches-5.4/902-MIPS-DTS-img-marduk-add-nand-device-support.patch b/target/linux/pistachio/patches-5.4/902-MIPS-DTS-img-marduk-add-nand-device-support.patch
new file mode 100644
index 000000000000..bd03f0812634
--- /dev/null
+++ b/target/linux/pistachio/patches-5.4/902-MIPS-DTS-img-marduk-add-nand-device-support.patch
@@ -0,0 +1,30 @@
+From c13cfb3a49cf1578bb85f19a7066f262503a39ba Mon Sep 17 00:00:00 2001
+From: Abhimanyu Vishwakarma <Abhimanyu.Vishwakarma at imgtec.com>
+Date: Thu, 24 Nov 2016 19:26:46 +0530
+Subject: MIPS: DTS: img: marduk: add nand device support
+
+Signed-off-by: Abhimanyu Vishwakarma <Abhimanyu.Vishwakarma at imgtec.com>
+---
+ arch/mips/boot/dts/img/pistachio_marduk.dts | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/arch/mips/boot/dts/img/pistachio_marduk.dts
++++ b/arch/mips/boot/dts/img/pistachio_marduk.dts
+@@ -92,6 +92,17 @@
+ 		spi-max-frequency = <50000000>;
+ 		linux,mtd-name = "spi-nor";
+ 	};
++	flash at 1 {
++		compatible = "gigadevice,gd5f";
++		reg = <1>;
++		spi-max-frequency = <50000000>;
++		nand-on-flash-bbt;
++		spi-rx-bus-width = <2>;
++		spi-tx-bus-width = <4>;
++		#address-cells = <1>;
++		#size-cells = <1>;
++		linux,mtd-name = "spi-nand";
++	};
+ };
+ 
+ &uart0 {
-- 
2.20.1




More information about the openwrt-devel mailing list