2016-03-15 11:16 GMT+01:00 Purna Chandra Mandal <purna.man...@microchip.com>: > PIC32 internal flash devices are parallel NOR flash divided into > number of banks to allow erase-programming in one while fetch and > execution continues on other. As the flash banks are memory mapped > stored code can be executed directly from flash (XIP), also there > is additional hardware logic to prefetch and cache contents to > improve execution performance. These flash can also be used to > store user data (like environment). > Flash erase and programming are handled by on-chip NVM controller. > > Signed-off-by: Purna Chandra Mandal <purna.man...@microchip.com> >
Reviewed-by: Daniel Schwierzeck <daniel.schwierz...@gmail.com> > --- > > Changes in v2: > - kconfig: add CONFIG_FLASH_PIC32 dependent on MACH_PIC32 > - fix single/multi-line comment style > - simplify byte-stream-to-word in little-endian format > - replace virt_to_phys() with CPHYSADDR() > - separate flash ID definition in different patch > > drivers/mtd/Kconfig | 7 + > drivers/mtd/Makefile | 1 + > drivers/mtd/pic32_flash.c | 380 > ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 388 insertions(+) > create mode 100644 drivers/mtd/pic32_flash.c > > diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig > index c58841e..5ed860d 100644 > --- a/drivers/mtd/Kconfig > +++ b/drivers/mtd/Kconfig > @@ -30,6 +30,13 @@ config ALTERA_QSPI > > endmenu > > +config FLASH_PIC32 > + bool "Microchip PIC32 Flash driver" > + depends on MACH_PIC32 > + 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..59555ca > --- /dev/null > +++ b/drivers/mtd/pic32_flash.c > @@ -0,0 +1,380 @@ > +/* > + * Copyright (C) 2015 > + * Cristian Birsan <cristian.bir...@microchip.com> > + * Purna Chandra Mandal <purna.man...@microchip.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <flash.h> > +#include <mach/pic32.h> > +#include <wait_bit.h> > + > +/* 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 rows and rows 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 tmp; > + > + tmp = readl(&nvm_regs_p->ctrl.raw); > + if (tmp & NVM_WRERR) { > + printf("Error in Block Erase - Lock Bit may be set!\n"); > + flash_initiate_operation(NVMOP_NOP); > + return ERR_PROTECTED; > + } > + > + if (tmp & 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; > +} > + > +/* > + * Erase flash sectors, returns: > + * ERR_OK - OK > + * ERR_INVAL - invalid sector arguments > + * ERR_TIMOUT - write timeout > + * ERR_NOT_ERASED - Flash not erased > + * ERR_UNKNOWN_FLASH_VENDOR - incorrect flash > + */ > +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 = CPHYSADDR(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 word) > +{ > + ulong flags; > + int rc; > + > + /* read flash to check if it is sufficiently erased */ > + if ((readl((void __iomem *)dest) & word) != word) { > + printf("Error, Flash not erased!\n"); > + return ERR_NOT_ERASED; > + } > + > + /* disable interrupts */ > + flags = disable_interrupts(); > + > + /* update destination page address (physical) */ > + writel(CPHYSADDR(dest), &nvm_regs_p->addr.raw); > + writel(word, &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 dst, tmp_le, len = cnt; > + int i, l, rc; > + uchar *cp; > + > + /* get lower word aligned address */ > + dst = (addr & ~3); > + > + /* handle unaligned start bytes */ > + l = addr - dst; > + if (l != 0) { > + tmp_le = 0; > + for (i = 0, cp = (uchar *)dst; i < l; ++i, ++cp) > + tmp_le |= *cp << (i * 8); > + > + for (; (i < 4) && (cnt > 0); ++i, ++src, --cnt, ++cp) > + tmp_le |= *src << (i * 8); > + > + for (; (cnt == 0) && (i < 4); ++i, ++cp) > + tmp_le |= *cp << (i * 8); > + > + rc = write_word(info, dst, tmp_le); > + if (rc) > + goto out; > + > + dst += 4; > + } > + > + /* handle word aligned part */ > + while (cnt >= 4) { > + tmp_le = 0; > + for (i = 0; i < 4; ++i, ++src) > + tmp_le |= *src << (i * 8); > + > + rc = write_word(info, dst, tmp_le); > + if (rc) > + goto out; > + > + dst += 4; > + cnt -= 4; > + } > + > + if (cnt == 0) { > + rc = ERR_OK; > + goto out; > + } > + > + /* handle unaligned tail bytes */ > + tmp_le = 0; > + for (i = 0, cp = (uchar *)dst; (i < 4) && (cnt > 0); ++i, ++cp) { > + tmp_le |= *src++ << (i * 8); > + --cnt; > + } > + > + for (; i < 4; ++i, ++cp) > + tmp_le |= *cp << (i * 8); > + > + rc = write_word(info, dst, tmp_le); > + > +out: > + /* > + * flash content updated by nvm controller but CPU cache might > + * have stale data, 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; > +} > -- > 1.8.3.1 > -- - Daniel _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot