uboot 添加W25Q64
发布时间:2020-12-15 19:56:11 所属栏目:百科 来源:网络整理
导读:/* ?* Copyright 2008,Network Appliance Inc. ?* Author: Jason McMullan mcmullan at netapp.com ?* Licensed under the GPL-2 or later. ?*/ #include common.h #include malloc.h #include spi_flash.h #include "spi_flash_internal.h" /* M25Pxx-speci
/* ?* Copyright 2008,Network Appliance Inc. ?* Author: Jason McMullan <mcmullan <at> netapp.com> ?* Licensed under the GPL-2 or later. ?*/ #include <common.h> #include <malloc.h> #include <spi_flash.h> #include "spi_flash_internal.h" /* M25Pxx-specific commands */ #define CMD_W25_WREN?? ??? ?0x06?? ?/* Write Enable */ #define CMD_W25_WRDI?? ??? ?0x04?? ?/* Write Disable */ #define CMD_W25_RDSR?? ??? ?0x05?? ?/* Read Status Register */ #define CMD_W25_WRSR?? ??? ?0x01?? ?/* Write Status Register */ #define CMD_W25_READ?? ??? ?0x03?? ?/* Read Data Bytes */ #define CMD_W25_FAST_READ?? ?0x0b?? ?/* Read Data Bytes at Higher Speed */ #define CMD_W25_PP?? ??? ?0x02?? ?/* Page Program */ #define CMD_W25_SE?? ??? ?0x20?? ?/* Sector (4K) Erase */ #define CMD_W25_BE?? ??? ?0xd8?? ?/* Block (64K) Erase */ #define CMD_W25_CE?? ??? ?0xc7?? ?/* Chip Erase */ #define CMD_W25_DP?? ??? ?0xb9?? ?/* Deep Power-down */ #define CMD_W25_RES?? ??? ?0xab?? ?/* Release from DP,and Read Signature */ #define WINBOND_ID_W25X10A?? ??? ?0x3011 #define WINBOND_ID_W25X20A?? ??? ?0x3012 #define WINBOND_ID_W25X40A?? ??? ?0x3013 #define WINBOND_ID_W25X80A?? ??? ?0x3014 #define WINBOND_ID_W25X16?? ??? ?0x3015 #define WINBOND_ID_W25X32?? ??? ?0x3016 #define WINBOND_ID_W25X64?? ??? ?0x3017 #define WINBOND_ID_W25Q16B?? ??? ?0x4015 #define WINBOND_ID_W25Q32B?? ??? ?0x4016 #define WINBOND_ID_W25Q64?? ??? ?0x4017 #define WINBOND_ID_W25Q128?? ??? ?0x4018 #define WINBOND_SR_WIP?? ??? ?(1 << 0)?? ?/* Write-in-Progress */ struct winbond_spi_flash_params { ?? ?uint16_t?? ?id; ?? ?/* Log2 of page size in power-of-two mode */ ?? ?uint8_t?? ??? ?l2_page_size; ?? ?uint16_t?? ?pages_per_sector; ?? ?uint16_t?? ?sectors_per_block; //?? ?uint8_t?? ??? ?nr_blocks; ?? ?uint16_t?? ?nr_blocks; ?? ?const char?? ?*name; }; /* spi_flash needs to be first so upper layers can free() it */ struct winbond_spi_flash { ?? ?struct spi_flash flash; ?? ?const struct winbond_spi_flash_params *params; }; static inline struct winbond_spi_flash * to_winbond_spi_flash(struct spi_flash *flash) { ?? ?return container_of(flash,struct winbond_spi_flash,flash); } static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { ?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X10A,?? ??? ?.l2_page_size?? ??? ?= 8,?? ??? ?.pages_per_sector?? ?= 16,?? ??? ?.sectors_per_block?? ?= 16,?? ??? ?.nr_blocks?? ??? ?= 2,?? ??? ?.name?? ??? ??? ?= "W25X10A",?? ?},?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X20A,?? ??? ?.nr_blocks?? ??? ?= 4,?? ??? ?.name?? ??? ??? ?= "W25X20A",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X40A,?? ??? ?.nr_blocks?? ??? ?= 8,?? ??? ?.name?? ??? ??? ?= "W25X40A",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X80A,?? ??? ?.nr_blocks?? ??? ?= 16,?? ??? ?.name?? ??? ??? ?= "W25X80A",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X16,?? ??? ?.nr_blocks?? ??? ?= 32,?? ??? ?.name?? ??? ??? ?= "W25X16",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X32,?? ??? ?.nr_blocks?? ??? ?= 64,?? ??? ?.name?? ??? ??? ?= "W25X32",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25X64,?? ??? ?.nr_blocks?? ??? ?= 128,?? ??? ?.name?? ??? ??? ?= "W25X64",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25Q16B,?? ??? ?.name?? ??? ??? ?= "W25Q16B",?? ?{ ?? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25Q32B,?? ??? ?.name?? ??? ??? ?= "W25Q32B",?? ?{ ?? ??? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25Q64,?? ??? ??? ?.l2_page_size?? ??? ?= 8,?? ??? ??? ?.pages_per_sector?? ?= 16,?? ??? ??? ?.sectors_per_block?? ?= 16,?? ??? ??? ?.nr_blocks?? ??? ?= 128,?? ??? ??? ?.name?? ??? ??? ?= "W25Q64",?? ?{ ?? ??? ??? ?.id?? ??? ??? ?= WINBOND_ID_W25Q128,?? ??? ??? ?.nr_blocks?? ??? ?= 256,?? ??? ??? ?.name?? ??? ??? ?= "W25Q128",}; static int winbond_wait_ready(struct spi_flash *flash,unsigned long timeout) { ?? ?struct spi_slave *spi = flash->spi; ?? ?unsigned long timebase; ?? ?int ret; ?? ?u8 status; ?? ?u8 cmd[4] = { CMD_W25_RDSR,0xff,0xff }; ?? ?ret = spi_xfer(spi,32,&cmd[0],NULL,SPI_XFER_BEGIN); ?? ?if (ret) { ?? ??? ?printf("SF: Failed to send command %02x: %dn",cmd[0],ret); ?? ??? ?return ret; ?? ?} ?? ?timebase = get_timer(0); ?? ?do { ?? ??? ?ret = spi_xfer(spi,8,&status,0); ?? ??? ?if (ret) { ?? ??? ??? ?printf("SF: Failed to get status for cmd %02x: %dn",ret); ?? ??? ??? ?return -1; ?? ??? ?} ?? ??? ?if ((status & WINBOND_SR_WIP) == 0) ?? ??? ??? ?break; ?? ?} while (get_timer(timebase) < timeout); ?? ?spi_xfer(spi,SPI_XFER_END); ?? ?if ((status & WINBOND_SR_WIP) == 0) ?? ??? ?return 0; ?? ?printf("SF: Timed out on command %02x,status %08X: %dn",status,ret); ?? ?/* Timed out */ ?? ?return -1; } /* ?* Assemble the address part of a command for Winbond devices in ?* non-power-of-two page size mode. ?*/ static void winbond_build_address(struct winbond_spi_flash *stm,u8 *cmd,u32 offset) { ?? ?unsigned long page_addr; ?? ?unsigned long byte_addr; ?? ?unsigned long page_size; ?? ?unsigned int page_shift; ?? ?/* ?? ? * The "extra" space per page is the power-of-two page size ?? ? * divided by 32. ?? ? */ ?? ?page_shift = stm->params->l2_page_size; ?? ?page_size = (1 << page_shift); ?? ?page_addr = offset / page_size; ?? ?byte_addr = offset % page_size; ?? ?cmd[0] = page_addr >> (16 - page_shift); ?? ?cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); ?? ?cmd[2] = byte_addr; } #ifdef CONFIG_SPI_FLASH_SLOW_READ static int winbond_read(struct spi_flash *flash,u32 offset,?? ??? ?size_t len,void *buf) { ?? ?struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); ?? ?static u8 cmd[4] = { CMD_W25_READ,0 }; ?? ?winbond_build_address(stm,cmd + 1,offset); ?? ?debug("SF: Winbond: Reading %u bytes @ 0x%xn",len,offset); ?? ?return spi_flash_read_common(flash,cmd,sizeof(cmd),buf,len); } #else static int winbond_read_fast(struct spi_flash *flash,?? ??? ?u32 offset,size_t len,void *buf) { ?? ?struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); ?? ?u8 cmd[5]; ?? ?cmd[0] = CMD_READ_ARRAY_FAST; ?? ?winbond_build_address(stm,offset); ?? ?cmd[4] = 0x00; ?? ?debug("SF: Winbond: Reading %u bytes @ 0x%xn",len); } #endif static int winbond_write(struct spi_flash *flash,const void *buf) { ?? ?struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); ?? ?unsigned long page_addr; ?? ?unsigned long byte_addr; ?? ?unsigned long page_size; ?? ?unsigned int page_shift; ?? ?size_t chunk_len; ?? ?size_t actual; ?? ?int ret; ?? ?u8 cmd[4]; ?? ?page_shift = stm->params->l2_page_size; ?? ?page_size = (1 << page_shift); ?? ?page_addr = offset / page_size; ?? ?byte_addr = offset % page_size; ?? ?ret = spi_claim_bus(flash->spi); ?? ?if (ret) { ?? ??? ?printf("SF: Unable to claim SPI busn"); ?? ??? ?return ret; ?? ?} ?? ?for (actual = 0; actual < len; actual += chunk_len) { ?? ??? ?chunk_len = min(len - actual,page_size - byte_addr); ?? ??? ?cmd[0] = CMD_W25_PP; ?? ??? ?cmd[1] = page_addr >> (16 - page_shift); ?? ??? ?cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); ?? ??? ?cmd[3] = byte_addr; ?? ??? ?debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %dn",?? ??? ??? ?buf + actual,?? ??? ??? ?cmd[0],cmd[1],cmd[2],cmd[3],chunk_len); ?? ??? ?ret = spi_flash_cmd(flash->spi,CMD_W25_WREN,0); ?? ??? ?if (ret < 0) { ?? ??? ??? ?printf("SF: Enabling Write failedn"); ?? ??? ??? ?goto out; ?? ??? ?} ?? ??? ?ret = spi_flash_cmd_write(flash->spi,4,?? ??? ??? ??? ?buf + actual,chunk_len); ?? ??? ?if (ret < 0) { ?? ??? ??? ?printf("SF: Winbond Page Program failedn"); ?? ??? ??? ?goto out; ?? ??? ?} ?? ??? ?ret = winbond_wait_ready(flash,SPI_FLASH_PROG_TIMEOUT); ?? ??? ?if (ret < 0) { ?? ??? ??? ?printf("SF: Winbond page programming timed outn"); ?? ??? ??? ?goto out; ?? ??? ?} ?? ??? ?page_addr++; ?? ??? ?byte_addr = 0; ?? ?} ?? ?debug("SF: Winbond: Successfully programmed %u bytes @ 0x%xn",?? ??? ??? ?len,offset); ?? ?ret = 0; out: ?? ?spi_release_bus(flash->spi); ?? ?return ret; } int winbond_erase(struct spi_flash *flash,size_t len) { ?? ?struct winbond_spi_flash *stm = to_winbond_spi_flash(flash); ?? ?unsigned long sector_size; ?? ?unsigned int page_shift; ?? ?size_t actual; ?? ?int ret; ?? ?u8 cmd[4]; ?? ?/* ?? ? * This function currently uses sector erase only. ?? ? * probably speed things up by using bulk erase ?? ? * when possible. ?? ? */ ?? ?page_shift = stm->params->l2_page_size; ?? ?sector_size = (1 << page_shift) * stm->params->pages_per_sector; ?? ?if (offset % sector_size || len % sector_size) { ?? ??? ?printf("SF: Erase offset/length not multiple of sector size (%lx)n",?? ??? ??? ?sector_size); ?? ??? ?return -1; ?? ?} ?? ?len /= sector_size; ?? ?cmd[0] = CMD_W25_SE; ?? ?ret = spi_claim_bus(flash->spi); ?? ?if (ret) { ?? ??? ?printf("SF: Unable to claim SPI busn"); ?? ??? ?return ret; ?? ?} ?? ?for (actual = 0; actual < len; actual++) { ?? ??? ?winbond_build_address(stm,&cmd[1],offset + actual * sector_size); ?? ??? ?debug("Erase: %02x %02x %02x %02xn",?? ??? ??? ??? ?cmd[0],cmd[3]); ?? ??? ?ret = spi_flash_cmd(flash->spi,0); ?? ??? ?if (ret < 0) { ?? ??? ??? ?printf("SF: Winbond sector erase failedn"); ?? ??? ??? ?goto out; ?? ??? ?} ?? ??? ?ret = winbond_wait_ready(flash,SPI_FLASH_PAGE_ERASE_TIMEOUT); ?? ??? ?if (ret < 0) { ?? ??? ??? ?printf("SF: Winbond sector erase timed outn"); ?? ??? ??? ?goto out; ?? ??? ?} ?? ?} ?? ?debug("SF: Winbond: Successfully erased %lu bytes @ 0x%xn",?? ??? ??? ?len * sector_size,offset); ?? ?ret = 0; out: ?? ?spi_release_bus(flash->spi); ?? ?return ret; } struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi,u8 *idcode) { ?? ?const struct winbond_spi_flash_params *params; ?? ?unsigned long page_size; ?? ?struct winbond_spi_flash *stm; ?? ?unsigned int i; ?? ?for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { ?? ??? ?params = &winbond_spi_flash_table[i]; ?? ??? ?if (params->id == ((idcode[1] << 8) | idcode[2])) ?? ??? ??? ?break; ?? ?} ?? ?if (i == ARRAY_SIZE(winbond_spi_flash_table)) { ?? ??? ?printf("SF: Unsupported Winbond ID %02x%02xn",?? ??? ??? ??? ?idcode[1],idcode[2]); ?? ??? ?return NULL; ?? ?} ?? ?stm = malloc(sizeof(struct winbond_spi_flash)); ?? ?if (!stm) { ?? ??? ?printf("SF: Failed to allocate memoryn"); ?? ??? ?return NULL; ?? ?} ?? ?stm->params = params; ?? ?stm->flash.spi = spi; ?? ?stm->flash.name = params->name; ?? ?/* Assuming power-of-two page size initially. */ ?? ?page_size = 1 << params->l2_page_size; ?? ?stm->flash.write = winbond_write; ?? ?stm->flash.erase = winbond_erase; #ifdef?? ?CONFIG_SPI_FLASH_SLOW_READ ?? ?stm->flash.read = winbond_read; #else ?? ?stm->flash.read = winbond_read_fast; #endif ?? ?stm->flash.size = page_size * params->pages_per_sector ?? ??? ??? ??? ?* params->sectors_per_block ?? ??? ??? ??? ?* params->nr_blocks; ?? ?debug("SF: Detected %s with page size %lu,total %lu bytesn",?? ??? ??? ?params->name,page_size,stm->flash.size); ?? ?return &stm->flash; }
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |