From mboxrd@z Thu Jan 1 00:00:00 1970 From: Purna Chandra Mandal Date: Fri, 8 Jan 2016 19:23:35 +0530 Subject: [U-Boot] [PATCH v1 08/18] MIPS: pic32: Add driver for Microchip PIC32 flash controller. In-Reply-To: <567818D7.6090602@denx.de> References: <5672F14B.8070006@microchip.com> <567813A8.3050706@gmail.com> <567818D7.6090602@denx.de> Message-ID: <568FBF5F.7020100@microchip.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 12/21/2015 08:50 PM, Stefan Roese wrote: > On 21.12.2015 15:58, Daniel Schwierzeck wrote: >> >> >> Am 17.12.2015 um 18:30 schrieb Purna Chandra Mandal: >>> From: Cristian Birsan >>> >>> Signed-off-by: Cristian Birsan >>> Signed-off-by: Purna Chandra Mandal >>> --- >>> >>> 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 >>> >> >> +cc Stefan Roese >> >> have you tried to use drivers/mtd/cfi_flash.c? You are duplicating some >> common code. If you need additional logic for your flash controller, >> then you can try to overwrite the weak flash_readX/flash_writeX >> accessors. You have to enable CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS to be >> able to do this. > > Yes, this really looks like it could use the common CFI flash driver. > You might need to add support for the new flash devices with the > commands, if its not already supported. But this should be better > than duplicating some of the code already available. > Thanks Stefan. Tried to understand and map PIC32 flash driver on cfi_flash.c. It is not straight forward and resultant code becomes clumsy, full of if-else. Please note PIC32 flash is not CFI complaint. Specific operations (like erase, and program) on the flash panel are handled by PIC32 nvm controller. NVM controller documentation: http://ww1.microchip.com/downloads/en/DeviceDoc/60001193B.pdf Instead I'll propose to improve current pic32_flash driver and move it to drivers/mtd/ (like st_smi.c). NVM controller: Thanks, Purna > So please take a deeper look at cfi_flash.c and try to integrate > your flash support there. > > Thanks, > Stefan > >>> 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 >>> + * >>> + * SPDX-License-Identifier: GPL-2.0+ >>> + * >>> + */ >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#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 */ >>> >> > >