summaryrefslogtreecommitdiffstats
path: root/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch
diff options
context:
space:
mode:
Diffstat (limited to 'bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch')
-rw-r--r--bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch4450
1 files changed, 4450 insertions, 0 deletions
diff --git a/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch
new file mode 100644
index 00000000..0b27c085
--- /dev/null
+++ b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0439-mtd-Consolidate-Renesas-RPC-drivers.patch
@@ -0,0 +1,4450 @@
+From 596c2def7a2037f33973c6a064ed54d465a157f0 Mon Sep 17 00:00:00 2001
+From: Valentine Barshak <valentine.barshak@cogentembedded.com>
+Date: Mon, 4 Nov 2019 01:01:19 +0300
+Subject: [PATCH 05/12] mtd: Consolidate Renesas RPC drivers
+
+This consolidates RPC HyperFlash and SPI NOR drivers.
+Flash device detection, and DMA read operation is done
+by the renesas-rpc driver. The operation mode is selected
+based on the child flash device node. If it's "jedec,spi-nor"
+compatible, the "renesas-rpc-qspi" platform device is created.
+If it's "cfi-flash" compatible, the "renesas-rpc-hyperflash"
+device is created instead.
+
+The RPC device compatibility string is adjusted to match U-Boot.
+The following configuration options are available:
+
+ CONFIG_MTD_RENESAS_RPC: enables generic RPC support;
+ CONFIG_MTD_RENESAS_RPC_QSPI: enables SPI NOR support;
+ CONFIG_MTD_RENESAS_RPC_HYPERFLASH: enables HyperFlash support.
+
+Signed-off-by: Valentine Barshak <valentine.barshak@cogentembedded.com>
+---
+ drivers/mtd/Kconfig | 6 -
+ drivers/mtd/Makefile | 1 -
+ drivers/mtd/rpc_hyperflash.c | 976 ------------------
+ drivers/mtd/spi-nor/Kconfig | 22 +-
+ drivers/mtd/spi-nor/Makefile | 4 +-
+ drivers/mtd/spi-nor/renesas-rpc-hyperflash.c | 742 ++++++++++++++
+ drivers/mtd/spi-nor/renesas-rpc-qspi.c | 794 +++++++++++++++
+ drivers/mtd/spi-nor/renesas-rpc.c | 1356 ++++----------------------
+ drivers/mtd/spi-nor/renesas-rpc.h | 267 +++++
+ 9 files changed, 1991 insertions(+), 2177 deletions(-)
+ delete mode 100644 drivers/mtd/rpc_hyperflash.c
+ create mode 100644 drivers/mtd/spi-nor/renesas-rpc-hyperflash.c
+ create mode 100644 drivers/mtd/spi-nor/renesas-rpc-qspi.c
+ create mode 100644 drivers/mtd/spi-nor/renesas-rpc.h
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index 0619e1f..89925b5 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -11,12 +11,6 @@ menuconfig MTD
+ particular hardware and users of MTD devices. If unsure, say N.
+
+ if MTD
+-config MTD_RPC_HYPERFLASH
+- tristate "MTD Renesas R-Car Gen3 RPC HyperFlash"
+- depends on ARCH_R8A7795
+- ---help---
+- This option includes Renesas R-Car Gen3 RPC HyperFlash support.
+-
+ config MTD_TESTS
+ tristate "MTD tests support (DANGEROUS)"
+ depends on m
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index f3fb2b0..d6f8f62 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+ obj-y += parsers/
+-obj-$(CONFIG_MTD_RPC_HYPERFLASH) += rpc_hyperflash.o
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
+diff --git a/drivers/mtd/rpc_hyperflash.c b/drivers/mtd/rpc_hyperflash.c
+deleted file mode 100644
+index cf4d56e..0000000
+--- a/drivers/mtd/rpc_hyperflash.c
++++ /dev/null
+@@ -1,976 +0,0 @@
+-/*
+- * Linux driver for R-Car Gen3 RPC HyperFlash
+- *
+- * Copyright (C) 2016 Renesas Electronics Corporation
+- * Copyright (C) 2016 Cogent Embedded, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+- */
+-
+-#include <linux/delay.h>
+-#include <linux/io.h>
+-#include <linux/module.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/mtd/partitions.h>
+-#include <linux/of.h>
+-#include <linux/rwsem.h>
+-#include <linux/slab.h>
+-
+-/* RPC */
+-#define RPC_BASE 0xEE200000
+-#define RPC_SIZE 0x8100
+-#define RPC_FLASH_BASE 0x08000000
+-#define RPC_FLASH_SIZE 0x04000000
+-
+-#define RPC_CMNCR 0x0000 /* R/W */
+-#define RPC_CMNCR_MD (0x1 << 31)
+-#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16)
+-#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18)
+-#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20)
+-#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22)
+-#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \
+- RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
+-#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8)
+-#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12)
+-#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14)
+-#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \
+- RPC_CMNCR_IO3FV(3))
+-#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0)
+-
+-#define RPC_SSLDR 0x0004 /* R/W */
+-#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16)
+-#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8)
+-#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0)
+-
+-#define RPC_DRCR 0x000C /* R/W */
+-#define RPC_DRCR_SSLN (0x1 << 24)
+-#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16)
+-#define RPC_DRCR_RCF (0x1 << 9)
+-#define RPC_DRCR_RBE (0x1 << 8)
+-#define RPC_DRCR_SSLE (0x1 << 0)
+-
+-#define RPC_DRCMR 0x0010 /* R/W */
+-#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16)
+-#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0)
+-
+-#define RPC_DREAR 0x0014 /* R/W */
+-#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16)
+-#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0)
+-
+-#define RPC_DROPR 0x0018 /* R/W */
+-#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24)
+-#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16)
+-#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8)
+-#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0)
+-
+-#define RPC_DRENR 0x001C /* R/W */
+-#define RPC_DRENR_CDB(o) (((o) & 0x3) << 30)
+-#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28)
+-#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24)
+-#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20)
+-#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16)
+-#define RPC_DRENR_DME (0x1 << 15)
+-#define RPC_DRENR_CDE (0x1 << 14)
+-#define RPC_DRENR_OCDE (0x1 << 12)
+-#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8)
+-#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4)
+-
+-#define RPC_SMCR 0x0020 /* R/W */
+-#define RPC_SMCR_SSLKP (0x1 << 8)
+-#define RPC_SMCR_SPIRE (0x1 << 2)
+-#define RPC_SMCR_SPIWE (0x1 << 1)
+-#define RPC_SMCR_SPIE (0x1 << 0)
+-
+-#define RPC_SMCMR 0x0024 /* R/W */
+-#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16)
+-#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0)
+-
+-#define RPC_SMADR 0x0028 /* R/W */
+-#define RPC_SMOPR 0x002C /* R/W */
+-#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0)
+-#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8)
+-#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16)
+-#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24)
+-
+-#define RPC_SMENR 0x0030 /* R/W */
+-#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30)
+-#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28)
+-#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24)
+-#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20)
+-#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16)
+-#define RPC_SMENR_DME (0x1 << 15)
+-#define RPC_SMENR_CDE (0x1 << 14)
+-#define RPC_SMENR_OCDE (0x1 << 12)
+-#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8)
+-#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4)
+-#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0)
+-
+-#define RPC_SMRDR0 0x0038 /* R */
+-#define RPC_SMRDR1 0x003C /* R */
+-#define RPC_SMWDR0 0x0040 /* R/W */
+-#define RPC_SMWDR1 0x0044 /* R/W */
+-#define RPC_CMNSR 0x0048 /* R */
+-#define RPC_CMNSR_SSLF (0x1 << 1)
+-#define RPC_CMNSR_TEND (0x1 << 0)
+-
+-#define RPC_DRDMCR 0x0058 /* R/W */
+-#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0)
+-
+-#define RPC_DRDRENR 0x005C /* R/W */
+-#define RPC_DRDRENR_HYPE (0x5 << 12)
+-#define RPC_DRDRENR_ADDRE (0x1 << 0x8)
+-#define RPC_DRDRENR_OPDRE (0x1 << 0x4)
+-#define RPC_DRDRENR_DRDRE (0x1 << 0x0)
+-
+-#define RPC_SMDMCR 0x0060 /* R/W */
+-#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0)
+-
+-#define RPC_SMDRENR 0x0064 /* R/W */
+-#define RPC_SMDRENR_HYPE (0x5 << 12)
+-#define RPC_SMDRENR_ADDRE (0x1 << 0x8)
+-#define RPC_SMDRENR_OPDRE (0x1 << 0x4)
+-#define RPC_SMDRENR_SPIDRE (0x1 << 0x0)
+-
+-#define RPC_PHYCNT 0x007C /* R/W */
+-#define RPC_PHYCNT_CAL (0x1 << 31)
+-#define PRC_PHYCNT_OCTA_AA (0x1 << 22)
+-#define PRC_PHYCNT_OCTA_SA (0x2 << 22)
+-#define PRC_PHYCNT_EXDS (0x1 << 21)
+-#define RPC_PHYCNT_OCT (0x1 << 20)
+-#define RPC_PHYCNT_WBUF2 (0x1 << 4)
+-#define RPC_PHYCNT_WBUF (0x1 << 2)
+-#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0)
+-
+-#define RPC_PHYINT 0x0088 /* R/W */
+-#define RPC_PHYINT_RSTEN (0x1 << 18)
+-#define RPC_PHYINT_WPEN (0x1 << 17)
+-#define RPC_PHYINT_INTEN (0x1 << 16)
+-#define RPC_PHYINT_RST (0x1 << 2)
+-#define RPC_PHYINT_WP (0x1 << 1)
+-#define RPC_PHYINT_INT (0x1 << 0)
+-
+-#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */
+-#define RPC_WBUF_SIZE 0x100
+-
+-struct rpc_info {
+- struct rw_semaphore lock;
+- void __iomem *rpc_base;
+- void __iomem *flash_base;
+- struct resource *rpc_res;
+- struct resource *flash_res;
+- u32 flash_id;
+- struct mtd_info mtd;
+-};
+-
+-static inline void __iomem *rpc_addr(struct rpc_info *info, u32 offset)
+-{
+- return info->rpc_base + offset;
+-}
+-
+-static inline u32 rpc_readl(struct rpc_info *info, u32 offset)
+-{
+- u32 val;
+-
+- val = readl(rpc_addr(info, offset));
+- return val;
+-}
+-
+-static inline void rpc_writel(struct rpc_info *info, u32 offset, u32 val)
+-{
+- writel(val, rpc_addr(info, offset));
+-}
+-
+-static inline void rpc_setl(struct rpc_info *info, u32 offset, u32 mask, u32 set)
+-{
+- void __iomem *addr;
+- u32 val;
+-
+- addr = rpc_addr(info, offset);
+- val = readl(addr);
+- val &= mask;
+- val |= set;
+- writel(val, addr);
+-}
+-
+-static void rpc_wait_tend(struct rpc_info *info)
+-{
+- while (!(rpc_readl(info, RPC_CMNSR) & RPC_CMNSR_TEND))
+- cpu_relax();
+-}
+-
+-/* RPC HyperFlash */
+-#define RPC_HF_CMD_CA47 (0x1 << 7) /* Read */
+-#define RPC_HF_CMD_CA46 (0x1 << 6) /* Register space */
+-#define RPC_HF_CMD_CA45 (0x1 << 5) /* Liner burst */
+-
+-#define RPC_HF_CMD_READ_REG (RPC_HF_CMD_CA47 | RPC_HF_CMD_CA46)
+-#define RPC_HF_CMD_READ_MEM RPC_HF_CMD_CA47
+-#define RPC_HF_CMD_WRITE_REG RPC_HF_CMD_CA46
+-#define RPC_HF_CMD_WRITE_MEM 0x0
+-
+-#define RPC_HF_ERASE_SIZE 0x40000
+-
+-#define RPC_CFI_STATUS_DRB (0x1 << 7)
+-#define RPC_CFI_STATUS_ESSB (0x1 << 6)
+-#define RPC_CFI_STATUS_ESB (0x1 << 5)
+-#define RPC_CFI_STATUS_PSB (0x1 << 4)
+-#define RPC_CFI_STATUS_WBASB (0x1 << 3)
+-#define RPC_CFI_STATUS_PSSB (0x1 << 2)
+-#define RPC_CFI_STATUS_SLSB (0x1 << 1)
+-#define RPC_CFI_STATUS_ESTAT (0x1 << 0)
+-
+-#define RPC_CFI_UNLOCK1 (0x555 << 1)
+-#define RPC_CFI_UNLOCK2 (0x2AA << 1)
+-
+-#define RPC_CFI_CMD_UNLOCK_START 0xAA
+-#define RPC_CFI_CMD_UNLOCK_ACK 0x55
+-#define RPC_CFI_CMD_RESET 0xF0
+-#define RPC_CFI_CMD_READ_STATUS 0x70
+-#define RPC_CFI_CMD_READ_ID 0x90
+-#define RPC_CFI_CMD_WRITE 0xA0
+-#define RPC_CFI_CMD_ERASE_START 0x80
+-#define RPC_CFI_CMD_ERASE_SECTOR 0x30
+-
+-#define RPC_CFI_ID_MASK 0x000F
+-#define RPC_CFI_ID_MAN_SPANSION 0x0001
+-#define RPC_CFI_ID_TYPE_HYPERFLASH 0x000E
+-
+-enum rpc_hf_size {
+- RPC_HF_SIZE_16BIT = RPC_SMENR_SPIDE(0x8),
+- RPC_HF_SIZE_32BIT = RPC_SMENR_SPIDE(0xC),
+- RPC_HF_SIZE_64BIT = RPC_SMENR_SPIDE(0xF),
+-};
+-
+-struct rpc_info *rpc_info;
+-
+-static void rpc_hf_mode_man(struct rpc_info *info)
+-{
+- rpc_wait_tend(info);
+-
+- /*
+- * RPC_PHYCNT = 0x80000263
+- * bit31 CAL = 1 : PHY calibration
+- * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
+- */
+- rpc_setl(info, RPC_PHYCNT,
+- ~(RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 |
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3)),
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
+-
+- /*
+- * RPC_CMNCR = 0x81FFF301
+- * bit31 MD = 1 : Manual mode
+- * bit1-0 BSZ[1:0] = 01 : QSPI Flash x 2 or HyperFlash
+- */
+- rpc_setl(info, RPC_CMNCR,
+- ~(RPC_CMNCR_MD | RPC_CMNCR_BSZ(3)),
+- RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
+- RPC_CMNCR_MD | RPC_CMNCR_BSZ(1));
+-}
+-
+-static void rpc_hf_mode_ext(struct rpc_info *info)
+-{
+- rpc_wait_tend(info);
+-
+- /*
+- * RPC_PHYCNT = 0x80000263
+- * bit31 CAL = 1 : PHY calibration
+- * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
+- */
+- rpc_setl(info, RPC_PHYCNT,
+- ~(RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 |
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3)),
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
+-
+- /*
+- * RPC_CMNCR = 0x81FFF301
+- * bit31 MD = 1 : Manual mode
+- * bit1-0 BSZ[1:0] = 01 : QSPI Flash x 2 or HyperFlash
+- */
+- rpc_setl(info, RPC_CMNCR,
+- ~(RPC_CMNCR_MD | RPC_CMNCR_BSZ(3)),
+- RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
+- RPC_CMNCR_BSZ(1));
+-
+- /*
+- * RPC_DRCR = 0x001F0100
+- * bit21-16 RBURST[4:0] = 11111 : Read burst 32 64-bit data units
+- * bit9 RCF = 1 : Clear cache
+- * bit8 RBE = 1 : Read burst enable
+- */
+- rpc_writel(info, RPC_DRCR,
+- RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE);
+-
+- rpc_writel(info, RPC_DRCMR, RPC_DRCMR_CMD(0xA0));
+- rpc_writel(info, RPC_DRENR,
+- RPC_DRENR_CDB(2) | RPC_DRENR_OCDB(2) |
+- RPC_DRENR_ADB(2) | RPC_DRENR_SPIDB(2) |
+- RPC_DRENR_CDE | RPC_DRENR_OCDE | RPC_DRENR_ADE(4));
+- rpc_writel(info, RPC_DRDMCR, RPC_DRDMCR_DMCYC(0xE));
+- rpc_writel(info, RPC_DRDRENR,
+- RPC_DRDRENR_HYPE | RPC_DRDRENR_ADDRE | RPC_DRDRENR_DRDRE);
+-
+- /* Dummy read */
+- rpc_readl(info, RPC_DRCR);
+-}
+-
+-static void rpc_hf_xfer(struct rpc_info *info, u32 addr, u16 *data,
+- enum rpc_hf_size size, u8 cmd)
+-{
+- u32 val;
+-
+- rpc_hf_mode_man(info);
+-
+- /*
+- * bit23-21 CMD[7:5] : CA47-45
+- * CA47 = 0/1 : Write/Read
+- * CA46 = 0/1 : Memory Space/Register Space
+- * CA45 = 0/1 : Wrapped Burst/Linear Burst
+- */
+- rpc_writel(info, RPC_SMCMR, RPC_SMCMR_CMD(cmd));
+-
+- rpc_writel(info, RPC_SMADR, addr >> 1);
+-
+- rpc_writel(info, RPC_SMOPR, 0x0);
+-
+- /*
+- * RPC_SMDRENR = 0x00005101
+- * bit14-12 HYPE = 101: Hyperflash mode
+- * bit8 ADDRE = 1 : Address DDR transfer
+- * bit0 SPIDRE = 1 : DATA DDR transfer
+- */
+- rpc_writel(info, RPC_SMDRENR,
+- RPC_SMDRENR_HYPE | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE);
+-
+- val = RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) |
+- RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) |
+- RPC_SMENR_CDE | RPC_SMENR_OCDE | RPC_SMENR_ADE(4) | size;
+-
+- if (cmd & RPC_HF_CMD_CA47)
+- goto read_transfer;
+-
+- /*
+- * RPC_SMENR = 0xA222540x
+- * bit31-30 CDB[1:0] = 10 : 4bit width command
+- * bit25-24 ADB[1:0] = 10 : 4bit width address
+- * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
+- * bit15 DME = 0 : dummy cycle disable
+- * bit14 CDE = 1 : Command enable
+- * bit12 OCDE = 1 : Option Command enable
+- * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output
+- * bit7-4 OPDE[3:0] = 0000 : Option data disable
+- * bit3-0 SPIDE[3:0] = xxxx : Transfer size
+- */
+- rpc_writel(info, RPC_SMENR, val);
+-
+- switch (size) {
+- case RPC_HF_SIZE_64BIT:
+- val = cmd & RPC_HF_CMD_CA46 ?
+- cpu_to_be16(data[0]) | cpu_to_be16(data[1]) << 16 :
+- data[0] | data[1] << 16;
+- rpc_writel(info, RPC_SMWDR1, val);
+- val = cmd & RPC_HF_CMD_CA46 ?
+- cpu_to_be16(data[2]) | cpu_to_be16(data[3]) << 16 :
+- data[2] | data[3] << 16;
+- break;
+- case RPC_HF_SIZE_32BIT:
+- val = cmd & RPC_HF_CMD_CA46 ?
+- cpu_to_be16(data[0]) | cpu_to_be16(data[1]) << 16 :
+- data[0] | data[1] << 16;
+- break;
+- default:
+- val = cmd & RPC_HF_CMD_CA46 ?
+- cpu_to_be16(data[0]) << 16 :
+- data[0] << 16;
+- break;
+- }
+-
+- rpc_writel(info, RPC_SMWDR0, val);
+- /*
+- * RPC_SMCR = 0x00000003
+- * bit1 SPIWE = 1 : Data write enable
+- * bit0 SPIE = 1 : SPI transfer start
+- */
+- rpc_writel(info, RPC_SMCR, RPC_SMCR_SPIWE | RPC_SMCR_SPIE);
+- return;
+-
+-read_transfer:
+- rpc_writel(info, RPC_SMDMCR, RPC_SMDMCR_DMCYC(0xE));
+- val |= RPC_SMENR_DME;
+-
+- /*
+- * RPC_SMENR = 0xA222D40x
+- * bit31-30 CDB[1:0] = 10 : 4bit width command
+- * bit25-24 ADB[1:0] = 10 : 4bit width address
+- * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
+- * bit15 DME = 1 : dummy cycle disable
+- * bit14 CDE = 1 : Command enable
+- * bit12 OCDE = 1 : Option Command enable
+- * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output (24 Bit Address)
+- * bit7-4 OPDE[3:0] = 0000 : Option data disable
+- * bit3-0 SPIDE[3:0] = xxxx : Transfer size
+- */
+- rpc_writel(info, RPC_SMENR, val);
+-
+- /*
+- * RPC_SMCR = 0x00000005
+- * bit2 SPIRE = 1 : Data read disable
+- * bit0 SPIE = 1 : SPI transfer start
+- */
+- rpc_writel(info, RPC_SMCR, RPC_SMCR_SPIRE | RPC_SMCR_SPIE);
+-
+- rpc_wait_tend(info);
+- val = rpc_readl(info, RPC_SMRDR0);
+-
+- /*
+- * Read data from either register or memory space.
+- * Register space is always big-endian.
+- */
+- switch (size) {
+- case RPC_HF_SIZE_64BIT:
+- if (cmd & RPC_HF_CMD_CA46) {
+- data[3] = be16_to_cpu((val >> 16) & 0xFFFF);
+- data[2] = be16_to_cpu(val & 0xFFFF);
+- } else {
+- data[3] = (val >> 16) & 0xFFFF;
+- data[2] = val & 0xFFFF;
+- }
+- val = rpc_readl(info, RPC_SMRDR1);
+- if (cmd & RPC_HF_CMD_CA46) {
+- data[1] = be16_to_cpu((val >> 16) & 0xFFFF);
+- data[0] = be16_to_cpu(val & 0xFFFF);
+- } else {
+- data[1] = (val >> 16) & 0xFFFF;
+- data[0] = val & 0xFFFF;
+- }
+- break;
+- case RPC_HF_SIZE_32BIT:
+- if (cmd & RPC_HF_CMD_CA46) {
+- data[1] = be16_to_cpu((val >> 16) & 0xFFFF);
+- data[0] = be16_to_cpu(val & 0xFFFF);
+- } else {
+- data[1] = (val >> 16) & 0xFFFF;
+- data[0] = val & 0xFFFF;
+- }
+- break;
+- default:
+- data[0] = cmd & RPC_HF_CMD_CA46 ?
+- be16_to_cpu((val >> 16) & 0xFFFF) :
+- (val >> 16) & 0xFFFF;
+- break;
+- }
+-}
+-
+-static void rpc_hf_wbuf_enable(struct rpc_info *info)
+-{
+- rpc_wait_tend(info);
+-
+- /*
+- * RPC_PHYCNT = 0x80000277
+- * bit31 CAL = 1 : PHY calibration
+- * bit4 WBUF2 = 1 : Write buffer enable 2
+- * bit2 WBUF = 1 : Write buffer enable
+- * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
+- */
+- rpc_setl(info, RPC_PHYCNT,
+- ~(RPC_PHYCNT_WBUF2 | RPC_PHYCNT_WBUF |
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3)),
+- RPC_PHYCNT_WBUF2 | RPC_PHYCNT_WBUF |
+- RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
+-
+- /*
+- * RPC_DRCR = 0x001F0100
+- * bit21-16 RBURST[4:0] = 11111 : Read burst 32 64-bit data units
+- * bit9 RCF = 1 : Clear cache
+- * bit8 RBE = 1 : Read burst enable
+- */
+- rpc_writel(info, RPC_DRCR,
+- RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE);
+-
+- rpc_writel(info, RPC_SMCMR, RPC_SMCMR_CMD(RPC_HF_CMD_WRITE_MEM));
+-
+- rpc_writel(info, RPC_SMOPR, 0x0);
+-
+- /*
+- * RPC_SMDRENR = 0x00005101
+- * bit14-12 HYPE = 101:Hyperflash mode
+- * bit8 ADDRE = 1 : Address DDR transfer
+- * bit0 SPIDRE = 1 : DATA DDR transfer
+- */
+- rpc_writel(info, RPC_SMDRENR,
+- RPC_SMDRENR_HYPE | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE);
+-
+- /*
+- * RPC_SMENR = 0xA222540F
+- * bit31-30 CDB[1:0] = 10 : 4bit width command
+- * bit25-24 ADB[1:0] = 10 : 4bit width address
+- * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
+- * bit15 DME = 0 : dummy cycle disable
+- * bit14 CDE = 1 : Command enable
+- * bit12 OCDE = 1 : Option Command enable
+- * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output (24 Bit Address)
+- * bit7-4 OPDE[3:0] = 0000 : Option data disable
+- * bit3-0 SPIDE[3:0] = 1111 : 64-bit transfer size
+- */
+- rpc_writel(info, RPC_SMENR,
+- RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) |
+- RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) |
+- RPC_SMENR_CDE | RPC_SMENR_OCDE |
+- RPC_SMENR_ADE(4) | RPC_HF_SIZE_64BIT);
+-
+- /* Dummy read */
+- rpc_readl(info, RPC_DRCR);
+-}
+-
+-static inline void rpc_hf_write_cmd(struct rpc_info *info, u32 addr, u16 cmd)
+-{
+- rpc_hf_xfer(info, addr, &cmd, RPC_HF_SIZE_16BIT, RPC_HF_CMD_WRITE_REG);
+-}
+-
+-static inline void rpc_hf_read_reg(struct rpc_info *info, u32 addr, u16 *data,
+- enum rpc_hf_size size)
+-{
+- rpc_hf_xfer(info, addr, data, size, RPC_HF_CMD_READ_REG);
+-}
+-
+-static inline void rpc_hf_write_reg(struct rpc_info *info, u32 addr, u16 *data,
+- enum rpc_hf_size size)
+-{
+- rpc_hf_xfer(info, addr, data, size, RPC_HF_CMD_WRITE_REG);
+-}
+-
+-static inline void rpc_hf_read_mem(struct rpc_info *info, u32 addr, u16 *data,
+- enum rpc_hf_size size)
+-{
+- rpc_hf_xfer(info, addr, data, size, RPC_HF_CMD_READ_MEM);
+-}
+-
+-static inline void rpc_hf_write_mem(struct rpc_info *info, u32 addr, u16 *data,
+- enum rpc_hf_size size)
+-{
+- rpc_hf_xfer(info, addr, data, size, RPC_HF_CMD_WRITE_MEM);
+-}
+-
+-static void rpc_hf_wp(struct rpc_info *info, int enable)
+-{
+- rpc_setl(info, RPC_PHYINT, ~RPC_PHYINT_WP, enable ? RPC_PHYINT_WP : 0);
+-}
+-
+-static void rpc_hf_unlock(struct rpc_info *info, u32 addr)
+-{
+- rpc_hf_write_cmd(info, addr + RPC_CFI_UNLOCK1,
+- RPC_CFI_CMD_UNLOCK_START);
+- rpc_hf_write_cmd(info, addr + RPC_CFI_UNLOCK2,
+- RPC_CFI_CMD_UNLOCK_ACK);
+-}
+-
+-static inline int rpc_hf_status(struct rpc_info *info, u32 addr,
+- int iterations, int delay)
+-{
+- int retval;
+- u16 status = 0;
+-
+- while (iterations-- > 0) {
+- rpc_hf_write_cmd(info, addr + RPC_CFI_UNLOCK1, RPC_CFI_CMD_READ_STATUS);
+- rpc_hf_read_reg(info, addr, &status, RPC_HF_SIZE_16BIT);
+-
+- if (status & RPC_CFI_STATUS_DRB)
+- break;
+-
+- if (delay < 10000)
+- usleep_range(delay, delay * 2);
+- else
+- msleep(delay / 1000);
+- }
+-
+- if (!(status & RPC_CFI_STATUS_DRB)) {
+- retval = -ETIMEDOUT;
+- goto out;
+- }
+-
+- if (status & (RPC_CFI_STATUS_PSB | RPC_CFI_STATUS_ESB)) {
+- retval = -EIO;
+- goto out;
+- }
+-
+- return 0;
+-
+-out:
+- /* Reset the flash */
+- rpc_hf_write_cmd(info, 0, RPC_CFI_CMD_RESET);
+- return retval;
+-}
+-
+-static int rpc_hf_sector_erase(struct rpc_info *info, u32 addr)
+-{
+- rpc_hf_unlock(info, addr);
+- rpc_hf_write_cmd(info, addr + RPC_CFI_UNLOCK1, RPC_CFI_CMD_ERASE_START);
+- rpc_hf_unlock(info, addr);
+- rpc_hf_write_cmd(info, addr, RPC_CFI_CMD_ERASE_SECTOR);
+-
+- return rpc_hf_status(info, addr, 1000, 10000);
+-}
+-
+-/* Flash read */
+-static int rpc_hf_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+- size_t *retlen, u_char *buf)
+-{
+- struct rpc_info *info = mtd->priv;
+-
+- down_read(&info->lock);
+- memcpy_fromio(buf, info->flash_base + from, len);
+- up_read(&info->lock);
+-
+- *retlen = len;
+- return 0;
+-}
+-
+-/* Flash erase */
+-static int rpc_hf_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+-{
+- struct rpc_info *info = mtd->priv;
+- u32 addr, end;
+- int retval = 0;
+-
+- if (mtd_mod_by_eb(instr->addr, mtd)) {
+- pr_debug("%s: unaligned address\n", __func__);
+- return -EINVAL;
+- }
+-
+- if (mtd_mod_by_eb(instr->len, mtd)) {
+- pr_debug("%s: unaligned length\n", __func__);
+- return -EINVAL;
+- }
+-
+- end = instr->addr + instr->len;
+-
+- down_write(&info->lock);
+- for (addr = instr->addr; addr < end; addr += mtd->erasesize) {
+- retval = rpc_hf_sector_erase(info, addr);
+-
+- if (retval)
+- break;
+- }
+-
+- rpc_hf_mode_ext(info);
+- up_write(&info->lock);
+-
+- instr->state = retval ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
+- mtd_erase_callback(instr);
+-
+- return retval;
+-}
+-
+-/* Copy memory to flash */
+-static int rpc_hf_mtd_write(struct mtd_info *mtd, loff_t offset, size_t len,
+- size_t *retlen, const u_char *src)
+-{
+- struct rpc_info *info = mtd->priv;
+- union {
+- u8 b[4];
+- u16 w[2];
+- u32 d;
+- } data;
+- loff_t addr;
+- size_t size, cnt;
+- int retval, idx;
+- u8 last;
+-
+- retval = 0;
+- *retlen = 0;
+- cnt = len;
+- idx = 0;
+-
+- down_write(&info->lock);
+-
+- /* Handle unaligned start */
+- if (offset & 0x1) {
+- offset--;
+- data.b[idx] = readb(info->flash_base + offset);
+- idx++;
+- }
+-
+- /* Handle unaligned end */
+- addr = offset + idx + len;
+- last = addr & 0x1 ? readb(info->flash_base + addr) : 0xFF;
+-
+- addr = offset - mtd_mod_by_eb(offset, mtd);
+- size = mtd->erasesize - (offset - addr);
+-
+- while (cnt) {
+- if (size > cnt)
+- size = cnt;
+-
+- cnt -= size;
+- while (size) {
+- rpc_hf_unlock(info, addr);
+- rpc_hf_write_cmd(info,
+- addr + RPC_CFI_UNLOCK1,
+- RPC_CFI_CMD_WRITE);
+-
+- if (size > 0x7) {
+- u32 wbuf = RPC_WBUF;
+- int block = size >= RPC_WBUF_SIZE ?
+- RPC_WBUF_SIZE : size & ~0x7;
+-
+- rpc_hf_wbuf_enable(info);
+-
+- rpc_writel(info, RPC_SMADR, offset >> 1);
+- offset += block;
+-
+- block >>= 3;
+- while (block--) {
+- while (idx < 4) {
+- data.b[idx++] = *src++;
+- size--;
+- }
+- rpc_writel(info, wbuf, data.d);
+- wbuf += 4;
+-
+- idx = 0;
+- while (idx < 4) {
+- data.b[idx++] = *src++;
+- size--;
+- }
+- rpc_writel(info, wbuf, data.d);
+- wbuf += 4;
+-
+- idx = 0;
+- }
+-
+- rpc_writel(info, RPC_SMCR,
+- RPC_SMCR_SPIWE | RPC_SMCR_SPIE);
+- } else {
+- enum rpc_hf_size bits;
+-
+- while (idx < 4) {
+- data.b[idx++] = *src++;
+- size--;
+-
+- if (!size)
+- break;
+- }
+-
+- if (idx & 0x1)
+- data.b[idx++] = last;
+-
+- switch (idx) {
+- case 2:
+- bits = RPC_HF_SIZE_16BIT;
+- break;
+- default:
+- bits = RPC_HF_SIZE_32BIT;
+- break;
+- }
+-
+- rpc_hf_write_mem(info, offset, data.w, bits);
+- offset += idx;
+- idx = 0;
+- }
+-
+- retval = rpc_hf_status(info, addr, 1000000, 10);
+- if (retval)
+- goto out;
+- }
+-
+- size = mtd->erasesize;
+- addr += size;
+- offset = addr;
+- *retlen = len - cnt;
+- }
+-
+-out:
+- rpc_hf_mode_ext(info);
+- up_write(&info->lock);
+- return retval;
+-}
+-
+-static struct mtd_partition partition_info[]={
+- {
+- .name = "bootparam",
+- .offset = 0,
+- .size = 0x40000,
+- }, {
+- .name = "bl2",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x140000
+- }, {
+- .name = "cert_header",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x40000,
+- }, {
+- .name = "bl31",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x40000,
+- }, {
+- .name = "optee",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x440000,
+- }, {
+- .name = "u-boot",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x80000,
+- }, {
+- .name = "reserved",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x80000,
+- }, {
+- .name = "u-boot-env",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x40000,
+- }, {
+- .name = "dtb",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x80000,
+- }, {
+- .name = "kernel",
+- .offset = MTDPART_OFS_APPEND,
+- .size = 0x1000000,
+- }, {
+- .name = "user",
+- .offset = MTDPART_OFS_APPEND,
+- .size = MTDPART_SIZ_FULL,
+- },
+-};
+-
+-static int rpc_hf_init_mtd(struct rpc_info *info)
+-{
+- struct mtd_info *mtd = &info->mtd;
+- u16 data[2] = { 0, 0 };
+- u32 id, size;
+- int retval;
+-
+- rpc_hf_mode_ext(info);
+-
+- rpc_hf_wp(info, 0);
+-
+- rpc_hf_unlock(info, 0);
+- rpc_hf_write_cmd(info, RPC_CFI_UNLOCK1, RPC_CFI_CMD_READ_ID);
+-
+- rpc_hf_read_reg(info, 0x0, data, RPC_HF_SIZE_32BIT);
+- if ((data[0] & RPC_CFI_ID_MASK) != RPC_CFI_ID_MAN_SPANSION ||
+- (data[1] & RPC_CFI_ID_MASK) != RPC_CFI_ID_TYPE_HYPERFLASH) {
+- retval = -ENODEV;
+- goto out;
+- }
+-
+- id = data[0] | data[1] << 16;
+-
+- rpc_hf_read_reg(info, 0x27 << 1, data, RPC_HF_SIZE_16BIT);
+- size = 1 << data[0];
+-
+- if (size > resource_size(info->flash_res))
+- size = resource_size(info->flash_res);
+-
+- if (size & (RPC_HF_ERASE_SIZE - 1)) {
+- retval = -EINVAL;
+- goto out;
+- }
+-
+- init_rwsem(&info->lock);
+- info->flash_id = id;
+- mtd->name = "HyperFlash";
+- mtd->type = MTD_NORFLASH;
+- mtd->flags = MTD_CAP_NORFLASH;
+- mtd->size = size;
+- mtd->writesize = 1;
+- mtd->writebufsize = RPC_WBUF_SIZE;
+- mtd->erasesize = RPC_HF_ERASE_SIZE;
+- mtd->owner = THIS_MODULE;
+- mtd->priv = info;
+- mtd->_erase = rpc_hf_mtd_erase;
+- mtd->_write = rpc_hf_mtd_write;
+- mtd->_read = rpc_hf_mtd_read;
+- retval = mtd_device_register(mtd, partition_info,
+- ARRAY_SIZE(partition_info));
+-out:
+- rpc_hf_write_cmd(info, 0, RPC_CFI_CMD_RESET);
+- rpc_hf_mode_ext(info);
+- return retval;
+-}
+-
+-static int rpc_flash_init(void)
+-{
+- struct rpc_info *info;
+- struct resource *res;
+- void __iomem *base;
+- int retval = -ENODEV;
+-
+- if (!of_machine_is_compatible("renesas,r8a7795"))
+- return -ENODEV;
+-
+- info = kzalloc(sizeof(*info), GFP_KERNEL);
+- if (!info)
+- return -ENOMEM;
+-
+- res = request_mem_region(RPC_BASE, RPC_SIZE, "RPC");
+- if (!res)
+- goto out_info;
+-
+- info->rpc_res = res;
+- base = ioremap(res->start, resource_size(res));
+- if (!base)
+- goto out_rpc_res;
+-
+- info->rpc_base = base;
+- res = request_mem_region(RPC_FLASH_BASE, RPC_FLASH_SIZE, "RPC-ext");
+- if (!res)
+- goto out_rpc_base;
+-
+- info->flash_res = res;
+- base = ioremap(res->start, resource_size(res));
+- if (!base)
+- goto out_flash_res;
+-
+- info->flash_base = base;
+- retval = rpc_hf_init_mtd(info);
+- if (retval)
+- goto out_flash_base;
+-
+- pr_info("HyperFlash Id: %x\n", info->flash_id);
+-
+- rpc_info = info;
+- return 0;
+-
+-out_flash_base:
+- iounmap(info->flash_base);
+-out_flash_res:
+- release_mem_region(info->flash_res->start,
+- resource_size(info->flash_res));
+-out_rpc_base:
+- iounmap(info->rpc_base);
+-out_rpc_res:
+- release_mem_region(info->rpc_res->start,
+- resource_size(info->rpc_res));
+-out_info:
+- kfree(info);
+- return retval;
+-}
+-
+-static void rpc_flash_exit(void)
+-{
+- struct rpc_info *info = rpc_info;
+-
+- if (!info)
+- return;
+-
+- rpc_info = NULL;
+-
+- mtd_device_unregister(&info->mtd);
+-
+- iounmap(info->flash_base);
+- release_mem_region(info->flash_res->start,
+- resource_size(info->flash_res));
+- iounmap(info->rpc_base);
+- release_mem_region(info->rpc_res->start,
+- resource_size(info->rpc_res));
+- kfree(info);
+-}
+-
+-module_init(rpc_flash_init);
+-module_exit(rpc_flash_exit);
+-
+-MODULE_LICENSE("GPL v2");
+-MODULE_DESCRIPTION("Renesas RPC HyperFlash MTD driver");
+diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
+index 2c73adb..bd316fd 100644
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -8,12 +8,26 @@ menuconfig MTD_SPI_NOR
+ if MTD_SPI_NOR
+
+
+-config SPI_RENESAS_RPC
++config MTD_RENESAS_RPC
+ tristate "Renesas RPC controller"
+- depends on ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || ARCH_R8A77980 || COMPILE_TEST
++ depends on ARCH_RENESAS || COMPILE_TEST
+ help
+- QSPI driver for Renesas SPI Multi I/O Bus controller. This controller
+- supports normal, dual and quad spi for one or two SPI NOR Flashes.
++ Renesas RPC interface driver. This controller support QSPI and HyperFlash
++ devices.
++
++config MTD_RENESAS_RPC_HYPERFLASH
++ tristate "Renesas RPC HyperFlash"
++ depends on ARCH_RENESAS || COMPILE_TEST
++ select MTD_RENESAS_RPC
++ help
++ HyperFlash driver for Renesas RPC Multi I/O Bus controller controller.
++
++config MTD_RENESAS_RPC_QSPI
++ tristate "Renesas RPC QSPI"
++ depends on ARCH_RENESAS || COMPILE_TEST
++ select MTD_RENESAS_RPC
++ help
++ QSPI driver for Renesas RPC Multi I/O Bus controller.
+
+ config MTD_MT81xx_NOR
+ tristate "Mediatek MT81xx SPI NOR flash controller"
+diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
+index 0c46c9c..a10c6ab 100644
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -7,8 +7,10 @@ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
+ obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
+ obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
+ obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
+-obj-$(CONFIG_SPI_RENESAS_RPC) += renesas-rpc.o
+ obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
+ obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o
+ obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
+ obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o
++obj-$(CONFIG_MTD_RENESAS_RPC) += renesas-rpc.o
++obj-$(CONFIG_MTD_RENESAS_RPC_QSPI) += renesas-rpc-qspi.o
++obj-$(CONFIG_MTD_RENESAS_RPC_HYPERFLASH) += renesas-rpc-hyperflash.o
+diff --git a/drivers/mtd/spi-nor/renesas-rpc-hyperflash.c b/drivers/mtd/spi-nor/renesas-rpc-hyperflash.c
+new file mode 100644
+index 0000000..aea42b6
+--- /dev/null
++++ b/drivers/mtd/spi-nor/renesas-rpc-hyperflash.c
+@@ -0,0 +1,742 @@
++/*
++ * Renesas RPC driver
++ *
++ * Copyright (C) 2018, Cogent Embedded Inc.
++ *
++ * 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.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/mtd.h>
++
++#include "renesas-rpc.h"
++
++/* RPC HyperFlash */
++#define RPC_HF_CMD_CA47 (0x1 << 7) /* Read */
++#define RPC_HF_CMD_CA46 (0x1 << 6) /* Register space */
++#define RPC_HF_CMD_CA45 (0x1 << 5) /* Liner burst */
++
++#define RPC_HF_CMD_READ_REG (RPC_HF_CMD_CA47 | RPC_HF_CMD_CA46)
++#define RPC_HF_CMD_READ_MEM RPC_HF_CMD_CA47
++#define RPC_HF_CMD_WRITE_REG RPC_HF_CMD_CA46
++#define RPC_HF_CMD_WRITE_MEM 0x0
++
++#define RPC_HF_ERASE_SIZE 0x40000
++
++#define RPC_CFI_STATUS_DRB (0x1 << 7)
++#define RPC_CFI_STATUS_ESSB (0x1 << 6)
++#define RPC_CFI_STATUS_ESB (0x1 << 5)
++#define RPC_CFI_STATUS_PSB (0x1 << 4)
++#define RPC_CFI_STATUS_WBASB (0x1 << 3)
++#define RPC_CFI_STATUS_PSSB (0x1 << 2)
++#define RPC_CFI_STATUS_SLSB (0x1 << 1)
++#define RPC_CFI_STATUS_ESTAT (0x1 << 0)
++
++#define RPC_CFI_UNLOCK1 (0x555 << 1)
++#define RPC_CFI_UNLOCK2 (0x2AA << 1)
++
++#define RPC_CFI_CMD_UNLOCK_START 0xAA
++#define RPC_CFI_CMD_UNLOCK_ACK 0x55
++#define RPC_CFI_CMD_RESET 0xF0
++#define RPC_CFI_CMD_READ_STATUS 0x70
++#define RPC_CFI_CMD_READ_ID 0x90
++#define RPC_CFI_CMD_WRITE 0xA0
++#define RPC_CFI_CMD_ERASE_START 0x80
++#define RPC_CFI_CMD_ERASE_SECTOR 0x30
++
++#define RPC_CFI_ID_MASK 0x000F
++#define RPC_CFI_ID_MAN_SPANSION 0x0001
++#define RPC_CFI_ID_TYPE_HYPERFLASH 0x000E
++
++struct rpc_hf_info {
++ struct mtd_info mtd;
++ struct mutex lock;
++ void *priv;
++};
++
++static void rpc_hf_mode_man(struct rpc_info *rpc)
++{
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ /*
++ * RPC_PHYCNT = 0x80000263
++ * bit31 CAL = 1 : PHY calibration
++ * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
++ */
++ rpc_clrsetl(rpc, RPC_PHYCNT,
++ RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 |
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3),
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
++
++ /*
++ * RPC_CMNCR = 0x81FFF301
++ * bit31 MD = 1 : Manual mode
++ * bit1-0 BSZ[1:0] = 01 : QSPI Flash x 2 or HyperFlash
++ */
++ rpc_clrsetl(rpc, RPC_CMNCR,
++ RPC_CMNCR_MD | RPC_CMNCR_BSZ(3),
++ RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
++ RPC_CMNCR_MD | RPC_CMNCR_BSZ(1));
++}
++
++static void rpc_hf_mode_ext(struct rpc_info *rpc)
++{
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ /*
++ * RPC_PHYCNT = 0x80000263
++ * bit31 CAL = 1 : PHY calibration
++ * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
++ */
++ rpc_clrsetl(rpc, RPC_PHYCNT,
++ RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 |
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3),
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
++
++ /*
++ * RPC_CMNCR = 0x81FFF301
++ * bit31 MD = 1 : Manual mode
++ * bit1-0 BSZ[1:0] = 01 : QSPI Flash x 2 or HyperFlash
++ */
++ rpc_clrsetl(rpc, RPC_CMNCR,
++ RPC_CMNCR_MD | RPC_CMNCR_BSZ(3),
++ RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
++ RPC_CMNCR_BSZ(1));
++
++ /*
++ * RPC_DRCR = 0x001F0100
++ * bit21-16 RBURST[4:0] = 11111 : Read burst 32 64-bit data units
++ * bit9 RCF = 1 : Clear cache
++ * bit8 RBE = 1 : Read burst enable
++ */
++ rpc_writel(rpc, RPC_DRCR,
++ RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE);
++
++ rpc_writel(rpc, RPC_DRCMR, RPC_DRCMR_CMD(0xA0));
++ rpc_writel(rpc, RPC_DRENR,
++ RPC_DRENR_CDB(2) | RPC_DRENR_OCDB(2) |
++ RPC_DRENR_ADB(2) | RPC_DRENR_DRDB(2) |
++ RPC_DRENR_CDE | RPC_DRENR_OCDE |
++ RPC_DRENR_DME | RPC_DRENR_ADE(4));
++ rpc_writel(rpc, RPC_DRDMCR, RPC_DRDMCR_DMCYC(0xE));
++ rpc_writel(rpc, RPC_DRDRENR,
++ RPC_DRDRENR_HYPE | RPC_DRDRENR_ADDRE | RPC_DRDRENR_DRDRE);
++
++ /* Dummy read */
++ rpc_readl(rpc, RPC_DRCR);
++}
++
++static void rpc_hf_xfer(struct rpc_info *rpc, u32 addr, u16 *data,
++ enum rpc_size size, u8 cmd)
++{
++ u32 val;
++
++ rpc_hf_mode_man(rpc);
++
++ /*
++ * bit23-21 CMD[7:5] : CA47-45
++ * CA47 = 0/1 : Write/Read
++ * CA46 = 0/1 : Memory Space/Register Space
++ * CA45 = 0/1 : Wrapped Burst/Linear Burst
++ */
++ rpc_writel(rpc, RPC_SMCMR, RPC_SMCMR_CMD(cmd));
++
++ rpc_writel(rpc, RPC_SMADR, addr >> 1);
++
++ rpc_writel(rpc, RPC_SMOPR, 0x0);
++
++ /*
++ * RPC_SMDRENR = 0x00005101
++ * bit14-12 HYPE = 101: Hyperflash mode
++ * bit8 ADDRE = 1 : Address DDR transfer
++ * bit0 SPIDRE = 1 : DATA DDR transfer
++ */
++ rpc_writel(rpc, RPC_SMDRENR,
++ RPC_SMDRENR_HYPE_HF | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE);
++
++ val = RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) |
++ RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) |
++ RPC_SMENR_CDE | RPC_SMENR_OCDE | RPC_SMENR_ADE(4) | size;
++
++ if (cmd & RPC_HF_CMD_CA47)
++ goto read_transfer;
++
++ /*
++ * RPC_SMENR = 0xA222540x
++ * bit31-30 CDB[1:0] = 10 : 4bit width command
++ * bit25-24 ADB[1:0] = 10 : 4bit width address
++ * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
++ * bit15 DME = 0 : dummy cycle disable
++ * bit14 CDE = 1 : Command enable
++ * bit12 OCDE = 1 : Option Command enable
++ * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output
++ * bit7-4 OPDE[3:0] = 0000 : Option data disable
++ * bit3-0 SPIDE[3:0] = xxxx : Transfer size
++ */
++ rpc_writel(rpc, RPC_SMENR, val);
++
++ switch (size) {
++ case RPC_SIZE_DUAL_64BIT:
++ val = cmd & RPC_HF_CMD_CA46 ?
++ cpu_to_be16(data[0]) | cpu_to_be16(data[1]) << 16 :
++ data[0] | data[1] << 16;
++ rpc_writel(rpc, RPC_SMWDR1, val);
++ val = cmd & RPC_HF_CMD_CA46 ?
++ cpu_to_be16(data[2]) | cpu_to_be16(data[3]) << 16 :
++ data[2] | data[3] << 16;
++ break;
++ case RPC_SIZE_DUAL_32BIT:
++ val = cmd & RPC_HF_CMD_CA46 ?
++ cpu_to_be16(data[0]) | cpu_to_be16(data[1]) << 16 :
++ data[0] | data[1] << 16;
++ break;
++ default:
++ val = cmd & RPC_HF_CMD_CA46 ?
++ cpu_to_be16(data[0]) << 16 :
++ data[0] << 16;
++ break;
++ }
++
++ rpc_writel(rpc, RPC_SMWDR0, val);
++ /*
++ * RPC_SMCR = 0x00000003
++ * bit1 SPIWE = 1 : Data write enable
++ * bit0 SPIE = 1 : SPI transfer start
++ */
++ rpc_writel(rpc, RPC_SMCR, RPC_SMCR_SPIWE | RPC_SMCR_SPIE);
++ return;
++
++read_transfer:
++ rpc_writel(rpc, RPC_SMDMCR, RPC_SMDMCR_DMCYC(0xE));
++ val |= RPC_SMENR_DME;
++
++ /*
++ * RPC_SMENR = 0xA222D40x
++ * bit31-30 CDB[1:0] = 10 : 4bit width command
++ * bit25-24 ADB[1:0] = 10 : 4bit width address
++ * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
++ * bit15 DME = 1 : dummy cycle disable
++ * bit14 CDE = 1 : Command enable
++ * bit12 OCDE = 1 : Option Command enable
++ * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output (24 Bit Address)
++ * bit7-4 OPDE[3:0] = 0000 : Option data disable
++ * bit3-0 SPIDE[3:0] = xxxx : Transfer size
++ */
++ rpc_writel(rpc, RPC_SMENR, val);
++
++ /*
++ * RPC_SMCR = 0x00000005
++ * bit2 SPIRE = 1 : Data read disable
++ * bit0 SPIE = 1 : SPI transfer start
++ */
++ rpc_writel(rpc, RPC_SMCR, RPC_SMCR_SPIRE | RPC_SMCR_SPIE);
++
++ rpc_wait(rpc, RPC_TIMEOUT);
++ val = rpc_readl(rpc, RPC_SMRDR0);
++
++ /*
++ * Read data from either register or memory space.
++ * Register space is always big-endian.
++ */
++ switch (size) {
++ case RPC_SIZE_DUAL_64BIT:
++ if (cmd & RPC_HF_CMD_CA46) {
++ data[3] = be16_to_cpu((val >> 16) & 0xFFFF);
++ data[2] = be16_to_cpu(val & 0xFFFF);
++ } else {
++ data[3] = (val >> 16) & 0xFFFF;
++ data[2] = val & 0xFFFF;
++ }
++ val = rpc_readl(rpc, RPC_SMRDR1);
++ if (cmd & RPC_HF_CMD_CA46) {
++ data[1] = be16_to_cpu((val >> 16) & 0xFFFF);
++ data[0] = be16_to_cpu(val & 0xFFFF);
++ } else {
++ data[1] = (val >> 16) & 0xFFFF;
++ data[0] = val & 0xFFFF;
++ }
++ break;
++ case RPC_SIZE_DUAL_32BIT:
++ if (cmd & RPC_HF_CMD_CA46) {
++ data[1] = be16_to_cpu((val >> 16) & 0xFFFF);
++ data[0] = be16_to_cpu(val & 0xFFFF);
++ } else {
++ data[1] = (val >> 16) & 0xFFFF;
++ data[0] = val & 0xFFFF;
++ }
++ break;
++ default:
++ data[0] = cmd & RPC_HF_CMD_CA46 ?
++ be16_to_cpu((val >> 16) & 0xFFFF) :
++ (val >> 16) & 0xFFFF;
++ break;
++ }
++}
++
++static void rpc_hf_wbuf_enable(struct rpc_info *rpc, u32 addr)
++{
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ /*
++ * RPC_PHYCNT = 0x80000277
++ * bit31 CAL = 1 : PHY calibration
++ * bit4 WBUF2 = 1 : Write buffer enable 2
++ * bit2 WBUF = 1 : Write buffer enable
++ * bit1-0 PHYMEM[1:0] = 11 : HyperFlash
++ */
++ rpc_clrsetl(rpc, RPC_PHYCNT,
++ RPC_PHYCNT_WBUF2 | RPC_PHYCNT_WBUF |
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3),
++ RPC_PHYCNT_WBUF2 | RPC_PHYCNT_WBUF |
++ RPC_PHYCNT_CAL | RPC_PHYCNT_MEM(3));
++
++ /*
++ * RPC_DRCR = 0x001F0100
++ * bit21-16 RBURST[4:0] = 11111 : Read burst 32 64-bit data units
++ * bit9 RCF = 1 : Clear cache
++ * bit8 RBE = 1 : Read burst enable
++ */
++ rpc_writel(rpc, RPC_DRCR,
++ RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RCF | RPC_DRCR_RBE);
++
++ rpc_writel(rpc, RPC_SMCMR, RPC_SMCMR_CMD(RPC_HF_CMD_WRITE_MEM));
++
++ rpc_writel(rpc, RPC_SMADR, addr >> 1);
++
++ rpc_writel(rpc, RPC_SMOPR, 0x0);
++
++ /*
++ * RPC_SMDRENR = 0x00005101
++ * bit14-12 HYPE = 101:Hyperflash mode
++ * bit8 ADDRE = 1 : Address DDR transfer
++ * bit0 SPIDRE = 1 : DATA DDR transfer
++ */
++ rpc_writel(rpc, RPC_SMDRENR,
++ RPC_SMDRENR_HYPE_HF | RPC_SMDRENR_ADDRE | RPC_SMDRENR_SPIDRE);
++
++ /*
++ * RPC_SMENR = 0xA222540F
++ * bit31-30 CDB[1:0] = 10 : 4bit width command
++ * bit25-24 ADB[1:0] = 10 : 4bit width address
++ * bit17-16 SPIDB[1:0] = 10 : 4bit width transfer data
++ * bit15 DME = 0 : dummy cycle disable
++ * bit14 CDE = 1 : Command enable
++ * bit12 OCDE = 1 : Option Command enable
++ * bit11-8 ADE[3:0] = 0100 : ADR[23:0] output (24 Bit Address)
++ * bit7-4 OPDE[3:0] = 0000 : Option data disable
++ * bit3-0 SPIDE[3:0] = 1111 : 64-bit transfer size
++ */
++ rpc_writel(rpc, RPC_SMENR,
++ RPC_SMENR_CDB(2) | RPC_SMENR_OCDB(2) |
++ RPC_SMENR_ADB(2) | RPC_SMENR_SPIDB(2) |
++ RPC_SMENR_CDE | RPC_SMENR_OCDE |
++ RPC_SMENR_ADE(4) | RPC_SIZE_DUAL_64BIT);
++
++ /* Dummy read */
++ rpc_readl(rpc, RPC_DRCR);
++}
++
++static inline void rpc_hf_write_cmd(struct rpc_info *rpc, u32 addr, u16 cmd)
++{
++ rpc_hf_xfer(rpc, addr, &cmd, RPC_SIZE_DUAL_16BIT, RPC_HF_CMD_WRITE_REG);
++}
++
++static inline void rpc_hf_read_reg(struct rpc_info *rpc, u32 addr, u16 *data,
++ enum rpc_size size)
++{
++ rpc_hf_xfer(rpc, addr, data, size, RPC_HF_CMD_READ_REG);
++}
++
++static inline void rpc_hf_write_reg(struct rpc_info *rpc, u32 addr, u16 *data,
++ enum rpc_size size)
++{
++ rpc_hf_xfer(rpc, addr, data, size, RPC_HF_CMD_WRITE_REG);
++}
++
++static inline void rpc_hf_read_mem(struct rpc_info *rpc, u32 addr, u16 *data,
++ enum rpc_size size)
++{
++ rpc_hf_xfer(rpc, addr, data, size, RPC_HF_CMD_READ_MEM);
++}
++
++static inline void rpc_hf_write_mem(struct rpc_info *rpc, u32 addr, u16 *data,
++ enum rpc_size size)
++{
++ rpc_hf_xfer(rpc, addr, data, size, RPC_HF_CMD_WRITE_MEM);
++}
++
++static void rpc_hf_wp(struct rpc_info *rpc, int enable)
++{
++ rpc_clrsetl(rpc, RPC_PHYINT, RPC_PHYINT_WP, enable ? RPC_PHYINT_WP : 0);
++}
++
++static void rpc_hf_unlock(struct rpc_info *rpc, u32 addr)
++{
++ rpc_hf_write_cmd(rpc, addr + RPC_CFI_UNLOCK1,
++ RPC_CFI_CMD_UNLOCK_START);
++ rpc_hf_write_cmd(rpc, addr + RPC_CFI_UNLOCK2,
++ RPC_CFI_CMD_UNLOCK_ACK);
++}
++
++static inline int rpc_hf_status(struct rpc_info *rpc, u32 addr,
++ int iterations, int delay)
++{
++ int ret;
++ u16 status = 0;
++
++ while (iterations-- > 0) {
++ rpc_hf_write_cmd(rpc, addr + RPC_CFI_UNLOCK1,
++ RPC_CFI_CMD_READ_STATUS);
++ rpc_hf_read_reg(rpc, addr, &status, RPC_SIZE_DUAL_16BIT);
++
++ if (status & RPC_CFI_STATUS_DRB)
++ break;
++
++ if (delay < 10000)
++ usleep_range(delay, delay * 2);
++ else
++ msleep(delay / 1000);
++ }
++
++ if (!(status & RPC_CFI_STATUS_DRB)) {
++ ret = -ETIMEDOUT;
++ goto out;
++ }
++
++ if (status & (RPC_CFI_STATUS_PSB | RPC_CFI_STATUS_ESB)) {
++ ret = -EIO;
++ goto out;
++ }
++
++ return 0;
++
++out:
++ /* Reset the flash */
++ rpc_hf_write_cmd(rpc, 0, RPC_CFI_CMD_RESET);
++ return ret;
++}
++
++static int rpc_hf_sector_erase(struct rpc_info *rpc, u32 addr)
++{
++ rpc_hf_unlock(rpc, addr);
++ rpc_hf_write_cmd(rpc, addr + RPC_CFI_UNLOCK1, RPC_CFI_CMD_ERASE_START);
++ rpc_hf_unlock(rpc, addr);
++ rpc_hf_write_cmd(rpc, addr, RPC_CFI_CMD_ERASE_SECTOR);
++
++ return rpc_hf_status(rpc, addr, 1000, 10000);
++}
++
++/* Flash read */
++static int rpc_hf_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ struct rpc_hf_info *hf = mtd->priv;
++ struct rpc_info *rpc = hf->priv;
++
++ mutex_lock(&hf->lock);
++ rpc_do_read_flash(rpc, from, len, buf, mtd->size > RPC_READ_ADDR_SIZE);
++ mutex_unlock(&hf->lock);
++
++ *retlen = len;
++ return 0;
++}
++
++/* Flash erase */
++static int rpc_hf_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct rpc_hf_info *hf = mtd->priv;
++ struct rpc_info *rpc = hf->priv;
++ u32 addr, end;
++ int ret = 0;
++
++ if (mtd_mod_by_eb(instr->addr, mtd)) {
++ dev_dbg(mtd->dev.parent, "%s: unaligned address\n", __func__);
++ return -EINVAL;
++ }
++
++ if (mtd_mod_by_eb(instr->len, mtd)) {
++ dev_dbg(mtd->dev.parent, "%s: unaligned length\n", __func__);
++ return -EINVAL;
++ }
++
++ end = instr->addr + instr->len;
++
++ mutex_lock(&hf->lock);
++ for (addr = instr->addr; addr < end; addr += mtd->erasesize) {
++ ret = rpc_hf_sector_erase(rpc, addr);
++
++ if (ret)
++ break;
++ }
++
++ rpc_hf_mode_ext(rpc);
++ mutex_unlock(&hf->lock);
++
++ instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
++ mtd_erase_callback(instr);
++
++ return ret;
++}
++
++/* Copy memory to flash */
++static int rpc_hf_mtd_write(struct mtd_info *mtd, loff_t offset, size_t len,
++ size_t *retlen, const u_char *src)
++{
++ struct rpc_hf_info *hf = mtd->priv;
++ struct rpc_info *rpc = hf->priv;
++ union {
++ u8 b[4];
++ u16 w[2];
++ u32 d;
++ } data;
++ loff_t addr;
++ size_t size, cnt;
++ int ret, idx;
++ u8 last;
++
++ ret = 0;
++ *retlen = 0;
++ cnt = len;
++ idx = 0;
++
++ mutex_lock(&hf->lock);
++
++ /* Handle unaligned start */
++ if (offset & 0x1) {
++ offset--;
++ data.b[idx] = readb(rpc->read_area + offset);
++ idx++;
++ }
++
++ /* Handle unaligned end */
++ addr = offset + idx + len;
++ last = addr & 0x1 ? readb(rpc->read_area + addr) : 0xFF;
++
++ addr = offset - mtd_mod_by_eb(offset, mtd);
++ size = mtd->erasesize - (offset - addr);
++
++ while (cnt) {
++ if (size > cnt)
++ size = cnt;
++
++ cnt -= size;
++ while (size) {
++ rpc_hf_unlock(rpc, addr);
++ rpc_hf_write_cmd(rpc,
++ addr + RPC_CFI_UNLOCK1,
++ RPC_CFI_CMD_WRITE);
++
++ if (rpc_wbuf_available(rpc) && (size > 0x7)) {
++ u32 wbuf = 0;
++ int block = size >= RPC_WBUF_SIZE ?
++ RPC_WBUF_SIZE : size & ~0x7;
++
++ rpc_hf_wbuf_enable(rpc, offset);
++ offset += block;
++
++ block >>= 3;
++ while (block--) {
++ while (idx < 4) {
++ data.b[idx++] = *src++;
++ size--;
++ }
++ rpc_wbuf_writel(rpc, wbuf, data.d);
++ wbuf += 4;
++
++ idx = 0;
++ while (idx < 4) {
++ data.b[idx++] = *src++;
++ size--;
++ }
++ rpc_wbuf_writel(rpc, wbuf, data.d);
++ wbuf += 4;
++
++ idx = 0;
++ }
++
++ rpc_writel(rpc, RPC_SMCR,
++ RPC_SMCR_SPIWE | RPC_SMCR_SPIE);
++ } else {
++ enum rpc_size bits;
++
++ while (idx < 4) {
++ data.b[idx++] = *src++;
++ size--;
++
++ if (!size)
++ break;
++ }
++
++ if (idx & 0x1)
++ data.b[idx++] = last;
++
++ switch (idx) {
++ case 2:
++ bits = RPC_SIZE_DUAL_16BIT;
++ break;
++ default:
++ bits = RPC_SIZE_DUAL_32BIT;
++ break;
++ }
++
++ rpc_hf_write_mem(rpc, offset, data.w, bits);
++ offset += idx;
++ idx = 0;
++ }
++
++ ret = rpc_hf_status(rpc, addr, 1000000, 10);
++ if (ret)
++ goto out;
++ }
++
++ size = mtd->erasesize;
++ addr += size;
++ offset = addr;
++ *retlen = len - cnt;
++ }
++
++out:
++ rpc_hf_mode_ext(rpc);
++ mutex_unlock(&hf->lock);
++ return ret;
++}
++
++static int rpc_hf_hw_init(struct rpc_info *rpc, u32 *id, u32 *size)
++{
++ u16 data[2] = { 0, 0 };
++ u32 sz;
++ int ret = 0;
++
++ rpc_hf_mode_ext(rpc);
++
++ rpc_hf_wp(rpc, 0);
++
++ rpc_hf_unlock(rpc, 0);
++ rpc_hf_write_cmd(rpc, RPC_CFI_UNLOCK1, RPC_CFI_CMD_READ_ID);
++
++ rpc_hf_read_reg(rpc, 0x0, data, RPC_SIZE_DUAL_32BIT);
++ if ((data[0] & RPC_CFI_ID_MASK) != RPC_CFI_ID_MAN_SPANSION ||
++ (data[1] & RPC_CFI_ID_MASK) != RPC_CFI_ID_TYPE_HYPERFLASH) {
++ ret = -ENXIO;
++ goto out;
++ }
++
++ if (id)
++ *id = data[0] | data[1] << 16;
++
++ rpc_hf_read_reg(rpc, 0x27 << 1, data, RPC_SIZE_DUAL_16BIT);
++ sz = 1 << data[0];
++
++ if (sz & (RPC_HF_ERASE_SIZE - 1)) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (size)
++ *size = sz;
++out:
++ rpc_hf_write_cmd(rpc, 0, RPC_CFI_CMD_RESET);
++ rpc_hf_mode_ext(rpc);
++ return ret;
++}
++
++int rpc_hf_probe(struct platform_device *pdev)
++{
++ struct rpc_info *rpc;
++ struct rpc_hf_info *hf;
++ struct mtd_info *mtd;
++ u32 flash_id, flash_size;
++ int ret;
++
++ rpc = dev_get_drvdata(pdev->dev.parent);
++ if (!rpc) {
++ dev_err(&pdev->dev, "invalid data\n");
++ return -EINVAL;
++ }
++
++ hf = devm_kzalloc(&pdev->dev, sizeof(*hf), GFP_KERNEL);
++ if (!hf) {
++ dev_err(&pdev->dev, "allocation failed\n");
++ return -ENOMEM;
++ }
++
++ mutex_init(&hf->lock);
++ mtd_set_of_node(&hf->mtd, rpc->flash);
++ hf->priv = rpc;
++
++ ret = clk_prepare_enable(rpc->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot prepare clock\n");
++ return ret;
++ }
++
++ ret = rpc_hf_hw_init(rpc, &flash_id, &flash_size);
++ if (ret) {
++ dev_err(&pdev->dev, "initialization failed\n");
++ goto error;
++ }
++
++ mtd = &hf->mtd;
++ mtd->name = "HyperFlash";
++ mtd->dev.parent = &pdev->dev;
++ mtd->type = MTD_NORFLASH;
++ mtd->flags = MTD_CAP_NORFLASH;
++ mtd->size = flash_size;
++ mtd->writesize = 1;
++ mtd->writebufsize = RPC_WBUF_SIZE;
++ mtd->erasesize = RPC_HF_ERASE_SIZE;
++ mtd->owner = THIS_MODULE;
++ mtd->priv = hf;
++ mtd->_erase = rpc_hf_mtd_erase;
++ mtd->_write = rpc_hf_mtd_write;
++ mtd->_read = rpc_hf_mtd_read;
++
++ ret = mtd_device_register(mtd, NULL, 0);
++ if (ret) {
++ dev_err(&pdev->dev, "MTD registration failed\n");
++ goto error;
++ }
++
++ platform_set_drvdata(pdev, hf);
++ dev_info(&pdev->dev, "probed flash id:%x\n", flash_id);
++ return 0;
++
++error:
++ clk_disable_unprepare(rpc->clk);
++ return ret;
++}
++
++static int rpc_hf_remove(struct platform_device *pdev)
++{
++ struct rpc_hf_info *hf = platform_get_drvdata(pdev);
++ struct rpc_info *rpc = hf->priv;
++
++ mtd_device_unregister(&hf->mtd);
++ clk_disable_unprepare(rpc->clk);
++ return 0;
++}
++
++/* platform driver interface */
++static struct platform_driver rpc_hf_platform_driver = {
++ .probe = rpc_hf_probe,
++ .remove = rpc_hf_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "renesas-rpc-hyperflash",
++ },
++};
++
++module_platform_driver(rpc_hf_platform_driver);
++
++MODULE_ALIAS("renesas-rpc-hyperflash");
++MODULE_AUTHOR("Cogent Embedded Inc. <sources@cogentembedded.com>");
++MODULE_DESCRIPTION("Renesas RPC HyperFlash Driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mtd/spi-nor/renesas-rpc-qspi.c b/drivers/mtd/spi-nor/renesas-rpc-qspi.c
+new file mode 100644
+index 0000000..3d2d5db
+--- /dev/null
++++ b/drivers/mtd/spi-nor/renesas-rpc-qspi.c
+@@ -0,0 +1,794 @@
++/*
++ * Renesas RPC QSPI driver
++ *
++ * Copyright (C) 2019, Cogent Embedded Inc.
++ *
++ * 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.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/spi-nor.h>
++
++#include "renesas-rpc.h"
++
++static void rpc_endisable_write_buf(struct rpc_info *rpc, bool en)
++{
++ rpc_clrsetl(rpc, RPC_PHYCNT,
++ RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2,
++ en ? RPC_PHYCNT_WBUF | RPC_PHYCNT_WBUF2 : 0);
++}
++
++static int rpc_begin(struct rpc_info *rpc,
++ bool rx, bool tx, bool last)
++{
++ u32 val = RPC_SMCR_SPIE;
++
++ if (rx)
++ val |= RPC_SMCR_SPIRE;
++
++ if (tx)
++ val |= RPC_SMCR_SPIWE;
++
++ if (!last)
++ val |= RPC_SMCR_SSLKP;
++
++ rpc_writel(rpc, RPC_SMCR, val);
++
++ return 0;
++}
++
++static int rpc_setup_reg_mode(struct rpc_info *rpc)
++{
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ rpc_endisable_write_buf(rpc, false);
++
++ /* ...setup manual mode */
++ rpc_clrsetl(rpc, RPC_CMNCR, 0, RPC_CMNCR_MD);
++
++ /* disable ddr */
++ rpc_clrsetl(rpc, RPC_SMDRENR,
++ RPC_SMDRENR_ADDRE | RPC_SMDRENR_OPDRE | RPC_SMDRENR_SPIDRE,
++ 0);
++
++ /* enable 1bit command */
++ rpc_clrsetl(rpc, RPC_SMENR,
++ RPC_SMENR_CDB(3) | RPC_SMENR_OCDB(3) | RPC_SMENR_DME |
++ RPC_SMENR_OCDE | RPC_SMENR_SPIDB(3) |
++ RPC_SMENR_ADE(0xF) | RPC_SMENR_ADB(3) |
++ RPC_SMENR_OPDE(0xF) | RPC_SMENR_SPIDE(0xF),
++ RPC_SMENR_CDB(0) | RPC_SMENR_CDE | RPC_SIZE_SINGLE_32BIT);
++
++ return 0;
++}
++
++static inline void rpc_flush_cache(struct rpc_info *rpc)
++{
++ rpc_clrsetl(rpc, RPC_DRCR, 0, RPC_DRCR_RCF);
++}
++
++static int rpc_setup_ext_mode(struct rpc_info *rpc)
++{
++ u32 val;
++ u32 cmncr;
++
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ rpc_endisable_write_buf(rpc, false);
++
++ /* ...setup ext mode */
++ val = rpc_readl(rpc, RPC_CMNCR);
++ cmncr = val;
++ val &= ~(RPC_CMNCR_MD);
++ rpc_writel(rpc, RPC_CMNCR, val);
++
++ /* ...enable burst and clear cache */
++ val = rpc_readl(rpc, RPC_DRCR);
++ val &= ~(RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RBE | RPC_DRCR_SSLE);
++ val |= RPC_DRCR_RBURST(0x1F) | RPC_DRCR_RBE;
++
++ if (cmncr & RPC_CMNCR_MD)
++ val |= RPC_DRCR_RCF;
++
++ rpc_writel(rpc, RPC_DRCR, val);
++
++ return 0;
++}
++
++static int rpc_setup_data_size(struct rpc_info *rpc, u32 size, bool copy)
++{
++ u32 val;
++
++ val = rpc_readl(rpc, RPC_SMENR);
++ val &= ~(RPC_SMENR_SPIDE(0xF));
++
++ if (rpc->mtdtype == RPC_DUAL && !copy)
++ size >>= 1;
++
++ switch (size) {
++ case 0:
++ break;
++ case 1:
++ val |= RPC_SIZE_SINGLE_8BIT;
++ break;
++ case 2:
++ val |= RPC_SIZE_SINGLE_16BIT;
++ break;
++ case 4:
++ val |= RPC_SIZE_SINGLE_32BIT;
++ break;
++ default:
++ dev_err(&rpc->pdev->dev, "Unsupported data width %d\n", size);
++ return -EINVAL;
++ }
++ rpc_writel(rpc, RPC_SMENR, val);
++
++ return 0;
++}
++
++static inline int rpc_get_read_addr_nbits(u8 opcode)
++{
++ if (opcode == SPINOR_OP_READ_1_4_4_4B)
++ return 4;
++ return 1;
++}
++
++#define RPC_NBITS_TO_VAL(v) ((v >> 1) & 3)
++static void rpc_setup_extmode_nbits(struct rpc_info *rpc,
++ int cnb, int anb, int dnb)
++{
++ rpc_clrsetl(rpc, RPC_DRENR,
++ RPC_DRENR_CDB(3) | RPC_DRENR_ADB(3) | RPC_DRENR_DRDB(3),
++ RPC_DRENR_CDB(RPC_NBITS_TO_VAL(cnb)) |
++ RPC_DRENR_ADB(RPC_NBITS_TO_VAL(anb)) |
++ RPC_DRENR_DRDB(RPC_NBITS_TO_VAL(dnb)));
++}
++
++static void rpc_setup_writemode_nbits(struct rpc_info *rpc,
++ int cnb, int anb, int dnb)
++{
++ rpc_clrsetl(rpc, RPC_SMENR,
++ RPC_SMENR_CDB(3) | RPC_SMENR_ADB(3) | RPC_SMENR_SPIDB(3),
++ RPC_SMENR_CDB(RPC_NBITS_TO_VAL(cnb)) |
++ RPC_SMENR_ADB(RPC_NBITS_TO_VAL(anb)) |
++ RPC_SMENR_SPIDB(RPC_NBITS_TO_VAL(dnb)));
++}
++
++static void rpc_setup_write_mode_command_and_adr(struct rpc_info *rpc,
++ int adr_width, bool ena)
++{
++ u32 val;
++
++ val = rpc_readl(rpc, RPC_SMENR);
++ val &= ~(RPC_SMENR_CDB(3) | RPC_SMENR_CDE | RPC_SMENR_ADE(0xF));
++
++ if (ena) {
++ /* enable 1bit command */
++ val |= RPC_SMENR_CDB(0) | RPC_SMENR_CDE;
++
++ if (adr_width == 4)
++ val |= RPC_SMENR_ADE(0xF); /* bits 31-0 */
++ else
++ val |= RPC_SMENR_ADE(0x7); /* bits 23-0 */
++ }
++ rpc_writel(rpc, RPC_SMENR, val);
++}
++
++static void rpc_setup_write_mode(struct rpc_info *rpc, u8 opcode)
++{
++ rpc_wait(rpc, RPC_TIMEOUT);
++
++ rpc_endisable_write_buf(rpc, true);
++
++ /* ...setup manual mode */
++ rpc_clrsetl(rpc, RPC_CMNCR, 0, RPC_CMNCR_MD);
++
++ /* disable ddr */
++ rpc_clrsetl(rpc, RPC_SMDRENR,
++ RPC_SMDRENR_ADDRE | RPC_SMDRENR_OPDRE | RPC_SMDRENR_SPIDRE,
++ 0);
++
++ rpc_clrsetl(rpc, RPC_SMENR,
++ RPC_SMENR_OCDB(3) | RPC_SMENR_DME | RPC_SMENR_OCDE |
++ RPC_SMENR_SPIDB(3) | RPC_SMENR_ADB(3) |
++ RPC_SMENR_OPDE(0xF) | RPC_SMENR_SPIDE(0xF),
++ opcode != SPINOR_OP_PP ? RPC_SIZE_SINGLE_32BIT :
++ RPC_SIZE_SINGLE_8BIT);
++}
++
++static void rpc_read_manual_data(struct rpc_info *rpc, u32 *pv0, u32 *pv1)
++{
++ u32 val0, val1, rd0, rd1;
++
++ val0 = rpc_readl(rpc, RPC_SMRDR0);
++ val1 = rpc_readl(rpc, RPC_SMRDR1);
++
++ if (rpc->mtdtype == RPC_DUAL) {
++ rd1 = (val0 & 0xFf000000) | ((val0 << 8) & 0xFf0000) |
++ ((val1 >> 16) & 0xff00) | ((val1 >> 8) & 0xff);
++ rd0 = ((val0 & 0xff0000) << 8) | ((val0 << 16) & 0xff0000) |
++ ((val1 >> 8) & 0xff00) | (val1 & 0xff);
++ } else
++ rd0 = val0;
++
++ if (pv0)
++ *pv0 = rd0;
++
++ if (pv1 && rpc->mtdtype == RPC_DUAL)
++ *pv1 = rd1;
++}
++
++static int rpc_datalen2trancfersize(struct rpc_info *rpc, int len, bool copy)
++{
++ int sz = len;
++
++ if (len >= 2)
++ sz = 2;
++
++ if (len >= 4)
++ sz = 4;
++
++ if (rpc->mtdtype == RPC_DUAL && len >= 8 && !copy)
++ sz = 8;
++
++ return sz;
++}
++
++static int __rpc_write_data2reg(struct rpc_info *rpc, int off,
++ const u8 *buf, int sz)
++{
++ const u32 *b32 = (const u32 *)buf;
++ const u16 *b16 = (const u16 *)buf;
++
++ if (sz == 4)
++ rpc_writel(rpc, off, *b32);
++ else if (sz == 2)
++ writew(*b16, rpc->base + off);
++ else if (sz == 1)
++ writeb(*buf, rpc->base + off);
++ else if (sz != 0) {
++ dev_err(&rpc->pdev->dev, "incorrect data size %d\n", sz);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define RPC_SETVAL(x) ((((x) & 0xff) << 8) | ((x) & 0xff))
++static int rpc_write_data2reg(struct rpc_info *rpc, const u8 *buf,
++ int sz, bool copy)
++{
++ int i, ret;
++ u32 v = 0;
++
++ if (rpc->mtdtype == RPC_DUAL) {
++ if (copy) {
++ for (i = 0; i < sz && i < 2; i++)
++ v |= (RPC_SETVAL(buf[i]) << 16*i);
++
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? RPC_SMWDR1 : RPC_SMWDR0,
++ (u8 *)&v,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++
++ v = 0;
++ for (; i < sz; i++)
++ v |= (RPC_SETVAL(buf[i]) << 16*i);
++
++
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? RPC_SMWDR0 : RPC_SMWDR1,
++ (u8 *)&v,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++
++ return 0;
++ }
++
++ sz >>= 1;
++ ret = __rpc_write_data2reg(rpc,
++ sz == 4 ? RPC_SMWDR1 : RPC_SMWDR0,
++ buf,
++ sz == 4 ? sz : sz * 2);
++ if (ret)
++ return ret;
++ buf += sz;
++
++ return __rpc_write_data2reg(rpc,
++ sz == 4 ? RPC_SMWDR0 : RPC_SMWDR1,
++ buf, sz == 4 ? sz : sz * 2);
++ }
++
++ return __rpc_write_data2reg(rpc, RPC_SMWDR0, buf, sz);
++}
++
++static ssize_t rpc_write_unaligned(struct spi_nor *nor, loff_t to, size_t len,
++ const u_char *buf, size_t fullen)
++{
++ int ret = len, dsize;
++ struct rpc_info *rpc = nor->priv;
++ bool copy = false, last;
++ loff_t _to;
++
++ rpc_endisable_write_buf(rpc, false);
++
++ while (len > 0) {
++ _to = to;
++ if (rpc->mtdtype == RPC_DUAL)
++ _to >>= 1;
++ rpc_writel(rpc, RPC_SMADR, _to);
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++
++ rpc_write_data2reg(rpc, buf, dsize, copy);
++
++ last = (len <= dsize && fullen <= ret);
++ rpc_begin(rpc, false, true, last);
++ if (rpc_wait(rpc, RPC_TIMEOUT))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ rpc_setup_write_mode_command_and_adr(rpc,
++ nor->addr_width, false);
++
++ buf += dsize;
++ len -= dsize;
++ to += dsize;
++ }
++
++ return ret;
++}
++
++static ssize_t rpc_write_flash(struct spi_nor *nor, loff_t to, size_t len,
++ const u_char *buf)
++{
++ ssize_t res = len, full = len;
++ u32 val;
++ u8 rval[2];
++ struct rpc_info *rpc = nor->priv;
++ loff_t bo;
++ loff_t offset;
++ bool is_rounded = false;
++
++ /* ...len should be rounded to 2 bytes */
++ if (rpc->mtdtype == RPC_DUAL && (len & 1)) {
++ is_rounded = true;
++ len &= ~(1);
++ }
++
++ bo = to & RPC_WBUF_MASK;
++
++ rpc_flush_cache(rpc);
++ rpc_setup_write_mode(rpc, nor->program_opcode);
++ rpc_setup_write_mode_command_and_adr(rpc, nor->addr_width, true);
++ rpc_setup_writemode_nbits(rpc, 1, 1, 1);
++
++ /* ...setup command */
++ val = rpc_readl(rpc, RPC_SMCMR);
++ val &= ~RPC_SMCMR_CMD(0xFF);
++ val |= RPC_SMCMR_CMD(nor->program_opcode);
++ rpc_writel(rpc, RPC_SMCMR, val);
++
++ offset = to & ~RPC_WBUF_MASK;
++
++ /* ...write unaligned first bytes */
++ if (bo) {
++ size_t min = (len < (RPC_WBUF_SIZE - bo)) ? len : (RPC_WBUF_SIZE - bo);
++
++ rpc_write_unaligned(nor, to, min, buf, full);
++ rpc_setup_write_mode(rpc, nor->program_opcode);
++
++ len -= min;
++ buf += min;
++ to += min;
++ full -= min;
++ }
++
++ /*
++ * TODO: Unfortunately RPC does not write properly in write buf mode
++ * without transferring command. Investigate this.
++ */
++
++ if (len) {
++ rpc_write_unaligned(nor, to, len, buf, full);
++ buf += len;
++ to += len;
++ full -= len;
++ len = 0;
++ }
++
++ if (is_rounded) {
++ rval[0] = *buf;
++ rval[1] = 0xFF;
++ rpc_write_unaligned(nor, to, 2, rval, full);
++ }
++
++ rpc_flush_cache(rpc);
++
++ return res;
++}
++
++static inline unsigned int rpc_rx_nbits(struct spi_nor *nor)
++{
++ return spi_nor_get_protocol_data_nbits(nor->read_proto);
++}
++
++static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len,
++ u_char *buf)
++{
++ u32 val;
++ struct rpc_info *rpc = nor->priv;
++ int opcode_nbits = 1;
++ int addr_nbits = rpc_get_read_addr_nbits(nor->read_opcode);
++ int data_nbits = rpc_rx_nbits(nor);
++ int dummy = nor->read_dummy - 1;
++ ssize_t ret = len;
++
++ rpc_setup_ext_mode(rpc);
++ /* ...setup n bits */
++ rpc_setup_extmode_nbits(rpc, opcode_nbits, addr_nbits, data_nbits);
++
++ /* TODO: setup DDR */
++
++ /* ...setup command */
++ val = rpc_readl(rpc, RPC_DRCMR);
++ val &= ~RPC_DRCMR_CMD(0xFF);
++ val |= RPC_DRCMR_CMD(nor->read_opcode);
++ rpc_writel(rpc, RPC_DRCMR, val);
++
++ /* ...setup dummy cycles */
++ val = rpc_readl(rpc, RPC_DRDMCR);
++ val &= ~RPC_DRDMCR_DMCYC(0x1F);
++ val |= RPC_DRDMCR_DMCYC(dummy);
++ rpc_writel(rpc, RPC_DRDMCR, val);
++
++ /* ...setup read sequence */
++ val = rpc_readl(rpc, RPC_DRENR);
++ val |= RPC_DRENR_DME | RPC_DRENR_CDE;
++ rpc_writel(rpc, RPC_DRENR, val);
++
++ rpc_do_read_flash(rpc, from, len, buf, nor->addr_width > 3);
++
++ return ret;
++}
++
++static int __rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ u32 val;
++ u32 val2 = 0;
++ u32 *buf32;
++ int i;
++ u32 mask = 0, type;
++ struct rpc_info *rpc = nor->priv;
++
++ type = rpc->mtdtype;
++
++ rpc_setup_reg_mode(rpc);
++ val = rpc_readl(rpc, RPC_SMCMR);
++ val &= ~RPC_SMCMR_CMD(0xFF);
++ val |= RPC_SMCMR_CMD(opcode);
++ rpc_writel(rpc, RPC_SMCMR, val);
++
++ rpc_begin(rpc, true, false, len <= 4);
++ if (rpc_wait(rpc, RPC_TIMEOUT))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ val = rpc_readl(rpc, RPC_SMENR);
++ val &= ~(RPC_SMENR_CDE);
++ rpc_writel(rpc, RPC_SMENR, val);
++
++ buf32 = (u32 *)buf;
++
++ while (len > 0) {
++ rpc_read_manual_data(rpc, &val, &val2);
++
++ if (mask) {
++ dev_warn(&rpc->pdev->dev,
++ "Using mask workaround (0x%x)\n", mask);
++ val &= ~(mask);
++ val2 &= ~(mask);
++ }
++
++ /* ... spi flashes should be the same */
++ if (type == RPC_DUAL && val != val2) {
++ /* clear cs */
++ rpc_begin(rpc, true, false, true);
++ return -EAGAIN;
++ }
++
++ if (len > 4) {
++ *buf32 = val;
++ buf32++;
++ len -= 4;
++ } else {
++ buf = (u8 *)buf32;
++ for (i = 0; i < len; i++) {
++ *buf = (val >> (8 * i)) & 0x000000ff;
++ buf++;
++ }
++ len = 0;
++ }
++
++ if (!len)
++ break;
++
++ mask = 0xff;
++
++ rpc_begin(rpc, true, false, len <= 4);
++ if (rpc_wait(rpc, RPC_TIMEOUT))
++ return -ETIMEDOUT;
++
++ }
++
++ return 0;
++}
++
++#define RPC_REPEAT_TIMEOUT 200
++static int rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ unsigned long end = jiffies + msecs_to_jiffies(RPC_REPEAT_TIMEOUT);
++
++ /* A few read commands like read status can
++ * generate different answers. We repeat reading
++ * in that case
++ */
++ while (true) {
++ int ret = __rpc_read_reg(nor, opcode, buf, len);
++
++ if (!ret || ret != -EAGAIN)
++ return ret;
++
++ if (time_after(jiffies, end))
++ return -ETIMEDOUT;
++
++ msleep(20);
++ }
++}
++
++static int rpc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
++{
++ struct rpc_info *rpc = nor->priv;
++ u32 val;
++ int dsize;
++ bool copy = true;
++
++ rpc_setup_reg_mode(rpc);
++
++ val = rpc_readl(rpc, RPC_SMCMR);
++ val &= ~RPC_SMCMR_CMD(0xFF);
++ val |= RPC_SMCMR_CMD(opcode);
++ rpc_writel(rpc, RPC_SMCMR, val);
++
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++
++ if (rpc_write_data2reg(rpc, buf, dsize, copy))
++ return -EINVAL;
++ buf += dsize;
++ len -= dsize;
++ rpc_begin(rpc, false, dsize > 0, len == 0);
++
++ if (rpc_wait(rpc, RPC_TIMEOUT))
++ return -ETIMEDOUT;
++
++ /* ...disable command */
++ val = rpc_readl(rpc, RPC_SMENR);
++ val &= ~(RPC_SMENR_CDE);
++ rpc_writel(rpc, RPC_SMENR, val);
++
++ while (len > 0) {
++ dsize = rpc_datalen2trancfersize(rpc, len, copy);
++ if (rpc_setup_data_size(rpc, dsize, copy))
++ return -EINVAL;
++ rpc_write_data2reg(rpc, buf, dsize, copy);
++ buf += dsize;
++ len -= dsize;
++
++ rpc_begin(rpc, false, dsize, len == 0);
++
++ if (rpc_wait(rpc, RPC_TIMEOUT))
++ return -ETIMEDOUT;
++
++ }
++
++ return 0;
++}
++
++/* hw init for spi-nor flashes */
++static int rpc_spi_hw_init(struct rpc_info *rpc)
++{
++ /* Exec calibration */
++ rpc_clrsetl(rpc, RPC_PHYCNT,
++ RPC_PHYCNT_OCTA(3) | RPC_PHYCNT_EXDS | RPC_PHYCNT_OCT |
++ RPC_PHYCNT_DDRCAL | RPC_PHYCNT_HS | RPC_PHYCNT_STRTIM(7) |
++ RPC_PHYCNT_WBUF2 | RPC_PHYCNT_WBUF | RPC_PHYCNT_MEM(3),
++ RPC_PHYCNT_CAL | RPC_PHYCNT_STRTIM(6));
++
++ /* Disable RPC pins */
++ rpc_clrsetl(rpc, RPC_PHYINT,
++ RPC_PHYINT_INTIE | RPC_PHYINT_RSTEN |
++ RPC_PHYINT_WPEN | RPC_PHYINT_INTEN,
++ 0);
++
++ rpc_clrsetl(rpc, RPC_SMDRENR,
++ RPC_SMDRENR_HYPE(7),
++ RPC_SMDRENR_HYPE_SPI);
++
++ rpc_clrsetl(rpc, RPC_CMNCR,
++ RPC_CMNCR_BSZ(3),
++ rpc->mtdtype != RPC_SINGLE ?
++ RPC_CMNCR_BSZ(1) :
++ RPC_CMNCR_BSZ(0));
++
++ rpc_clrsetl(rpc, RPC_PHYOFFSET1,
++ RPC_PHYOFFSET1_DDRTMG(3),
++ RPC_PHYOFFSET1_DDRTMG_SDR);
++
++ rpc_writel(rpc, RPC_SSLDR,
++ RPC_SSLDR_SPNDL(0) | RPC_SSLDR_SLNDL(4) |
++ RPC_SSLDR_SCKDL(0));
++
++ return 0;
++}
++
++static int rpc_erase_sector(struct spi_nor *nor, loff_t addr)
++{
++ struct rpc_info *rpc = nor->priv;
++ u8 buf[6];
++ int i;
++
++ if (rpc->mtdtype == RPC_DUAL)
++ addr >>= 1;
++
++ for (i = nor->addr_width - 1; i >= 0; i--) {
++ buf[i] = addr & 0xff;
++ addr >>= 8;
++ }
++
++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
++}
++
++int rpc_qspi_probe(struct platform_device *pdev)
++{
++ struct rpc_info *rpc;
++ struct spi_nor *nor;
++ struct spi_nor_hwcaps hwcaps = {
++ .mask = SNOR_HWCAPS_READ |
++ SNOR_HWCAPS_READ_FAST |
++ SNOR_HWCAPS_PP,
++ };
++ u32 property;
++ int ret;
++
++ rpc = dev_get_drvdata(pdev->dev.parent);
++ if (!rpc || !rpc->flash) {
++ dev_err(&pdev->dev, "invalid data\n");
++ return -EINVAL;
++ }
++
++ if (!of_property_read_u32(rpc->flash, "spi-rx-bus-width", &property)) {
++ switch (property) {
++ case 1:
++ break;
++ case 2:
++ hwcaps.mask |= SNOR_HWCAPS_READ_DUAL;
++ break;
++ case 4:
++ hwcaps.mask |= SNOR_HWCAPS_READ_QUAD;
++ break;
++ default:
++ dev_err(&pdev->dev, "unsupported rx-bus-width\n");
++ return -EINVAL;
++ }
++ }
++
++ /* ... setup nor hooks */
++ nor = devm_kzalloc(&pdev->dev, sizeof(*nor), GFP_KERNEL);
++ if (!nor) {
++ dev_err(&pdev->dev, "allocation failed\n");
++ return -ENOMEM;
++ }
++
++ nor->dev = &pdev->dev;
++ spi_nor_set_flash_node(nor, rpc->flash);
++ nor->read = rpc_read_flash;
++ nor->write = rpc_write_flash;
++ nor->read_reg = rpc_read_reg;
++ nor->write_reg = rpc_write_reg;
++ nor->priv = rpc;
++
++ /* ... enable clk */
++ ret = clk_prepare_enable(rpc->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot prepare clock\n");
++ return ret;
++ }
++
++ /* ...init device */
++ ret = rpc_spi_hw_init(rpc);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "rpc_spi_hw_init error.\n");
++ goto error;
++ }
++
++ ret = spi_nor_scan(nor, NULL, &hwcaps);
++ if (ret) {
++ dev_err(&pdev->dev, "spi_nor_scan error.\n");
++ goto error;
++ }
++
++ /* Dual mode support */
++ if (rpc->mtdtype == RPC_DUAL) {
++ nor->page_size <<= 1;
++ nor->mtd.erasesize <<= 1;
++ nor->mtd.size <<= 1;
++ nor->mtd.writebufsize <<= 1;
++ nor->erase = rpc_erase_sector;
++ }
++
++ /* Workaround data size limitation */
++ if (nor->page_size > RPC_WBUF_SIZE) {
++ nor->page_size = RPC_WBUF_SIZE;
++ nor->mtd.writebufsize = RPC_WBUF_SIZE;
++ }
++
++ ret = mtd_device_register(&nor->mtd, NULL, 0);
++ if (ret) {
++ dev_err(&pdev->dev, "MTD registration failed\n");
++ goto error;
++ }
++
++ dev_info(&pdev->dev, "probed as %s\n",
++ rpc->mtdtype == RPC_SINGLE ? "single" : "dual");
++
++ platform_set_drvdata(pdev, nor);
++ return 0;
++
++error:
++ clk_disable_unprepare(rpc->clk);
++ return ret;
++}
++
++static int rpc_qspi_remove(struct platform_device *pdev)
++{
++ struct spi_nor *nor = platform_get_drvdata(pdev);
++ struct rpc_info *rpc = nor->priv;
++
++ mtd_device_unregister(&nor->mtd);
++ clk_disable_unprepare(rpc->clk);
++ return 0;
++}
++
++/* platform driver interface */
++static struct platform_driver rpc_qspi_platform_driver = {
++ .probe = rpc_qspi_probe,
++ .remove = rpc_qspi_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "renesas-rpc-qspi",
++ },
++};
++
++module_platform_driver(rpc_qspi_platform_driver);
++
++MODULE_ALIAS("renesas-rpc-qspi");
++MODULE_AUTHOR("Cogent Embedded Inc. <sources@cogentembedded.com>");
++MODULE_DESCRIPTION("Renesas RPC QSPI Driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mtd/spi-nor/renesas-rpc.c b/drivers/mtd/spi-nor/renesas-rpc.c
+index 0026b99..3aea0ae 100644
+--- a/drivers/mtd/spi-nor/renesas-rpc.c
++++ b/drivers/mtd/spi-nor/renesas-rpc.c
+@@ -1,7 +1,7 @@
+ /*
+ * Renesas RPC driver
+ *
+- * Copyright (C) 2018, Cogent Embedded Inc.
++ * Copyright (C) 2019, Cogent Embedded Inc.
+ *
+ * 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
+@@ -13,268 +13,28 @@
+ * GNU General Public License for more details.
+ */
+
+-#include <linux/clk.h>
++#include <linux/delay.h>
+ #include <linux/dma-mapping.h>
+-#include <linux/dmaengine.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/of_device.h>
+ #include <linux/interrupt.h>
+-#include <linux/platform_device.h>
+-#include <linux/delay.h>
+-#include <linux/mtd/spi-nor.h>
+-
+-/* regs offsets */
+-#define CMNCR 0x0000
+-#define SSLDR 0x0004
+-#define DRCR 0x000C
+-#define DRCMR 0x0010
+-#define DREAR 0x0014
+-#define DROPR 0x0018
+-#define DRENR 0x001C
+-#define SMCR 0x0020
+-#define SMCMR 0x0024
+-#define SMADR 0x0028
+-#define SMOPR 0x002C
+-#define SMENR 0x0030
+-#define SMRDR0 0x0038
+-#define SMRDR1 0x003C
+-#define SMWDR0 0x0040
+-#define SMWDR1 0x0044
+-#define CMNSR 0x0048
+-#define DRDMCR 0x0058
+-#define DRDRENR 0x005C
+-#define SMDMCR 0x0060
+-#define SMDRENR 0x0064
+-#define PHYCNT 0x007C
+-#define PHYOFFSET1 0x0080
+-#define PHYOFFSET2 0x0084
+-#define PHYINT 0x0088
+-#define DIV_REG 0x00A8
+-
+-/* CMNCR */
+-#define CMNCR_BSZ_MASK (0x03)
+-#define CMNCR_BSZ_4x1 (0x0)
+-#define CMNCR_BSZ_8x1 (0x1)
+-#define CMNCR_BSZ_4x2 (0x1)
+-#define CMNCR_MD (0x1 << 31)
+-#define CMNCR_MOIIO3_MASK (0x3 << 22)
+-#define CMNCR_MOIIO3_HIZ (0x3 << 22)
+-#define CMNCR_MOIIO2_MASK (0x3 << 20)
+-#define CMNCR_MOIIO2_HIZ (0x3 << 20)
+-#define CMNCR_MOIIO1_MASK (0x3 << 18)
+-#define CMNCR_MOIIO1_HIZ (0x3 << 18)
+-#define CMNCR_MOIIO0_MASK (0x3 << 16)
+-#define CMNCR_MOIIO0_HIZ (0x3 << 16)
+-#define CMNCR_IO0FV_MASK (0x3 << 8)
+-#define CMNCR_IO0FV_HIZ (0x3 << 8)
+-
+-/* DRCR */
+-#define DRCR_RBURST_MASK (0x1f << 16)
+-#define DRCR_RBURST(v) (((v) & 0x1f) << 16)
+-#define DRCR_SSLE (0x1)
+-#define DRCR_RBE (0x1 << 8)
+-#define DRCR_RCF (0x1 << 9)
+-#define DRCR_RBURST_32 (0x1f)
+-
+-/* SMENR */
+-#define SMENR_CDB_MASK (0x03 << 30)
+-#define SMENR_CDB(v) (((v) & 0x03) << 30)
+-#define SMENR_CDB_1B (0)
+-#define SMENR_CDB_2B (0x1 << 30)
+-#define SMENR_CDB_4B (0x2 << 30)
+-#define SMENR_OCDB_MASK (0x03 << 28)
+-#define SMENR_OCDB_1B (0)
+-#define SMENR_OCDB_2B (0x1 << 28)
+-#define SMENR_OCDB_4B (0x2 << 28)
+-#define SMENR_ADB_MASK (0x03 << 24)
+-#define SMENR_ADB(v) (((v) & 0x03) << 24)
+-#define SMENR_ADB_1B (0)
+-#define SMENR_ADB_2B (0x1 << 24)
+-#define SMENR_ADB_4B (0x2 << 24)
+-#define SMENR_OPDB_MASK (0x03 << 20)
+-#define SMENR_OPDB_1B (0)
+-#define SMENR_OPDB_2B (0x1 << 20)
+-#define SMENR_OPDB_4B (0x2 << 20)
+-#define SMENR_SPIDB_MASK (0x03 << 16)
+-#define SMENR_SPIDB(v) (((v) & 0x03) << 16)
+-#define SMENR_SPIDB_1B (0)
+-#define SMENR_SPIDB_2B (0x1 << 16)
+-#define SMENR_SPIDB_4B (0x2 << 16)
+-#define SMENR_OPDE_MASK (0xf << 4)
+-#define SMENR_OPDE_DISABLE (0)
+-#define SMENR_OPDE3 (0x8 << 4)
+-#define SMENR_OPDE32 (0xC << 4)
+-#define SMENR_OPDE321 (0xE << 4)
+-#define SMENR_OPDE3210 (0xF << 4)
+-#define SMENR_SPIDE_MASK (0x0F)
+-#define SMENR_SPIDE_DISABLE (0)
+-#define SMENR_SPIDE_8B (0x08)
+-#define SMENR_SPIDE_16B (0x0C)
+-#define SMENR_SPIDE_32B (0x0F)
+-#define SMENR_DME (1<<15)
+-#define SMENR_CDE (1<<14)
+-#define SMENR_OCDE (1<<12)
+-#define SMENR_ADE_MASK (0xf << 8)
+-#define SMENR_ADE_DISABLE (0)
+-#define SMENR_ADE_23_16 (0x4 << 8)
+-#define SMENR_ADE_23_8 (0x6 << 8)
+-#define SMENR_ADE_23_0 (0x7 << 8)
+-#define SMENR_ADE_31_0 (0xf << 8)
+-
+-/* SMCMR */
+-#define SMCMR_CMD(cmd) (((cmd) & 0xff) << 16)
+-#define SMCMR_CMD_MASK (0xff << 16)
+-#define SMCMR_OCMD(cmd) (((cmd) & 0xff))
+-#define SMCMR_OCMD_MASK (0xff)
+-
+-/* SMDRENR */
+-#define SMDRENR_HYPE_MASK (0x7 << 12)
+-#define SMDRENR_HYPE_SPI_FLASH (0x0)
+-#define SMDRENR_ADDRE (0x1 << 8)
+-#define SMDRENR_OPDRE (0x1 << 4)
+-#define SMDRENR_SPIDRE (0x1)
+-
+-/* PHYCNT */
+-#define PHYCNT_CAL (0x1 << 31)
+-#define PHYCNT_OCTA_MASK (0x3 << 22)
+-#define PHYCNT_EXDS (0x1 << 21)
+-#define PHYCNT_OCT (0x1 << 20)
+-#define PHYCNT_DDRCAL (0x1 << 19)
+-#define PHYCNT_HS (0x1 << 18)
+-#define PHYCNT_STREAM_MASK (0x7 << 15)
+-#define PHYCNT_STREAM(o) (((o) & 0x7) << 15)
+-#define PHYCNT_WBUF2 (0x1 << 4)
+-#define PHYCNT_WBUF (0x1 << 2)
+-#define PHYCNT_PHYMEM_MASK (0x3)
+-
+-/* SMCR */
+-#define SMCR_SSLKP (0x1 << 8)
+-#define SMCR_SPIRE (0x1 << 2)
+-#define SMCR_SPIWE (0x1 << 1)
+-#define SMCR_SPIE (0x1)
+-
+-/* CMNSR */
+-#define CMNSR_TEND (0x1 << 0)
+-
+-/* SSLDR */
+-#define SSLDR_SPNDL(v) (((v) & 0x7) << 16)
+-#define SSLDR_SLNDL(v) ((((v) | 0x4) & 0x7) << 8)
+-#define SSLDR_SCKDL(v) ((v) & 0x7)
+-
+-/* DREAR */
+-#define DREAR_EAV_MASK (0xff << 16)
+-#define DREAR_EAV(v) (((v) & 0xff) << 16)
+-#define DREAR_EAC_MASK (0x7)
+-#define DREAR_24B (0)
+-#define DREAR_25B (1)
+-
+-/* DRENR */
+-#define DRENR_CDB_MASK (0x03 << 30)
+-#define DRENR_CDB(v) (((v) & 0x3) << 30)
+-#define DRENR_CDB_1B (0)
+-#define DRENR_CDB_2B (0x1 << 30)
+-#define DRENR_CDB_4B (0x2 << 30)
+-#define DRENR_OCDB_MASK (0x03 << 28)
+-#define DRENR_OCDB_1B (0)
+-#define DRENR_OCDB_2B (0x1 << 28)
+-#define DRENR_OCDB_4B (0x2 << 28)
+-#define DRENR_ADB_MASK (0x03 << 24)
+-#define DRENR_ADB(v) (((v) & 0x3) << 24)
+-#define DRENR_ADB_1B (0)
+-#define DRENR_ADB_2B (0x1 << 24)
+-#define DRENR_ADB_4B (0x2 << 24)
+-#define DRENR_OPDB_MASK (0x03 << 20)
+-#define DRENR_OPDB_1B (0)
+-#define DRENR_OPDB_2B (0x1 << 20)
+-#define DRENR_OPDB_4B (0x2 << 20)
+-#define DRENR_DRDB_MASK (0x03 << 16)
+-#define DRENR_DRDB(v) (((v) & 0x3) << 16)
+-#define DRENR_DRDB_1B (0)
+-#define DRENR_DRDB_2B (0x1 << 16)
+-#define DRENR_DRDB_4B (0x2 << 16)
+-#define DRENR_OPDE_MASK (0xf << 4)
+-#define DRENR_OPDE_DISABLE (0)
+-#define DRENR_OPDE3 (0x8 << 4)
+-#define DRENR_OPDE32 (0xC << 4)
+-#define DRENR_OPDE321 (0xE << 4)
+-#define DRENR_OPDE3210 (0xF << 4)
+-#define DRENR_DME (1<<15)
+-#define DRENR_CDE (1<<14)
+-#define DRENR_OCDE (1<<12)
+-#define DRENR_ADE_MASK (0xf << 8)
+-#define DRENR_ADE_DISABLE (0)
+-#define DRENR_ADE_23_0 (0x7 << 8)
+-#define DRENR_ADE_31_0 (0xf << 8)
+-
+-/* DRCMR */
+-#define DRCMR_CMD(cmd) (((cmd) & 0xff) << 16)
+-#define DRCMR_CMD_MASK (0xff << 16)
+-#define DRCMR_OCMD(cmd) (((cmd) & 0xff))
+-#define DRCMR_OCMD_MASK (0xff)
+-
+-/* DRCMR */
+-#define DRDMCR_DMCYC(v) ((v) & 0x1f)
+-#define DRDMCR_DMCYC_MASK (0x1f)
+-
+-/* SMDMCR */
+-#define SMDMCR_DMCYC(v) ((v) & 0x0f)
+-#define SMDMCR_DMCYC_MASK (0x0f)
+-
+-/* PHYOFFSET1 */
+-#define PHYOFFSET1_DDRTMG (1 << 28)
+-
+-/* DIVREG */
+-#define DIVREG_RATIO_MASK (0x03)
+-#define DIVREG_RATIO(v) ((v) & 0x03)
+-#define DIVREG_RATIO_MAX (0x2)
+-
+-
+-#define DEFAULT_TO (100)
+-#define WRITE_BUF_SIZE (0x100)
+-#define WRITE_BUF_ADR_MASK (0xff)
+-
+-#define REPEAT_MAX (20)
+-#define REPEAT_TIME (10)
+-
+-struct rpc_spi {
+- struct platform_device *pdev;
+- void __iomem *base;
+- void __iomem *read_area;
+- void __iomem *write_area;
+- dma_addr_t read_area_dma;
+- struct completion comp;
+- struct dma_chan *dma_chan;
+- struct clk *clk;
+- unsigned int irq;
+- struct spi_nor spi_nor;
+-
+-#define MTD_QSPI_1x 0
+-#define MTD_QSPI_2x 1
+-
+- u32 mtdtype;
+-};
+-
+-/* IP block use it's own clock divigion register */
+-#define OWN_CLOCK_DIVIDER BIT(0)
++#include <linux/of_device.h>
+
+-#define RPC_DMA_BURST ((DRCR_RBURST_32 + 1) << 3)
+-#define RPC_DMA_SIZE_MIN (RPC_DMA_BURST << 3)
++#include "renesas-rpc.h"
+
+ static bool use_dma = true;
+ module_param(use_dma, bool, 0);
+ MODULE_PARM_DESC(use_dma, "DMA support. 0 = Disable, 1 = Enable");
+
+-/* debug */
+-static void __maybe_unused regs_dump(struct rpc_spi *rpc)
++/* Debug */
++#ifdef DEBUG
++void rpc_regs_dump(struct rpc_info *rpc)
+ {
+ static u32 regs[] = {
+- CMNCR, SSLDR, DRCR, DRCMR, DREAR,
+- DROPR, DRENR, SMCR, SMCMR, SMADR,
+- SMOPR, SMENR, SMRDR0, SMRDR1, SMWDR0,
+- SMWDR1, CMNSR, DRDMCR, DRDRENR, SMDMCR,
+- SMDRENR, PHYCNT, PHYOFFSET1, PHYOFFSET2,
+- PHYINT
++ RPC_CMNCR, RPC_SSLDR, RPC_DRCR, RPC_DRCMR, RPC_DREAR,
++ RPC_DROPR, RPC_DRENR, RPC_SMCR, RPC_SMCMR, RPC_SMADR,
++ RPC_SMOPR, RPC_SMENR, RPC_SMRDR0, RPC_SMRDR1, RPC_SMWDR0,
++ RPC_SMWDR1, RPC_CMNSR, RPC_DRDMCR, RPC_DRDRENR, RPC_SMDMCR,
++ RPC_SMDRENR, RPC_PHYCNT, RPC_PHYOFFSET1, RPC_PHYOFFSET2,
++ RPC_PHYINT
+ };
+
+ static const char *const names[] = {
+@@ -291,23 +51,43 @@ static void __maybe_unused regs_dump(struct rpc_spi *rpc)
+ dev_dbg(&rpc->pdev->dev, "RPC regs dump:\n");
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ dev_dbg(&rpc->pdev->dev, "%s = 0x%08x\n", names[i],
+- readl(rpc->base + regs[i]));
++ rpc_readl(rpc, regs[i]));
++}
++EXPORT_SYMBOL(rpc_regs_dump);
++#endif
++
++/* Poll operation end */
++int rpc_wait(struct rpc_info *rpc, int timeout)
++{
++ unsigned long end = jiffies + msecs_to_jiffies(timeout);
++
++ while (!(rpc_readl(rpc, RPC_CMNSR) & RPC_CMNSR_TEND)) {
++ if (time_after(jiffies, end)) {
++ dev_err(&rpc->pdev->dev, "timed out\n");
++ return -ETIMEDOUT;
++ }
++
++ cpu_relax();
++ }
++
++ return 0;
+ }
++EXPORT_SYMBOL(rpc_wait);
+
++/* DMA support */
+ static void rpc_dma_complete_func(void *completion)
+ {
+ complete(completion);
+ }
+
+-static int rpc_dma_read(struct rpc_spi *rpc, void *buf,
+- loff_t from, ssize_t *plen)
++int rpc_dma_read(struct rpc_info *rpc, void *buf, loff_t from, ssize_t *plen)
+ {
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_dst_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+- int retval = 0;
++ int ret = 0;
+ ssize_t len;
+
+ len = *plen;
+@@ -322,7 +102,7 @@ static int rpc_dma_read(struct rpc_spi *rpc, void *buf,
+
+ dma_dst_addr = dma_map_single(dma_dev->dev, buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dma_dev->dev, dma_dst_addr)) {
+- dev_err(&rpc->pdev->dev, "Failed to dma_map_single\n");
++ dev_err(&rpc->pdev->dev, "DMA map single failed\n");
+ return -ENXIO;
+ }
+
+@@ -331,8 +111,8 @@ static int rpc_dma_read(struct rpc_spi *rpc, void *buf,
+ rpc->read_area_dma + from,
+ len, flags);
+ if (!tx) {
+- dev_err(&rpc->pdev->dev, "Failed to prepare DMA memcpy\n");
+- retval = -EIO;
++ dev_err(&rpc->pdev->dev, "DMA prepare memcpy failed\n");
++ ret = -EIO;
+ goto out_dma;
+ }
+
+@@ -341,9 +121,9 @@ static int rpc_dma_read(struct rpc_spi *rpc, void *buf,
+ tx->callback_param = &rpc->comp;
+
+ cookie = tx->tx_submit(tx);
+- retval = dma_submit_error(cookie);
+- if (retval) {
+- dev_err(&rpc->pdev->dev, "Failed to do DMA tx_submit\n");
++ ret = dma_submit_error(cookie);
++ if (ret) {
++ dev_err(&rpc->pdev->dev, "DMA tx submit failed\n");
+ goto out_dma;
+ }
+
+@@ -355,968 +135,154 @@ static int rpc_dma_read(struct rpc_spi *rpc, void *buf,
+
+ out_dma:
+ dma_unmap_single(dma_dev->dev, dma_dst_addr, len, DMA_FROM_DEVICE);
+- return retval;
+-}
+-
+-/* register acces */
+-static u32 rpc_read(struct rpc_spi *rpc, unsigned int reg)
+-{
+- u32 val;
+-
+- val = readl(rpc->base + reg);
+- return val;
+-}
+-
+-static void rpc_write(struct rpc_spi *rpc, unsigned int reg, u32 val)
+-{
+- writel(val, rpc->base + reg);
+-}
+-
+-static int rpc_wait(struct rpc_spi *rpc, u32 to)
+-{
+- u32 val;
+- int i;
+-
+- for (i = 0; i < to; i++) {
+- val = rpc_read(rpc, CMNSR);
+- val &= CMNSR_TEND;
+- if (val)
+- break;
+-
+- udelay(100);
+- }
+-
+- if (i == to) {
+- dev_err(&rpc->pdev->dev, "timeout waiting for operation end %d\n",
+- rpc_read(rpc, CMNSR));
+- return -ETIMEDOUT;
+- }
+-
+- return 0;
+-}
+-
+-static int rpc_setup_clk_ratio(struct rpc_spi *rpc, u32 max_clk_rate)
+-{
+- unsigned long rate = clk_get_rate(rpc->clk);
+- u32 ratio;
+- u32 val;
+-
+- ratio = DIV_ROUND_UP(rate, max_clk_rate * 2) >> 1;
+- if (ratio > DIVREG_RATIO_MAX)
+- ratio = DIVREG_RATIO_MAX;
+-
+- val = rpc_read(rpc, DIV_REG);
+- val &= DIVREG_RATIO_MASK;
+- val |= DIVREG_RATIO(ratio);
+- rpc_write(rpc, DIV_REG, val);
+-
+- return 0;
+-}
+-
+-static int rpc_endisable_write_buf(struct rpc_spi *rpc, bool en)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, PHYCNT);
+-
+- if (en)
+- val |= PHYCNT_WBUF | PHYCNT_WBUF2;
+- else
+- val &= ~(PHYCNT_WBUF | PHYCNT_WBUF2);
+-
+- rpc_write(rpc, PHYCNT, val);
+-
+- return 0;
+-}
+-
+-static int rpc_begin(struct rpc_spi *rpc,
+- bool rx, bool tx, bool last)
+-{
+- u32 val = SMCR_SPIE;
+-
+- if (rx)
+- val |= SMCR_SPIRE;
+-
+- if (tx)
+- val |= SMCR_SPIWE;
+-
+- if (!last)
+- val |= SMCR_SSLKP;
+-
+- rpc_write(rpc, SMCR, val);
+-
+- return 0;
+-}
+-
+-static int rpc_setup_reg_mode(struct rpc_spi *rpc)
+-{
+- u32 val;
+-
+- rpc_wait(rpc, DEFAULT_TO);
+-
+- rpc_endisable_write_buf(rpc, false);
+-
+- /* ...setup manual mode */
+- val = rpc_read(rpc, CMNCR);
+- val |= CMNCR_MD;
+- rpc_write(rpc, CMNCR, val);
+-
+- /* disable ddr */
+- val = rpc_read(rpc, SMDRENR);
+- val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE);
+- rpc_write(rpc, SMDRENR, val);
+-
+- /* enable 1bit command */
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_CDB_MASK | SMENR_OCDB_MASK | SMENR_DME
+- | SMENR_OCDE | SMENR_SPIDB_MASK
+- | SMENR_ADE_MASK | SMENR_ADB_MASK
+- | SMENR_OPDE_MASK | SMENR_SPIDE_MASK);
+- val |= SMENR_CDB_1B | SMENR_CDE | SMENR_SPIDE_32B;
+- rpc_write(rpc, SMENR, val);
+-
+-
+- return 0;
+-}
+-
+-static void rpc_flush_cache(struct rpc_spi *rpc)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, DRCR);
+- val |= DRCR_RCF;
+- rpc_write(rpc, DRCR, val);
+-}
+-
+-static int rpc_setup_ext_mode(struct rpc_spi *rpc)
+-{
+- u32 val;
+- u32 cmncr;
+-
+- rpc_wait(rpc, DEFAULT_TO);
+-
+- rpc_endisable_write_buf(rpc, false);
+-
+- /* ...setup ext mode */
+- val = rpc_read(rpc, CMNCR);
+- cmncr = val;
+- val &= ~(CMNCR_MD);
+- rpc_write(rpc, CMNCR, val);
+-
+- /* ...enable burst and clear cache */
+- val = rpc_read(rpc, DRCR);
+- val &= ~(DRCR_RBURST_MASK | DRCR_RBE | DRCR_SSLE);
+- val |= DRCR_RBURST(DRCR_RBURST_32) | DRCR_RBE;
+-
+- if (cmncr & CMNCR_MD)
+- val |= DRCR_RCF;
+-
+- rpc_write(rpc, DRCR, val);
+-
+- return 0;
+-}
+-
+-static int rpc_setup_data_size(struct rpc_spi *rpc, u32 size, bool copy)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_SPIDE_MASK);
+-
+- if (rpc->mtdtype == MTD_QSPI_2x && !copy)
+- size >>= 1;
+-
+- switch (size) {
+- case 0:
+- break;
+- case 1:
+- val |= SMENR_SPIDE_8B;
+- break;
+- case 2:
+- val |= SMENR_SPIDE_16B;
+- break;
+- case 4:
+- val |= SMENR_SPIDE_32B;
+- break;
+- default:
+- dev_err(&rpc->pdev->dev, "Unsupported data width %d\n", size);
+- return -EINVAL;
+- }
+- rpc_write(rpc, SMENR, val);
+-
+- return 0;
+-}
+-
+-static int rpc_setup_extmode_read_addr(struct rpc_spi *rpc,
+- int adr_width, loff_t adr)
+-{
+- u32 val;
+- u32 v;
+-
+- val = rpc_read(rpc, DREAR);
+- val &= ~(DREAR_EAV_MASK | DREAR_EAC_MASK);
+-
+- if (adr_width == 4) {
+- v = adr >> 25;
+- val |= DREAR_EAV(v) | DREAR_25B;
+- }
+- rpc_write(rpc, DREAR, val);
+-
+- val = rpc_read(rpc, DRENR);
+- val &= ~(DRENR_ADE_MASK);
+- if (adr_width == 4)
+- val |= DRENR_ADE_31_0;
+- else
+- val |= DRENR_ADE_23_0;
+- rpc_write(rpc, DRENR, val);
+-
+- return 0;
+-}
+-
+-static inline int rpc_get_read_addr_nbits(u8 opcode)
+-{
+- if (opcode == SPINOR_OP_READ_1_4_4_4B)
+- return 4;
+- return 1;
+-}
+-
+-#define NBITS_TO_VAL(v) ((v >> 1) & 3)
+-static int rpc_setup_extmode_nbits(struct rpc_spi *rpc, int cnb,
+- int anb, int dnb)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, DRENR);
+- val &= ~(DRENR_CDB_MASK | DRENR_ADB_MASK | DRENR_DRDB_MASK);
+- val |= DRENR_CDB(NBITS_TO_VAL(cnb))
+- | DRENR_ADB(NBITS_TO_VAL(anb))
+- | DRENR_DRDB(NBITS_TO_VAL(dnb));
+- rpc_write(rpc, DRENR, val);
+-
+- return 0;
+-}
+-
+-static int rpc_setup_writemode_nbits(struct rpc_spi *rpc, int cnb,
+- int anb, int dnb)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_CDB_MASK | SMENR_ADB_MASK | SMENR_SPIDB_MASK);
+- val |= SMENR_CDB(NBITS_TO_VAL(cnb))
+- | SMENR_ADB(NBITS_TO_VAL(anb))
+- | SMENR_SPIDB(NBITS_TO_VAL(dnb));
+- rpc_write(rpc, SMENR, val);
+-
+- return 0;
+-}
+-
+-static void rpc_setup_write_mode_command_and_adr(struct rpc_spi *rpc,
+- int adr_width, bool ena)
+-{
+- u32 val;
+-
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_CDB_MASK | SMENR_CDE | SMENR_ADE_MASK);
+-
+- if (ena) {
+- /* enable 1bit command */
+- val |= SMENR_CDB_1B | SMENR_CDE;
+-
+- if (adr_width == 4)
+- val |= SMENR_ADE_31_0;
+- else
+- val |= SMENR_ADE_23_0;
+- }
+- rpc_write(rpc, SMENR, val);
+-}
+-
+-static int rpc_setup_write_mode(struct rpc_spi *rpc, u8 opcode)
+-{
+- u32 val;
+-
+- rpc_wait(rpc, DEFAULT_TO);
+-
+- rpc_endisable_write_buf(rpc, true);
+-
+- /* ...setup manual mode */
+- val = rpc_read(rpc, CMNCR);
+- val |= CMNCR_MD;
+- rpc_write(rpc, CMNCR, val);
+-
+- /* disable ddr */
+- val = rpc_read(rpc, SMDRENR);
+- val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE);
+- rpc_write(rpc, SMDRENR, val);
+-
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_OCDB_MASK | SMENR_DME | SMENR_OCDE | SMENR_SPIDB_MASK
+- | SMENR_ADB_MASK | SMENR_OPDE_MASK | SMENR_SPIDE_MASK);
+- if (opcode != SPINOR_OP_PP)
+- val |= SMENR_SPIDE_32B;
+- else
+- val |= SMENR_SPIDE_8B;
+-
+- rpc_write(rpc, SMENR, val);
+-
+- return 0;
+-}
+-
+-static void rpc_read_manual_data(struct rpc_spi *rpc, u32 *pv0, u32 *pv1)
+-{
+- u32 val0, val1, rd0, rd1;
+-
+- val0 = rpc_read(rpc, SMRDR0);
+- val1 = rpc_read(rpc, SMRDR1);
+-
+- if (rpc->mtdtype == MTD_QSPI_2x) {
+- rd1 = (val0 & 0xff000000) | ((val0 << 8) & 0xff0000) |
+- ((val1 >> 16) & 0xff00) | ((val1 >> 8) & 0xff);
+- rd0 = ((val0 & 0xff0000) << 8) | ((val0 << 16) & 0xff0000) |
+- ((val1 >> 8) & 0xff00) | (val1 & 0xff);
+- } else
+- rd0 = val0;
+-
+- if (pv0)
+- *pv0 = rd0;
+-
+- if (pv1 && rpc->mtdtype == MTD_QSPI_2x)
+- *pv1 = rd1;
+-}
+-
+-static int rpc_datalen2trancfersize(struct rpc_spi *rpc, int len, bool copy)
+-{
+- int sz = len;
+-
+- if (len >= 2)
+- sz = 2;
+-
+- if (len >= 4)
+- sz = 4;
+-
+- if (rpc->mtdtype == MTD_QSPI_2x && len >= 8 && !copy)
+- sz = 8;
+-
+- return sz;
+-}
+-
+-static int __rpc_write_data2reg(struct rpc_spi *rpc, int off,
+- const u8 *buf, int sz)
+-{
+- const u32 *b32 = (const u32 *)buf;
+- const u16 *b16 = (const u16 *)buf;
+-
+- if (sz == 4)
+- rpc_write(rpc, off, *b32);
+- else if (sz == 2)
+- writew(*b16, rpc->base + off);
+- else if (sz == 1)
+- writeb(*buf, rpc->base + off);
+- else if (sz != 0) {
+- dev_err(&rpc->pdev->dev, "incorrect data size %d\n", sz);
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-#define __SETVAL(x) ((((x) & 0xff) << 8) | ((x) & 0xff))
+-static int rpc_write_data2reg(struct rpc_spi *rpc, const u8 *buf,
+- int sz, bool copy)
+-{
+- int i, ret;
+- u32 v = 0;
+-
+- if (rpc->mtdtype == MTD_QSPI_2x) {
+- if (copy) {
+- for (i = 0; i < sz && i < 2; i++)
+- v |= (__SETVAL(buf[i]) << 16*i);
+-
+- ret = __rpc_write_data2reg(rpc,
+- sz == 4 ? SMWDR1 : SMWDR0,
+- (u8 *)&v,
+- sz == 4 ? sz : sz * 2);
+- if (ret)
+- return ret;
+-
+- v = 0;
+- for (; i < sz; i++)
+- v |= (__SETVAL(buf[i]) << 16*i);
+-
+-
+- ret = __rpc_write_data2reg(rpc,
+- sz == 4 ? SMWDR0 : SMWDR1,
+- (u8 *)&v,
+- sz == 4 ? sz : sz * 2);
+- if (ret)
+- return ret;
+-
+- return 0;
+- }
+-
+- sz >>= 1;
+- ret = __rpc_write_data2reg(rpc,
+- sz == 4 ? SMWDR1 : SMWDR0,
+- buf,
+- sz == 4 ? sz : sz * 2);
+- if (ret)
+- return ret;
+- buf += sz;
+-
+- return __rpc_write_data2reg(rpc,
+- sz == 4 ? SMWDR0 : SMWDR1,
+- buf, sz == 4 ? sz : sz * 2);
+- }
+-
+- return __rpc_write_data2reg(rpc, SMWDR0, buf, sz);
+-}
+-
+-static ssize_t rpc_write_unaligned(struct spi_nor *nor, loff_t to, size_t len,
+- const u_char *buf, size_t fullen)
+-{
+- int ret = len, dsize;
+- struct rpc_spi *rpc = nor->priv;
+- bool copy = false, last;
+- loff_t _to;
+-
+- rpc_endisable_write_buf(rpc, false);
+-
+- while (len > 0) {
+- _to = to;
+- if (rpc->mtdtype == MTD_QSPI_2x)
+- _to >>= 1;
+- rpc_write(rpc, SMADR, _to);
+- dsize = rpc_datalen2trancfersize(rpc, len, copy);
+-
+- if (rpc_setup_data_size(rpc, dsize, copy))
+- return -EINVAL;
+-
+- rpc_write_data2reg(rpc, buf, dsize, copy);
+-
+- last = (len <= dsize && fullen <= ret);
+- rpc_begin(rpc, false, true, last);
+- if (rpc_wait(rpc, DEFAULT_TO))
+- return -ETIMEDOUT;
+-
+- /* ...disable command */
+- rpc_setup_write_mode_command_and_adr(rpc,
+- nor->addr_width, false);
+-
+- buf += dsize;
+- len -= dsize;
+- to += dsize;
+- }
+-
+ return ret;
+ }
++EXPORT_SYMBOL(rpc_dma_read);
+
+-static ssize_t rpc_write_flash(struct spi_nor *nor, loff_t to, size_t len,
+- const u_char *buf)
+-{
+- ssize_t res = len, full = len;
+- u32 val;
+- u8 rval[2];
+- struct rpc_spi *rpc = nor->priv;
+- loff_t bo;
+- loff_t offset;
+- bool is_rounded = false;
+-
+- /* ...len should be rounded to 2 bytes */
+- if (rpc->mtdtype == MTD_QSPI_2x && (len & 1)) {
+- is_rounded = true;
+- len &= ~(1);
+- }
+-
+- bo = to & (WRITE_BUF_ADR_MASK);
+-
+- rpc_flush_cache(rpc);
+- rpc_setup_write_mode(rpc, nor->program_opcode);
+- rpc_setup_write_mode_command_and_adr(rpc, nor->addr_width, true);
+- rpc_setup_writemode_nbits(rpc, 1, 1, 1);
+-
+- /* ...setup command */
+- val = rpc_read(rpc, SMCMR);
+- val &= ~(SMCMR_CMD_MASK);
+- val |= SMCMR_CMD(nor->program_opcode);
+- rpc_write(rpc, SMCMR, val);
+-
+- offset = (to & (~WRITE_BUF_ADR_MASK));
+-
+- /* ...write unaligned first bytes */
+- if (bo) {
+- size_t min = (len < (WRITE_BUF_SIZE - bo)) ? len : (WRITE_BUF_SIZE - bo);
+-
+- rpc_write_unaligned(nor, to, min, buf, full);
+- rpc_setup_write_mode(rpc, nor->program_opcode);
+-
+- len -= min;
+- buf += min;
+- to += min;
+- full -= min;
+- }
+-
+- /*
+- * TODO: Unfortunately RPC does not write properly in write buf mode
+- * without transferring command. Investigate this.
+- */
+-
+- if (len) {
+- rpc_write_unaligned(nor, to, len, buf, full);
+- buf += len;
+- to += len;
+- full -= len;
+- len = 0;
+- }
+-
+- if (is_rounded) {
+- rval[0] = *buf;
+- rval[1] = 0xFF;
+- rpc_write_unaligned(nor, to, 2, rval, full);
+- }
+-
+- rpc_flush_cache(rpc);
+-
+- return res;
+-}
+-
+-static inline unsigned int rpc_rx_nbits(struct spi_nor *nor)
+-{
+- return spi_nor_get_protocol_data_nbits(nor->read_proto);
+-}
+-
+-#define READ_ADR_MASK (BIT(26) - 1)
+-static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len,
+- u_char *buf)
++/* Read */
++void rpc_do_read_flash(struct rpc_info *rpc, loff_t from,
++ size_t len, u_char *buf, bool addr32)
+ {
+- u32 val;
+- struct rpc_spi *rpc = nor->priv;
+- int adr_width = nor->addr_width;
+- int opcode_nbits = 1;
+- int addr_nbits = rpc_get_read_addr_nbits(nor->read_opcode);
+- int data_nbits = rpc_rx_nbits(nor);
+- int dummy = nor->read_dummy - 1;
+- ssize_t ret = len;
+- ssize_t readlen;
+- loff_t _from;
+-
+- rpc_setup_ext_mode(rpc);
+- /* ...setup n bits */
+- rpc_setup_extmode_nbits(rpc, opcode_nbits, addr_nbits, data_nbits);
+-
+- /* TODO: setup DDR */
+-
+- /* ...setup command */
+- val = rpc_read(rpc, DRCMR);
+- val &= ~(DRCMR_CMD_MASK);
+- val |= DRCMR_CMD(nor->read_opcode);
+- rpc_write(rpc, DRCMR, val);
+-
+- /* ...setup dummy cycles */
+- val = rpc_read(rpc, DRDMCR);
+- val &= ~(DRDMCR_DMCYC_MASK);
+- val |= DRDMCR_DMCYC(dummy);
+- rpc_write(rpc, DRDMCR, val);
+-
+- /* ...setup read sequence */
+- val = rpc_read(rpc, DRENR);
+- val |= DRENR_DME | DRENR_CDE;
+- rpc_write(rpc, DRENR, val);
+-
+ while (len > 0) {
+- int retval;
+-
+- /* ...setup address */
+- rpc_setup_extmode_read_addr(rpc, adr_width, from);
+- /* ...use adr [25...0] */
+- _from = from & READ_ADR_MASK;
+-
+- readlen = READ_ADR_MASK - _from + 1;
++ ssize_t readlen;
++ loff_t _from;
++ int ret;
++
++ /* Setup ext mode address */
++ rpc_clrsetl(rpc, RPC_DREAR,
++ RPC_DREAR_EAV(0xFF) | RPC_DREAR_EAC(7),
++ addr32 ?
++ RPC_DREAR_EAV(from >> 25) | RPC_DREAR_EAC(1) :
++ 0);
++
++ rpc_clrsetl(rpc, RPC_DRENR,
++ RPC_DRENR_ADE(0xF),
++ addr32 ?
++ /* bits 31-0 */
++ RPC_DRENR_ADE(0xF) :
++ /* bits 23-0 */
++ RPC_DRENR_ADE(0x7));
++
++ /* Use address bits [25...0] */
++ _from = from & RPC_READ_ADDR_MASK;
++
++ readlen = RPC_READ_ADDR_MASK - _from + 1;
+ readlen = readlen > len ? len : readlen;
+
+- retval = rpc_dma_read(rpc, buf, _from, &readlen);
+- if (retval)
++ ret = rpc_dma_read(rpc, buf, _from, &readlen);
++ if (ret)
+ memcpy_fromio(buf, rpc->read_area + _from, readlen);
+
+ buf += readlen;
+ from += readlen;
+ len -= readlen;
+ }
+-
+- return ret;
+-}
+-
+-static int __rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+-{
+- u32 val;
+- u32 val2 = 0;
+- u32 *buf32;
+- int i;
+- u32 mask = 0, type;
+- struct rpc_spi *rpc = nor->priv;
+-
+- type = rpc->mtdtype;
+-
+- rpc_setup_reg_mode(rpc);
+- val = rpc_read(rpc, SMCMR);
+- val &= ~(SMCMR_CMD_MASK);
+- val |= SMCMR_CMD(opcode);
+- rpc_write(rpc, SMCMR, val);
+-
+- rpc_begin(rpc, true, false, len <= 4);
+- if (rpc_wait(rpc, DEFAULT_TO))
+- return -ETIMEDOUT;
+-
+- /* ...disable command */
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_CDE);
+- rpc_write(rpc, SMENR, val);
+-
+- buf32 = (u32 *)buf;
+-
+- while (len > 0) {
+- rpc_read_manual_data(rpc, &val, &val2);
+-
+- if (mask) {
+- dev_warn(&rpc->pdev->dev,
+- "Using mask workaround (0x%x)\n", mask);
+- val &= ~(mask);
+- val2 &= ~(mask);
+- }
+-
+- /* ... spi flashes should be the same */
+- if (type == MTD_QSPI_2x && val != val2) {
+- /* clear cs */
+- rpc_begin(rpc, true, false, true);
+- return -EAGAIN;
+- }
+-
+- if (len > 4) {
+- *buf32 = val;
+- buf32++;
+- len -= 4;
+- } else {
+- buf = (u8 *)buf32;
+- for (i = 0; i < len; i++) {
+- *buf = (val >> (8 * i)) & 0x000000ff;
+- buf++;
+- }
+- len = 0;
+- }
+-
+- if (!len)
+- break;
+-
+- mask = 0xff;
+-
+- rpc_begin(rpc, true, false, len <= 4);
+- if (rpc_wait(rpc, DEFAULT_TO))
+- return -ETIMEDOUT;
+-
+- }
+-
+- return 0;
+-}
+-
+-static int rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+-{
+- int i, ret;
+-
+- /* A few read commands like read status can
+- * generate different answers. We repeat reading
+- * in that case
+- */
+- for (i = 0; i < REPEAT_MAX; i++) {
+- ret = __rpc_read_reg(nor, opcode, buf, len);
+- if (!ret || ret != -EAGAIN)
+- break;
+- mdelay(REPEAT_TIME);
+- }
+-
+- return ret;
+-}
+-
+-static int rpc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+-{
+- struct rpc_spi *rpc = nor->priv;
+- u32 val;
+- int dsize;
+- bool copy = true;
+-
+- rpc_setup_reg_mode(rpc);
+-
+- val = rpc_read(rpc, SMCMR);
+- val &= ~(SMCMR_CMD_MASK);
+- val |= SMCMR_CMD(opcode);
+- rpc_write(rpc, SMCMR, val);
+-
+- dsize = rpc_datalen2trancfersize(rpc, len, copy);
+-
+- if (rpc_setup_data_size(rpc, dsize, copy))
+- return -EINVAL;
+-
+- if (rpc_write_data2reg(rpc, buf, dsize, copy))
+- return -EINVAL;
+- buf += dsize;
+- len -= dsize;
+- rpc_begin(rpc, false, dsize > 0, len == 0);
+-
+- if (rpc_wait(rpc, DEFAULT_TO))
+- return -ETIMEDOUT;
+-
+- /* ...disable command */
+- val = rpc_read(rpc, SMENR);
+- val &= ~(SMENR_CDE);
+- rpc_write(rpc, SMENR, val);
+-
+- while (len > 0) {
+- dsize = rpc_datalen2trancfersize(rpc, len, copy);
+- if (rpc_setup_data_size(rpc, dsize, copy))
+- return -EINVAL;
+- rpc_write_data2reg(rpc, buf, dsize, copy);
+- buf += dsize;
+- len -= dsize;
+-
+- rpc_begin(rpc, false, dsize, len == 0);
+-
+- if (rpc_wait(rpc, DEFAULT_TO))
+- return -ETIMEDOUT;
+-
+- }
+-
+- return 0;
+ }
++EXPORT_SYMBOL(rpc_do_read_flash);
+
+-/* hw init for spi-nor flashes */
+-static int rpc_hw_init_1x2x(struct rpc_spi *rpc)
++/* Own clock setup */
++static int rpc_own_clk_set_rate(struct rpc_info *rpc, u32 max_clk_rate)
+ {
+- u32 val;
+-
+- /* Exec calibration */
+- val = rpc_read(rpc, PHYCNT);
+- val &= ~(PHYCNT_OCTA_MASK | PHYCNT_EXDS | PHYCNT_OCT
+- | PHYCNT_DDRCAL | PHYCNT_HS | PHYCNT_STREAM_MASK
+- | PHYCNT_WBUF2 | PHYCNT_WBUF | PHYCNT_PHYMEM_MASK);
+- val |= (PHYCNT_CAL) | PHYCNT_STREAM(6);
+- rpc_write(rpc, PHYCNT, val);
+-
+- /* disable rpc_* pins */
+- val = rpc_read(rpc, PHYINT);
+- val &= ~((1<<24) | (7<<16));
+- rpc_write(rpc, PHYINT, val);
+-
+- val = rpc_read(rpc, SMDRENR);
+- val &= ~(SMDRENR_HYPE_MASK);
+- val |= SMDRENR_HYPE_SPI_FLASH;
+- rpc_write(rpc, SMDRENR, val);
+-
+- val = rpc_read(rpc, CMNCR);
+- val &= ~(CMNCR_BSZ_MASK);
+- if (rpc->mtdtype != MTD_QSPI_1x)
+- val |= CMNCR_BSZ_4x2;
+- rpc_write(rpc, CMNCR, val);
+-
+- val = rpc_read(rpc, PHYOFFSET1);
+- val |= PHYOFFSET1_DDRTMG;
+- rpc_write(rpc, PHYOFFSET1, val);
++ unsigned long rate = clk_get_rate(rpc->clk);
++ u32 ratio;
+
+- val = SSLDR_SPNDL(0) | SSLDR_SLNDL(4) | SSLDR_SCKDL(0);
+- rpc_write(rpc, SSLDR, val);
++ ratio = DIV_ROUND_UP(rate, max_clk_rate * 2) >> 1;
++ if (ratio > RPC_DIVREG_RATIO_MAX)
++ ratio = RPC_DIVREG_RATIO_MAX;
+
++ rpc_clrsetl(rpc, RPC_DIVREG,
++ RPC_DIVREG_RATIO(0x3),
++ RPC_DIVREG_RATIO(ratio));
+ return 0;
+ }
+
+-static int rpc_hw_init(struct rpc_spi *rpc)
+-{
+- switch (rpc->mtdtype) {
+- case MTD_QSPI_1x:
+- case MTD_QSPI_2x:
+- return rpc_hw_init_1x2x(rpc);
+-
+- default:
+- dev_err(&rpc->pdev->dev, "Unsupported connection mode\n");
+- return -ENODEV;
+- }
+-}
+-
+-static int rpc_erase_sector(struct spi_nor *nor, loff_t addr)
+-{
+- struct rpc_spi *rpc = nor->priv;
+- u8 buf[6];
+- int i;
+-
+- if (rpc->mtdtype == MTD_QSPI_2x)
+- addr >>= 1;
+-
+- for (i = nor->addr_width - 1; i >= 0; i--) {
+- buf[i] = addr & 0xff;
+- addr >>= 8;
+- }
+-
+- return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+-}
+-
+-static const struct of_device_id rpc_of_match[] = {
+- { .compatible = "renesas,qspi-rpc-r8a77980" },
+- { .compatible = "renesas,qspi-rpc-r8a77970", .data = (void *)OWN_CLOCK_DIVIDER },
+- { },
+-};
+-
+-MODULE_DEVICE_TABLE(of, rpc_of_match);
+-
+-static int rpc_spi_probe(struct platform_device *pdev)
++static int rpc_probe(struct platform_device *pdev)
+ {
+- struct device_node *flash_np;
+- struct spi_nor *nor;
+- struct rpc_spi *rpc;
++ struct rpc_info *rpc;
+ struct resource *res;
+- struct spi_nor_hwcaps hwcaps = {
+- .mask = SNOR_HWCAPS_READ |
+- SNOR_HWCAPS_READ_FAST |
+- SNOR_HWCAPS_PP,
+- };
+- u32 max_clk_rate = 50000000;
+- u32 property;
++ const char *name = NULL;
++ u32 rate = 0;
+ int ret;
+- int own_clk;
+-
+-
+- flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
+- if (!flash_np) {
+- dev_err(&pdev->dev, "no SPI flash device to configure\n");
+- return -ENODEV;
+- }
+-
+- if (!of_property_read_u32(flash_np, "spi-rx-bus-width", &property)) {
+- switch (property) {
+- case 1:
+- break;
+- case 2:
+- hwcaps.mask |= SNOR_HWCAPS_READ_DUAL;
+- break;
+- case 4:
+- hwcaps.mask |= SNOR_HWCAPS_READ_QUAD;
+- break;
+- default:
+- dev_err(&pdev->dev, "unsupported rx-bus-width\n");
+- return -EINVAL;
+- }
+- }
+-
+- of_property_read_u32(flash_np, "spi-max-frequency", &max_clk_rate);
+- own_clk = (of_device_get_match_data(&pdev->dev) == (void *)OWN_CLOCK_DIVIDER);
+
+ rpc = devm_kzalloc(&pdev->dev, sizeof(*rpc), GFP_KERNEL);
+- if (!rpc)
++ if (!rpc) {
++ dev_err(&pdev->dev, "allocation failed\n");
+ return -ENOMEM;
+-
+- rpc->pdev = pdev;
+-
+- /* ... setup nor hooks */
+- nor = &rpc->spi_nor;
+- nor->dev = &pdev->dev;
+- spi_nor_set_flash_node(nor, flash_np);
+- nor->read = rpc_read_flash;
+- nor->write = rpc_write_flash;
+- nor->read_reg = rpc_read_reg;
+- nor->write_reg = rpc_write_reg;
+- nor->priv = rpc;
+- rpc->mtdtype = MTD_QSPI_1x;
+-
+- if (of_find_property(pdev->dev.of_node, "dual", NULL)) {
+- rpc->mtdtype = MTD_QSPI_2x;
+- nor->erase = rpc_erase_sector;
+ }
+
+- /* ...get memory */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rpc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rpc->base)) {
+- dev_err(&pdev->dev, "cannot get resources\n");
+- ret = PTR_ERR(rpc->base);
+- goto error;
++ dev_err(&pdev->dev, "cannot get base resource\n");
++ return PTR_ERR(rpc->base);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+-
+- rpc->read_area_dma = res->start;
+ rpc->read_area = devm_ioremap_resource(&pdev->dev, res);
+- if (IS_ERR(rpc->base)) {
+- dev_err(&pdev->dev, "cannot get resources\n");
+- ret = PTR_ERR(rpc->base);
+- goto error;
++ if (IS_ERR(rpc->read_area)) {
++ dev_err(&pdev->dev, "cannot get read resource\n");
++ return PTR_ERR(rpc->read_area);
++ }
++
++ if (resource_size(res) & RPC_READ_ADDR_MASK) {
++ dev_err(&pdev->dev, "invalid read resource\n");
++ return -EINVAL;
+ }
+
+- /* ...get memory */
++ rpc->read_area_dma = res->start;
++
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ rpc->write_area = devm_ioremap_resource(&pdev->dev, res);
+- if (IS_ERR(rpc->base)) {
+- dev_err(&pdev->dev, "cannot get resources\n");
+- ret = PTR_ERR(rpc->base);
+- goto error;
++ if (IS_ERR(rpc->write_area)) {
++ dev_warn(&pdev->dev, "cannot get write resource\n");
++ rpc->write_area = NULL;
+ }
+
+- /* ...get clk */
+ rpc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rpc->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+- ret = PTR_ERR(rpc->clk);
+- goto error;
++ return PTR_ERR(rpc->clk);
+ }
+
+- /* ...set max clk rate */
+- if (!own_clk) {
+- ret = clk_set_rate(rpc->clk, max_clk_rate);
+- if (ret) {
+- dev_err(&pdev->dev, "cannot set clock rate\n");
+- goto error;
+- }
++ rpc->flash = of_get_next_available_child(pdev->dev.of_node, NULL);
++ if (!rpc->flash) {
++ dev_err(&pdev->dev, "no flash device to configure\n");
++ return -ENOTSUPP;
+ }
+
+- /* ... enable clk */
+- ret = clk_prepare_enable(rpc->clk);
+- if (ret) {
+- dev_err(&pdev->dev, "cannot prepare clock\n");
+- goto error;
++ rpc->mtdtype = of_find_property(pdev->dev.of_node, "dual", NULL) ?
++ RPC_DUAL : RPC_SINGLE;
++
++ of_property_read_u32(rpc->flash, "spi-max-frequency", &rate);
++
++ if (of_device_is_compatible(rpc->flash, "jedec,spi-nor")) {
++ name = "renesas-rpc-qspi";
++ if (!rate)
++ rate = 50000000;
++ } else if (of_device_is_compatible(rpc->flash, "cfi-flash")) {
++ rpc->mtdtype = RPC_DUAL;
++ name = "renesas-rpc-hyperflash";
++ if (!rate)
++ rate = 80000000;
+ }
+
+- /* ...init device */
+- ret = rpc_hw_init(rpc);
+- if (ret < 0) {
+- dev_err(&pdev->dev, "rpc_hw_init error.\n");
+- goto error_clk_disable;
++ if (!name) {
++ dev_err(&pdev->dev, "no supported flash device detected\n");
++ ret = -ENODEV;
++ goto error;
+ }
+
+- /* ...set clk ratio */
+- if (own_clk) {
+- ret = rpc_setup_clk_ratio(rpc, max_clk_rate);
++ if (rate) {
++ ret = of_device_is_compatible(pdev->dev.of_node,
++ "renesas,rpc-r8a77970") ?
++ rpc_own_clk_set_rate(rpc, rate) :
++ clk_set_rate(rpc->clk, rate);
+ if (ret) {
+- dev_err(&pdev->dev, "cannot set clock ratio\n");
++ dev_err(&pdev->dev, "clock rate setup failed\n");
+ goto error;
+ }
+ }
+
+- platform_set_drvdata(pdev, rpc);
+-
+- ret = spi_nor_scan(nor, NULL, &hwcaps);
+- if (ret) {
+- dev_err(&pdev->dev, "spi_nor_scan error.\n");
+- goto error_clk_disable;
+- }
+-
+- /* Dual mode support */
+- if (rpc->mtdtype == MTD_QSPI_2x) {
+- nor->page_size <<= 1;
+- nor->mtd.erasesize <<= 1;
+- nor->mtd.size <<= 1;
+- nor->mtd.writebufsize <<= 1;
+- }
+-
+- /* Workaround data size limitation */
+- if (nor->page_size > WRITE_BUF_SIZE) {
+- nor->page_size = WRITE_BUF_SIZE;
+- nor->mtd.writebufsize = WRITE_BUF_SIZE;
+- }
+-
+ if (use_dma) {
+ dma_cap_mask_t mask;
+
+@@ -1324,58 +290,70 @@ static int rpc_spi_probe(struct platform_device *pdev)
+ dma_cap_set(DMA_MEMCPY, mask);
+ rpc->dma_chan = dma_request_channel(mask, NULL, NULL);
+ if (!rpc->dma_chan)
+- dev_warn(&pdev->dev, "Failed to request DMA channel\n");
++ dev_warn(&pdev->dev, "DMA channel request failed\n");
+ else
+- dev_info(&pdev->dev, "Using DMA read (%s)\n",
++ dev_info(&pdev->dev, "using DMA read (%s)\n",
+ dma_chan_name(rpc->dma_chan));
+ }
+
+- ret = mtd_device_register(&nor->mtd, NULL, 0);
+- if (ret) {
+- dev_err(&pdev->dev, "mtd_device_register error.\n");
+- goto error_dma;
++ platform_set_drvdata(pdev, rpc);
++ rpc->pdev = platform_device_register_data(&pdev->dev, name, -1, NULL, 0);
++ if (IS_ERR(rpc->pdev)) {
++ dev_err(&pdev->dev, "%s device registration failed\n", name);
++ ret = PTR_ERR(rpc->pdev);
++ goto error;
+ }
+
+- dev_info(&pdev->dev, "probed as %s\n",
+- rpc->mtdtype == MTD_QSPI_1x ? "single" : "dual");
+-
+ return 0;
+
+-error_dma:
++error:
+ if (rpc->dma_chan)
+ dma_release_channel(rpc->dma_chan);
+-error_clk_disable:
+- clk_disable_unprepare(rpc->clk);
+-error:
++
++ of_node_put(rpc->flash);
+ return ret;
+ }
+
+-static int rpc_spi_remove(struct platform_device *pdev)
++static int rpc_remove(struct platform_device *pdev)
+ {
+- struct rpc_spi *rpc = platform_get_drvdata(pdev);
++ struct rpc_info *rpc = platform_get_drvdata(pdev);
+
+- /* HW shutdown */
+- clk_disable_unprepare(rpc->clk);
+- mtd_device_unregister(&rpc->spi_nor.mtd);
++ if (rpc->pdev)
++ platform_device_unregister(rpc->pdev);
+ if (rpc->dma_chan)
+ dma_release_channel(rpc->dma_chan);
++ if (rpc->flash)
++ of_node_put(rpc->flash);
++
+ return 0;
+ }
+
++static const struct of_device_id rpc_of_match[] = {
++ { .compatible = "renesas,rpc-r8a7795" },
++ { .compatible = "renesas,rpc-r8a7796" },
++ { .compatible = "renesas,rpc-r8a77965" },
++ {
++ .compatible = "renesas,rpc-r8a77970",
++ .data = (void *)RPC_OWN_CLOCK_DIVIDER,
++ },
++ { .compatible = "renesas,rpc-r8a77980" },
++ { },
++};
++
+ /* platform driver interface */
+ static struct platform_driver rpc_platform_driver = {
+- .probe = rpc_spi_probe,
+- .remove = rpc_spi_remove,
++ .probe = rpc_probe,
++ .remove = rpc_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+- .name = "rpc",
++ .name = "renesas-rpc",
+ .of_match_table = of_match_ptr(rpc_of_match),
+ },
+ };
+
+ module_platform_driver(rpc_platform_driver);
+
+-MODULE_ALIAS("rpc");
++MODULE_ALIAS("renesas-rpc");
+ MODULE_AUTHOR("Cogent Embedded Inc. <sources@cogentembedded.com>");
+ MODULE_DESCRIPTION("Renesas RPC Driver");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/mtd/spi-nor/renesas-rpc.h b/drivers/mtd/spi-nor/renesas-rpc.h
+new file mode 100644
+index 0000000..e500a62
+--- /dev/null
++++ b/drivers/mtd/spi-nor/renesas-rpc.h
+@@ -0,0 +1,267 @@
++/*
++ * Renesas RPC driver
++ *
++ * Copyright (C) 2019, Cogent Embedded Inc.
++ *
++ * 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.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __RENESAS_RPC_H__
++#define __RENESAS_RPC_H__
++
++#include <linux/clk.h>
++#include <linux/dmaengine.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++
++/* RPC */
++#define RPC_CMNCR 0x0000 /* R/W */
++#define RPC_CMNCR_MD (0x1 << 31)
++#define RPC_CMNCR_MOIIO0(val) (((val) & 0x3) << 16)
++#define RPC_CMNCR_MOIIO1(val) (((val) & 0x3) << 18)
++#define RPC_CMNCR_MOIIO2(val) (((val) & 0x3) << 20)
++#define RPC_CMNCR_MOIIO3(val) (((val) & 0x3) << 22)
++#define RPC_CMNCR_MOIIO_HIZ (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \
++ RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
++#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8)
++#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12)
++#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14)
++#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \
++ RPC_CMNCR_IO3FV(3))
++#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0)
++
++#define RPC_SSLDR 0x0004 /* R/W */
++#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16)
++#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8)
++#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0)
++
++#define RPC_DRCR 0x000C /* R/W */
++#define RPC_DRCR_SSLN (0x1 << 24)
++#define RPC_DRCR_RBURST(v) (((v) & 0x1F) << 16)
++#define RPC_DRCR_RCF (0x1 << 9)
++#define RPC_DRCR_RBE (0x1 << 8)
++#define RPC_DRCR_SSLE (0x1 << 0)
++
++#define RPC_DRCMR 0x0010 /* R/W */
++#define RPC_DRCMR_CMD(c) (((c) & 0xFF) << 16)
++#define RPC_DRCMR_OCMD(c) (((c) & 0xFF) << 0)
++
++#define RPC_DREAR 0x0014 /* R/W */
++#define RPC_DREAR_EAV(v) (((v) & 0xFF) << 16)
++#define RPC_DREAR_EAC(v) (((v) & 0x7) << 0)
++
++#define RPC_DROPR 0x0018 /* R/W */
++#define RPC_DROPR_OPD3(o) (((o) & 0xFF) << 24)
++#define RPC_DROPR_OPD2(o) (((o) & 0xFF) << 16)
++#define RPC_DROPR_OPD1(o) (((o) & 0xFF) << 8)
++#define RPC_DROPR_OPD0(o) (((o) & 0xFF) << 0)
++
++#define RPC_DRENR 0x001C /* R/W */
++#define RPC_DRENR_CDB(o) (((o) & 0x3) << 30)
++#define RPC_DRENR_OCDB(o) (((o) & 0x3) << 28)
++#define RPC_DRENR_ADB(o) (((o) & 0x3) << 24)
++#define RPC_DRENR_OPDB(o) (((o) & 0x3) << 20)
++#define RPC_DRENR_DRDB(o) (((o) & 0x3) << 16)
++#define RPC_DRENR_DME (0x1 << 15)
++#define RPC_DRENR_CDE (0x1 << 14)
++#define RPC_DRENR_OCDE (0x1 << 12)
++#define RPC_DRENR_ADE(v) (((v) & 0xF) << 8)
++#define RPC_DRENR_OPDE(v) (((v) & 0xF) << 4)
++
++#define RPC_SMCR 0x0020 /* R/W */
++#define RPC_SMCR_SSLKP (0x1 << 8)
++#define RPC_SMCR_SPIRE (0x1 << 2)
++#define RPC_SMCR_SPIWE (0x1 << 1)
++#define RPC_SMCR_SPIE (0x1 << 0)
++
++#define RPC_SMCMR 0x0024 /* R/W */
++#define RPC_SMCMR_CMD(c) (((c) & 0xFF) << 16)
++#define RPC_SMCMR_OCMD(c) (((c) & 0xFF) << 0)
++
++#define RPC_SMADR 0x0028 /* R/W */
++#define RPC_SMOPR 0x002C /* R/W */
++#define RPC_SMOPR_OPD0(o) (((o) & 0xFF) << 0)
++#define RPC_SMOPR_OPD1(o) (((o) & 0xFF) << 8)
++#define RPC_SMOPR_OPD2(o) (((o) & 0xFF) << 16)
++#define RPC_SMOPR_OPD3(o) (((o) & 0xFF) << 24)
++
++#define RPC_SMENR 0x0030 /* R/W */
++#define RPC_SMENR_CDB(o) (((o) & 0x3) << 30)
++#define RPC_SMENR_OCDB(o) (((o) & 0x3) << 28)
++#define RPC_SMENR_ADB(o) (((o) & 0x3) << 24)
++#define RPC_SMENR_OPDB(o) (((o) & 0x3) << 20)
++#define RPC_SMENR_SPIDB(o) (((o) & 0x3) << 16)
++#define RPC_SMENR_DME (0x1 << 15)
++#define RPC_SMENR_CDE (0x1 << 14)
++#define RPC_SMENR_OCDE (0x1 << 12)
++#define RPC_SMENR_ADE(v) (((v) & 0xF) << 8)
++#define RPC_SMENR_OPDE(v) (((v) & 0xF) << 4)
++#define RPC_SMENR_SPIDE(v) (((v) & 0xF) << 0)
++
++#define RPC_SMRDR0 0x0038 /* R */
++#define RPC_SMRDR1 0x003C /* R */
++#define RPC_SMWDR0 0x0040 /* R/W */
++#define RPC_SMWDR1 0x0044 /* R/W */
++#define RPC_CMNSR 0x0048 /* R */
++#define RPC_CMNSR_SSLF (0x1 << 1)
++#define RPC_CMNSR_TEND (0x1 << 0)
++
++#define RPC_DRDMCR 0x0058 /* R/W */
++#define RPC_DRDMCR_DMCYC(v) (((v) & 0xF) << 0)
++
++#define RPC_DRDRENR 0x005C /* R/W */
++#define RPC_DRDRENR_HYPE (0x5 << 12)
++#define RPC_DRDRENR_ADDRE (0x1 << 0x8)
++#define RPC_DRDRENR_OPDRE (0x1 << 0x4)
++#define RPC_DRDRENR_DRDRE (0x1 << 0x0)
++
++#define RPC_SMDMCR 0x0060 /* R/W */
++#define RPC_SMDMCR_DMCYC(v) (((v) & 0xF) << 0)
++
++#define RPC_SMDRENR 0x0064 /* R/W */
++#define RPC_SMDRENR_HYPE(v) (((v) & 0x7) << 12)
++#define RPC_SMDRENR_HYPE_HF RPC_SMDRENR_HYPE(0x5)
++#define RPC_SMDRENR_HYPE_SPI RPC_SMDRENR_HYPE(0)
++#define RPC_SMDRENR_ADDRE (0x1 << 0x8)
++#define RPC_SMDRENR_OPDRE (0x1 << 0x4)
++#define RPC_SMDRENR_SPIDRE (0x1 << 0x0)
++
++#define RPC_PHYCNT 0x007C /* R/W */
++#define RPC_PHYCNT_CAL (0x1 << 31)
++#define RPC_PHYCNT_OCTA(v) (((v) & 0x3) << 22)
++#define RPC_PHYCNT_OCTA_AA (0x1 << 22)
++#define RPC_PHYCNT_OCTA_SA (0x2 << 22)
++#define RPC_PHYCNT_EXDS (0x1 << 21)
++#define RPC_PHYCNT_OCT (0x1 << 20)
++#define RPC_PHYCNT_DDRCAL (0x1 << 19)
++#define RPC_PHYCNT_HS (0x1 << 18)
++#define RPC_PHYCNT_STRTIM(v) (((v) & 0x7) << 15)
++#define RPC_PHYCNT_WBUF2 (0x1 << 4)
++#define RPC_PHYCNT_WBUF (0x1 << 2)
++#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0)
++
++#define RPC_PHYINT 0x0088 /* R/W */
++#define RPC_PHYINT_INTIE (0x1 << 24)
++#define RPC_PHYINT_RSTEN (0x1 << 18)
++#define RPC_PHYINT_WPEN (0x1 << 17)
++#define RPC_PHYINT_INTEN (0x1 << 16)
++#define RPC_PHYINT_RST (0x1 << 2)
++#define RPC_PHYINT_WP (0x1 << 1)
++#define RPC_PHYINT_INT (0x1 << 0)
++
++#define RPC_PHYOFFSET1 0x0080
++#define RPC_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28)
++#define RPC_PHYOFFSET1_DDRTMG_SDR RPC_PHYOFFSET1_DDRTMG(3)
++#define RPC_PHYOFFSET1_DDRTMG_DDR RPC_PHYOFFSET1_DDRTMG(2)
++
++#define RPC_PHYOFFSET2 0x0084
++#define RPC_PHYOFFSET2_OCTTMG(v) (((v) & 0x7) << 8)
++#define RPC_PHYOFFSET2_OCTAL RPC_PHYOFFSET2_OCTTMG(3)
++#define RPC_PHYOFFSET2_SERIAL RPC_PHYOFFSET2_OCTTMG(4)
++
++#define RPC_DIVREG 0x00A8
++#define RPC_DIVREG_RATIO(v) ((v) & 0x03)
++#define RPC_DIVREG_RATIO_MAX (0x2)
++
++#define RPC_WBUF 0x8000 /* R/W size=4/8/16/32/64Bytes */
++#define RPC_WBUF_SIZE 0x100
++#define RPC_WBUF_MASK (RPC_WBUF_SIZE - 1)
++
++/* DMA transfer */
++#define RPC_DMA_BURST (0x20 << 3)
++#define RPC_DMA_SIZE_MIN (RPC_DMA_BURST << 3)
++
++#define RPC_READ_ADDR_SIZE BIT(26)
++#define RPC_READ_ADDR_MASK (RPC_READ_ADDR_SIZE - 1)
++
++/* Default timeout in mS */
++#define RPC_TIMEOUT 5000
++
++/* Device flags */
++#define RPC_OWN_CLOCK_DIVIDER BIT(0)
++
++enum rpc_size {
++ /* singe flash: 8 bit; dual flash: 16 bit */
++ RPC_SIZE_SINGLE_8BIT = RPC_SMENR_SPIDE(0x8),
++ RPC_SIZE_DUAL_16BIT = RPC_SMENR_SPIDE(0x8),
++ /* singe flash: 16 bit; dual flash: 32 bit */
++ RPC_SIZE_SINGLE_16BIT = RPC_SMENR_SPIDE(0xC),
++ RPC_SIZE_DUAL_32BIT = RPC_SMENR_SPIDE(0xC),
++ /* singe flash: 32 bit; dual flash: 64 bit */
++ RPC_SIZE_SINGLE_32BIT = RPC_SMENR_SPIDE(0xF),
++ RPC_SIZE_DUAL_64BIT = RPC_SMENR_SPIDE(0xF),
++};
++
++enum rpc_type {
++ RPC_SINGLE = 0,
++ RPC_DUAL,
++};
++
++struct rpc_info {
++ struct platform_device *pdev;
++ struct device_node *flash;
++ void __iomem *base;
++ void __iomem *read_area;
++ void __iomem *write_area;
++ dma_addr_t read_area_dma;
++ struct completion comp;
++ struct dma_chan *dma_chan;
++ struct clk *clk;
++ unsigned int irq;
++ enum rpc_type mtdtype;
++};
++
++/* Register access */
++static inline u32 rpc_readl(struct rpc_info *rpc, u32 offset)
++{
++ return readl(rpc->base + offset);
++}
++
++static inline void rpc_writel(struct rpc_info *rpc, u32 offset, u32 val)
++{
++ writel(val, rpc->base + offset);
++}
++
++static inline void rpc_clrsetl(struct rpc_info *rpc, u32 offset,
++ u32 clr, u32 set)
++{
++ void __iomem *addr = rpc->base + offset;
++ u32 val = readl(addr);
++
++ val &= ~clr;
++ val |= set;
++ writel(val, addr);
++}
++
++static inline void rpc_wbuf_writel(struct rpc_info *rpc, u32 offset, u32 val)
++{
++ writel(val, rpc->write_area + offset);
++}
++
++/* Check whether write buffer should be used */
++static inline bool rpc_wbuf_available(struct rpc_info *rpc)
++{
++ return rpc->write_area;
++}
++
++#ifdef DEBUG
++extern void rpc_regs_dump(struct rpc_info *rpc);
++#else
++static inline void rpc_regs_dump(struct rpc_info *rpc) { }
++#endif
++
++extern int rpc_wait(struct rpc_info *rpc, int timeout);
++extern int rpc_dma_read(struct rpc_info *rpc, void *buf,
++ loff_t from, ssize_t *plen);
++extern void rpc_do_read_flash(struct rpc_info *rpc, loff_t from,
++ size_t len, u_char *buf, bool addr32);
++#endif
+--
+2.7.4
+