From patchwork Thu Mar 10 13:12:54 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Purna Chandra Mandal X-Patchwork-Id: 595784 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 2C785140317 for ; Fri, 11 Mar 2016 00:14:49 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3A582A761B; Thu, 10 Mar 2016 14:14:44 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id n3Ys6iUR0_yx; Thu, 10 Mar 2016 14:14:43 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9F962A75BC; Thu, 10 Mar 2016 14:14:43 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2EE4AA75BC for ; Thu, 10 Mar 2016 14:14:39 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ptQuPkwlELDg for ; Thu, 10 Mar 2016 14:14:39 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from email.microchip.com (exsmtp03.microchip.com [198.175.253.49]) by theia.denx.de (Postfix) with ESMTPS id 40799A745C for ; Thu, 10 Mar 2016 14:14:34 +0100 (CET) Received: from mx.microchip.com (10.10.76.4) by chn-sv-exch03.mchp-main.com (10.10.76.49) with Microsoft SMTP Server id 14.3.181.6; Thu, 10 Mar 2016 06:14:30 -0700 Received: by mx.microchip.com (sSMTP sendmail emulation); Thu, 10 Mar 2016 18:42:59 +0530 From: Purna Chandra Mandal To: Date: Thu, 10 Mar 2016 18:42:54 +0530 Message-ID: <1457615574-5246-1-git-send-email-purna.mandal@microchip.com> X-Mailer: git-send-email 1.8.3.1 MIME-Version: 1.0 Cc: Stefan Roese , Jagannadh Teki , Scott Wood , "rev13@wp.pl" , Purna Chandra Mandal , Matt Porter Subject: [U-Boot] [PATCH] drivers: mtd: add Microchip PIC32 internal non-CFI flash driver. X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" PIC32 embedded flash banks are memory mapped, directly read by CPU, and programming (erase followed by write) operation on them are handled by on-chip NVM controller. Signed-off-by: Purna Chandra Mandal --- drivers/mtd/Kconfig | 6 + drivers/mtd/Makefile | 1 + drivers/mtd/pic32_flash.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++ include/flash.h | 5 +- 4 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/pic32_flash.c diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index c58841e..e3c6b9f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -30,6 +30,12 @@ config ALTERA_QSPI endmenu +config FLASH_PIC32 + bool "Microchip PIC32 Flash driver" + help + This enables access to Microchip PIC32 internal non-CFI flash + chips through PIC32 Non-Volatile-Memory Controller. + source "drivers/mtd/nand/Kconfig" source "drivers/mtd/spi/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 7f018a4..9380085 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -19,4 +19,5 @@ obj-$(CONFIG_HAS_DATAFLASH) += dataflash.o obj-$(CONFIG_FTSMC020) += ftsmc020.o obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o +obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c new file mode 100644 index 0000000..9a226b1 --- /dev/null +++ b/drivers/mtd/pic32_flash.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2015 + * Cristian Birsan + * Purna Chandra Mandal + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +/* NVM Controller registers */ +struct pic32_reg_nvm { + struct pic32_reg_atomic ctrl; + struct pic32_reg_atomic key; + struct pic32_reg_atomic addr; + struct pic32_reg_atomic data; +}; + +/* NVM operations */ +#define NVMOP_NOP 0 +#define NVMOP_WORD_WRITE 1 +#define NVMOP_PAGE_ERASE 4 + +/* NVM control bits */ +#define NVM_WR BIT(15) +#define NVM_WREN BIT(14) +#define NVM_WRERR BIT(13) +#define NVM_LVDERR BIT(12) + +/* NVM programming unlock register */ +#define LOCK_KEY 0x0 +#define UNLOCK_KEY1 0xaa996655 +#define UNLOCK_KEY2 0x556699aa + +/* PIC32 flash banks consist of number of pages, each page + * into number of row and rows are into number of words. + * Here we will maintain page information instead of sector. + */ +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; +static struct pic32_reg_nvm *nvm_regs_p; + +static inline void flash_initiate_operation(u32 nvmop) +{ + /* set operation */ + writel(nvmop, &nvm_regs_p->ctrl.raw); + + /* enable flash write */ + writel(NVM_WREN, &nvm_regs_p->ctrl.set); + + /* unlock sequence */ + writel(LOCK_KEY, &nvm_regs_p->key.raw); + writel(UNLOCK_KEY1, &nvm_regs_p->key.raw); + writel(UNLOCK_KEY2, &nvm_regs_p->key.raw); + + /* initiate operation */ + writel(NVM_WR, &nvm_regs_p->ctrl.set); +} + +static int flash_wait_till_busy(const char *func, ulong timeout) +{ + int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false); + + return ret ? ERR_TIMOUT : ERR_OK; +} + +static inline int flash_complete_operation(void) +{ + u32 v; + + v = readl(&nvm_regs_p->ctrl.raw); + if (v & NVM_WRERR) { + printf("Error in Block Erase - Lock Bit may be set!\n"); + flash_initiate_operation(NVMOP_NOP); + return ERR_PROTECTED; + } + + if (v & NVM_LVDERR) { + printf("Error in Block Erase - low-vol detected!\n"); + flash_initiate_operation(NVMOP_NOP); + return ERR_NOT_ERASED; + } + + /* disable flash write or erase operation */ + writel(NVM_WREN, &nvm_regs_p->ctrl.clr); + + return ERR_OK; +} + +int flash_erase(flash_info_t *info, int s_first, int s_last) +{ + ulong sect_start, sect_end, flags; + int prot, sect; + int rc; + + 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"); + + /* erase on unprotected sectors */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) + continue; + + /* disable interrupts */ + flags = disable_interrupts(); + + /* write destination page address (physical) */ + sect_start = virt_to_phys((void *)info->start[sect]); + writel(sect_start, &nvm_regs_p->addr.raw); + + /* page erase */ + flash_initiate_operation(NVMOP_PAGE_ERASE); + + /* wait */ + rc = flash_wait_till_busy(__func__, + CONFIG_SYS_FLASH_ERASE_TOUT); + + /* re-enable interrupts if necessary */ + if (flags) + enable_interrupts(); + + if (rc != ERR_OK) + return rc; + + rc = flash_complete_operation(); + if (rc != ERR_OK) + return rc; + + /* flash content is updated but cache might contain stale + * data, so invalidate dcache. + */ + sect_end = info->start[sect] + info->size / info->sector_count; + invalidate_dcache_range(info->start[sect], sect_end); + } + + printf(" done\n"); + return ERR_OK; +} + +int page_erase(flash_info_t *info, int sect) +{ + return 0; +} + +/* Write a word to flash */ +static int write_word(flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long *)dest; + ulong flags; + int rc; + + /* check if flash is (sufficiently) erased */ + if ((*addr & data) != data) { + printf("Error, Flash not erased!\n"); + return ERR_NOT_ERASED; + } + + /* disable interrupts */ + flags = disable_interrupts(); + + /* update destination page address (physical) */ + writel(virt_to_phys(addr), &nvm_regs_p->addr.raw); + writel(data, &nvm_regs_p->data.raw); + + /* word write */ + flash_initiate_operation(NVMOP_WORD_WRITE); + + /* wait for operation to complete */ + rc = flash_wait_till_busy(__func__, CONFIG_SYS_FLASH_WRITE_TOUT); + + /* re-enable interrupts if necessary */ + if (flags) + enable_interrupts(); + + if (rc != ERR_OK) + return rc; + + return flash_complete_operation(); +} + +/* Copy memory to flash, returns: + * ERR_OK - OK + * ERR_TIMOUT - write timeout + * ERR_NOT_ERASED - Flash not erased + */ +int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong wp, data, len = cnt; + int i, l, rc; + uchar *cp; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + l = addr - wp; + if (l != 0) { + data = 0; + for (i = 0, cp = (uchar *)wp; i < l; ++i, ++cp) + data = (data << 8) | *cp; + + for (; (i < 4) && (cnt > 0); ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + + for (; (cnt == 0) && (i < 4); ++i, ++cp) + data = (data << 8) | *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 = (uchar *)wp; (i < 4) && (cnt > 0); ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + + for (; i < 4; ++i, ++cp) + data = (data << 8) | *cp; + + rc = write_word(info, wp, __swab32(data)); + +out: + /* flash content updated by nvm controller, + * but cache might have stale, so invalidate dcache. + */ + invalidate_dcache_range(addr, addr + len); + + printf(" done\n"); + return rc; +} + +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 Technology"); + 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"); +} + +unsigned long flash_init(void) +{ + phys_addr_t flash_banks[] = CONFIG_SYS_FLASH_BANKS_LIST; + flash_info_t *info = &flash_info[0]; + ulong total_size = 0; + ulong sect_size; + ulong base; + int bank, sect; + + sect_size = CONFIG_SYS_FLASH_SIZE / CONFIG_SYS_MAX_FLASH_SECT; + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) { + /* combined device & manufacturer code */ + info->flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T; + info->sector_count = CONFIG_SYS_MAX_FLASH_SECT; + info->size = CONFIG_SYS_FLASH_SIZE; + + /* update sector (i.e page) info */ + base = (ulong)phys_to_virt(flash_banks[bank]); + for (sect = 0; sect < info->sector_count; sect++) { + info->start[sect] = base; + /* protect each sector by default */ + info->protect[sect] = 1; + base += sect_size; + } + total_size += info->size; + } + + /* NVM controller iobase */ + nvm_regs_p = ioremap(PIC32_NVM_BASE, sizeof(*nvm_regs_p)); + + /* disable flash write/erase operations */ + writel(NVM_WREN, &nvm_regs_p->ctrl.clr); + +#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 total_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 */