Am 18.03.2016 um 14:06 schrieb Purna Chandra Mandal: > 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. > > Driver implemented driver model but MTD is not really support. > > Signed-off-by: Purna Chandra Mandal <[email protected]> > > ---
applied to u-boot-mips/next, thanks. > > Changes in v3: > - add driver model support but MTD is not implemented > > 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 | 444 > ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 452 insertions(+) > create mode 100644 drivers/mtd/pic32_flash.c > > diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig > index c58841e..390e9e4 100644 > --- a/drivers/mtd/Kconfig > +++ b/drivers/mtd/Kconfig > @@ -28,6 +28,13 @@ config ALTERA_QSPI > NOR flash to parallel flash interface. Please find details on the > "Embedded Peripherals IP User Guide" of Altera. > > +config FLASH_PIC32 > + bool "Microchip PIC32 Flash driver" > + depends on MACH_PIC32 && MTD > + help > + This enables access to Microchip PIC32 internal non-CFI flash > + chips through PIC32 Non-Volatile-Memory Controller. > + > endmenu > > source "drivers/mtd/nand/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..9166fcd > --- /dev/null > +++ b/drivers/mtd/pic32_flash.c > @@ -0,0 +1,444 @@ > +/* > + * Copyright (C) 2015 > + * Cristian Birsan <[email protected]> > + * Purna Chandra Mandal <[email protected]> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <fdt_support.h> > +#include <flash.h> > +#include <mach/pic32.h> > +#include <wait_bit.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +/* 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 = src[0] | src[1] << 8 | src[2] << 16 | src[3] << 24; > + rc = write_word(info, dst, tmp_le); > + if (rc) > + goto out; > + src += 4; > + 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) > +{ > + unsigned long size = 0; > + struct udevice *dev; > + int bank; > + > + /* probe every MTD device */ > + for (uclass_first_device(UCLASS_MTD, &dev); dev; > + uclass_next_device(&dev)) { > + /* nop */ > + } > + > + /* calc total flash size */ > + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) > + size += flash_info[bank].size; > + > + return size; > +} > + > +static void pic32_flash_bank_init(flash_info_t *info, > + ulong base, ulong size) > +{ > + ulong sect_size; > + int sect; > + > + /* device & manufacturer code */ > + info->flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T; > + info->sector_count = CONFIG_SYS_MAX_FLASH_SECT; > + info->size = size; > + > + /* update sector (i.e page) info */ > + sect_size = info->size / info->sector_count; > + for (sect = 0; sect < info->sector_count; sect++) { > + info->start[sect] = base; > + /* protect each sector by default */ > + info->protect[sect] = 1; > + base += sect_size; > + } > +} > + > +static int pic32_flash_probe(struct udevice *dev) > +{ > + void *blob = (void *)gd->fdt_blob; > + int node = dev->of_offset; > + const char *list, *end; > + const fdt32_t *cell; > + unsigned long addr, size; > + int parent, addrc, sizec; > + flash_info_t *info; > + int len, idx; > + > + /* > + * decode regs. there are multiple reg tuples, and they need to > + * match with reg-names. > + */ > + parent = fdt_parent_offset(blob, node); > + of_bus_default_count_cells(blob, parent, &addrc, &sizec); > + list = fdt_getprop(blob, node, "reg-names", &len); > + if (!list) > + return -ENOENT; > + > + end = list + len; > + cell = fdt_getprop(blob, node, "reg", &len); > + if (!cell) > + return -ENOENT; > + > + for (idx = 0, info = &flash_info[0]; list < end;) { > + addr = fdt_translate_address((void *)blob, node, cell + idx); > + size = fdt_addr_to_cpu(cell[idx + addrc]); > + len = strlen(list); > + if (!strncmp(list, "nvm", len)) { > + /* NVM controller */ > + nvm_regs_p = ioremap(addr, size); > + } else if (!strncmp(list, "bank", 4)) { > + /* Flash bank: use kseg0 cached address */ > + pic32_flash_bank_init(info, CKSEG0ADDR(addr), size); > + info++; > + } > + idx += addrc + sizec; > + list += len + 1; > + } > + > + /* 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 0; > +} > + > +static const struct udevice_id pic32_flash_ids[] = { > + { .compatible = "microchip,pic32mzda-flash" }, > + {} > +}; > + > +U_BOOT_DRIVER(pic32_flash) = { > + .name = "pic32_flash", > + .id = UCLASS_MTD, > + .of_match = pic32_flash_ids, > + .probe = pic32_flash_probe, > +}; > -- - Daniel
signature.asc
Description: OpenPGP digital signature
_______________________________________________ U-Boot mailing list [email protected] http://lists.denx.de/mailman/listinfo/u-boot

