summaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch')
-rw-r--r--meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch1028
1 files changed, 1028 insertions, 0 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch
new file mode 100644
index 0000000..a6d08e7
--- /dev/null
+++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0012-mtd-Add-RPC-HyperFlash-driver.patch
@@ -0,0 +1,1028 @@
+From cc3bccbe6c479d8ad76a09b62fb63126403d0bab Mon Sep 17 00:00:00 2001
+From: Valentine Barshak <valentine.barshak@cogentembedded.com>
+Date: Fri, 3 Jun 2016 23:04:20 +0300
+Subject: [PATCH] mtd: Add RPC HyperFlash driver
+
+This adds RPC HyperFlash driver.
+
+Signed-off-by: Valentine Barshak <valentine.barshak@cogentembedded.com>
+---
+ drivers/mtd/Kconfig | 5 +
+ drivers/mtd/Makefile | 1 +
+ drivers/mtd/rpc_hyperflash.c | 976 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 982 insertions(+)
+ create mode 100644 drivers/mtd/rpc_hyperflash.c
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index 42cc953..0c58a89 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -11,6 +11,11 @@ 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)"
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index 99bb9a1..3b81efb 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
++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
+new file mode 100644
+index 0000000..f8a2c90
+--- /dev/null
++++ b/drivers/mtd/rpc_hyperflash.c
+@@ -0,0 +1,976 @@
++/*
++ * 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");
+--
+2.7.4
+