From 00ea5714ecc7ca61919cb3942d0edf20e8190160 Mon Sep 17 00:00:00 2001
From: Valentine Barshak <valentine.barshak@cogentembedded.com>
Date: Mon, 4 Apr 2016 18:40:39 +0300
Subject: [PATCH] mtd: Add RPC HyperFlash support

This adds RCAR Gen3 RPC HyperFlash driver.

Signed-off-by: Valentine Barshak <valentine.barshak@cogentembedded.com>
---
 drivers/mtd/Makefile         |   1 +
 drivers/mtd/rpc_hyperflash.c | 695 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 696 insertions(+)
 create mode 100644 drivers/mtd/rpc_hyperflash.c

diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 5467a95..8e794a2 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_FTSMC020) += ftsmc020.o
 obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o
 obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o
 obj-$(CONFIG_ST_SMI) += st_smi.o
+obj-$(CONFIG_RPC_HYPERFLASH) += rpc_hyperflash.o
diff --git a/drivers/mtd/rpc_hyperflash.c b/drivers/mtd/rpc_hyperflash.c
new file mode 100644
index 0000000..fc67ecd
--- /dev/null
+++ b/drivers/mtd/rpc_hyperflash.c
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2016 Cogent Embedded, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <asm/arch/rpc-flash.h>
+
+#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),
+};
+
+static inline u32 rpc_hf_flash_map(flash_info_t *info, int sector)
+{
+	return info->start[sector] - CONFIG_SYS_RPC_FLASH_BASE;
+}
+
+static inline int rpc_hf_flash_sector(flash_info_t *info, u32 addr)
+{
+	return (addr - info->start[0]) / RPC_HF_ERASE_SIZE;
+}
+
+static inline int rpc_hf_flash_sector_size(flash_info_t *info, int sector)
+{
+	return RPC_HF_ERASE_SIZE;
+}
+
+static void rpc_hf_mode_man(flash_info_t *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(flash_info_t *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(flash_info_t *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(flash_info_t *info, u32 addr)
+{
+	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_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);
+
+	/*
+	 * 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(flash_info_t *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(flash_info_t *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(flash_info_t *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(flash_info_t *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(flash_info_t *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(flash_info_t *info, int enable)
+{
+	rpc_setl(info, RPC_PHYINT, ~RPC_PHYINT_WP, enable ? RPC_PHYINT_WP : 0);
+}
+
+static void rpc_hf_unlock(flash_info_t *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(flash_info_t *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)
+			udelay(delay);
+	}
+
+	if (!(status & RPC_CFI_STATUS_DRB)) {
+		retval = ERR_TIMOUT;
+		goto out;
+	}
+
+	if (status & RPC_CFI_STATUS_PSB) {
+		retval = ERR_PROG_ERROR;
+		goto out;
+	}
+
+	if (status & RPC_CFI_STATUS_ESB) {
+		retval = ERR_NOT_ERASED;
+		goto out;
+	}
+
+	return ERR_OK;
+
+out:
+	rpc_hf_write_cmd(info, 0, RPC_CFI_CMD_RESET);
+	return retval;
+}
+
+static int rpc_hf_sector_erase(flash_info_t *info, int sector)
+{
+	u32 addr = rpc_hf_flash_map(info, sector);
+
+	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, 10000, 1000);
+}
+
+static ulong rpc_hf_get_size(phys_addr_t base, int banknum)
+{
+	flash_info_t *info = &flash_info[banknum];
+	u16 data[2] = { 0, 0 };
+	ulong id, size = 0;
+	ushort sectors, i;
+
+	info->flash_id = FLASH_UNKNOWN;
+	info->sector_count = -1;
+	info->size = 0;
+
+	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)
+		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 & (RPC_HF_ERASE_SIZE - 1))
+		goto out;
+
+	sectors = size / RPC_HF_ERASE_SIZE;
+	if (sectors < 1 || sectors > CONFIG_SYS_MAX_FLASH_SECT)
+		goto out;
+
+	info->flash_id = id;
+	info->size = size;
+	info->sector_count = sectors;
+
+	for (i = 0; i < sectors; i++) {
+		info->start[i] = base;
+		base += RPC_HF_ERASE_SIZE;
+	}
+
+out:
+	rpc_hf_write_cmd(info, 0, RPC_CFI_CMD_RESET);
+	rpc_hf_mode_ext(info);
+	return info->size;
+}
+
+/*
+ * Flash erase, returns:
+ * ERR_OK        : OK
+ * ERR_ABORTED   : Aborted by user
+ * ERR_PROTECTED : Protected sector
+ */
+int flash_erase(flash_info_t *info, int s_first, int s_last)
+{
+	int s, retval = ERR_OK;
+
+	puts("Erasing Flash... ");
+	for (s = s_first; s <= s_last; s++) {
+		if (ctrlc()) {
+			printf("aborted sector %i\n", s);
+			retval = ERR_ABORTED;
+			goto out;
+		}
+
+		if (info->protect[s]) {
+			printf("protected sector %i\n", s);
+			retval = ERR_PROTECTED;
+			goto out;
+		}
+
+		putc('.');
+
+		retval = rpc_hf_sector_erase(info, s);
+		if (retval != ERR_OK) {
+			printf("error sector %i\n", s);
+			goto out;
+		}
+	}
+	puts("done\n");
+
+out:
+	rpc_hf_mode_ext(info);
+	return retval;
+}
+
+/*
+ * Copy memory to flash, returns:
+ * ERR_OK        : OK
+ * ERR_ABORTED   : Aborted by user
+ * ERR_PROTECTED : Protected sector
+ */
+int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
+{
+	union {
+		u8 b[4];
+		u16 w[2];
+		u32 d;
+	} data;
+	ulong offset, size;
+	int sector, idx, retval;
+	u8 last;
+
+	retval = ERR_OK;
+	idx = 0;
+
+	/* Handle unaligned start */
+	if (addr & 0x1) {
+		addr--;
+		data.b[idx] = readb(addr);
+		idx++;
+	}
+
+	/* Handle unaligned end */
+	offset = addr + idx + cnt;
+	last = offset & 0x1 ? readb(offset) : 0xFF;
+
+	sector = rpc_hf_flash_sector(info, addr);
+	offset = addr - info->start[sector];
+	size = rpc_hf_flash_sector_size(info, sector) - offset;
+	offset += rpc_hf_flash_map(info, sector);
+
+	while (cnt) {
+		if (ctrlc()) {
+			retval = ERR_ABORTED;
+			goto out;
+		}
+
+		if (info->protect[sector]) {
+			retval = ERR_PROTECTED;
+			goto out;
+		}
+
+		if (size > cnt)
+			size = cnt;
+
+		putc('.');
+
+		cnt -= size;
+		while (size) {
+			rpc_hf_unlock(info, info->start[sector]);
+			rpc_hf_write_cmd(info,
+					 info->start[sector] + 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, offset);
+				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;
+			}
+
+			rpc_wait_tend(info);
+			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));
+
+			retval = rpc_hf_status(info, info->start[sector],
+					       1000000, 10);
+			if (retval)
+				goto out;
+		}
+
+		sector++;
+		offset = rpc_hf_flash_map(info, sector);
+		size = rpc_hf_flash_sector_size(info, sector);
+	}
+
+out:
+	rpc_hf_mode_ext(info);
+	return retval;
+}
+
+/* Print flash information */
+void flash_print_info(flash_info_t *info)
+{
+	if (info->flash_id == FLASH_UNKNOWN)
+		return;
+
+	printf("HyperFlash Id: %lx\n", info->flash_id);
+	printf("Base Address: %lx\n", info->start[0]);
+	printf("Size: %lu MiB\n", info->size >> 20);
+	printf("Sectors: %u\n", info->sector_count);
+}
+
+flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
+
+unsigned long flash_init(void)
+{
+	int i;
+
+	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
+		flash_info[i].flash_id = FLASH_UNKNOWN;
+		flash_info[i].sector_count = -1;
+		flash_info[i].size = 0;
+	}
+
+	return rpc_hf_get_size(CONFIG_SYS_RPC_FLASH_BASE, 0);
+}
-- 
1.9.3

