diff options
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.patch | 1028 |
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 + |