rockchip: rkflash: add slc nand flash and spi nand flash support
1.All three types of flash including slc nand flash, spi nand flash and spi nor flash are code compatibility 2.Add vendor partition ops 3.Unified naming format and variable with code in kernel Change-Id: I0aa1c5daf9ec51296a6d2d904b2cf1ea7a0dd077 Signed-off-by: Dingqiang Lin <jon.lin@rock-chips.com>
This commit is contained in:
parent
504d9922d7
commit
ba0501aca2
|
|
@ -6,26 +6,53 @@
|
|||
if ARCH_ROCKCHIP
|
||||
|
||||
menuconfig RKFLASH
|
||||
tristate "Rockchip Flash Devices Support"
|
||||
bool "Rockchip Flash Devices support"
|
||||
default n
|
||||
help
|
||||
Enable rockchip flash devices support.
|
||||
rkflash driver support 3-type flash devices: NANDC NAND, SFC_NOR
|
||||
and SFC_NAND.
|
||||
This enables support for Rockchip Flash Devices including Nandc Slc
|
||||
Nand, SFC SPI Nand and SFC SPI Nor.
|
||||
|
||||
They're block interface.
|
||||
|
||||
Say Y when you have a board with one of them.
|
||||
|
||||
if RKFLASH
|
||||
|
||||
comment "Rockchip Flash Devices"
|
||||
|
||||
config RKNANDC_NAND
|
||||
bool "Rockchip NANDC Slc Nand Devices support"
|
||||
depends on RKNAND != y
|
||||
default n
|
||||
help
|
||||
This enables support for NANDC Slc Nand Devices.
|
||||
|
||||
It's block interface, 512Kb/sector.
|
||||
|
||||
Say Y when you have a board with Slc Nand Flash supported by Rockchip
|
||||
Nandc controller.
|
||||
|
||||
config RKSFC_NAND
|
||||
bool "Rockchip SFC SPI Nand Devices support"
|
||||
depends on RKNAND != y
|
||||
default n
|
||||
help
|
||||
This enables support for Rockchip SFC SPI Nand Devices.
|
||||
|
||||
It's block interface, 512Kb/sector.
|
||||
|
||||
Say Y when you have a board with SPI Nand Flash supported by Rockchip
|
||||
Serial Flash Controller(SFC).
|
||||
config RKSFC_NOR
|
||||
bool "Rockchip SFC NOR flash support"
|
||||
bool "Rockchip SFC SPI Nor Devices Support"
|
||||
depends on BLK
|
||||
help
|
||||
This option enables support for Rockchip SFC nor devices.
|
||||
It's block interface.
|
||||
512Kb/sector.
|
||||
Say y here to enable rockchip spi nor flash driver.
|
||||
This enables support for Rockchip SFC SPI Nor Devices.
|
||||
|
||||
It's block interface,512Kb/sector.
|
||||
|
||||
Say Y when you have a board with SPI Nor Flash supported by Rockchip
|
||||
Serial Flash Controller(SFC).
|
||||
|
||||
endif # RKFLASH
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
#
|
||||
# Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
obj-y += rkflash_debug.o
|
||||
obj-$(CONFIG_RKSFC_NOR) += sfc.o sfc_nor.o rkflash_api.o rksfc_base.o rkflash_blk.o
|
||||
obj-$(CONFIG_RKNANDC_NAND) += rkflash_blk.o rknandc_base.o rkflash_api.o rkflash_debug.o flash.o nandc.o
|
||||
obj-$(CONFIG_RKSFC_NAND) += rkflash_blk.o rksfc_base.o rkflash_api.o rkflash_debug.o sfc_nand.o sfc.o
|
||||
obj-$(CONFIG_RKSFC_NOR) += rkflash_blk.o rksfc_base.o rkflash_api.o rkflash_debug.o sfc_nor.o sfc.o
|
||||
|
||||
ifneq (, $(CONFIG_RKNANDC_NAND)$(CONFIG_RKSFC_NAND))
|
||||
|
||||
ifdef CONFIG_ARM64
|
||||
obj-y += rk_sftl_arm_v8.o
|
||||
else
|
||||
obj-y += rk_sftl_arm_v7.o
|
||||
endif
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "flash_com.h"
|
||||
#include "nandc.h"
|
||||
#include "typedef.h"
|
||||
#include "rkflash_debug.h"
|
||||
|
||||
#define FLASH_STRESS_TEST_EN 0
|
||||
|
||||
static u8 id_byte[MAX_FLASH_NUM][8];
|
||||
static u8 die_cs_index[MAX_FLASH_NUM];
|
||||
static u8 g_nand_max_die;
|
||||
static u16 g_totle_block;
|
||||
static u8 g_nand_flash_ecc_bits;
|
||||
static u8 g_nand_idb_res_blk_num;
|
||||
|
||||
static struct NAND_PARA_INFO_T nand_para = {
|
||||
2,
|
||||
{0x98, 0xF1, 0, 0, 0, 0},
|
||||
TOSHIBA,
|
||||
1,
|
||||
4,
|
||||
64,
|
||||
1,
|
||||
1,
|
||||
1024,
|
||||
0x100,
|
||||
LSB_0,
|
||||
RR_NONE,
|
||||
16,
|
||||
40,
|
||||
1,
|
||||
0,
|
||||
BBF_1,
|
||||
MPM_0,
|
||||
{0}
|
||||
}; /* TC58NVG0S3HTA00 */
|
||||
|
||||
void nandc_flash_reset(u8 cs)
|
||||
{
|
||||
nandc_flash_cs(cs);
|
||||
nandc_writel(RESET_CMD, NANDC_CHIP_CMD(cs));
|
||||
nandc_wait_flash_ready(cs);
|
||||
nandc_flash_de_cs(cs);
|
||||
}
|
||||
|
||||
static void flash_read_id_raw(u8 cs, u8 *buf)
|
||||
{
|
||||
u8 *ptr = (u8 *)buf;
|
||||
|
||||
nandc_flash_reset(cs);
|
||||
nandc_flash_cs(cs);
|
||||
nandc_writel(READ_ID_CMD, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(0x00, NANDC_CHIP_ADDR(cs));
|
||||
nandc_delayns(200);
|
||||
|
||||
ptr[0] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[1] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[2] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[3] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[4] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[5] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[6] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
ptr[7] = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
|
||||
nandc_flash_de_cs(cs);
|
||||
if (ptr[0] != 0xFF && ptr[0] && ptr[1] != 0xFF)
|
||||
PRINT_NANDC_E("No.%d FLASH ID:%x %x %x %x %x %x\n",
|
||||
cs + 1, ptr[0], ptr[1], ptr[2],
|
||||
ptr[3], ptr[4], ptr[5]);
|
||||
}
|
||||
|
||||
static void flash_bch_sel(u8 bits)
|
||||
{
|
||||
g_nand_flash_ecc_bits = bits;
|
||||
nandc_bch_sel(bits);
|
||||
}
|
||||
|
||||
static __maybe_unused void flash_timing_cfg(u32 ahb_khz)
|
||||
{
|
||||
nandc_time_cfg(nand_para.access_freq);
|
||||
}
|
||||
|
||||
static void flash_read_cmd(u8 cs, u32 page_addr)
|
||||
{
|
||||
nandc_writel(READ_CMD >> 8, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(0x00, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(0x00, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr & 0x00ff, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 16, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(READ_CMD & 0x00ff, NANDC_CHIP_CMD(cs));
|
||||
}
|
||||
|
||||
static void flash_prog_first_cmd(u8 cs, u32 page_addr)
|
||||
{
|
||||
nandc_writel(PAGE_PROG_CMD >> 8, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(0x00, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(0x00, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr & 0x00ff, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 16, NANDC_CHIP_ADDR(cs));
|
||||
}
|
||||
|
||||
static void flash_erase_cmd(u8 cs, u32 page_addr)
|
||||
{
|
||||
nandc_writel(BLOCK_ERASE_CMD >> 8, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(page_addr & 0x00ff, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 16, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(BLOCK_ERASE_CMD & 0x00ff, NANDC_CHIP_CMD(cs));
|
||||
}
|
||||
|
||||
static void flash_prog_second_cmd(u8 cs, u32 page_addr)
|
||||
{
|
||||
nandc_writel(PAGE_PROG_CMD & 0x00ff, NANDC_CHIP_CMD(cs));
|
||||
}
|
||||
|
||||
static u32 flash_read_status(u8 cs, u32 page_addr)
|
||||
{
|
||||
nandc_writel(READ_STATUS_CMD, NANDC_CHIP_CMD(cs));
|
||||
nandc_delayns(80);
|
||||
|
||||
return nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
}
|
||||
|
||||
static void flash_read_random_dataout_cmd(u8 cs, u32 col_addr)
|
||||
{
|
||||
nandc_writel(READ_DP_OUT_CMD >> 8, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(col_addr & 0x00ff, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(col_addr >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(READ_DP_OUT_CMD & 0x00ff, NANDC_CHIP_CMD(cs));
|
||||
}
|
||||
|
||||
static u32 flash_read_page_raw(u8 cs, u32 page_addr, u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
u32 ret = 0;
|
||||
u32 error_ecc_bits;
|
||||
u32 sec_per_page = nand_para.sec_per_page;
|
||||
|
||||
nandc_wait_flash_ready(cs);
|
||||
nandc_flash_cs(cs);
|
||||
flash_read_cmd(cs, page_addr);
|
||||
nandc_wait_flash_ready(cs);
|
||||
flash_read_random_dataout_cmd(cs, 0);
|
||||
nandc_wait_flash_ready(cs);
|
||||
|
||||
error_ecc_bits = nandc_xfer_data(cs, NANDC_READ, sec_per_page,
|
||||
p_data, p_spare);
|
||||
if (error_ecc_bits > 2) {
|
||||
PRINT_NANDC_E("FlashReadRawPage %x %x error_ecc_bits %d\n",
|
||||
cs, page_addr, error_ecc_bits);
|
||||
if (p_data)
|
||||
PRINT_NANDC_HEX("data:", p_data, 4, 8);
|
||||
if (p_spare)
|
||||
PRINT_NANDC_HEX("spare:", p_spare, 4, 2);
|
||||
}
|
||||
nandc_flash_de_cs(cs);
|
||||
|
||||
if (error_ecc_bits != NAND_STS_ECC_ERR) {
|
||||
if (error_ecc_bits >= (u32)nand_para.ecc_bits - 3)
|
||||
ret = NAND_STS_REFRESH;
|
||||
else
|
||||
ret = NAND_STS_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 flash_read_page(u8 cs, u32 page_addr, u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
ret = flash_read_page_raw(cs, page_addr, p_data, p_spare);
|
||||
if (ret == NAND_STS_ECC_ERR)
|
||||
ret = flash_read_page_raw(cs, page_addr, p_data, p_spare);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 flash_prog_page(u8 cs, u32 page_addr, u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
u32 status;
|
||||
u32 sec_per_page = nand_para.sec_per_page;
|
||||
|
||||
nandc_wait_flash_ready(cs);
|
||||
nandc_flash_cs(cs);
|
||||
flash_prog_first_cmd(cs, page_addr);
|
||||
nandc_xfer_data(cs, NANDC_WRITE, sec_per_page, p_data, p_spare);
|
||||
flash_prog_second_cmd(cs, page_addr);
|
||||
nandc_wait_flash_ready(cs);
|
||||
status = flash_read_status(cs, page_addr);
|
||||
nandc_flash_de_cs(cs);
|
||||
status &= 0x01;
|
||||
if (status) {
|
||||
PRINT_NANDC_I("%s addr=%x status=%x\n",
|
||||
__func__, page_addr, status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static u32 flash_erase_block(u8 cs, u32 page_addr)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
nandc_wait_flash_ready(cs);
|
||||
nandc_flash_cs(cs);
|
||||
flash_erase_cmd(cs, page_addr);
|
||||
nandc_wait_flash_ready(cs);
|
||||
status = flash_read_status(cs, page_addr);
|
||||
nandc_flash_de_cs(cs);
|
||||
status &= 0x01;
|
||||
if (status) {
|
||||
PRINT_NANDC_I("%s pageadd=%x status=%x\n",
|
||||
__func__, page_addr, status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void flash_read_spare(u8 cs, u32 page_addr, u8 *spare)
|
||||
{
|
||||
u32 col = nand_para.sec_per_page << 9;
|
||||
|
||||
nandc_writel(READ_CMD >> 8, NANDC_CHIP_CMD(cs));
|
||||
nandc_writel(col, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(col >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr & 0x00ff, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 8, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(page_addr >> 16, NANDC_CHIP_ADDR(cs));
|
||||
nandc_writel(READ_CMD & 0x00ff, NANDC_CHIP_CMD(cs));
|
||||
|
||||
nandc_wait_flash_ready(cs);
|
||||
|
||||
*spare = nandc_readl(NANDC_CHIP_DATA(cs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the 1st page's 1st spare byte of a phy_blk
|
||||
* If not FF, it's bad blk
|
||||
*/
|
||||
static s32 get_bad_blk_list(u16 *table, u32 die)
|
||||
{
|
||||
u16 blk;
|
||||
u32 bad_cnt, page_addr0, page_addr1, page_addr2;
|
||||
u32 blk_per_die;
|
||||
u8 bad_flag0, bad_flag1, bad_flag2;
|
||||
|
||||
bad_cnt = 0;
|
||||
blk_per_die = nand_para.plane_per_die * nand_para.blk_per_plane;
|
||||
for (blk = 0; blk < blk_per_die; blk++) {
|
||||
bad_flag0 = 0xFF;
|
||||
bad_flag1 = 0xFF;
|
||||
bad_flag2 = 0xFF;
|
||||
page_addr0 = (blk + blk_per_die * die) *
|
||||
nand_para.page_per_blk + 0;
|
||||
page_addr1 = page_addr0 + 1;
|
||||
page_addr2 = page_addr0 + nand_para.page_per_blk - 1;
|
||||
flash_read_spare(die, page_addr0, &bad_flag0);
|
||||
flash_read_spare(die, page_addr1, &bad_flag1);
|
||||
flash_read_spare(die, page_addr2, &bad_flag2);
|
||||
if (bad_flag0 != 0xFF ||
|
||||
bad_flag1 != 0xFF ||
|
||||
bad_flag2 != 0xFF) {
|
||||
table[bad_cnt++] = blk;
|
||||
PRINT_NANDC_E("die[%d], bad_blk[%d]\n", die, blk);
|
||||
}
|
||||
}
|
||||
return bad_cnt;
|
||||
}
|
||||
|
||||
#if FLASH_STRESS_TEST_EN
|
||||
|
||||
#define FLASH_PAGE_SIZE 2048
|
||||
#define FLASH_SPARE_SIZE 8
|
||||
|
||||
static u16 bad_blk_list[1024];
|
||||
static u32 pwrite[FLASH_PAGE_SIZE / 4];
|
||||
static u32 pread[FLASH_PAGE_SIZE / 4];
|
||||
static u32 pspare_write[FLASH_SPARE_SIZE / 4];
|
||||
static u32 pspare_read[FLASH_SPARE_SIZE / 4];
|
||||
static u32 bad_blk_num;
|
||||
static u32 bad_page_num;
|
||||
|
||||
static void flash_test(void)
|
||||
{
|
||||
u32 i, blk, page, bad_cnt, page_addr;
|
||||
int ret;
|
||||
u32 pages_num = 64;
|
||||
u32 blk_addr = 64;
|
||||
u32 is_bad_blk = 0;
|
||||
|
||||
PRINT_NANDC_E("%s\n", __func__);
|
||||
bad_blk_num = 0;
|
||||
bad_page_num = 0;
|
||||
bad_cnt = get_bad_blk_list(bad_blk_list, 0);
|
||||
|
||||
for (blk = 0; blk < 1024; blk++) {
|
||||
for (i = 0; i < bad_cnt; i++) {
|
||||
if (bad_blk_list[i] == blk)
|
||||
break;
|
||||
}
|
||||
if (i < bad_cnt)
|
||||
continue;
|
||||
is_bad_blk = 0;
|
||||
PRINT_NANDC_E("Flash prog block: %x\n", blk);
|
||||
flash_erase_block(0, blk * blk_addr);
|
||||
for (page = 0; page < pages_num; page++) {
|
||||
page_addr = blk * blk_addr + page;
|
||||
for (i = 0; i < 512; i++)
|
||||
pwrite[i] = (page_addr << 16) + i;
|
||||
pspare_write[0] = pwrite[0] + 0x5AF0;
|
||||
pspare_write[1] = pspare_write[0] + 1;
|
||||
flash_prog_page(0, page_addr, pwrite, pspare_write);
|
||||
memset(pread, 0, 2048);
|
||||
memset(pspare_read, 0, 8);
|
||||
ret = flash_read_page(0, page_addr, pread,
|
||||
pspare_read);
|
||||
if (ret != NAND_STS_OK)
|
||||
is_bad_blk = 1;
|
||||
for (i = 0; i < 512; i++) {
|
||||
if (pwrite[i] != pread[i]) {
|
||||
is_bad_blk = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (pspare_write[i] != pspare_read[i]) {
|
||||
is_bad_blk = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_bad_blk) {
|
||||
bad_page_num++;
|
||||
PRINT_NANDC_E("ERR:page %x, ret= %x\n",
|
||||
page_addr,
|
||||
ret);
|
||||
PRINT_NANDC_HEX("data:", pread, 4, 8);
|
||||
PRINT_NANDC_HEX("spare:", pspare_read, 4, 2);
|
||||
}
|
||||
}
|
||||
flash_erase_block(0, blk * blk_addr);
|
||||
if (is_bad_blk)
|
||||
bad_blk_num++;
|
||||
}
|
||||
PRINT_NANDC_E("bad_blk_num = %d, bad_page_num = %d\n",
|
||||
bad_blk_num, bad_page_num);
|
||||
|
||||
PRINT_NANDC_E("Flash Test Finish!!!\n");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void flash_die_info_init(void)
|
||||
{
|
||||
u32 cs;
|
||||
|
||||
g_nand_max_die = 0;
|
||||
for (cs = 0; cs < MAX_FLASH_NUM; cs++) {
|
||||
if (nand_para.nand_id[1] == id_byte[cs][1]) {
|
||||
die_cs_index[g_nand_max_die] = cs;
|
||||
g_nand_max_die++;
|
||||
}
|
||||
}
|
||||
g_totle_block = g_nand_max_die * nand_para.plane_per_die *
|
||||
nand_para.blk_per_plane;
|
||||
}
|
||||
|
||||
static void nandc_flash_print_info(void)
|
||||
{
|
||||
PRINT_NANDC_I("No.0 FLASH ID: %x %x %x %x %x %x\n",
|
||||
nand_para.nand_id[0],
|
||||
nand_para.nand_id[1],
|
||||
nand_para.nand_id[2],
|
||||
nand_para.nand_id[3],
|
||||
nand_para.nand_id[4],
|
||||
nand_para.nand_id[5]);
|
||||
PRINT_NANDC_I("die_per_chip: %x\n", nand_para.die_per_chip);
|
||||
PRINT_NANDC_I("sec_per_page: %x\n", nand_para.sec_per_page);
|
||||
PRINT_NANDC_I("page_per_blk: %x\n", nand_para.page_per_blk);
|
||||
PRINT_NANDC_I("cell: %x\n", nand_para.cell);
|
||||
PRINT_NANDC_I("plane_per_die: %x\n", nand_para.plane_per_die);
|
||||
PRINT_NANDC_I("blk_per_plane: %x\n", nand_para.blk_per_plane);
|
||||
PRINT_NANDC_I("TotleBlock: %x\n", g_totle_block);
|
||||
PRINT_NANDC_I("die gap: %x\n", nand_para.die_gap);
|
||||
PRINT_NANDC_I("lsb_mode: %x\n", nand_para.lsb_mode);
|
||||
PRINT_NANDC_I("read_retry_mode: %x\n", nand_para.read_retry_mode);
|
||||
PRINT_NANDC_I("ecc_bits: %x\n", nand_para.ecc_bits);
|
||||
PRINT_NANDC_I("Use ecc_bits: %x\n", g_nand_flash_ecc_bits);
|
||||
PRINT_NANDC_I("access_freq: %x\n", nand_para.access_freq);
|
||||
PRINT_NANDC_I("opt_mode: %x\n", nand_para.opt_mode);
|
||||
|
||||
PRINT_NANDC_I("Cache read enable: %x\n",
|
||||
nand_para.operation_opt & NAND_CACHE_READ_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("Cache random read enable: %x\n",
|
||||
nand_para.operation_opt &
|
||||
NAND_CACHE_RANDOM_READ_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("Cache prog enable: %x\n",
|
||||
nand_para.operation_opt & NAND_CACHE_PROG_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("multi read enable: %x\n",
|
||||
nand_para.operation_opt & NAND_MULTI_READ_EN ? 1 : 0);
|
||||
|
||||
PRINT_NANDC_I("multi prog enable: %x\n",
|
||||
nand_para.operation_opt & NAND_MULTI_PROG_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("interleave enable: %x\n",
|
||||
nand_para.operation_opt & NAND_INTERLEAVE_EN ? 1 : 0);
|
||||
|
||||
PRINT_NANDC_I("read retry enable: %x\n",
|
||||
nand_para.operation_opt & NAND_READ_RETRY_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("randomizer enable: %x\n",
|
||||
nand_para.operation_opt & NAND_RANDOMIZER_EN ? 1 : 0);
|
||||
|
||||
PRINT_NANDC_I("SDR enable: %x\n",
|
||||
nand_para.operation_opt & NAND_SDR_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("ONFI enable: %x\n",
|
||||
nand_para.operation_opt & NAND_ONFI_EN ? 1 : 0);
|
||||
PRINT_NANDC_I("TOGGLE enable: %x\n",
|
||||
nand_para.operation_opt & NAND_TOGGLE_EN ? 1 : 0);
|
||||
|
||||
PRINT_NANDC_I("g_nand_idb_res_blk_num: %x\n", g_nand_idb_res_blk_num);
|
||||
}
|
||||
|
||||
static void ftl_flash_init(void)
|
||||
{
|
||||
/* para init */
|
||||
g_nand_phy_info.nand_type = nand_para.cell;
|
||||
g_nand_phy_info.die_num = nand_para.die_per_chip;
|
||||
g_nand_phy_info.plane_per_die = nand_para.plane_per_die;
|
||||
g_nand_phy_info.blk_per_plane = nand_para.blk_per_plane;
|
||||
g_nand_phy_info.page_per_blk = nand_para.page_per_blk;
|
||||
g_nand_phy_info.page_per_slc_blk = nand_para.page_per_blk /
|
||||
nand_para.cell;
|
||||
g_nand_phy_info.byte_per_sec = 512;
|
||||
g_nand_phy_info.sec_per_page = nand_para.sec_per_page;
|
||||
g_nand_phy_info.sec_per_blk = nand_para.sec_per_page *
|
||||
nand_para.page_per_blk;
|
||||
g_nand_phy_info.reserved_blk = 8;
|
||||
g_nand_phy_info.blk_per_die = nand_para.plane_per_die *
|
||||
nand_para.blk_per_plane;
|
||||
g_nand_phy_info.ecc_bits = nand_para.ecc_bits;
|
||||
|
||||
/* driver register */
|
||||
g_nand_ops.get_bad_blk_list = get_bad_blk_list;
|
||||
g_nand_ops.erase_blk = flash_erase_block;
|
||||
g_nand_ops.prog_page = flash_prog_page;
|
||||
g_nand_ops.read_page = flash_read_page;
|
||||
}
|
||||
|
||||
u32 nandc_flash_init(void __iomem *nandc_addr)
|
||||
{
|
||||
u32 cs;
|
||||
|
||||
PRINT_NANDC_I("...%s enter...\n", __func__);
|
||||
g_nand_idb_res_blk_num = MAX_IDB_RESERVED_BLOCK;
|
||||
|
||||
nandc_init(nandc_addr);
|
||||
|
||||
for (cs = 0; cs < MAX_FLASH_NUM; cs++) {
|
||||
flash_read_id_raw(cs, id_byte[cs]);
|
||||
if (cs == 0) {
|
||||
if (id_byte[0][0] == 0xFF ||
|
||||
id_byte[0][0] == 0 ||
|
||||
id_byte[0][1] == 0xFF)
|
||||
return FTL_NO_FLASH;
|
||||
if (id_byte[0][1] != 0xF1 &&
|
||||
id_byte[0][1] != 0xDA &&
|
||||
id_byte[0][1] != 0xD1 &&
|
||||
id_byte[0][1] != 0x95 &&
|
||||
id_byte[0][1] != 0xDC)
|
||||
|
||||
return FTL_UNSUPPORTED_FLASH;
|
||||
}
|
||||
}
|
||||
nand_para.nand_id[1] = id_byte[0][1];
|
||||
if (id_byte[0][1] == 0xDA) {
|
||||
nand_para.plane_per_die = 2;
|
||||
nand_para.nand_id[1] = 0xDA;
|
||||
} else if (id_byte[0][1] == 0xDC) {
|
||||
nand_para.nand_id[1] = 0xDC;
|
||||
if (id_byte[0][0] == 0x2C && id_byte[0][3] == 0xA6) {
|
||||
nand_para.plane_per_die = 2;
|
||||
nand_para.sec_per_page = 8;
|
||||
} else {
|
||||
nand_para.plane_per_die = 2;
|
||||
nand_para.blk_per_plane = 2048;
|
||||
}
|
||||
}
|
||||
flash_die_info_init();
|
||||
flash_bch_sel(nand_para.ecc_bits);
|
||||
nandc_flash_print_info();
|
||||
/* flash_print_info(); */
|
||||
ftl_flash_init();
|
||||
|
||||
#if FLASH_STRESS_TEST_EN
|
||||
flash_test();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nandc_flash_get_id(u8 cs, void *buf)
|
||||
{
|
||||
memcpy(buf, id_byte[cs], 5);
|
||||
}
|
||||
|
||||
u32 nandc_flash_deinit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_H
|
||||
#define __FLASH_H
|
||||
|
||||
#include "typedef.h"
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(nr) (1 << (nr))
|
||||
#endif
|
||||
|
||||
#define MAX_FLASH_NUM 2
|
||||
#define MAX_IDB_RESERVED_BLOCK 12
|
||||
|
||||
#define NAND_CACHE_READ_EN BIT(0)
|
||||
#define NAND_CACHE_RANDOM_READ_EN BIT(1)
|
||||
#define NAND_CACHE_PROG_EN BIT(2)
|
||||
#define NAND_MULTI_READ_EN BIT(3)
|
||||
|
||||
#define NAND_MULTI_PROG_EN BIT(4)
|
||||
#define NAND_INTERLEAVE_EN BIT(5)
|
||||
#define NAND_READ_RETRY_EN BIT(6)
|
||||
#define NAND_RANDOMIZER_EN BIT(7)
|
||||
|
||||
#define NAND_INTER_MODE_OFFSET (0x8)
|
||||
#define NAND_INTER_MODE_MARK (0x07)
|
||||
#define NAND_INTER_SDR_EN BIT(0)
|
||||
#define NAND_INTER_ONFI_EN BIT(1)
|
||||
#define NAND_INTER_TOGGLE_EN BIT(2)
|
||||
|
||||
#define NAND_SDR_EN BIT(8)
|
||||
#define NAND_ONFI_EN BIT(9)
|
||||
#define NAND_TOGGLE_EN BIT(10)
|
||||
#define NAND_UNIQUE_ID_EN BIT(11)
|
||||
|
||||
#define RESET_CMD 0xff
|
||||
#define READ_ID_CMD 0x90
|
||||
#define READ_STATUS_CMD 0x70
|
||||
#define PAGE_PROG_CMD 0x8010
|
||||
#define BLOCK_ERASE_CMD 0x60d0
|
||||
#define READ_CMD 0x0030
|
||||
#define READ_DP_OUT_CMD 0x05E0
|
||||
|
||||
#define SAMSUNG 0x00 /* SAMSUNG */
|
||||
#define TOSHIBA 0x01 /* TOSHIBA */
|
||||
#define HYNIX 0x02 /* HYNIX */
|
||||
#define INFINEON 0x03 /* INFINEON */
|
||||
#define MICRON 0x04 /* MICRON */
|
||||
#define RENESAS 0x05 /* RENESAS */
|
||||
#define ST 0x06 /* ST */
|
||||
#define INTEL 0x07 /* intel */
|
||||
#define Sandisk 0x08 /* Sandisk */
|
||||
|
||||
#define RR_NONE 0x00
|
||||
#define RR_HY_1 0x01 /* hynix H27UCG8T2M */
|
||||
#define RR_HY_2 0x02 /* hynix H27UBG08U0B */
|
||||
#define RR_HY_3 0x03 /* hynix H27UCG08U0B H27UBG08U0C */
|
||||
#define RR_HY_4 0x04 /* hynix H27UCG8T2A */
|
||||
#define RR_HY_5 0x05 /* hynix H27UCG8T2E */
|
||||
#define RR_HY_6 0x06 /* hynix H27QCG8T2F5R-BCG */
|
||||
#define RR_MT_1 0x11 /* micron */
|
||||
#define RR_MT_2 0x12 /* micron L94C L95B */
|
||||
#define RR_TH_1 0x21 /* toshiba */
|
||||
#define RR_TH_2 0x22 /* toshiba */
|
||||
#define RR_TH_3 0x23 /* toshiba */
|
||||
#define RR_SS_1 0x31 /* samsung */
|
||||
#define RR_SD_1 0x41 /* Sandisk */
|
||||
#define RR_SD_2 0x42 /* Sandisk */
|
||||
#define RR_SD_3 0x43 /* Sandisk */
|
||||
#define RR_SD_4 0x44 /* Sandisk */
|
||||
|
||||
/* 0 1 2 3 4 5 6 7 8 9 slc */
|
||||
#define LSB_0 0
|
||||
/* 0 1 2 3 6 7 A B E F hynix, micron 74A */
|
||||
#define LSB_1 1
|
||||
/* 0 1 3 5 7 9 B D toshiba samsung sandisk */
|
||||
#define LSB_2 2
|
||||
/* 0 1 2 3 4 5 8 9 C D 10 11 micron 84A */
|
||||
#define LSB_3 3
|
||||
/* 0 1 2 3 4 5 7 8 A B E F micron L95B */
|
||||
#define LSB_4 4
|
||||
/* 0 1 2 3 4 5 8 9 14 15 20 21 26 27 micron B74A TLC */
|
||||
#define LSB_6 6
|
||||
/* 0 3 6 9 C F 12 15 18 15 1B 1E 21 24 K9ABGD8U0C TLC */
|
||||
#define LSB_7 7
|
||||
|
||||
/* BadBlockFlagMode */
|
||||
/* first spare @ first page of each blocks */
|
||||
#define BBF_1 1
|
||||
/* first spare @ last page of each blocks */
|
||||
#define BBF_2 2
|
||||
/* first spare @ first and last page of each blocks */
|
||||
#define BBF_11 3
|
||||
/* sandisk 15nm flash prog first page without data and check status */
|
||||
#define BBF_3 4
|
||||
|
||||
#define MPM_0 0 /* block 0 ~ 1 */
|
||||
#define MPM_1 1 /* block 0 ~ 2048... */
|
||||
|
||||
struct NAND_PARA_INFO_T {
|
||||
u8 id_bytes;
|
||||
u8 nand_id[6];
|
||||
u8 vendor;
|
||||
u8 die_per_chip;
|
||||
u8 sec_per_page;
|
||||
u16 page_per_blk;
|
||||
u8 cell; /* 1 slc , 2 mlc , 3 tlc */
|
||||
u8 plane_per_die;
|
||||
u16 blk_per_plane;
|
||||
u16 operation_opt;
|
||||
u8 lsb_mode;
|
||||
u8 read_retry_mode;
|
||||
u8 ecc_bits;
|
||||
u8 access_freq;
|
||||
u8 opt_mode;
|
||||
u8 die_gap;
|
||||
u8 bad_block_mode;
|
||||
u8 multi_plane_mode;
|
||||
u8 reversd2[6]; /* 32 bytes */
|
||||
};
|
||||
|
||||
struct FLASH_INFO_T {
|
||||
u16 block_size;
|
||||
u8 ecc_bits;
|
||||
u32 flash_size;
|
||||
u16 page_size;
|
||||
u8 access_time;
|
||||
u8 manufacturer_name;
|
||||
u8 flash_mask;
|
||||
};
|
||||
|
||||
extern struct nand_phy_info g_nand_phy_info;
|
||||
extern struct nand_ops g_nand_ops;
|
||||
extern void __iomem *nandc_base;
|
||||
|
||||
void nandc_flash_get_id(u8 cs, void *buf);
|
||||
void nandc_flash_reset(u8 chip_sel);
|
||||
u32 nandc_flash_init(void __iomem *nandc_addr);
|
||||
u32 nandc_flash_deinit(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_COM_H
|
||||
#define __FLASH_COM_H
|
||||
|
||||
#include "typedef.h"
|
||||
|
||||
#define NAND_ERROR INVALID_UINT32
|
||||
#define NAND_OK 0
|
||||
|
||||
#define NAND_STS_OK 0 /* bit 0 ecc error or ok */
|
||||
#define NAND_STS_REFRESH 256 /* need refresh */
|
||||
#define NAND_STS_EMPTY 512 /* page is not proged */
|
||||
#define NAND_STS_ECC_ERR NAND_ERROR
|
||||
|
||||
#define FULL_SLC 0
|
||||
#define SLC 1
|
||||
|
||||
#define NAND_FLASH_MLC_PAGE_TAG 0xFFFF
|
||||
#define MAX_FLASH_PAGE_SIZE 0x1000 /* 4KB */
|
||||
|
||||
#define PAGE_ADDR_BITS 0
|
||||
#define PAGE_ADDR_MASK ((1u << 11) - 1)
|
||||
#define BLOCK_ADDR_BITS 11
|
||||
#define BLOCK_ADDR_MASK ((1u << 14) - 1)
|
||||
#define DIE_ADDR_BITS 25
|
||||
#define DIE_ADDR_MASK ((1u << 3) - 1)
|
||||
#define FLAG_ADDR_BITS 28
|
||||
#define FLAG_ADDR_MASK ((1u << 4) - 1)
|
||||
#define PHY_BLK_DIE_ADDR_BITS 14
|
||||
|
||||
struct nand_req {
|
||||
u32 status;
|
||||
u32 page_addr; /* 31:28 flag, 27:25: die, 24:11 block, 10:0 page */
|
||||
u32 *p_data;
|
||||
u32 *p_spare;
|
||||
u32 lpa;
|
||||
};
|
||||
|
||||
struct nand_phy_info {
|
||||
u16 nand_type; /* SLC,MLC,TLC */
|
||||
u16 die_num; /* number of LUNs */
|
||||
u16 plane_per_die;
|
||||
u16 blk_per_plane;
|
||||
u16 blk_per_die;
|
||||
u16 page_per_blk; /* in MLC mode */
|
||||
u16 page_per_slc_blk; /* in SLC mode */
|
||||
u16 sec_per_page; /* physical page data size */
|
||||
u16 sec_per_blk; /* physical page data size */
|
||||
u16 byte_per_sec; /* size of logical sectors */
|
||||
u16 reserved_blk; /* reserved for boot loader in die 0*/
|
||||
u8 ecc_bits;
|
||||
};
|
||||
|
||||
struct nand_ops {
|
||||
s32 (*get_bad_blk_list)(u16 *table, u32 die);
|
||||
u32 (*erase_blk)(u8 cs, u32 page_addr);
|
||||
u32 (*prog_page)(u8 cs, u32 page_addr, u32 *data, u32 *spare);
|
||||
u32 (*read_page)(u8 cs, u32 page_addr, u32 *data, u32 *spare);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "flash_com.h"
|
||||
#include "nandc.h"
|
||||
#include "typedef.h"
|
||||
|
||||
#define CPU_DELAY_NS(n) ndelay(n)
|
||||
|
||||
#define NANDC_MASTER_EN
|
||||
|
||||
void __iomem *nandc_base;
|
||||
|
||||
static u32 g_nandc_ecc_bits;
|
||||
#ifdef NANDC_MASTER_EN
|
||||
static struct MASTER_INFO_T master;
|
||||
static u32 *g_master_temp_buf;
|
||||
#endif
|
||||
|
||||
void nandc_init(void __iomem *nandc_addr)
|
||||
{
|
||||
union FM_CTL_T ctl_reg;
|
||||
|
||||
nandc_base = nandc_addr;
|
||||
|
||||
ctl_reg.d32 = 0;
|
||||
ctl_reg.V6.wp = 1;
|
||||
nandc_writel(ctl_reg.d32, NANDC_FMCTL);
|
||||
nandc_writel(0, NANDC_RANDMZ_CFG);
|
||||
nandc_time_cfg(40);
|
||||
|
||||
#ifdef NANDC_MASTER_EN
|
||||
if (!g_master_temp_buf)
|
||||
g_master_temp_buf = (u32 *)ftl_malloc(MAX_FLASH_PAGE_SIZE +
|
||||
MAX_FLASH_PAGE_SIZE / 8);
|
||||
master.page_buf = &g_master_temp_buf[0];
|
||||
master.spare_buf = &g_master_temp_buf[MAX_FLASH_PAGE_SIZE / 4];
|
||||
master.mapped = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void nandc_flash_cs(u8 chip_sel)
|
||||
{
|
||||
union FM_CTL_T tmp;
|
||||
|
||||
tmp.d32 = nandc_readl(NANDC_FMCTL);
|
||||
tmp.V6.cs = 0x01 << chip_sel;
|
||||
nandc_writel(tmp.d32, NANDC_FMCTL);
|
||||
}
|
||||
|
||||
void nandc_flash_de_cs(u8 chip_sel)
|
||||
{
|
||||
union FM_CTL_T tmp;
|
||||
|
||||
tmp.d32 = nandc_readl(NANDC_FMCTL);
|
||||
tmp.V6.cs = 0;
|
||||
tmp.V6.flash_abort_clear = 0;
|
||||
nandc_writel(tmp.d32, NANDC_FMCTL);
|
||||
}
|
||||
|
||||
u32 nandc_delayns(u32 count)
|
||||
{
|
||||
CPU_DELAY_NS(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nandc_wait_flash_ready(u8 chip_sel)
|
||||
{
|
||||
union FM_CTL_T tmp;
|
||||
u32 status;
|
||||
u32 i;
|
||||
|
||||
status = 0;
|
||||
for (i = 0; i < 100000; i++) {
|
||||
nandc_delayns(100);
|
||||
tmp.d32 = nandc_readl(NANDC_FMCTL);
|
||||
if (tmp.V6.rdy != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= 100000)
|
||||
status = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
void nandc_randmz_sel(u8 chip_sel, u32 randmz_seed)
|
||||
{
|
||||
nandc_writel(randmz_seed, NANDC_RANDMZ_CFG);
|
||||
}
|
||||
|
||||
void nandc_time_cfg(u32 ns)
|
||||
{
|
||||
if (ns < 36)
|
||||
nandc_writel(0x1061, NANDC_FMWAIT);
|
||||
else if (ns >= 100)
|
||||
nandc_writel(0x2082, NANDC_FMWAIT);
|
||||
else
|
||||
nandc_writel(0x1081, NANDC_FMWAIT);
|
||||
}
|
||||
|
||||
void nandc_bch_sel(u8 bits)
|
||||
{
|
||||
union BCH_CTL_T tmp;
|
||||
union FL_CTL_T fl_reg;
|
||||
|
||||
fl_reg.d32 = 0;
|
||||
fl_reg.V6.rst = 1;
|
||||
nandc_writel(fl_reg.d32, NANDC_FLCTL);
|
||||
g_nandc_ecc_bits = bits;
|
||||
tmp.d32 = 0;
|
||||
tmp.V6.addr = 0x10;
|
||||
tmp.V6.bch_mode1 = 0;
|
||||
if (bits == 16) {
|
||||
tmp.V6.bch_mode = 0;
|
||||
} else if (bits == 24) {
|
||||
tmp.V6.bch_mode = 1;
|
||||
} else {
|
||||
tmp.V6.bch_mode1 = 1;
|
||||
tmp.V6.bch_mode = 1;
|
||||
if (bits == 40)
|
||||
tmp.V6.bch_mode = 0;
|
||||
}
|
||||
tmp.V6.rst = 1;
|
||||
nandc_writel(tmp.d32, NANDC_BCHCTL);
|
||||
}
|
||||
|
||||
static void nandc_xfer_start(u8 chip_sel,
|
||||
u8 dir,
|
||||
u8 sector_count,
|
||||
u8 st_buf,
|
||||
u32 *p_data,
|
||||
u32 *p_spare)
|
||||
{
|
||||
union BCH_CTL_T bch_reg;
|
||||
union FL_CTL_T fl_reg;
|
||||
u8 bus_mode = (p_spare || p_data);
|
||||
u32 i;
|
||||
union MTRANS_CFG_T master_reg;
|
||||
u16 *p_spare_tmp = (u16 *)p_spare;
|
||||
u64 vir_addr;
|
||||
|
||||
fl_reg.d32 = 0;
|
||||
bch_reg.d32 = nandc_readl(NANDC_BCHCTL);
|
||||
bch_reg.V6.addr = 0x10;
|
||||
bch_reg.V6.power_down = 0;
|
||||
bch_reg.V6.region = chip_sel;
|
||||
|
||||
fl_reg.V6.rdn = dir;
|
||||
fl_reg.V6.dma = 1;
|
||||
fl_reg.V6.tr_count = 1;
|
||||
fl_reg.V6.async_tog_mix = 1;
|
||||
fl_reg.V6.cor_en = 1;
|
||||
fl_reg.V6.st_addr = st_buf / 2;
|
||||
|
||||
master_reg.d32 = nandc_readl(NANDC_MTRANS_CFG);
|
||||
master_reg.V6.bus_mode = 0;
|
||||
#ifdef NANDC_MASTER_EN
|
||||
if (bus_mode != 0 && dir != 0) {
|
||||
u32 spare_sz = 64;
|
||||
|
||||
for (i = 0; i < sector_count / 2; i++) {
|
||||
if (p_spare) {
|
||||
master.spare_buf[i * spare_sz / 4] =
|
||||
(p_spare_tmp[0]) | ((u32)p_spare_tmp[1] << 16);
|
||||
p_spare_tmp += 2;
|
||||
} else{
|
||||
master.spare_buf[i * spare_sz / 4] =
|
||||
0xffffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
fl_reg.V6.page_num = (sector_count + 1) / 2;
|
||||
master.page_vir = (u32 *)((p_data == (u32 *)NULL) ?
|
||||
master.page_buf :
|
||||
(u32 *)p_data);
|
||||
master.spare_vir = (u32 *)master.spare_buf;
|
||||
|
||||
master.page_phy = (u32)((unsigned long)master.page_vir);
|
||||
master.spare_phy = (u32)((unsigned long)master.spare_vir);
|
||||
vir_addr = ((unsigned long)master.page_phy);
|
||||
flush_dcache_range(vir_addr & (~0x3FuL),
|
||||
((vir_addr + 63) & (~0x3FuL)) +
|
||||
fl_reg.V6.page_num * 1024);
|
||||
vir_addr = ((unsigned long)master.spare_phy);
|
||||
flush_dcache_range(vir_addr & (~0x3FuL),
|
||||
((vir_addr + 63) & (~0x3FuL)) +
|
||||
fl_reg.V6.page_num * 128);
|
||||
master.mapped = 1;
|
||||
nandc_writel(master.page_phy, NANDC_MTRANS_SADDR0);
|
||||
nandc_writel(master.spare_phy, NANDC_MTRANS_SADDR1);
|
||||
master_reg.d32 = 0;
|
||||
master_reg.V6.incr_num = 16;
|
||||
master_reg.V6.burst = 7;
|
||||
if ((((unsigned long)p_data) & 0x03) == 0)
|
||||
master_reg.V6.hsize = 2;
|
||||
master_reg.V6.bus_mode = 1;
|
||||
master_reg.V6.ahb_wr = !dir;
|
||||
master_reg.V6.ahb_wr_st = 1;
|
||||
#endif
|
||||
|
||||
nandc_writel(master_reg.d32, NANDC_MTRANS_CFG);
|
||||
nandc_writel(bch_reg.d32, NANDC_BCHCTL);
|
||||
nandc_writel(fl_reg.d32, NANDC_FLCTL);
|
||||
fl_reg.V6.start = 1;
|
||||
nandc_writel(fl_reg.d32, NANDC_FLCTL);
|
||||
}
|
||||
|
||||
static void nandc_xfer_comp(u8 chip_sel)
|
||||
{
|
||||
union FL_CTL_T fl_reg;
|
||||
union MTRANS_CFG_T master_reg;
|
||||
|
||||
master_reg.d32 = nandc_readl(NANDC_MTRANS_CFG);
|
||||
if (master_reg.V6.bus_mode != 0) {
|
||||
union MTRANS_STAT_T stat_reg;
|
||||
|
||||
if (master_reg.V6.ahb_wr != 0) {
|
||||
do {
|
||||
fl_reg.d32 = nandc_readl(NANDC_FLCTL);
|
||||
stat_reg.d32 = nandc_readl(NANDC_MTRANS_STAT);
|
||||
} while (stat_reg.V6.mtrans_cnt < fl_reg.V6.page_num);
|
||||
} else {
|
||||
do {
|
||||
fl_reg.d32 = nandc_readl(NANDC_FLCTL);
|
||||
} while (fl_reg.V6.tr_rdy == 0);
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
fl_reg.d32 = nandc_readl(NANDC_FLCTL);
|
||||
} while ((fl_reg.V6.tr_rdy == 0));
|
||||
}
|
||||
}
|
||||
|
||||
u32 nandc_xfer_data(u8 chip_sel, u8 dir, u8 sector_count,
|
||||
u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
u32 status = NAND_STS_OK;
|
||||
u32 i;
|
||||
u32 spare[16];
|
||||
union BCH_ST_T bch_st_reg;
|
||||
|
||||
if (dir == NANDC_WRITE && !p_spare) {
|
||||
p_spare = (u32 *)spare;
|
||||
memset(spare, 0xFF, sizeof(spare));
|
||||
}
|
||||
nandc_xfer_start(chip_sel, dir, sector_count, 0, p_data, p_spare);
|
||||
nandc_xfer_comp(chip_sel);
|
||||
if (dir == NANDC_READ) {
|
||||
if (p_spare) {
|
||||
u32 spare_sz = 64;
|
||||
u32 temp_data;
|
||||
u8 *p_spare_temp = (u8 *)p_spare;
|
||||
|
||||
for (i = 0; i < sector_count / 2; i++) {
|
||||
temp_data = master.spare_buf[i * spare_sz / 4];
|
||||
*p_spare_temp++ = (u8)temp_data;
|
||||
*p_spare_temp++ = (u8)(temp_data >> 8);
|
||||
*p_spare_temp++ = (u8)(temp_data >> 16);
|
||||
*p_spare_temp++ = (u8)(temp_data >> 24);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < sector_count / 4 ; i++) {
|
||||
bch_st_reg.d32 = nandc_readl(NANDC_BCHST(i));
|
||||
if (bch_st_reg.V6.fail0 || bch_st_reg.V6.fail1) {
|
||||
status = NAND_STS_ECC_ERR;
|
||||
} else {
|
||||
u32 tmp = 0;
|
||||
|
||||
tmp =
|
||||
max(bch_st_reg.V6.err_bits0 |
|
||||
((u32)bch_st_reg.V6.err_bits0_5 << 5),
|
||||
bch_st_reg.V6.err_bits1 |
|
||||
((u32)bch_st_reg.V6.err_bits1_5 << 5));
|
||||
status = max(tmp, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
nandc_writel(0, NANDC_MTRANS_CFG);
|
||||
return status;
|
||||
}
|
||||
|
||||
void nandc_clean_irq(void)
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __NAND_H
|
||||
#define __NAND_H
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define nandc_writel(v, offs) writel((v), (offs) + nandc_base)
|
||||
#define nandc_readl(offs) readl((offs) + nandc_base)
|
||||
|
||||
#define NANDC_READ 0
|
||||
#define NANDC_WRITE 1
|
||||
|
||||
/* INT ID */
|
||||
enum NANDC_IRQ_NUM_T {
|
||||
NC_IRQ_DMA = 0,
|
||||
NC_IRQ_FRDY,
|
||||
NC_IRQ_BCHERR,
|
||||
NC_IRQ_BCHFAIL,
|
||||
NC_IRQ_LLP
|
||||
};
|
||||
|
||||
union FM_CTL_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned cs : 8; /* bits[0:7] */
|
||||
unsigned wp : 1; /* bits[8] */
|
||||
unsigned rdy : 1; /* bits[9] */
|
||||
unsigned fifo_empty : 1; /* bits[10] */
|
||||
unsigned reserved11 : 1; /* bits[11] */
|
||||
unsigned dwidth : 1; /* bits[12] */
|
||||
unsigned tm : 1; /* bits[13] */
|
||||
unsigned onficlk_en : 1; /* bits[14] */
|
||||
unsigned toggle_en : 1; /* bits[15] */
|
||||
unsigned flash_abort_en : 1; /* bits[16] */
|
||||
unsigned flash_abort_clear : 1; /* bits[17] */
|
||||
unsigned reserved18_23 : 6; /* bits[18:23] */
|
||||
unsigned read_delay : 3; /* bits[24:26] */
|
||||
unsigned reserved27_31 : 5; /* bits[27:31] */
|
||||
} V6;
|
||||
};
|
||||
|
||||
union FM_WAIT_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned csrw : 5;
|
||||
unsigned rwpw : 6;
|
||||
unsigned rdy : 1;
|
||||
unsigned rwcs : 6;
|
||||
unsigned reserved18_23 : 6;
|
||||
unsigned fmw_dly : 6;
|
||||
unsigned fmw_dly_en : 1;
|
||||
unsigned reserved31_31 : 1;
|
||||
} V6;
|
||||
};
|
||||
|
||||
union FL_CTL_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned rst : 1;
|
||||
unsigned rdn : 1;
|
||||
unsigned start : 1;
|
||||
unsigned dma : 1;
|
||||
unsigned st_addr : 1;
|
||||
unsigned tr_count : 2;
|
||||
unsigned rdy_ignore : 1;
|
||||
/* unsigned int_clr : 1; */
|
||||
/* unsigned int_en : 1; */
|
||||
unsigned reserved8_9 : 2;
|
||||
unsigned cor_en : 1;
|
||||
unsigned lba_en : 1;
|
||||
unsigned spare_size : 7;
|
||||
unsigned reserved19 : 1;
|
||||
unsigned tr_rdy : 1;
|
||||
unsigned page_size : 1;
|
||||
unsigned page_num : 6;
|
||||
unsigned low_power : 1;
|
||||
unsigned async_tog_mix : 1;
|
||||
unsigned reserved30_31 : 2;
|
||||
} V6;
|
||||
};
|
||||
|
||||
union BCH_CTL_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned rst : 1;
|
||||
unsigned reserved : 1;
|
||||
unsigned addr_not_care : 1;
|
||||
unsigned power_down : 1;
|
||||
unsigned bch_mode : 1; /* 0-16bit/1KB, 1-24bit/1KB */
|
||||
unsigned region : 3;
|
||||
unsigned addr : 8;
|
||||
unsigned bchpage : 1;
|
||||
unsigned reserved17 : 1;
|
||||
unsigned bch_mode1 : 1;
|
||||
unsigned thres : 8;
|
||||
unsigned reserved27_31 : 5;
|
||||
} V6;
|
||||
};
|
||||
|
||||
union BCH_ST_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned errf0 : 1;
|
||||
unsigned done0 : 1;
|
||||
unsigned fail0 : 1;
|
||||
unsigned err_bits0 : 5;
|
||||
unsigned err_bits_low0 : 5;
|
||||
unsigned errf1 : 1;
|
||||
unsigned done1 : 1;
|
||||
unsigned fail1 : 1;
|
||||
unsigned err_bits1 : 5;
|
||||
unsigned err_bits_low1 : 5;
|
||||
unsigned rdy : 1;
|
||||
/* unsigned cnt : 1; */
|
||||
unsigned err_bits0_5 : 1;
|
||||
unsigned err_bits_low0_5 : 1;
|
||||
unsigned err_bits1_5 : 1;
|
||||
unsigned err_bits_low1_5 : 1;
|
||||
unsigned reserved31_31 : 1;
|
||||
} V6;
|
||||
};
|
||||
|
||||
union MTRANS_CFG_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned ahb_wr_st : 1;
|
||||
unsigned ahb_wr : 1;
|
||||
unsigned bus_mode : 1;
|
||||
unsigned hsize : 3;
|
||||
unsigned burst : 3;
|
||||
unsigned incr_num : 5;
|
||||
unsigned fl_pwd : 1;
|
||||
unsigned ahb_rst : 1;
|
||||
unsigned reserved16_31 : 16;
|
||||
} V6;
|
||||
};
|
||||
|
||||
union MTRANS_STAT_T {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned bus_err : 16;
|
||||
unsigned mtrans_cnt : 5;
|
||||
unsigned reserved21_31 : 11;
|
||||
} V6;
|
||||
};
|
||||
|
||||
/* NANDC Registers */
|
||||
#define NANDC_FMCTL 0x0
|
||||
#define NANDC_FMWAIT 0x4
|
||||
#define NANDC_FLCTL 0x8
|
||||
#define NANDC_BCHCTL 0xc
|
||||
#define NANDC_MTRANS_CFG 0x10
|
||||
#define NANDC_MTRANS_SADDR0 0x14
|
||||
#define NANDC_MTRANS_SADDR1 0x18
|
||||
#define NANDC_MTRANS_STAT 0x1c
|
||||
#define NANDC_DLL_CTL_REG0 0x130
|
||||
#define NANDC_DLL_CTL_REG1 0x134
|
||||
#define NANDC_DLL_OBS_REG0 0x138
|
||||
#define NANDC_RANDMZ_CFG 0x150
|
||||
#define NANDC_EBI_EN 0x154
|
||||
#define NANDC_FMWAIT_SYN 0x158
|
||||
#define NANDC_MTRANS_STAT2 0x15c
|
||||
#define NANDC_NANDC_VER 0x160
|
||||
#define NANDC_LLP_CTL 0x164
|
||||
#define NANDC_LLP_STAT 0x168
|
||||
#define NANDC_INTEN 0x16c
|
||||
#define NANDC_INTCLR 0x170
|
||||
#define NANDC_INTST 0x174
|
||||
#define NANDC_SPARE0 0x200
|
||||
#define NANDC_SPARE1 0x230
|
||||
|
||||
#define NANDC_BCHST(i) ({ \
|
||||
u32 x = (i); \
|
||||
4 * x + x < 8 ? 0x20 : 0x520; })
|
||||
|
||||
#define NANDC_CHIP_DATA(id) (0x800 + (id) * 0x100)
|
||||
#define NANDC_CHIP_ADDR(id) (0x800 + (id) * 0x100 + 0x4)
|
||||
#define NANDC_CHIP_CMD(id) (0x800 + (id) * 0x100 + 0x8)
|
||||
|
||||
struct MASTER_INFO_T {
|
||||
u32 *page_buf; /* [DATA_LEN]; */
|
||||
u32 *spare_buf; /* [DATA_LEN / (1024/128)]; */
|
||||
u32 *page_vir; /* page_buf_vir_addr */
|
||||
u32 *spare_vir; /* spare_buf_vir_addr */
|
||||
u32 page_phy; /* page_buf_phy_addr */
|
||||
u32 spare_phy; /* spare_buf_phy_addr*/
|
||||
u32 mapped;
|
||||
u32 cnt;
|
||||
};
|
||||
|
||||
struct CHIP_MAP_INFO_T {
|
||||
u32 *nandc_addr;
|
||||
u32 chip_num;
|
||||
};
|
||||
|
||||
unsigned long rknandc_dma_map_single(unsigned long ptr,
|
||||
int size,
|
||||
int dir);
|
||||
void rknandc_dma_unmap_single(unsigned long ptr,
|
||||
int size,
|
||||
int dir);
|
||||
|
||||
void nandc_init(void __iomem *nandc_addr);
|
||||
void nandc_flash_cs(u8 chip_sel);
|
||||
void nandc_flash_de_cs(u8 chip_sel);
|
||||
u32 nandc_wait_flash_ready(u8 chip_sel);
|
||||
u32 nandc_delayns(u32 count);
|
||||
u32 nandc_xfer_data(u8 chip_sel,
|
||||
u8 dir,
|
||||
u8 sector_count,
|
||||
u32 *p_data,
|
||||
u32 *p_spare);
|
||||
void nandc_randmz_sel(u8 chip_sel, u32 randmz_seed);
|
||||
void nandc_bch_sel(u8 bits);
|
||||
void nandc_read_not_case_busy_en(u8 en);
|
||||
void nandc_time_cfg(u32 ns);
|
||||
void nandc_clean_irq(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __RK_SFTL_H
|
||||
#define __RK_SFTL_H
|
||||
|
||||
u32 ftl_low_format(void);
|
||||
int sftl_init(void);
|
||||
int sftl_deinit(void);
|
||||
int sftl_read(u32 index, u32 count, u8 *buf);
|
||||
int sftl_write(u32 index, u32 count, u8 *buf);
|
||||
u32 sftl_get_density(void);
|
||||
s32 sftl_gc(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
|
||||
|
|
@ -10,7 +11,7 @@
|
|||
#include "rkflash_blk.h"
|
||||
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
int rk_snor_init(struct udevice *udev)
|
||||
int rksfc_nor_init(struct udevice *udev)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
|
@ -18,7 +19,7 @@ int rk_snor_init(struct udevice *udev)
|
|||
return snor_init(p_dev);
|
||||
}
|
||||
|
||||
u32 rk_snor_get_capacity(struct udevice *udev)
|
||||
u32 rksfc_nor_get_capacity(struct udevice *udev)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
|
@ -26,20 +27,182 @@ u32 rk_snor_get_capacity(struct udevice *udev)
|
|||
return snor_get_capacity(p_dev);
|
||||
}
|
||||
|
||||
int rk_snor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data)
|
||||
int rksfc_nor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data)
|
||||
{
|
||||
u32 ret;
|
||||
u32 offset, count = 0;
|
||||
char *buf = (char *)p_data;
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
||||
if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
|
||||
sec > FLASH_VENDOR_PART_END) {
|
||||
ret = snor_read(p_dev, sec, n_sec, p_data);
|
||||
if (ret != n_sec)
|
||||
return ret;
|
||||
} else {
|
||||
memset(p_data, 0, 512 * n_sec);
|
||||
if (sec < FLASH_VENDOR_PART_START) {
|
||||
count = FLASH_VENDOR_PART_START - sec;
|
||||
buf = (char *)p_data;
|
||||
ret = snor_read(p_dev, sec, count, buf);
|
||||
if (ret != count)
|
||||
return ret;
|
||||
}
|
||||
if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
|
||||
count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
|
||||
offset = FLASH_VENDOR_PART_END - sec + 1;
|
||||
buf = (char *)p_data + offset * 512;
|
||||
ret = snor_read(p_dev,
|
||||
FLASH_VENDOR_PART_END + 1,
|
||||
count, buf);
|
||||
if (ret != count)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return n_sec;
|
||||
}
|
||||
|
||||
int rksfc_nor_write(struct udevice *udev,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
const void *p_data)
|
||||
{
|
||||
u32 ret;
|
||||
u32 offset, count = 0;
|
||||
char *buf = (char *)p_data;
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
||||
if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
|
||||
sec > FLASH_VENDOR_PART_END) {
|
||||
ret = snor_write(p_dev, sec, n_sec, p_data);
|
||||
if (ret != n_sec)
|
||||
return ret;
|
||||
} else {
|
||||
if (sec < FLASH_VENDOR_PART_START) {
|
||||
count = FLASH_VENDOR_PART_START - sec;
|
||||
buf = (char *)p_data;
|
||||
ret = snor_write(p_dev, sec, count, buf);
|
||||
if (ret != count)
|
||||
return ret;
|
||||
}
|
||||
if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
|
||||
count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
|
||||
offset = FLASH_VENDOR_PART_END - sec + 1;
|
||||
buf = (char *)p_data + offset * 512;
|
||||
ret = snor_write(p_dev,
|
||||
FLASH_VENDOR_PART_END + 1,
|
||||
count, buf);
|
||||
if (ret != count)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return n_sec;
|
||||
}
|
||||
|
||||
int rksfc_nor_vendor_read(struct blk_desc *dev_desc,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
void *p_data)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
||||
return snor_read(p_dev, sec, n_sec, p_data);
|
||||
}
|
||||
|
||||
int rk_snor_write(struct udevice *udev, u32 sec, u32 n_sec, const void *p_data)
|
||||
int rksfc_nor_vendor_write(struct blk_desc *dev_desc,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
void *p_data)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
||||
return snor_write(p_dev, sec, n_sec, p_data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKSFC_NAND
|
||||
int rksfc_nand_init(struct udevice *udev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sfc_nand_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return sftl_init();
|
||||
}
|
||||
|
||||
int rksfc_nand_read(struct udevice *udev, u32 index, u32 count, void *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sftl_read(index, count, (u8 *)buf);
|
||||
if (!ret)
|
||||
return count;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int rksfc_nand_write(struct udevice *udev,
|
||||
u32 index,
|
||||
u32 count,
|
||||
const void *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sftl_write(index, count, (u8 *)buf);
|
||||
if (!ret)
|
||||
return count;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
u32 rksfc_nand_get_density(struct udevice *udev)
|
||||
{
|
||||
return sftl_get_density();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKNANDC_NAND
|
||||
int rknand_flash_init(struct udevice *udev)
|
||||
{
|
||||
return sftl_init();
|
||||
}
|
||||
|
||||
int rknand_flash_read(struct udevice *udev, u32 index, u32 count, void *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sftl_read(index, count, (u8 *)buf);
|
||||
if (!ret)
|
||||
return count;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int rknand_flash_write(struct udevice *udev,
|
||||
u32 index,
|
||||
u32 count,
|
||||
const void *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sftl_write(index, count, (u8 *)buf);
|
||||
if (!ret)
|
||||
return count;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
u32 rknand_flash_get_density(struct udevice *udev)
|
||||
{
|
||||
return sftl_get_density();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,20 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __RKFLASH_API_H
|
||||
#define __RKFLASH_API_H
|
||||
|
||||
#define FLASH_VENDOR_PART_START 8
|
||||
#define FLASH_VENDOR_PART_END 39 /* 8 + 8 * 4 - 1 */
|
||||
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
#include "sfc_nor.h"
|
||||
#include "sfc.h"
|
||||
|
||||
int rk_snor_init(struct udevice *udev);
|
||||
u32 rk_snor_get_capacity(struct udevice *udev);
|
||||
int rk_snor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data);
|
||||
int rk_snor_write(struct udevice *udev, u32 sec, u32 n_sec, const void *p_data);
|
||||
#endif
|
||||
int rksfc_nor_init(struct udevice *udev);
|
||||
u32 rksfc_nor_get_capacity(struct udevice *udev);
|
||||
int rksfc_nor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data);
|
||||
int rksfc_nor_write(struct udevice *udev,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
const void *p_data);
|
||||
int rksfc_nor_vendor_read(struct blk_desc *dev_desc,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
void *p_data);
|
||||
int rksfc_nor_vendor_write(struct blk_desc *dev_desc,
|
||||
u32 sec,
|
||||
u32 n_sec,
|
||||
void *p_data);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKSFC_NAND
|
||||
#include "sfc_nand.h"
|
||||
#include "sfc.h"
|
||||
#include "rk_sftl.h"
|
||||
int rksfc_nand_init(struct udevice *udev);
|
||||
u32 rksfc_nand_get_density(struct udevice *udev);
|
||||
int rksfc_nand_read(struct udevice *udev, u32 index, u32 count, void *buf);
|
||||
int rksfc_nand_write(struct udevice *udev,
|
||||
u32 index,
|
||||
u32 count,
|
||||
const void *buf);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RKNANDC_NAND
|
||||
#include "flash.h"
|
||||
#include "rk_sftl.h"
|
||||
int rknand_flash_init(struct udevice *udev);
|
||||
u32 rknand_flash_get_density(struct udevice *udev);
|
||||
int rknand_flash_read(struct udevice *udev, u32 index, u32 count, void *buf);
|
||||
int rknand_flash_write(struct udevice *udev,
|
||||
u32 index,
|
||||
u32 count,
|
||||
const void *buf);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
|
@ -13,20 +13,26 @@
|
|||
#include "rkflash_blk.h"
|
||||
#include "rkflash_debug.h"
|
||||
|
||||
void ftl_free(void *buf)
|
||||
{
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
ulong rkflash_bread(struct udevice *udev, lbaint_t start,
|
||||
lbaint_t blkcnt, void *dst)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
debug("%s lba %x cnt %x", __func__, (u32)start, (u32)blkcnt);
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->read)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
return (ulong)priv->read(udev->parent, (u32)start, (u32)blkcnt, dst);
|
||||
}
|
||||
|
|
@ -38,13 +44,13 @@ ulong rkflash_bwrite(struct udevice *udev, lbaint_t start,
|
|||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->write)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
return (ulong)priv->write(udev->parent, (u32)start, (u32)blkcnt, src);
|
||||
}
|
||||
|
|
@ -56,13 +62,13 @@ ulong rkflash_berase(struct udevice *udev, lbaint_t start,
|
|||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if (!priv->erase)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
return (ulong)priv->erase(udev->parent, (u32)start, (u32)blkcnt);
|
||||
}
|
||||
|
|
@ -77,6 +83,9 @@ static int rkflash_blk_probe(struct udevice *udev)
|
|||
priv->child_dev = udev;
|
||||
if (priv->flash_con_type == FLASH_CON_TYPE_SFC)
|
||||
desc->if_type = IF_TYPE_RKSFC;
|
||||
else if (priv->flash_con_type == FLASH_CON_TYPE_NANDC)
|
||||
desc->if_type = IF_TYPE_RKNAND;
|
||||
|
||||
desc->lba = priv->density;
|
||||
desc->log2blksz = 9;
|
||||
desc->blksz = 512;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __RKFLASH_BLK_H__
|
||||
|
|
@ -32,6 +32,17 @@ struct flash_operation {
|
|||
u32 start,
|
||||
u32 blkcnt,
|
||||
const void *buffer);
|
||||
int (*flash_erase)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt);
|
||||
int (*vendor_read)(struct blk_desc *dev_desc,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
void *buffer);
|
||||
int (*vendor_write)(struct blk_desc *dev_desc,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
void *buffer);
|
||||
};
|
||||
|
||||
struct rkflash_dev {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <blk.h>
|
||||
|
|
@ -37,9 +37,9 @@ void rkflash_print_hex(char *s, void *buf, u32 width, u32 len)
|
|||
|
||||
#if (BLK_STRESS_TEST_EN)
|
||||
#define max_test_sector 64
|
||||
u8 pwrite[max_test_sector * 512];
|
||||
u8 pread[max_test_sector * 512];
|
||||
u32 *pwrite32;
|
||||
static u8 pwrite[max_test_sector * 512];
|
||||
static u8 pread[max_test_sector * 512];
|
||||
static u32 *pwrite32;
|
||||
void blk_stress_test(struct udevice *udev)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef _RKFLASH_DEBUG_H
|
||||
|
|
@ -10,6 +10,11 @@
|
|||
#include <common.h>
|
||||
#include <dm.h>
|
||||
|
||||
/*
|
||||
* Test switch
|
||||
*/
|
||||
#define BLK_STRESS_TEST_EN 0
|
||||
|
||||
/*
|
||||
* Print switch, set to 1 if needed
|
||||
* I - info
|
||||
|
|
@ -21,12 +26,11 @@
|
|||
#define PRINT_SWI_SFC_E 1
|
||||
#define PRINT_SWI_SFC_HEX 1
|
||||
|
||||
/*
|
||||
* Test switch
|
||||
*/
|
||||
#define BLK_STRESS_TEST_EN 0
|
||||
#define PRINT_SWI_NANDC_I 0
|
||||
#define PRINT_SWI_NANDC_E 1
|
||||
#define PRINT_SWI_NANDC_HEX 1
|
||||
|
||||
#if (RINT_SWI_SFC_I)
|
||||
#if (PRINT_SWI_SFC_I)
|
||||
#define PRINT_SFC_I(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_SFC_I(...)
|
||||
|
|
@ -45,6 +49,25 @@
|
|||
#define PRINT_SFC_HEX(s, buf, width, len)
|
||||
#endif
|
||||
|
||||
#if (PRINT_SWI_NANDC_I)
|
||||
#define PRINT_NANDC_I(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_NANDC_I(...)
|
||||
#endif
|
||||
|
||||
#if (PRINT_SWI_NANDC_E)
|
||||
#define PRINT_NANDC_E(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_NANDC_E(...)
|
||||
#endif
|
||||
|
||||
#if (PRINT_SWI_NANDC_HEX)
|
||||
#define PRINT_NANDC_HEX(s, buf, width, len)\
|
||||
rkflash_print_hex(s, buf, width, len)
|
||||
#else
|
||||
#define PRINT_NANDC_HEX(s, buf, width, len)
|
||||
#endif
|
||||
|
||||
void rkflash_print_hex(char *s, void *buf, u32 width, u32 len);
|
||||
void rkflash_test(struct udevice *p_dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
#include "rkflash_blk.h"
|
||||
#include "rkflash_api.h"
|
||||
|
||||
static struct flash_operation nandc_flash_op = {
|
||||
#ifdef CONFIG_RKNANDC_NAND
|
||||
FLASH_TYPE_NANDC_NAND,
|
||||
rknand_flash_init,
|
||||
rknand_flash_get_density,
|
||||
rknand_flash_read,
|
||||
rknand_flash_write,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
#else
|
||||
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
int rknand_scan_namespace(void)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_RKNAND, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
debug("%s %d %p\n", __func__, __LINE__, dev);
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rknand_blk_bind(struct udevice *udev)
|
||||
{
|
||||
struct udevice *bdev;
|
||||
int ret;
|
||||
|
||||
ret = blk_create_devicef(udev, "rkflash_blk", "blk",
|
||||
IF_TYPE_RKNAND,
|
||||
0, 512, 0, &bdev);
|
||||
if (ret) {
|
||||
debug("Cannot create block device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_nand_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(dev);
|
||||
|
||||
priv->ioaddr = dev_read_addr_ptr(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_nand_probe(struct udevice *udev)
|
||||
{
|
||||
int ret;
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
|
||||
debug("%s %d %p ndev = %p\n", __func__, __LINE__, udev, priv);
|
||||
|
||||
ret = nandc_flash_init(priv->ioaddr);
|
||||
if (ret) {
|
||||
debug("nandc_flash_init failed, ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = nandc_flash_op.flash_init(udev);
|
||||
if (!ret) {
|
||||
priv->flash_con_type = FLASH_CON_TYPE_NANDC;
|
||||
priv->density = nandc_flash_op.flash_get_capacity(udev);
|
||||
priv->read = nandc_flash_op.flash_read;
|
||||
priv->write = nandc_flash_op.flash_write;
|
||||
priv->erase = nandc_flash_op.flash_erase;
|
||||
debug("%s probe success\n", __func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(rknand) = {
|
||||
.id = UCLASS_RKNAND,
|
||||
.name = "rknand",
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_nand_ids[] = {
|
||||
{ .compatible = "rockchip,rk-nandc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rknand) = {
|
||||
.name = "rknand",
|
||||
.id = UCLASS_RKNAND,
|
||||
.of_match = rockchip_nand_ids,
|
||||
.bind = rknand_blk_bind,
|
||||
.probe = rockchip_nand_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rkflash_info),
|
||||
.ofdata_to_platdata = rockchip_nand_ofdata_to_platdata,
|
||||
};
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
|
@ -13,18 +13,41 @@
|
|||
#include "rkflash_blk.h"
|
||||
#include "rkflash_api.h"
|
||||
|
||||
static struct flash_operation spi_flash_op = {
|
||||
static struct flash_operation sfc_nor_op = {
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
FLASH_TYPE_SFC_NOR,
|
||||
rk_snor_init,
|
||||
rk_snor_get_capacity,
|
||||
rk_snor_read,
|
||||
rk_snor_write,
|
||||
rksfc_nor_init,
|
||||
rksfc_nor_get_capacity,
|
||||
rksfc_nor_read,
|
||||
rksfc_nor_write,
|
||||
NULL,
|
||||
rksfc_nor_vendor_read,
|
||||
rksfc_nor_vendor_write,
|
||||
#else
|
||||
-1, NULL, NULL, NULL, NULL,
|
||||
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct flash_operation sfc_nand_op = {
|
||||
#ifdef CONFIG_RKSFC_NAND
|
||||
FLASH_TYPE_SFC_NAND,
|
||||
rksfc_nand_init,
|
||||
rksfc_nand_get_density,
|
||||
rksfc_nand_read,
|
||||
rksfc_nand_write,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
#else
|
||||
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct flash_operation *spi_flash_op[2] = {
|
||||
&sfc_nor_op,
|
||||
&sfc_nand_op,
|
||||
};
|
||||
|
||||
int rksfc_scan_namespace(void)
|
||||
{
|
||||
struct uclass *uc;
|
||||
|
|
@ -72,22 +95,29 @@ static int rockchip_rksfc_ofdata_to_platdata(struct udevice *dev)
|
|||
|
||||
static int rockchip_rksfc_probe(struct udevice *udev)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int i;
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
|
||||
debug("%s %d %p ndev = %p\n", __func__, __LINE__, udev, priv);
|
||||
|
||||
sfc_init(priv->ioaddr);
|
||||
if (spi_flash_op.id == -1) {
|
||||
debug("%s no optional spi flash\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
ret = spi_flash_op.flash_init(udev);
|
||||
if (!ret) {
|
||||
priv->flash_con_type = FLASH_CON_TYPE_SFC;
|
||||
priv->density = spi_flash_op.flash_get_capacity(udev);
|
||||
priv->read = spi_flash_op.flash_read;
|
||||
priv->write = spi_flash_op.flash_write;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (spi_flash_op[i]->id == -1) {
|
||||
debug("%s no optional spi flash for type %x\n",
|
||||
__func__, i);
|
||||
continue;
|
||||
}
|
||||
ret = spi_flash_op[i]->flash_init(udev);
|
||||
if (!ret) {
|
||||
priv->flash_con_type = FLASH_CON_TYPE_SFC;
|
||||
priv->density =
|
||||
spi_flash_op[i]->flash_get_capacity(udev);
|
||||
priv->read = spi_flash_op[i]->flash_read;
|
||||
priv->write = spi_flash_op[i]->flash_write;
|
||||
debug("%s probe success\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef _SFC_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "flash_com.h"
|
||||
#include "sfc.h"
|
||||
#include "sfc_nand.h"
|
||||
#include "rkflash_debug.h"
|
||||
|
||||
static struct nand_info spi_nand_tbl[] = {
|
||||
/* TC58CVG0S0HxAIx */
|
||||
{0x98C2, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x02, 0xD8, 0x00, 18, 8, 0xB0, 0XFF, 4, 8},
|
||||
/* TC58CVG1S0HxAIx */
|
||||
{0x98CB, 4, 64, 2, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x02, 0xD8, 0x00, 19, 8, 0xB0, 0XFF, 4, 8},
|
||||
/* MX35LF1GE4AB */
|
||||
{0xC212, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* MX35LF2GE4AB */
|
||||
{0xC222, 4, 64, 2, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 19, 1, 0xB0, 0, 4, 8},
|
||||
/* GD5F1GQ4UAYIG */
|
||||
{0xC8F1, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* GD5F2GQ40BY2GR */
|
||||
{0xC8D2, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* MT29F1G01ZAC */
|
||||
{0x2C12, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x00, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* GD5F1GQ4U */
|
||||
{0xC8B1, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* GD5F2GQ4U */
|
||||
{0xC8B2, 4, 64, 2, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 19, 1, 0xB0, 0, 4, 8},
|
||||
/* GD5F1GQ4U */
|
||||
{0xC8D1, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xB0, 0, 4, 8},
|
||||
/* IS37SML01G1 */
|
||||
{0xC821, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x00, 18, 1, 0xB0, 0XFF, 8, 12},
|
||||
/* W25N01GV */
|
||||
{0xEFAA, 4, 64, 1, 1024, 0x13, 0x10, 0x03, 0x02, 0x6B, 0x32, 0xD8, 0x0C, 18, 1, 0xFF, 0XFF, 4, 20},
|
||||
};
|
||||
|
||||
static u8 id_byte[8];
|
||||
static struct nand_info *p_nand_info;
|
||||
static u32 gp_page_buf[SFC_NAND_PAGE_MAX_SIZE / 4];
|
||||
static struct SFNAND_DEV sfc_nand_dev;
|
||||
|
||||
static struct nand_info *spi_nand_get_info(u8 *nand_id)
|
||||
{
|
||||
u32 i;
|
||||
u32 id = (nand_id[0] << 8) | (nand_id[1] << 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(spi_nand_tbl); i++) {
|
||||
if (spi_nand_tbl[i].id == id)
|
||||
return &spi_nand_tbl[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sfc_nand_write_en(void)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_WRITE_EN;
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sfc_nand_rw_preset(void)
|
||||
{
|
||||
int ret;
|
||||
union SFCCTRL_DATA sfctrl;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u8 status = 0xFF;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = 0;
|
||||
sfcmd.b.datasize = 1;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
sfctrl.b.datalines = 2;
|
||||
ret = sfc_request(sfcmd.d32, sfctrl.d32, 0, &status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sfc_nand_read_feature(u8 addr, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = 0x0F;
|
||||
sfcmd.b.datasize = 1;
|
||||
sfcmd.b.addrbits = SFC_ADDR_XBITS;
|
||||
*data = 0;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0x8 << 16, addr, data);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
return SFC_OK;
|
||||
}
|
||||
|
||||
static int sfc_nand_write_feature(u32 addr, u8 status)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfc_nand_write_en();
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = 0x1F;
|
||||
sfcmd.b.datasize = 1;
|
||||
sfcmd.b.addrbits = SFC_ADDR_XBITS;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0x8 << 16, addr, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sfc_nand_wait_busy(u8 *data, int timeout)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
u8 status;
|
||||
|
||||
*data = 0;
|
||||
for (i = 0; i < timeout; i++) {
|
||||
ret = sfc_nand_read_feature(0xC0, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
*data = status;
|
||||
if (!(status & (1 << 0)))
|
||||
return SFC_OK;
|
||||
sfc_delay(1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u32 sfc_nand_erase_block(u8 cs, u32 addr)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u8 status;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = p_nand_info->block_erase_cmd;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfc_nand_write_en();
|
||||
ret = sfc_request(sfcmd.d32, 0, addr, NULL);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
ret = sfc_nand_wait_busy(&status, 1000 * 1000);
|
||||
if (status & (1 << 2))
|
||||
return SFC_NAND_PROG_ERASE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 sfc_nand_prog_page(u8 cs, u32 addr, u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
union SFCCTRL_DATA sfctrl;
|
||||
u8 status;
|
||||
u32 data_sz = 2048;
|
||||
u32 spare_offs_1 = p_nand_info->spare_offs_1;
|
||||
u32 spare_offs_2 = p_nand_info->spare_offs_2;
|
||||
|
||||
memcpy(gp_page_buf, p_data, data_sz);
|
||||
gp_page_buf[(data_sz + spare_offs_1) / 4] = p_spare[0];
|
||||
gp_page_buf[(data_sz + spare_offs_2) / 4] = p_spare[1];
|
||||
|
||||
sfc_nand_write_en();
|
||||
if (sfc_nand_dev.prog_lines == DATA_LINES_X4 &&
|
||||
p_nand_info->QE_address == 0xFF &&
|
||||
sfc_get_version() != SFC_VER_3)
|
||||
sfc_nand_rw_preset();
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = sfc_nand_dev.page_prog_cmd;
|
||||
sfcmd.b.addrbits = SFC_ADDR_XBITS;
|
||||
sfcmd.b.datasize = SFC_NAND_PAGE_MAX_SIZE;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
sfctrl.d32 = 0;
|
||||
sfctrl.b.datalines = sfc_nand_dev.prog_lines;
|
||||
sfctrl.b.addrbits = 16;
|
||||
sfc_request(sfcmd.d32, sfctrl.d32, 0, gp_page_buf);
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = p_nand_info->page_prog_cmd;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfcmd.b.datasize = 0;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
ret = sfc_request(sfcmd.d32, 0, addr, p_data);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
ret = sfc_nand_wait_busy(&status, 1000 * 1000);
|
||||
if (status & (1 << 3))
|
||||
return SFC_NAND_PROG_ERASE_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 sfc_nand_read_page(u8 cs, u32 addr, u32 *p_data, u32 *p_spare)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
union SFCCTRL_DATA sfctrl;
|
||||
u8 status;
|
||||
u8 ecc;
|
||||
u32 data_sz = 2048;
|
||||
u32 spare_offs_1 = p_nand_info->spare_offs_1;
|
||||
u32 spare_offs_2 = p_nand_info->spare_offs_2;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = p_nand_info->page_read_cmd;
|
||||
sfcmd.b.datasize = 0;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfc_request(sfcmd.d32, 0, addr, p_data);
|
||||
|
||||
sfc_nand_wait_busy(&status, 1000 * 1000);
|
||||
ecc = (status >> 4) & 0x03;
|
||||
if (sfc_nand_dev.read_lines == DATA_LINES_X4 &&
|
||||
p_nand_info->QE_address == 0xFF &&
|
||||
sfc_get_version() != SFC_VER_3)
|
||||
sfc_nand_rw_preset();
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = sfc_nand_dev.page_read_cmd;
|
||||
sfcmd.b.datasize = SFC_NAND_PAGE_MAX_SIZE;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfctrl.d32 = 0;
|
||||
sfctrl.b.datalines = sfc_nand_dev.read_lines;
|
||||
|
||||
memset(gp_page_buf, 0, SFC_NAND_PAGE_MAX_SIZE);
|
||||
ret = sfc_request(sfcmd.d32, sfctrl.d32, 0, gp_page_buf);
|
||||
|
||||
memcpy(p_data, gp_page_buf, data_sz);
|
||||
p_spare[0] = gp_page_buf[(data_sz + spare_offs_1) / 4];
|
||||
p_spare[1] = gp_page_buf[(data_sz + spare_offs_2) / 4];
|
||||
if (ret != SFC_OK)
|
||||
return SFC_NAND_ECC_ERROR;
|
||||
|
||||
/*
|
||||
* ecc status:
|
||||
* 0, No bit errors were detected
|
||||
* 1, Bit errors were detected and corrected. If max_ecc_bits equals 1,
|
||||
* Bit error count exceed the bit flip detection threshold.
|
||||
* 2, Multiple bit errors were detected and not corrected.
|
||||
* 3, If max_ecc_bits equals 1, reserved, else bit errors were detected
|
||||
* and corrected, bit error count exceed the bit flip detection
|
||||
* threshold
|
||||
*/
|
||||
|
||||
if (ecc == 0) {
|
||||
ret = SFC_NAND_ECC_OK;
|
||||
} else if (ecc == 1) {
|
||||
if (p_nand_info->max_ecc_bits == 1)
|
||||
ret = SFC_NAND_ECC_REFRESH;
|
||||
else
|
||||
ret = SFC_NAND_ECC_OK;
|
||||
} else if (ecc == 2) {
|
||||
ret = SFC_NAND_ECC_ERROR;
|
||||
} else {
|
||||
if (p_nand_info->max_ecc_bits == 1)
|
||||
ret = SFC_NAND_ECC_ERROR;
|
||||
else
|
||||
ret = SFC_NAND_ECC_REFRESH;
|
||||
}
|
||||
|
||||
if (ret != SFC_NAND_ECC_OK) {
|
||||
PRINT_SFC_E("%s[0x%x], ret=0x%x\n", __func__, addr, ret);
|
||||
if (p_data)
|
||||
PRINT_SFC_HEX("data:", p_data, 4, 8);
|
||||
if (p_spare)
|
||||
PRINT_SFC_HEX("spare:", p_spare, 4, 2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sfc_nand_read_id_raw(u8 *data)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_READ_JEDECID;
|
||||
sfcmd.b.datasize = 3;
|
||||
sfcmd.b.addrbits = SFC_ADDR_XBITS;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0x8 << 16, 0, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the 1st page's 1st byte of a phy_blk
|
||||
* If not FF, it's bad blk
|
||||
*/
|
||||
static int sfc_nand_get_bad_block_list(u16 *table, u32 die)
|
||||
{
|
||||
u16 blk;
|
||||
u32 bad_cnt, page;
|
||||
u32 blk_per_die;
|
||||
u32 *pread;
|
||||
u32 *pspare_read;
|
||||
|
||||
PRINT_SFC_E("%s\n", __func__);
|
||||
pread = ftl_malloc(2048);
|
||||
pspare_read = ftl_malloc(8);
|
||||
bad_cnt = 0;
|
||||
blk_per_die = p_nand_info->plane_per_die *
|
||||
p_nand_info->blk_per_plane;
|
||||
for (blk = 0; blk < blk_per_die; blk++) {
|
||||
page = (blk + blk_per_die * die) *
|
||||
p_nand_info->page_per_blk;
|
||||
sfc_nand_read_page(0, page, pread, pspare_read);
|
||||
|
||||
if (pread[0] != 0xFFFFFFFF ||
|
||||
pspare_read[0] != 0xFFFFFFFF) {
|
||||
table[bad_cnt++] = blk;
|
||||
PRINT_SFC_E("die[%d], bad_blk[%d]\n", die, blk);
|
||||
}
|
||||
}
|
||||
ftl_free(pread);
|
||||
ftl_free(pspare_read);
|
||||
return (int)bad_cnt;
|
||||
}
|
||||
|
||||
#if SFC_NAND_STRESS_TEST_EN
|
||||
|
||||
#define SFC_NAND_PAGE_SIZE 2048
|
||||
#define SFC_NAND_SPARE_SIZE 8
|
||||
|
||||
static u16 bad_blk_list[1024];
|
||||
static u32 pwrite[SFC_NAND_PAGE_SIZE / 4];
|
||||
static u32 pread[SFC_NAND_PAGE_SIZE / 4];
|
||||
static u32 pspare_write[SFC_NAND_SPARE_SIZE / 4];
|
||||
static u32 pspare_read[SFC_NAND_SPARE_SIZE / 4];
|
||||
static u32 bad_blk_num;
|
||||
static u32 bad_page_num;
|
||||
|
||||
static void sfc_nand_test(void)
|
||||
{
|
||||
u32 i, blk, page, bad_cnt, page_addr;
|
||||
int ret;
|
||||
u32 pages_num = 64;
|
||||
u32 blk_addr = 64;
|
||||
u32 is_bad_blk = 0;
|
||||
|
||||
PRINT_SFC_E("%s\n", __func__);
|
||||
|
||||
bad_blk_num = 0;
|
||||
bad_page_num = 0;
|
||||
bad_cnt = sfc_nand_get_bad_block_list(bad_blk_list, 0);
|
||||
|
||||
for (blk = 0; blk < 1024; blk++) {
|
||||
for (i = 0; i < bad_cnt; i++) {
|
||||
if (bad_blk_list[i] == blk)
|
||||
break;
|
||||
}
|
||||
if (i < bad_cnt)
|
||||
continue;
|
||||
is_bad_blk = 0;
|
||||
PRINT_SFC_E("Flash prog block: %x\n", blk);
|
||||
sfc_nand_erase_block(0, blk * blk_addr);
|
||||
for (page = 0; page < pages_num; page++) {
|
||||
page_addr = blk * blk_addr + page;
|
||||
for (i = 0; i < 512; i++)
|
||||
pwrite[i] = (page_addr << 16) + i;
|
||||
pspare_write[0] = pwrite[0] + 0x5AF0;
|
||||
pspare_write[1] = pspare_write[0] + 1;
|
||||
sfc_nand_prog_page(0, page_addr, pwrite, pspare_write);
|
||||
memset(pread, 0, 2048);
|
||||
memset(pspare_read, 0, 8);
|
||||
ret = sfc_nand_read_page(0, page_addr, pread,
|
||||
pspare_read);
|
||||
if (ret != SFC_NAND_ECC_OK)
|
||||
is_bad_blk = 1;
|
||||
for (i = 0; i < 512; i++) {
|
||||
if (pwrite[i] != pread[i]) {
|
||||
is_bad_blk = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (pspare_write[i] != pspare_read[i]) {
|
||||
is_bad_blk = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_bad_blk) {
|
||||
bad_page_num++;
|
||||
PRINT_SFC_E("ERR:page%x, ret=%x\n",
|
||||
page_addr, ret);
|
||||
PRINT_SFC_HEX("data:", pread, 4, 8);
|
||||
PRINT_SFC_HEX("spare:", pspare_read, 4, 2);
|
||||
}
|
||||
}
|
||||
sfc_nand_erase_block(0, blk * blk_addr);
|
||||
if (is_bad_blk)
|
||||
bad_blk_num++;
|
||||
}
|
||||
PRINT_SFC_E("bad_blk_num = %d, bad_page_num = %d\n",
|
||||
bad_blk_num, bad_page_num);
|
||||
|
||||
PRINT_SFC_E("Flash Test Finish!!!\n");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ftl_flash_init(void)
|
||||
{
|
||||
/* para init */
|
||||
g_nand_phy_info.nand_type = 1;
|
||||
g_nand_phy_info.die_num = 1;
|
||||
g_nand_phy_info.plane_per_die = p_nand_info->plane_per_die;
|
||||
g_nand_phy_info.blk_per_plane = p_nand_info->blk_per_plane;
|
||||
g_nand_phy_info.page_per_blk = p_nand_info->page_per_blk;
|
||||
g_nand_phy_info.page_per_slc_blk = p_nand_info->page_per_blk;
|
||||
g_nand_phy_info.byte_per_sec = 512;
|
||||
g_nand_phy_info.sec_per_page = p_nand_info->sec_per_page;
|
||||
g_nand_phy_info.sec_per_blk = p_nand_info->sec_per_page *
|
||||
p_nand_info->page_per_blk;
|
||||
g_nand_phy_info.reserved_blk = 8;
|
||||
g_nand_phy_info.blk_per_die = p_nand_info->plane_per_die *
|
||||
p_nand_info->blk_per_plane;
|
||||
g_nand_phy_info.ecc_bits = p_nand_info->max_ecc_bits;
|
||||
|
||||
/* driver register */
|
||||
g_nand_ops.get_bad_blk_list = sfc_nand_get_bad_block_list;
|
||||
g_nand_ops.erase_blk = sfc_nand_erase_block;
|
||||
g_nand_ops.prog_page = sfc_nand_prog_page;
|
||||
g_nand_ops.read_page = sfc_nand_read_page;
|
||||
}
|
||||
|
||||
static int spi_nand_enable_QE(void)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
u8 status;
|
||||
int bit_offset = p_nand_info->QE_bits;
|
||||
|
||||
if (bit_offset == 0xFF)
|
||||
return SFC_OK;
|
||||
|
||||
ret = sfc_nand_read_feature(p_nand_info->QE_address, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
if (status & (1 << bit_offset)) /* is QE bit set */
|
||||
return SFC_OK;
|
||||
|
||||
status |= (1 << bit_offset);
|
||||
return sfc_nand_write_feature(p_nand_info->QE_address, status);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 sfc_nand_init(void)
|
||||
{
|
||||
PRINT_SFC_I("...%s enter...\n", __func__);
|
||||
|
||||
sfc_nand_read_id_raw(id_byte);
|
||||
PRINT_SFC_E("sfc_nand id: %x %x %x\n",
|
||||
id_byte[0], id_byte[1], id_byte[2]);
|
||||
if (id_byte[0] == 0xFF || id_byte[0] == 0x00)
|
||||
return FTL_NO_FLASH;
|
||||
|
||||
p_nand_info = spi_nand_get_info(id_byte);
|
||||
if (!p_nand_info)
|
||||
return FTL_UNSUPPORTED_FLASH;
|
||||
|
||||
sfc_nand_dev.manufacturer = id_byte[0];
|
||||
sfc_nand_dev.mem_type = id_byte[1];
|
||||
|
||||
/* disable block lock */
|
||||
sfc_nand_write_feature(0xA0, 0);
|
||||
sfc_nand_dev.read_lines = DATA_LINES_X1;
|
||||
sfc_nand_dev.prog_lines = DATA_LINES_X1;
|
||||
sfc_nand_dev.page_read_cmd = p_nand_info->read_cache_cmd_1;
|
||||
sfc_nand_dev.page_prog_cmd = p_nand_info->prog_cache_cmd_1;
|
||||
if (p_nand_info->feature & FEA_4BIT_READ) {
|
||||
if (spi_nand_enable_QE() == SFC_OK) {
|
||||
sfc_nand_dev.read_lines = DATA_LINES_X4;
|
||||
sfc_nand_dev.page_read_cmd =
|
||||
p_nand_info->read_cache_cmd_4;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_nand_info->feature & FEA_4BIT_PROG &&
|
||||
sfc_nand_dev.read_lines == DATA_LINES_X4) {
|
||||
sfc_nand_dev.prog_lines = DATA_LINES_X4;
|
||||
sfc_nand_dev.page_prog_cmd = p_nand_info->prog_cache_cmd_4;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
u8 status;
|
||||
|
||||
sfc_nand_read_feature(0xA0, &status);
|
||||
PRINT_SFC_I("sfc_nand A0 = 0x%x\n", status);
|
||||
sfc_nand_read_feature(0xB0, &status);
|
||||
PRINT_SFC_I("sfc_nand B0 = 0x%x\n", status);
|
||||
sfc_nand_read_feature(0xC0, &status);
|
||||
PRINT_SFC_I("sfc_nand C0 = 0x%x\n", status);
|
||||
PRINT_SFC_I("read_lines = %x\n", sfc_nand_dev.read_lines);
|
||||
PRINT_SFC_I("prog_lines = %x\n", sfc_nand_dev.prog_lines);
|
||||
PRINT_SFC_I("page_read_cmd = %x\n", sfc_nand_dev.page_read_cmd);
|
||||
PRINT_SFC_I("page_prog_cmd = %x\n", sfc_nand_dev.page_prog_cmd);
|
||||
}
|
||||
ftl_flash_init();
|
||||
|
||||
#if SFC_NAND_STRESS_TEST_EN
|
||||
sfc_nand_test();
|
||||
#endif
|
||||
|
||||
return SFC_OK;
|
||||
}
|
||||
|
||||
int sfc_nand_read_id(u8 *data)
|
||||
{
|
||||
memcpy(data, id_byte, 3);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __SFC_NAND_H
|
||||
#define __SFC_NAND_H
|
||||
|
||||
#define SFC_NAND_STRESS_TEST_EN 0
|
||||
|
||||
#define SFC_NAND_PROG_ERASE_ERROR -2
|
||||
#define SFC_NAND_HW_ERROR -1
|
||||
#define SFC_NAND_ECC_ERROR NAND_ERROR
|
||||
#define SFC_NAND_ECC_REFRESH NAND_STS_REFRESH
|
||||
#define SFC_NAND_ECC_OK NAND_STS_OK
|
||||
|
||||
#define SFC_NAND_PAGE_MAX_SIZE 2112
|
||||
|
||||
#define FEA_READ_STATUE_MASK (0x3 << 0)
|
||||
#define FEA_STATUE_MODE1 0
|
||||
#define FEA_STATUE_MODE2 1
|
||||
#define FEA_4BIT_READ BIT(2)
|
||||
#define FEA_4BIT_PROG BIT(3)
|
||||
#define FEA_4BYTE_ADDR BIT(4)
|
||||
#define FEA_4BYTE_ADDR_MODE BIT(5)
|
||||
|
||||
#define MID_WINBOND 0xEF
|
||||
#define MID_GIGADEV 0xC8
|
||||
#define MID_MICRON 0x2C
|
||||
#define MID_MACRONIX 0xC2
|
||||
#define MID_SPANSION 0x01
|
||||
#define MID_EON 0x1C
|
||||
#define MID_ST 0x20
|
||||
|
||||
/* Command Set */
|
||||
#define CMD_READ_JEDECID (0x9F)
|
||||
#define CMD_READ_DATA (0x03)
|
||||
#define CMD_READ_STATUS (0x05)
|
||||
#define CMD_WRITE_STATUS (0x01)
|
||||
#define CMD_PAGE_PROG (0x02)
|
||||
#define CMD_SECTOR_ERASE (0x20)
|
||||
#define CMD_BLK64K_ERASE (0xD8)
|
||||
#define CMD_BLK32K_ERASE (0x52)
|
||||
#define CMD_CHIP_ERASE (0xC7)
|
||||
#define CMD_WRITE_EN (0x06)
|
||||
#define CMD_WRITE_DIS (0x04)
|
||||
#define CMD_PAGE_READ (0x13)
|
||||
#define CMD_GET_FEATURE (0x0F)
|
||||
#define CMD_SET_FEATURE (0x1F)
|
||||
#define CMD_PROG_LOAD (0x02)
|
||||
#define CMD_PROG_EXEC (0x10)
|
||||
#define CMD_BLOCK_ERASE (0xD8)
|
||||
#define CMD_READ_DATA_X2 (0x3B)
|
||||
#define CMD_READ_DATA_X4 (0x6B)
|
||||
#define CMD_PROG_LOAD_X4 (0x32)
|
||||
#define CMD_READ_STATUS2 (0x35)
|
||||
#define CMD_READ_STATUS3 (0x15)
|
||||
#define CMD_WRITE_STATUS2 (0x31)
|
||||
#define CMD_WRITE_STATUS3 (0x11)
|
||||
#define CMD_FAST_READ_X1 (0x0B) /* X1 cmd, X1 addr, X1 data */
|
||||
#define CMD_FAST_READ_X2 (0x3B) /* X1 cmd, X1 addr, X2 data */
|
||||
/* X1 cmd, X1 addr, X4 data SUPPORT GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_READ_X4 (0x6B)
|
||||
/* X1 cmd, X1 addr, X4 data SUPPORT GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_4READ_X4 (0x6C)
|
||||
/* X1 cmd, X4 addr, X4 data SUPPORT EON GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_READ_A4 (0xEB)
|
||||
/* X1 cmd, X1 addr, X4 data, SUPPORT GD WINBOND */
|
||||
#define CMD_PAGE_PROG_X4 (0x32)
|
||||
/* X1 cmd, X4 addr, X4 data, SUPPORT MARCONIX */
|
||||
#define CMD_PAGE_PROG_A4 (0x38)
|
||||
#define CMD_RESET_NAND (0xFF)
|
||||
|
||||
#define CMD_ENTER_4BYTE_MODE (0xB7)
|
||||
#define CMD_EXIT_4BYTE_MODE (0xE9)
|
||||
#define CMD_ENABLE_RESER (0x66)
|
||||
#define CMD_RESET_DEVICE (0x99)
|
||||
|
||||
struct SFNAND_DEV {
|
||||
u32 capacity;
|
||||
u32 block_size;
|
||||
u16 page_size;
|
||||
u8 manufacturer;
|
||||
u8 mem_type;
|
||||
u8 read_lines;
|
||||
u8 prog_lines;
|
||||
u8 page_read_cmd;
|
||||
u8 page_prog_cmd;
|
||||
};
|
||||
|
||||
struct nand_info {
|
||||
u32 id;
|
||||
|
||||
u16 sec_per_page;
|
||||
u16 page_per_blk;
|
||||
u16 plane_per_die;
|
||||
u16 blk_per_plane;
|
||||
|
||||
u8 page_read_cmd;
|
||||
u8 page_prog_cmd;
|
||||
u8 read_cache_cmd_1;
|
||||
u8 prog_cache_cmd_1;
|
||||
|
||||
u8 read_cache_cmd_4;
|
||||
u8 prog_cache_cmd_4;
|
||||
u8 block_erase_cmd;
|
||||
u8 feature;
|
||||
|
||||
u8 density; /* (1 << density) sectors*/
|
||||
u8 max_ecc_bits;
|
||||
u8 QE_address;
|
||||
u8 QE_bits;
|
||||
|
||||
u8 spare_offs_1;
|
||||
u8 spare_offs_2;
|
||||
};
|
||||
|
||||
u32 sfc_nand_init(void);
|
||||
int sfc_nand_read_id(u8 *buf);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "sfc_nor.h"
|
||||
|
|
@ -103,7 +103,8 @@ static int snor_wait_busy(int timeout)
|
|||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u32 i, status;
|
||||
int i;
|
||||
u32 status;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_READ_STATUS;
|
||||
|
|
@ -195,7 +196,7 @@ static int snor_erase(struct SFNOR_DEV *p_dev,
|
|||
|
||||
sfcmd.b.addrbits = (erase_type != ERASE_CHIP) ?
|
||||
SFC_ADDR_24BITS : SFC_ADDR_0BITS;
|
||||
if ((p_dev->addr_mode == ADDR_MODE_4BYTE) && (erase_type != ERASE_CHIP))
|
||||
if (p_dev->addr_mode == ADDR_MODE_4BYTE && erase_type != ERASE_CHIP)
|
||||
sfcmd.b.addrbits = SFC_ADDR_32BITS;
|
||||
|
||||
snor_write_en();
|
||||
|
|
@ -401,6 +402,7 @@ int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, const void *p_data)
|
|||
int ret = SFC_OK;
|
||||
u32 len, blk_size, offset;
|
||||
u8 *p_buf = (u8 *)p_data;
|
||||
u32 total_sec = n_sec;
|
||||
|
||||
if ((sec + n_sec) > p_dev->capacity)
|
||||
return SFC_PARAM_ERR;
|
||||
|
|
@ -436,7 +438,7 @@ int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, const void *p_data)
|
|||
out:
|
||||
mutex_unlock(&p_dev->lock);
|
||||
if (!ret)
|
||||
ret = n_sec;
|
||||
ret = total_sec;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -492,9 +494,7 @@ static struct flash_info *snor_get_flash_info(u8 *flash_id)
|
|||
u32 i;
|
||||
u32 id = (flash_id[0] << 16) | (flash_id[1] << 8) | (flash_id[2] << 0);
|
||||
|
||||
for (i = 0;
|
||||
i < (sizeof(spi_flash_tbl) / sizeof(struct flash_info));
|
||||
i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(spi_flash_tbl); i++) {
|
||||
if (spi_flash_tbl[i].id == id)
|
||||
return &spi_flash_tbl[i];
|
||||
}
|
||||
|
|
@ -520,7 +520,7 @@ static void *snor_flash_info_adjust(struct flash_info *spi_flash_info)
|
|||
|
||||
int snor_init(struct SFNOR_DEV *p_dev)
|
||||
{
|
||||
int i;
|
||||
u32 i;
|
||||
u8 id_byte[5];
|
||||
int err;
|
||||
|
||||
|
|
@ -562,8 +562,8 @@ int snor_init(struct SFNOR_DEV *p_dev)
|
|||
p_dev->read_cmd = g_spi_flash_info->read_cmd_4;
|
||||
}
|
||||
}
|
||||
if ((g_spi_flash_info->feature & FEA_4BIT_PROG) &&
|
||||
(p_dev->read_lines == DATA_LINES_X4)) {
|
||||
if (g_spi_flash_info->feature & FEA_4BIT_PROG &&
|
||||
p_dev->read_lines == DATA_LINES_X4) {
|
||||
p_dev->prog_lines = DATA_LINES_X4;
|
||||
p_dev->prog_cmd = g_spi_flash_info->prog_cmd_4;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef _SFNOR_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef __TYPE_DEF_H
|
||||
#define __TYPE_DEF_H
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#define OK 0
|
||||
#define ERROR (-1)
|
||||
|
||||
#define FTL_ERROR ERROR
|
||||
#define FTL_OK OK
|
||||
#define FTL_NO_FLASH -2
|
||||
#define FTL_NO_IDB -3
|
||||
#define FTL_UNSUPPORTED_FLASH -4
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE (!FALSE)
|
||||
|
||||
#define INVALID_UINT8 ((u8)0xFF)
|
||||
#define INVALID_UINT16 ((u16)0xFFFF)
|
||||
#define INVALID_UINT32 ((u32)0xFFFFFFFFL)
|
||||
|
||||
#define PRINT_E pr_info
|
||||
#define PRINT_I pr_info
|
||||
|
||||
void *ftl_malloc(int n_size);
|
||||
void *ftl_memset(void *s, int c, unsigned int n);
|
||||
void *ftl_memcpy(void *pv_to,
|
||||
const void *pv_from,
|
||||
unsigned int size);
|
||||
void ftl_free(void *p);
|
||||
void rknand_print_hex(char *s, void *buf, int width, int len);
|
||||
|
||||
#endif /*__TYPEDEF_H */
|
||||
Loading…
Reference in New Issue