Add basic NOR flash driver for ARM platforms

FVP and Juno platforms include a NOR flash memory to store and
load the FIP, the kernel or a ramdisk. This NOR flash is arranged
as 2 x 16 bit flash devices and can be programmed using CFI
standard commands.

This patch provides a basic API to write single 32 bit words of
data into the NOR flash. Functions to lock/unlock blocks against
erase or write operations are also provided.

Change-Id: I1da7ad3105b1ea409c976adc863954787cbd90d2
diff --git a/include/plat/arm/board/common/drivers/norflash.h b/include/plat/arm/board/common/drivers/norflash.h
new file mode 100644
index 0000000..0bf35a5
--- /dev/null
+++ b/include/plat/arm/board/common/drivers/norflash.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __NORFLASH_H_
+#define __NORFLASH_H_
+
+#include <stdint.h>
+
+/* First bus cycle */
+#define NOR_CMD_READ_ARRAY		0xFF
+#define NOR_CMD_READ_ID_CODE		0x90
+#define NOR_CMD_READ_QUERY		0x98
+#define NOR_CMD_READ_STATUS_REG		0x70
+#define NOR_CMD_CLEAR_STATUS_REG	0x50
+#define NOR_CMD_WRITE_TO_BUFFER		0xE8
+#define NOR_CMD_WORD_PROGRAM		0x40
+#define NOR_CMD_BLOCK_ERASE		0x20
+#define NOR_CMD_LOCK_UNLOCK		0x60
+
+/* Second bus cycle */
+#define NOR_LOCK_BLOCK			0x01
+#define NOR_UNLOCK_BLOCK		0xD0
+
+/* Status register bits */
+#define NOR_DWS				(1 << 7)
+#define NOR_ESS				(1 << 6)
+#define NOR_ES				(1 << 5)
+#define NOR_PS				(1 << 4)
+#define NOR_VPPS			(1 << 3)
+#define NOR_PSS				(1 << 2)
+#define NOR_BLS				(1 << 1)
+#define NOR_BWS				(1 << 0)
+
+/* Public API */
+void nor_send_cmd(uintptr_t base_addr, unsigned long cmd);
+int nor_word_program(uintptr_t base_addr, unsigned long data);
+void nor_lock(uintptr_t base_addr);
+void nor_unlock(uintptr_t base_addr);
+
+#endif /* __NORFLASH_H_ */
+
diff --git a/plat/arm/board/common/drivers/norflash/norflash.c b/plat/arm/board/common/drivers/norflash/norflash.c
new file mode 100644
index 0000000..8895461
--- /dev/null
+++ b/plat/arm/board/common/drivers/norflash/norflash.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <mmio.h>
+#include <norflash.h>
+
+/* Helper macros to access two flash banks in parallel */
+#define NOR_2X16(d)			((d << 16) | (d & 0xffff))
+
+/*
+ * DWS ready poll retries. The number of retries in this driver have been
+ * obtained empirically from Juno. FVP implements a zero wait state NOR flash
+ * model
+ */
+#define DWS_WORD_PROGRAM_RETRIES	1000
+
+/*
+ * Poll Write State Machine. Return values:
+ *    0      = WSM ready
+ *    -EBUSY = WSM busy after the number of retries
+ */
+static int nor_poll_dws(uintptr_t base_addr, unsigned int retries)
+{
+	uint32_t status;
+	int ret;
+
+	for (;;) {
+		nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
+		status = mmio_read_32(base_addr);
+		if ((status & NOR_DWS) &&
+		    (status & (NOR_DWS << 16))) {
+			ret = 0;
+			break;
+		}
+		if (retries-- == 0) {
+			ret = -EBUSY;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+void nor_send_cmd(uintptr_t base_addr, unsigned long cmd)
+{
+	mmio_write_32(base_addr, NOR_2X16(cmd));
+}
+
+/*
+ * Return values:
+ *    0      = success
+ *    -EBUSY = WSM not ready
+ *    -EPERM = Device protected or Block locked
+ */
+int nor_word_program(uintptr_t base_addr, unsigned long data)
+{
+	uint32_t status;
+	int ret;
+
+	/* Set the device in write word mode */
+	nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM);
+	mmio_write_32(base_addr, data);
+
+	ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES);
+	if (ret != 0) {
+		goto word_program_end;
+	}
+
+	/* Full status check */
+	nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
+	status = mmio_read_32(base_addr);
+
+	if (status & (NOR_PS | NOR_BLS)) {
+		nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
+		ret = -EPERM;
+	}
+
+word_program_end:
+	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
+	return ret;
+}
+
+void nor_lock(uintptr_t base_addr)
+{
+	nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
+	mmio_write_32(base_addr, NOR_2X16(NOR_LOCK_BLOCK));
+	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
+}
+
+void nor_unlock(uintptr_t base_addr)
+{
+	nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
+	mmio_write_32(base_addr, NOR_2X16(NOR_UNLOCK_BLOCK));
+	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
+}
+