commit 9a806e19292e0135d23f11874c6d00017fdbf33b
parent 2b0ab579ae6da0a3ae144bd364874ea967279eb8
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun, 12 Jul 2015 05:56:48 -0700
agent: lpc43xx-spifi suitable for flashing lpclink2
Diffstat:
2 files changed, 202 insertions(+), 0 deletions(-)
diff --git a/agents/lpc43xx-spifi.c b/agents/lpc43xx-spifi.c
@@ -0,0 +1,195 @@
+// agent-lpc15xx/main.c
+//
+// Copyright 2015 Brian Swetland <swetland@frotz.net>
+// 
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <agent/flash.h>
+#include <fw/io.h>
+
+// ---- pinmux
+
+#define PIN_CFG(m,n)	(0x40086000 + ((m) * 0x80) + ((n) * 4))
+#define PIN_MODE(n)	((n) & 3)
+#define PIN_PULLUP	(0 << 3) // pull-up, no pull-down
+#define PIN_REPEATER	(1 << 3) // repeater mode
+#define PIN_PLAIN	(2 << 3) // no pull-up, no pull-down
+#define PIN_PULLDOWN	(3 << 3) // pull-down, no pull-up
+#define PIN_SLOW	(0 << 5) // slow slew rate (low noise, medium speed)
+#define PIN_FAST	(1 << 5) // fast slew rate (medium noise, fast speed)
+#define PIN_INPUT	(1 << 6) // enable input buffer, required for inputs
+#define PIN_FILTER	(1 << 7) // enable glitch filter, not for >30MHz signals
+
+// ---- spifi serial flash controller
+
+#define SPIFI_CTRL		0x40003000 // Control
+#define SPIFI_CMD		0x40003004 // Command
+#define SPIFI_ADDR		0x40003008 // Address
+#define SPIFI_IDATA		0x4000300C // Intermediate Data
+#define SPIFI_CLIMIT		0x40003010 // Cache Limit
+#define SPIFI_DATA		0x40003014 // Data
+#define SPIFI_MCMD		0x40003018 // Memory Command
+#define SPIFI_STAT		0x4000301C // Status
+
+#define CTRL_TIMEOUT(n)		((n) & 0xFFFF)
+#define CTRL_CSHIGH(n)		(((n) & 0xF) << 16) // Minimum /CS high time (serclks - 1)
+#define CTRL_D_PRFTCH_DIS	(1 << 21) // Disable Prefetch of Data
+#define CTRL_INTEN		(1 << 22) // Enable IRQ on end of command
+#define CTRL_MODE3		(1 << 23) // 0=SCK low after +edge of last bit, 1=high
+#define CTRL_PRFTCH_DIS		(1 << 27) // Disable Prefetch
+#define CTRL_DUAL		(1 << 28) // 0=Quad 1=Dual (bits in "wide" ops)
+#define CTRL_QUAD		(0 << 28)
+#define CTRL_RFCLK		(1 << 29) // 1=sample read data on -edge clock
+#define CTRL_FBCLK		(1 << 30) // use feedback clock from SCK pin for sampling
+#define CTRL_DMAEN		(1 << 31) // enable DMA request output
+
+#define CMD_DATALEN(n)		((n) & 0x3FFF)
+#define CMD_POLL		(1 << 14) // if set, read byte repeatedly until condition
+#define CMD_POLLBIT(n)		((n) & 7) // which bit# to check
+#define CMD_POLLSET		(1 << 3)  // condition is bit# set
+#define CMD_POLLCLR		(0 << 3)  // condition is bit# clear
+#define CMD_DOUT		(1 << 15) // 1=data phase output, 0=data phase input
+#define CMD_DIN			(0 << 15)
+#define CMD_INTLEN(n)		(((n) & 7) << 16) // count of intermediate bytes
+#define CMD_FF_SERIAL		(0 << 19) // all command fields serial
+#define CMD_FF_WIDE_DATA	(1 << 19) // data is wide, all other fields serial
+#define CMD_FF_SERIAL_OPCODE	(2 << 19) // opcode is serial, all other fields wide
+#define CMD_FF_WIDE		(3 << 19) // all command fields wide
+#define CMD_FR_OP		(1 << 21) // frame format: opcode only
+#define CMD_FR_OP_1B		(2 << 21) // opcode, lsb addr
+#define CMD_FR_OP_2B		(3 << 21) // opcode, 2 lsb addr
+#define CMD_FR_OP_3B		(4 << 21) // opcode, 3 lsb addr
+#define CMD_FR_OP_4B		(5 << 21) // opcode, 4b address
+#define CMD_FR_3B		(6 << 21) // 3 lsb addr
+#define CMD_FR_4B		(7 << 21) // 4 lsb addr
+#define CMD_OPCODE(n)		((n) << 24)
+
+#define STAT_MCINIT		(1 << 0) // set on sw write to MCMD, clear on RST, wr(0)
+#define STAT_CMD		(1 << 1) // set when CMD written, clear on CS, RST
+#define STAT_RESET		(1 << 4) // write 1 to abort current txn or memory mode
+#define STAT_INTRQ		(1 << 5) // read IRQ status, wr(1) to clear
+
+#define CMD_PAGE_PROGRAM		0x02
+#define CMD_READ_DATA			0x03
+#define CMD_READ_STATUS			0x05
+#define CMD_WRITE_ENABLE		0x06
+#define CMD_SECTOR_ERASE		0x20
+
+static void spifi_write_enable(void) {
+	writel(CMD_FF_SERIAL | CMD_FR_OP | CMD_OPCODE(CMD_WRITE_ENABLE),
+		SPIFI_CMD);
+	while (readl(SPIFI_STAT) & STAT_CMD) ;
+}
+
+static void spifi_wait_busy(void) {
+	while (readl(SPIFI_STAT) & STAT_CMD) ;
+	writel(CMD_POLLBIT(0) | CMD_POLLCLR | CMD_POLL |
+		CMD_FF_SERIAL | CMD_FR_OP | CMD_OPCODE(CMD_READ_STATUS),
+		SPIFI_CMD);
+	while (readl(SPIFI_STAT) & STAT_CMD) ;
+	// discard matching status byte from fifo
+	readb(SPIFI_DATA);
+}
+
+static void spifi_page_program(u32 addr, u32 *ptr, u32 count) {
+	spifi_write_enable();
+	writel(addr, SPIFI_ADDR);
+	writel(CMD_DATALEN(count * 4) | CMD_FF_SERIAL | CMD_FR_OP_3B |
+		CMD_DOUT | CMD_OPCODE(CMD_PAGE_PROGRAM), SPIFI_CMD);
+	while (count-- > 0) {
+		writel(*ptr++, SPIFI_DATA);
+	}
+	spifi_wait_busy();
+}
+
+static void spifi_sector_erase(u32 addr) {
+	spifi_write_enable();
+	writel(addr, SPIFI_ADDR);
+	writel(CMD_FF_SERIAL | CMD_FR_OP_3B | CMD_OPCODE(CMD_SECTOR_ERASE),
+		SPIFI_CMD);
+	spifi_wait_busy();
+}
+
+// at reset-stop, all clocks are running from 12MHz internal osc
+// todo: run SPIFI_CLK at a much higher rate
+// todo: use 4bit modes
+int flash_agent_setup(flash_agent *agent) {
+	// configure pinmux
+	writel(PIN_MODE(3) | PIN_PLAIN, PIN_CFG(3,3)); // SPIFI_SCK
+	writel(PIN_MODE(3) | PIN_PLAIN | PIN_INPUT, PIN_CFG(3, 4)); // SPIFI_SIO3
+	writel(PIN_MODE(3) | PIN_PLAIN | PIN_INPUT, PIN_CFG(3, 5)); // SPIFI_SIO2
+	writel(PIN_MODE(3) | PIN_PLAIN | PIN_INPUT, PIN_CFG(3, 6)); // SPIFI_MISO
+	writel(PIN_MODE(3) | PIN_PLAIN | PIN_INPUT, PIN_CFG(3, 7)); // SPIFI_MOSI
+	writel(PIN_MODE(3) | PIN_PLAIN, PIN_CFG(3, 8)); // SPIFI_CS
+
+	// reset spifi controller
+	writel(STAT_RESET, SPIFI_STAT);
+	while (readl(SPIFI_STAT) & STAT_RESET) ;
+	writel(0xFFFFF, SPIFI_CTRL);
+
+	return ERR_NONE;
+}
+
+int flash_agent_erase(u32 flash_addr, u32 length) {
+	if (flash_addr & 0xFFF) {
+		return ERR_ALIGNMENT;
+	}
+	while (length != 0) {
+		spifi_sector_erase(flash_addr);
+		if (length < 0x1000) break;
+		length -= 0x1000;
+		flash_addr += 0x1000;
+	}
+	return ERR_NONE;
+}
+
+int flash_agent_write(u32 flash_addr, const void *data, u32 length) {
+	char *x = (void*) data;
+	if (flash_addr & 0xFFF) {
+		return ERR_ALIGNMENT;
+	}
+	while (length != 0) {
+		if (length < 256) {
+			int n;
+			for (n = length; n < 256; n++) {
+				x[n] = 0;
+			}
+		}
+		spifi_page_program(flash_addr, (void*) x, 256 / 4);
+		if (length < 256) break;
+		length -= 256;
+		flash_addr += 256;
+		x += 256;
+	}
+	writel(CMD_FF_SERIAL | CMD_FR_OP_3B | CMD_OPCODE(CMD_READ_DATA), SPIFI_MCMD);
+	return ERR_NONE;
+}
+
+int flash_agent_ioctl(u32 op, void *ptr, u32 arg0, u32 arg1) {
+	return ERR_INVALID;
+}
+
+const flash_agent __attribute((section(".vectors"))) FlashAgent = {
+	.magic =	AGENT_MAGIC,
+	.version =	AGENT_VERSION,
+	.flags =	0,
+	.load_addr =	CONFIG_LOADADDR,
+	.data_addr =	CONFIG_LOADADDR + 0x400,
+	.data_size =	0x8000,
+	.flash_addr =	CONFIG_FLASHADDR,
+	.flash_size =	CONFIG_FLASHSIZE,
+	.setup =	flash_agent_setup,
+	.erase =	flash_agent_erase,
+	.write =	flash_agent_write,
+	.ioctl =	flash_agent_ioctl,
+};
diff --git a/agents/module.mk b/agents/module.mk
@@ -24,3 +24,10 @@ M_RAMSIZE := 0x00000400
 M_OBJS := agents/stm32fxxx.o
 $(call build-target-agent)
 
+M_NAME := agent-lpclink2
+M_ROMBASE := 0x00000000
+M_ROMSIZE := 0x00100000
+M_RAMBASE := 0x10080400
+M_RAMSIZE := 0x10000400
+M_OBJS := agents/lpc43xx-spifi.o
+$(call build-target-agent)