[U-Boot] [PATCH v1 08/18] MIPS: pic32: Add driver for Microchip PIC32 flash controller.
Purna Chandra Mandal
purna.mandal at microchip.com
Thu Dec 17 18:30:51 CET 2015
From: Cristian Birsan <cristi.birsan at microchip.com>
Signed-off-by: Cristian Birsan <cristi.birsan at microchip.com>
Signed-off-by: Purna Chandra Mandal <purna.mandal at microchip.com>
---
arch/mips/mach-pic32/Makefile | 5 +-
arch/mips/mach-pic32/flash.c | 471 ++++++++++++++++++++++++++++++++++++++++++
include/flash.h | 5 +-
3 files changed, 479 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/mach-pic32/flash.c
diff --git a/arch/mips/mach-pic32/Makefile b/arch/mips/mach-pic32/Makefile
index 03d5f27..3a621c3 100644
--- a/arch/mips/mach-pic32/Makefile
+++ b/arch/mips/mach-pic32/Makefile
@@ -4,4 +4,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-y = cpu.o reset.o lowlevel_init.o
\ No newline at end of file
+obj-y = cpu.o reset.o lowlevel_init.o
+ifndef CONFIG_SYS_NO_FLASH
+obj-y += flash.o
+endif
\ No newline at end of file
diff --git a/arch/mips/mach-pic32/flash.c b/arch/mips/mach-pic32/flash.c
new file mode 100644
index 0000000..b3c1e0a
--- /dev/null
+++ b/arch/mips/mach-pic32/flash.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2015
+ * Cristian Birsan <cristian.birsan at microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <flash.h>
+#include <asm/io.h>
+#include <linux/byteorder/swab.h>
+#include <asm/arch-pic32/pic32.h>
+
+#if defined(CONFIG_ENV_IS_IN_FLASH)
+#ifndef CONFIG_ENV_ADDR
+#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET)
+#endif
+
+#ifndef CONFIG_ENV_SIZE
+#define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
+#endif
+
+#ifndef CONFIG_ENV_SECT_SIZE
+#define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE
+#endif
+#endif
+
+/* NVM Controller registers */
+#define NVMCON (PIC32_NVM_BASE + 0x00)
+#define NVMCONCLR (NVMCON + _CLR_OFFSET)
+#define NVMCONSET (NVMCON + _SET_OFFSET)
+#define NVMKEY (PIC32_NVM_BASE + 0x10)
+#define NVMADDR (PIC32_NVM_BASE + 0x20)
+#define NVMDATA0 (PIC32_NVM_BASE + 0x30)
+
+/* NVM Operations */
+#define NVMOP_NOP 0x00000000
+#define NVMOP_WORD_WRITE 0x00000001
+#define NVMOP_PAGE_ERASE 0x00000004
+
+/* NVM Programming Control Register*/
+#define NVMCON_WREN 0x00004000
+#define NVMCON_WR 0x00008000
+#define NVMCON_WRERR 0x00002000
+#define NVMCON_LVDERR 0x00001000
+
+/*-----------------------------------------------------------------------
+ */
+flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
+
+/*
+ * The following code cannot be run from FLASH!
+ */
+static ulong flash_get_size(vu_long *addr, flash_info_t *info)
+{
+ short i;
+ ulong base = (ulong)addr;
+ ulong sector_offset;
+
+ /* On chip flash ID */
+ switch (info->flash_id & FLASH_VENDMASK) {
+ case FLASH_MAN_MCHP:
+ break;
+ default:
+ /* no or unknown flash */
+ printf("unknown manufacturer: 0x%lx\n",
+ info->flash_id & FLASH_VENDMASK);
+ info->flash_id = FLASH_UNKNOWN;
+ info->sector_count = 0;
+ info->size = 0;
+ return 0;
+ }
+
+ switch (info->flash_id & FLASH_TYPEMASK) {
+ case FLASH_MCHP100T:
+ info->sector_count = CONFIG_SYS_MAX_FLASH_SECT;
+ info->size = CONFIG_SYS_FLASH_SIZE;
+ sector_offset = info->size / info->sector_count;
+ break;
+ default:
+ info->flash_id = FLASH_UNKNOWN;
+ return 0; /* => no or unknown flash */
+ }
+
+ /* set up sector start address table */
+ for (i = 0; i < info->sector_count; i++) {
+ info->start[i] = base;
+ base += sector_offset;
+ /* protect each sector by default */
+ info->protect[i] = 1;
+ }
+
+ /* Disable Flash Write/Erase operations */
+ writel(NVMCON_WREN, NVMCONCLR);
+
+ if (info->flash_id != FLASH_UNKNOWN)
+ addr = (vu_long *)info->start[0];
+
+ return info->size;
+}
+
+/*-----------------------------------------------------------------------
+ */
+void flash_print_info(flash_info_t *info)
+{
+ int i;
+
+ if (info->flash_id == FLASH_UNKNOWN) {
+ printf("missing or unknown FLASH type\n");
+ return;
+ }
+
+ switch (info->flash_id & FLASH_VENDMASK) {
+ case FLASH_MAN_MCHP:
+ printf("Microchip ");
+ break;
+ default:
+ printf("Unknown Vendor ");
+ break;
+ }
+
+ switch (info->flash_id & FLASH_TYPEMASK) {
+ case FLASH_MCHP100T:
+ printf("Internal (8 Mbit, 64 x 16k)\n");
+ break;
+ default:
+ printf("Unknown Chip Type\n");
+ break;
+ }
+
+ printf(" Size: %ld MB in %d Sectors\n",
+ info->size >> 20, info->sector_count);
+
+ printf(" Sector Start Addresses:");
+ for (i = 0; i < info->sector_count; ++i) {
+ if ((i % 5) == 0)
+ printf("\n ");
+
+ printf(" %08lX%s", info->start[i],
+ info->protect[i] ? " (RO)" : " ");
+ }
+ printf("\n");
+}
+
+static inline void flash_initiate_operation(void)
+{
+ /* Unlock sequence */
+ writel(0x00000000, NVMKEY);
+ writel(0xAA996655, NVMKEY);
+ writel(0x556699AA, NVMKEY);
+
+ writel(NVMCON_WR, NVMCON);
+}
+
+static inline void flash_nop_operation(void)
+{
+ /* reset error bits using a flash NOP command */
+
+ writel(NVMOP_NOP, NVMCON); /* NVMOP for page erase*/
+ writel(NVMCON_WREN, NVMCONSET); /* Enable Flash Write*/
+ flash_initiate_operation();
+}
+
+int flash_erase(flash_info_t *info, int s_first, int s_last)
+{
+ int flag, prot, sect;
+ ulong base, elapsed, last = 0, tmp, addr;
+
+ if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_MCHP) {
+ printf("Can't erase unknown flash type %08lx - aborted\n",
+ info->flash_id);
+ return ERR_UNKNOWN_FLASH_VENDOR;
+ }
+
+ if ((s_first < 0) || (s_first > s_last)) {
+ printf("- no sectors to erase\n");
+ return ERR_INVAL;
+ }
+
+ prot = 0;
+ for (sect = s_first; sect <= s_last; ++sect) {
+ if (info->protect[sect])
+ prot++;
+ }
+
+ if (prot)
+ printf("- Warning: %d protected sectors will not be erased!\n",
+ prot);
+ else
+ printf("\n");
+
+ base = get_timer(0);
+
+ /* Start erase on unprotected sectors */
+ for (sect = s_first; sect <= s_last; sect++) {
+ if (info->protect[sect]) /* skip protected sector */
+ continue;
+
+ /* Disable interrupts which might cause timeout */
+ flag = disable_interrupts();
+
+ /* destination page physical address */
+ addr = virt_to_phys((void *)info->start[sect]);
+ writel(addr, NVMADDR);
+
+ /* NVMOP for page erase*/
+ writel(NVMOP_PAGE_ERASE, NVMCON);
+ /* Enable Flash Write*/
+ writel(NVMCON_WREN, NVMCONSET);
+
+ /* Initiate operation */
+ flash_initiate_operation();
+
+ /* Wait for WR bit to clear */
+ while (readl(NVMCON) & NVMCON_WR) {
+ elapsed = get_timer(base);
+ if (elapsed > CONFIG_SYS_FLASH_ERASE_TOUT) {
+ printf("Timeout\n");
+ /* reset bank */
+ return ERR_TIMOUT;
+ }
+
+ /* show that we're waiting */
+ if ((elapsed - last) > 100) { /* every 100msec */
+ putc('.');
+ last = elapsed;
+ }
+ }
+
+ tmp = readl(NVMCON);
+ if (tmp & NVMCON_WRERR) {
+ printf("Error in Block Erase - Lock Bit may be set!\n");
+ flash_nop_operation();
+ return ERR_PROTECTED;
+ }
+
+ if (tmp & NVMCON_LVDERR) {
+ printf("Error in Block Erase - low-vol detected!\n");
+ flash_nop_operation();
+ return ERR_NOT_ERASED;
+ }
+
+ /* Disable future Flash Write/Erase operations */
+ writel(NVMCON_WREN, NVMCONCLR);
+
+ /* re-enable interrupts if necessary */
+ if (flag)
+ enable_interrupts();
+ }
+
+ for (sect = s_first; sect <= s_last; sect++) {
+ addr = info->start[sect];
+ tmp = addr + (info->size / info->sector_count);
+ invalidate_dcache_range(addr, tmp);
+ }
+
+ printf(" done\n");
+ return ERR_OK;
+}
+
+int page_erase(flash_info_t *info, int sect)
+{
+ return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Write a word to Flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ */
+static int write_word(flash_info_t *info, ulong dest, ulong data)
+{
+ vu_long *addr = (vu_long *)dest;
+ ulong base, elapsed, last = 0, tmp;
+ int rc;
+
+ /* Check if Flash is (sufficiently) erased */
+ if ((*addr & data) != data) {
+ printf("Error, Flash not erased!\n");
+ return ERR_NOT_ERASED;
+ }
+
+ base = get_timer(0);
+
+ /* Disable interrupts which might cause a timeout here */
+ rc = disable_interrupts();
+
+ /* destination page physical address*/
+ writel(virt_to_phys(addr), NVMADDR);
+ writel(data, NVMDATA0);
+
+ /* NVMOP for word write*/
+ writel(NVMOP_WORD_WRITE, NVMCON);
+
+ /* Enable Flash Write*/
+ writel(NVMCON_WREN, NVMCONSET);
+
+ /* Initiate operation */
+ flash_initiate_operation();
+
+ /* re-enable interrupts if necessary */
+ if (rc)
+ enable_interrupts();
+
+ /* Wait for WR bit to clear */
+ while (readl(NVMCON) & NVMCON_WR) {
+ elapsed = get_timer(base);
+ if (elapsed > CONFIG_SYS_FLASH_WRITE_TOUT) {
+ printf("Timeout\n");
+ /* reset bank */
+ return ERR_TIMOUT;
+ }
+
+ /* show that we're waiting */
+ if ((elapsed - last) > 10) { /* every 10msec */
+ putc('.');
+ last = elapsed;
+ }
+ }
+
+ rc = 0;
+ tmp = readl(NVMCON);
+ if (tmp & NVMCON_WRERR) {
+ printf("Error in Block Write - Flash may be locked !\n");
+ flash_nop_operation();
+ rc |= ERR_PROG_ERROR;
+ }
+
+ if (tmp & NVMCON_LVDERR) {
+ printf("Error in Block Write - Brown out Reset detected!\n");
+ flash_nop_operation();
+ rc |= ERR_ABORTED;
+ }
+
+ /* Disable future Flash Write/Erase operations */
+ writel(NVMCON_WREN, NVMCONCLR);
+
+ return rc;
+}
+
+/*-----------------------------------------------------------------------
+ * Copy memory to flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ */
+
+int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
+{
+ ulong cp, wp, data, n = cnt;
+ int i, l, rc;
+
+ wp = (addr & ~3); /* get lower word aligned address */
+
+ /*
+ * handle unaligned start bytes
+ */
+ l = addr - wp;
+ if (l != 0) {
+ data = 0;
+ for (i = 0, cp = wp; i < l; ++i, ++cp)
+ data = (data << 8) | (*(uchar *)cp);
+
+ for (; (i < 4) && (cnt > 0); ++i) {
+ data = (data << 8) | *src++;
+ --cnt;
+ ++cp;
+ }
+
+ for (; (cnt == 0) && (i < 4); ++i, ++cp)
+ data = (data << 8) | (*(uchar *)cp);
+
+ rc = write_word(info, wp, __swab32(data));
+ if (rc)
+ goto out;
+
+ wp += 4;
+ }
+
+ /*
+ * handle word aligned part
+ */
+ while (cnt >= 4) {
+ data = 0;
+ for (i = 0; i < 4; ++i)
+ data = (data << 8) | *src++;
+
+ rc = write_word(info, wp, __swab32(data));
+ if (rc)
+ goto out;
+
+ wp += 4;
+ cnt -= 4;
+ }
+
+ if (cnt == 0) {
+ rc = ERR_OK;
+ goto out;
+ }
+
+ /*
+ * handle unaligned tail bytes
+ */
+ data = 0;
+ for (i = 0, cp = wp; (i < 4) && (cnt > 0); ++i, ++cp) {
+ data = (data << 8) | *src++;
+ --cnt;
+ }
+
+ for (; i < 4; ++i, ++cp)
+ data = (data << 8) | (*(uchar *)cp);
+
+ rc = write_word(info, wp, __swab32(data));
+
+out:
+ invalidate_dcache_range(addr, addr + n);
+ return rc;
+}
+
+unsigned long flash_init(void)
+{
+ unsigned long size;
+ vu_long *addr;
+ int i;
+
+ /* Init: enable write,
+ * or we cannot even write flash commands
+ */
+
+ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i)
+ flash_info[i].flash_id = FLASH_UNKNOWN;
+
+ /* flash info: combined device & manufacturer code */
+ flash_info[0].flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T;
+ flash_info[1].flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T;
+
+ /* Static FLASH Bank configuration here */
+ addr = (vu_long *)phys_to_virt(PHYS_FLASH_1);
+ flash_info[0].size = flash_get_size(addr, &flash_info[0]);
+ size = flash_info[0].size;
+
+ addr = (vu_long *)phys_to_virt(PHYS_FLASH_2);
+ flash_info[1].size = flash_get_size(addr, &flash_info[1]);
+ size += flash_info[1].size;
+
+ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
+ if (flash_info[i].flash_id == FLASH_UNKNOWN) {
+ printf("## Unknown FLASH on Bank 0 - Size = 0x%08lx\n",
+ size);
+ }
+ }
+
+#if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
+ /* monitor protection ON by default */
+ flash_protect(FLAG_PROTECT_SET,
+ CONFIG_SYS_MONITOR_BASE,
+ CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
+ &flash_info[0]);
+#endif
+
+#ifdef CONFIG_ENV_IS_IN_FLASH
+ /* ENV protection ON by default */
+ flash_protect(FLAG_PROTECT_SET,
+ CONFIG_ENV_ADDR,
+ CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1,
+ &flash_info[0]);
+#endif
+ return size;
+}
diff --git a/include/flash.h b/include/flash.h
index f53ace7..c9aacd5 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -400,6 +400,9 @@ extern flash_info_t *flash_get_info(ulong base);
#define FLASH_STM800DT 0x00D7 /* STM M29W800DT (1M = 64K x 16, top) */
#define FLASH_STM800DB 0x005B /* STM M29W800DB (1M = 64K x 16, bottom)*/
+#define FLASH_MCHP100T 0x0060 /* MCHP internal (1M = 64K x 16) */
+#define FLASH_MCHP100B 0x0061 /* MCHP internal (1M = 64K x 16) */
+
#define FLASH_28F400_T 0x0062 /* MT 28F400B3 ID ( 4M = 256K x 16 ) */
#define FLASH_28F400_B 0x0063 /* MT 28F400B3 ID ( 4M = 256K x 16 ) */
@@ -486,7 +489,7 @@ extern flash_info_t *flash_get_info(ulong base);
#define FLASH_MAN_SHARP 0x00500000
#define FLASH_MAN_ATM 0x00600000
#define FLASH_MAN_CFI 0x01000000
-
+#define FLASH_MAN_MCHP 0x02000000 /* Microchip Technology */
#define FLASH_TYPEMASK 0x0000FFFF /* extract FLASH type information */
#define FLASH_VENDMASK 0xFFFF0000 /* extract FLASH vendor information */
--
1.8.3.1
More information about the U-Boot
mailing list