drivers: rkflash: add rkflash drivers

Change-Id: I6faf12d63088b8df345b69fc4665915429c856c9
Signed-off-by: Dingqiang Lin <jon.lin@rock-chips.com>
This commit is contained in:
Dingqiang Lin 2018-05-16 15:40:27 +08:00 committed by Tao Huang
parent 4f7c7cdaef
commit a38fd055d2
34 changed files with 19225 additions and 0 deletions

View File

@ -202,6 +202,8 @@ source "drivers/fpga/Kconfig"
source "drivers/tee/Kconfig"
source "drivers/rkflash/Kconfig"
source "drivers/rk_nand/Kconfig"
source "drivers/headset_observe/Kconfig"

View File

@ -178,3 +178,5 @@ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_RK_NAND) += rk_nand/
obj-$(CONFIG_RK_HEADSET) += headset_observe/
obj-$(CONFIG_RK_FLASH) += rkflash/
obj-y += rk_nand/

59
drivers/rkflash/Kconfig Normal file
View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0
if ARCH_ROCKCHIP
menuconfig RK_FLASH
tristate "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.
Say Y when you have a board with one of them.
if RK_FLASH
comment "Rockchip Flash Devices"
config RK_NANDC_NAND
tristate "RK NANDC NAND Device Support"
default n
help
Enable NANDC_NAND device support.
It's block interface.
Say Y when you have a board with nand flash supported by rockchip
nandc controller.
config RK_SFC_NOR
tristate "RK SFC NOR Device Support"
default n
help
Enable SFC_NOR device support.
It's block interface.
Say Y when you have a board with nor flash supported by rockchip
sfc controller.
config RK_SFC_NAND
tristate "RK SFC NAND Device Support"
default n
help
Enable SFC_NAND device support.
It's block interface
Say Y when you have a board with nand flash supported by rockchip
sfc controller.
config RK_SFC_NOR_MTD
bool "RK SFC NOR mtd Interface Support"
depends on RK_SFC_NOR
default n
help
Enable mtd interface for SFC_NOR device.
It's mtd block interface.
Say Y when you have a board with mtd interface nor flash supported
by rockchip sfc controller.
endif # RK_FLASH
endif # ARCH_ROCKCHIP

6
drivers/rkflash/Makefile Normal file
View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RK_NANDC_NAND) += rkflash_blk.o rkflash_debug.o rknandc_base.o nand_boot.o flash.o nandc.o ftl_flash_plat.o rk_sftl.o
obj-$(CONFIG_RK_SFC_NOR) += rkflash_blk.o rkflash_debug.o rksfc_base.o sfc_nor_boot.o sfc_nor.o sfc.o
obj-$(CONFIG_RK_SFC_NOR_MTD) += sfc_nor_mtd.o
obj-$(CONFIG_RK_SFC_NAND) += rkflash_blk.o rkflash_debug.o rksfc_base.o sfc_nand_boot.o sfc_nand.o sfc.o ftl_flash_plat.o rk_sftl.o

500
drivers/rkflash/flash.c Normal file
View File

@ -0,0 +1,500 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/delay.h>
#include <linux/kernel.h>
#include "flash.h"
#include "flash_com.h"
#include "nandc.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_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_E("FlashReadRawPage %x %x error_ecc_bits %d\n",
cs, page_addr, error_ecc_bits);
if (p_data)
rknand_print_hex("data:", p_data, 4, 8);
if (p_spare)
rknand_print_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_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_I("%s addr=%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_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_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_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_E("ERR:page%x, ret=%x\n", page_addr, ret);
rknand_print_hex("data:", pread, 4, 8);
rknand_print_hex("spare:", pspare_read, 4, 2);
}
}
flash_erase_block(0, blk * blk_addr);
if (is_bad_blk)
bad_blk_num++;
}
PRINT_E("bad_blk_num = %d, bad_page_num = %d\n",
bad_blk_num, bad_page_num);
PRINT_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 flash_print_info(void)
{
PRINT_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_I("die_per_chip: %x\n", nand_para.die_per_chip);
PRINT_I("sec_per_page: %x\n", nand_para.sec_per_page);
PRINT_I("page_per_blk: %x\n", nand_para.page_per_blk);
PRINT_I("cell: %x\n", nand_para.cell);
PRINT_I("plane_per_die: %x\n", nand_para.plane_per_die);
PRINT_I("blk_per_plane: %x\n", nand_para.blk_per_plane);
PRINT_I("TotleBlock: %x\n", g_totle_block);
PRINT_I("die gap: %x\n", nand_para.die_gap);
PRINT_I("lsb_mode: %x\n", nand_para.lsb_mode);
PRINT_I("read_retry_mode: %x\n", nand_para.read_retry_mode);
PRINT_I("ecc_bits: %x\n", nand_para.ecc_bits);
PRINT_I("Use ecc_bits: %x\n", g_nand_flash_ecc_bits);
PRINT_I("access_freq: %x\n", nand_para.access_freq);
PRINT_I("opt_mode: %x\n", nand_para.opt_mode);
PRINT_I("Cache read enable: %x\n",
nand_para.operation_opt & NAND_CACHE_READ_EN ? 1 : 0);
PRINT_I("Cache random read enable: %x\n",
nand_para.operation_opt & NAND_CACHE_RANDOM_READ_EN ? 1 : 0);
PRINT_I("Cache prog enable: %x\n",
nand_para.operation_opt & NAND_CACHE_PROG_EN ? 1 : 0);
PRINT_I("multi read enable: %x\n",
nand_para.operation_opt & NAND_MULTI_READ_EN ? 1 : 0);
PRINT_I("multi prog enable: %x\n",
nand_para.operation_opt & NAND_MULTI_PROG_EN ? 1 : 0);
PRINT_I("interleave enable: %x\n",
nand_para.operation_opt & NAND_INTERLEAVE_EN ? 1 : 0);
PRINT_I("read retry enable: %x\n",
nand_para.operation_opt & NAND_READ_RETRY_EN ? 1 : 0);
PRINT_I("randomizer enable: %x\n",
nand_para.operation_opt & NAND_RANDOMIZER_EN ? 1 : 0);
PRINT_I("SDR enable: %x\n",
nand_para.operation_opt & NAND_SDR_EN ? 1 : 0);
PRINT_I("ONFI enable: %x\n",
nand_para.operation_opt & NAND_ONFI_EN ? 1 : 0);
PRINT_I("TOGGLE enable: %x\n",
nand_para.operation_opt & NAND_TOGGLE_EN ? 1 : 0);
PRINT_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_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)
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;
}
flash_die_info_init();
flash_bch_sel(nand_para.ecc_bits);
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;
}

143
drivers/rkflash/flash.h Normal file
View File

@ -0,0 +1,143 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#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

View File

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#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);
};
s32 ftl_flash_prog_pages(void *req, u32 num_req, u32 flash_type, u32 check);
s32 ftl_flash_read_pages(void *req, u32 num_req, u32 flash_type);
s32 ftl_flash_erase_blocks(void *req, u32 num_req);
s32 ftl_flash_test_blk(u16 phy_block);
s32 ftl_flash_get_bad_blk_list(u16 *table, u32 die);
#endif

View File

@ -0,0 +1,120 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include "flash_com.h"
struct nand_phy_info g_nand_phy_info;
struct nand_ops g_nand_ops;
static u32 check_buf[MAX_FLASH_PAGE_SIZE / 4];
static u32 check_spare_buf[MAX_FLASH_PAGE_SIZE / 8 / 4];
static u32 pg_buf0[MAX_FLASH_PAGE_SIZE / 4];
static u32 l2p_addr_tran(struct nand_req *req, u32 *addr, u32 *p_die)
{
u16 block_index, page_index;
u16 blk_per_die = g_nand_phy_info.blk_per_die;
u32 die_index;
block_index = (u16)((req[0].page_addr >> BLOCK_ADDR_BITS) &
BLOCK_ADDR_MASK);
page_index = (u16)(req[0].page_addr & PAGE_ADDR_MASK);
die_index = (u16)((req[0].page_addr >> DIE_ADDR_BITS) &
DIE_ADDR_MASK);
*p_die = die_index;
*addr = (block_index + blk_per_die * die_index) *
g_nand_phy_info.page_per_blk + page_index;
return 0;
}
s32 ftl_flash_prog_pages(void *request, u32 num_req, u32 flash_type, u32 check)
{
u32 i, cs, status, addr;
struct nand_req *req = (struct nand_req *)request;
for (i = 0; i < num_req; i++) {
l2p_addr_tran(&req[i], &addr, &cs);
status = g_nand_ops.prog_page(cs,
addr,
req[i].p_data,
req[i].p_spare);
req[i].status = status;
if (status != NAND_STS_OK)
req[i].status = NAND_STS_ECC_ERR;
}
if (check == 0)
return 0;
for (i = 0; i < num_req; i++) {
l2p_addr_tran(&req[i], &addr, &cs);
status = g_nand_ops.read_page(cs,
addr,
check_buf,
check_spare_buf);
if (status != NAND_STS_ECC_ERR)
req[i].status = NAND_STS_OK;
if (check_buf[0] != req[i].p_data[0])
req[i].status = NAND_STS_ECC_ERR;
}
return 0;
}
s32 ftl_flash_read_pages(void *request, u32 num_req, u32 flash_type)
{
u32 i, cs, status, addr;
struct nand_req *req = (struct nand_req *)request;
for (i = 0; i < num_req; i++) {
l2p_addr_tran(&req[i], &addr, &cs);
status = g_nand_ops.read_page(cs,
addr,
req[i].p_data,
req[i].p_spare);
req[i].status = status;
}
return OK;
}
s32 ftl_flash_erase_blocks(void *request, u32 num_req)
{
u32 i, cs, status, addr;
struct nand_req *req = (struct nand_req *)request;
for (i = 0; i < num_req; i++) {
l2p_addr_tran(&req[i], &addr, &cs);
status = g_nand_ops.erase_blk(cs, addr);
req[i].status = status;
if (status != NAND_STS_OK)
req[i].status = NAND_STS_ECC_ERR;
}
return OK;
}
s32 ftl_flash_get_bad_blk_list(u16 *table, u32 die)
{
return g_nand_ops.get_bad_blk_list(table, die);
}
s32 ftl_flash_test_blk(u16 phy_block)
{
s32 sts = 0;
u32 spare[16];
struct nand_req req;
req.p_data = pg_buf0;
req.p_spare = spare;
memset(spare, 0xA5, 32);
memset(pg_buf0, 0x5A, 8);
req.page_addr = phy_block << BLOCK_ADDR_BITS;
ftl_flash_erase_blocks((void *)&req, 1);
ftl_flash_prog_pages((void *)&req, 1, SLC, 1);
if (req.status == NAND_STS_ECC_ERR) {
PRINT_E("%s %x is bad block\n", __func__, phy_block);
sts = -1;
}
ftl_flash_erase_blocks((void *)&req, 1);
return sts;
}

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _FTL_INCLUDE_
#define _FTL_INCLUDE_
#include <linux/kernel.h>
#include "flash_com.h"
#include "typedef.h"
#define ENABLE_LOW_FORMAT
#define SYS_FTL_VERSION "ftl_ver 1.2.2"
/*
* debug
*/
#define FTL_DEBUG_LEVEL D_INF
#define FTL_DEBUG(level, format, arg...) \
do {\
if ((level) <= FTL_DEBUG_LEVEL) {\
pr_info(format, ##arg);\
} \
} while (0)
#define D_ERR 0
#define D_WAN 1
#define D_INF 2
#define D_DBG 3
/* For init, load, recovery, flush_all */
#define FTL_DBG_GLB D_DBG
/* For open_blk, erase_blk, write_trace_page, get_trace_list */
#define FTL_DBG_BLK (FTL_DBG_GLB + 1)
/* For flush 1 cache, write page */
#define FTL_DBG_PAGE (FTL_DBG_GLB + 2)
/* For lookup/update l2p */
#define FTL_DBG_MAP (FTL_DBG_GLB + 3)
#define FTL_DEBUG_BREAK(exp) \
do { \
if (exp) { \
FTL_DEBUG(0, "FILE: %s:%d:\n", __FILE__, __LINE__);\
dump_ftl_info();\
while (1)\
;\
} \
} while (0)
#endif

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include <linux/mutex.h>
#include "flash.h"
#include "nand_boot.h"
#include "rk_sftl.h"
#include "typedef.h"
int sftl_flash_init(void __iomem *reg_addr)
{
int ret;
ret = nandc_flash_init(reg_addr);
if (ret == 0)
ret = sftl_init();
return ret;
}
EXPORT_SYMBOL_GPL(sftl_flash_init);
void sftl_flash_read_id(u8 chip_sel, void *buf)
{
nandc_flash_get_id(chip_sel, buf);
}
EXPORT_SYMBOL_GPL(sftl_flash_read_id);
unsigned int sftl_flash_get_capacity(void)
{
return sftl_get_density();
}
int sftl_flash_write(u32 sec, u32 n_sec, void *p_data)
{
sftl_write(sec, n_sec, p_data);
return 0;
}
int sftl_flash_read(u32 sec, u32 n_sec, void *p_data)
{
sftl_read(sec, n_sec, p_data);
return 0;
}
void sftl_flash_deinit(void)
{
u8 chip_sel = 0;
sftl_deinit();
nandc_flash_reset(chip_sel);
}
int sftl_flash_resume(void __iomem *reg_addr)
{
return nandc_flash_init(reg_addr);
}
void sftl_flash_clean_irq(void)
{
}

View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _SFC_H
#define _SFC_H
int sftl_flash_init(void __iomem *reg_addr);
void sftl_flash_read_id(u8 chip_sel, void *buf);
int sftl_flash_read(unsigned int sec, unsigned int n_sec, void *p_data);
int sftl_flash_write(unsigned int sec, unsigned int n_sec, void *p_data);
unsigned int sftl_flash_get_capacity(void);
void sftl_flash_deinit(void);
int sftl_flash_resume(void __iomem *reg_addr);
void sftl_flash_clean_irq(void);
#endif

294
drivers/rkflash/nandc.c Normal file
View File

@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/delay.h>
#include <linux/kernel.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;
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)rknandc_dma_map_single((unsigned long)master.page_vir,
fl_reg.V6.page_num * 1024,
dir);
master.spare_phy =
(u32)rknandc_dma_map_single((unsigned long)master.spare_vir,
fl_reg.V6.page_num * 64,
dir);
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);
if (master.mapped) {
rknandc_dma_unmap_single((u64)master.page_phy,
fl_reg.V6.page_num * 1024,
0);
rknandc_dma_unmap_single((u64)master.spare_phy,
fl_reg.V6.page_num * 64,
0);
}
} 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)
{
}

222
drivers/rkflash/nandc.h Normal file
View File

@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef __NAND_H
#define __NAND_H
#include <linux/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

14046
drivers/rkflash/rk_sftl.S Normal file

File diff suppressed because it is too large Load Diff

16
drivers/rkflash/rk_sftl.h Normal file
View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#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

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef __RK_FLASH_API_H
#define __RK_FLASH_API_H
#ifdef CONFIG_RK_NANDC_NAND
#include "nand_boot.h"
#endif
#ifdef CONFIG_RK_SFC_NOR
#include "sfc_nor_boot.h"
#endif
#ifdef CONFIG_RK_SFC_NAND
#include "sfc_nand_boot.h"
#endif
#endif

View File

@ -0,0 +1,704 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/version.h>
#include "rkflash_api.h"
#include "rkflash_blk.h"
#include "../soc/rockchip/flash_vendor_storage.h"
static struct flash_boot_ops nandc_nand_ops = {
#ifdef CONFIG_RK_NANDC_NAND
FLASH_TYPE_NANDC_NAND,
sftl_flash_init,
sftl_flash_read,
sftl_flash_write,
sftl_flash_get_capacity,
sftl_flash_deinit,
sftl_flash_resume,
sftl_flash_read,
sftl_flash_write,
#else
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
#endif
};
static struct flash_boot_ops sfc_nor_ops = {
#ifdef CONFIG_RK_SFC_NOR
FLASH_TYPE_SFC_NOR,
spi_flash_init,
snor_read_lba,
snor_write_lba,
snor_capacity,
snor_deinit,
snor_resume,
snor_vendor_read,
snor_vendor_write,
#else
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
#endif
};
static struct flash_boot_ops sfc_nand_ops = {
#ifdef CONFIG_RK_SFC_NAND
FLASH_TYPE_SFC_NAND,
snand_init,
snand_read,
snand_write,
snand_get_capacity,
snand_deinit,
snand_resume,
snand_read,
snand_write,
#else
-1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
#endif
};
static struct flash_boot_ops *g_boot_ops[] = {
&nandc_nand_ops,
&sfc_nor_ops,
&sfc_nand_ops,
};
static int g_flash_type = -1;
static struct flash_part disk_array[MAX_PART_COUNT];
static int g_max_part_num = 4;
#define FW_HRADER_PT_NAME ("fw_header_p")
static struct flash_part fw_header_p;
#define PART_READONLY 0x85
#define PART_WRITEONLY 0x86
#define PART_NO_ACCESS 0x87
static unsigned long totle_read_data;
static unsigned long totle_write_data;
static unsigned long totle_read_count;
static unsigned long totle_write_count;
static int rkflash_dev_initialised;
static char *mtd_read_temp_buffer;
#define MTD_RW_SECTORS (512)
static DEFINE_MUTEX(g_flash_ops_mutex);
static unsigned int rk_partition_init(struct flash_part *part)
{
int i, part_num = 0;
int desity;
struct STRUCT_PART_INFO *g_part; /* size 2KB */
g_part = kmalloc(sizeof(*g_part), GFP_KERNEL | GFP_DMA);
if (!g_part)
return 0;
if (g_boot_ops[g_flash_type]->read(0, 4, g_part) == 0) {
if (g_part->hdr.ui_fw_tag == RK_PARTITION_TAG) {
part_num = g_part->hdr.ui_part_entry_count;
desity = g_boot_ops[g_flash_type]->get_capacity();
for (i = 0; i < part_num; i++) {
memcpy(part[i].name,
g_part->part[i].sz_name,
32);
part[i].offset = g_part->part[i].ui_pt_off;
part[i].size = g_part->part[i].ui_pt_sz;
part[i].type = 0;
if (part[i].size == UINT_MAX)
part[i].size = desity - part[i].offset;
}
}
}
kfree(g_part);
memset(&fw_header_p, 0x0, sizeof(fw_header_p));
memcpy(fw_header_p.name, FW_HRADER_PT_NAME, strlen(FW_HRADER_PT_NAME));
fw_header_p.offset = 0x0;
fw_header_p.size = 0x4;
fw_header_p.type = 0;
return part_num;
}
static int rkflash_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "Totle Read %ld KB\n", totle_read_data >> 1);
seq_printf(m, "Totle Write %ld KB\n", totle_write_data >> 1);
seq_printf(m, "totle_write_count %ld\n", totle_write_count);
seq_printf(m, "totle_read_count %ld\n", totle_read_count);
return 0;
}
static int rkflash_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, rkflash_proc_show, PDE_DATA(inode));
}
static const struct file_operations rkflash_proc_fops = {
.owner = THIS_MODULE,
.open = rkflash_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int rkflash_create_procfs(void)
{
struct proc_dir_entry *ent;
ent = proc_create_data("rkflash", 0x664, NULL, &rkflash_proc_fops,
(void *)0);
if (!ent)
return -1;
return 0;
}
static int rkflash_xfer(struct flash_blk_dev *dev,
unsigned long start,
unsigned long nsector,
char *buf,
int cmd,
int totle_nsec)
{
int ret;
if (dev->disable_access ||
(cmd == WRITE && dev->readonly) ||
(cmd == READ && dev->writeonly)) {
return -EIO;
}
start += dev->off_size;
switch (cmd) {
case READ:
totle_read_data += nsector;
totle_read_count++;
mutex_lock(&g_flash_ops_mutex);
ret = g_boot_ops[g_flash_type]->read(start, nsector, buf);
mutex_unlock(&g_flash_ops_mutex);
if (ret)
ret = -EIO;
break;
case WRITE:
totle_write_data += nsector;
totle_write_count++;
mutex_lock(&g_flash_ops_mutex);
ret = g_boot_ops[g_flash_type]->write(start, nsector, buf);
mutex_unlock(&g_flash_ops_mutex);
if (ret)
ret = -EIO;
break;
default:
ret = -EIO;
break;
}
return ret;
}
static DECLARE_WAIT_QUEUE_HEAD(rkflash_thread_wait);
static unsigned long rkflash_req_jiffies;
static unsigned int rknand_req_do;
static int req_check_buffer_align(struct request *req, char **pbuf)
{
int nr_vec = 0;
struct bio_vec bvec;
struct req_iterator iter;
char *buffer;
void *firstbuf = 0;
char *nextbuffer = 0;
rq_for_each_segment(bvec, req, iter) {
buffer = page_address(bvec.bv_page) + bvec.bv_offset;
if (!firstbuf)
firstbuf = buffer;
nr_vec++;
if (nextbuffer && nextbuffer != buffer)
return 0;
nextbuffer = buffer + bvec.bv_len;
}
*pbuf = firstbuf;
return 1;
}
static int rkflash_blktrans_thread(void *arg)
{
struct flash_blk_ops *blk_ops = arg;
struct request_queue *rq = blk_ops->rq;
struct request *req = NULL;
char *buf;
struct req_iterator rq_iter;
struct bio_vec bvec;
unsigned long long sector_index = ULLONG_MAX;
unsigned long totle_nsect;
unsigned long rq_len = 0;
int rw_flag = 0;
spin_lock_irq(rq->queue_lock);
while (!blk_ops->quit) {
int res;
struct flash_blk_dev *dev;
DECLARE_WAITQUEUE(wait, current);
if (!req)
req = blk_fetch_request(rq);
if (!req) {
add_wait_queue(&blk_ops->thread_wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irq(rq->queue_lock);
rkflash_req_jiffies = HZ / 10;
wait_event_timeout(blk_ops->thread_wq,
blk_ops->quit || rknand_req_do,
rkflash_req_jiffies);
rknand_req_do = 0;
spin_lock_irq(rq->queue_lock);
remove_wait_queue(&blk_ops->thread_wq, &wait);
continue;
} else {
rkflash_req_jiffies = 1 * HZ;
}
dev = req->rq_disk->private_data;
totle_nsect = (req->__data_len) >> 9;
sector_index = blk_rq_pos(req);
rq_len = 0;
buf = 0;
res = 0;
if (req->cmd_flags & REQ_DISCARD) {
if (!__blk_end_request_cur(req, res))
req = NULL;
continue;
} else if (req->cmd_flags & REQ_FLUSH) {
if (!__blk_end_request_cur(req, res))
req = NULL;
continue;
}
rw_flag = req->cmd_flags & REQ_WRITE;
if (rw_flag == READ && mtd_read_temp_buffer) {
buf = mtd_read_temp_buffer;
req_check_buffer_align(req, &buf);
spin_unlock_irq(rq->queue_lock);
res = rkflash_xfer(dev,
sector_index,
totle_nsect,
buf,
rw_flag,
totle_nsect);
spin_lock_irq(rq->queue_lock);
if (buf == mtd_read_temp_buffer) {
char *p = buf;
rq_for_each_segment(bvec, req, rq_iter) {
memcpy(page_address(bvec.bv_page) +
bvec.bv_offset,
p,
bvec.bv_len);
p += bvec.bv_len;
}
}
} else {
rq_for_each_segment(bvec, req, rq_iter) {
if ((page_address(bvec.bv_page)
+ bvec.bv_offset)
== (buf + rq_len)) {
rq_len += bvec.bv_len;
} else {
if (rq_len) {
spin_unlock_irq(rq->queue_lock);
res = rkflash_xfer(dev,
sector_index,
rq_len >> 9,
buf,
rw_flag,
totle_nsect);
spin_lock_irq(rq->queue_lock);
}
sector_index += rq_len >> 9;
buf = (page_address(bvec.bv_page) +
bvec.bv_offset);
rq_len = bvec.bv_len;
}
}
if (rq_len) {
spin_unlock_irq(rq->queue_lock);
res = rkflash_xfer(dev,
sector_index,
rq_len >> 9,
buf,
rw_flag,
totle_nsect);
spin_lock_irq(rq->queue_lock);
}
}
__blk_end_request_all(req, res);
req = NULL;
}
pr_info("flash th quited\n");
blk_ops->flash_th_quited = 1;
if (req)
__blk_end_request_all(req, -EIO);
while ((req = blk_fetch_request(rq)) != NULL)
__blk_end_request_all(req, -ENODEV);
spin_unlock_irq(rq->queue_lock);
complete_and_exit(&blk_ops->thread_exit, 0);
return 0;
}
static void rkflash_blk_request(struct request_queue *rq)
{
struct flash_blk_ops *blk_ops = rq->queuedata;
struct request *req = NULL;
if (blk_ops->flash_th_quited) {
while ((req = blk_fetch_request(rq)) != NULL)
__blk_end_request_all(req, -ENODEV);
return;
}
rknand_req_do = 1;
wake_up(&blk_ops->thread_wq);
}
static int rkflash_open(struct block_device *bdev, fmode_t mode)
{
return 0;
}
static void rkflash_release(struct gendisk *disk, fmode_t mode)
{
};
#define DISABLE_WRITE _IO('V', 0)
#define ENABLE_WRITE _IO('V', 1)
#define DISABLE_READ _IO('V', 2)
#define ENABLE_READ _IO('V', 3)
static int rkflash_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd,
unsigned long arg)
{
struct flash_blk_dev *dev = bdev->bd_disk->private_data;
switch (cmd) {
case ENABLE_WRITE:
dev->disable_access = 0;
dev->readonly = 0;
set_disk_ro(dev->blkcore_priv, 0);
return 0;
case DISABLE_WRITE:
dev->readonly = 1;
set_disk_ro(dev->blkcore_priv, 1);
return 0;
case ENABLE_READ:
dev->disable_access = 0;
dev->writeonly = 0;
return 0;
case DISABLE_READ:
dev->writeonly = 1;
return 0;
default:
return -ENOTTY;
}
}
const struct block_device_operations rkflash_blktrans_ops = {
.owner = THIS_MODULE,
.open = rkflash_open,
.release = rkflash_release,
.ioctl = rkflash_ioctl,
};
static struct flash_blk_ops mytr = {
.name = "rkflash",
.major = 31,
.minorbits = 0,
.owner = THIS_MODULE,
};
static int rkflash_add_dev(struct flash_blk_ops *blk_ops,
struct flash_part *part)
{
struct flash_blk_dev *dev;
struct gendisk *gd;
if (part->size == 0)
return -1;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
gd = alloc_disk(1 << blk_ops->minorbits);
if (!gd) {
kfree(dev);
return -ENOMEM;
}
dev->blk_ops = blk_ops;
dev->size = part->size;
dev->off_size = part->offset;
dev->devnum = blk_ops->last_dev_index;
list_add_tail(&dev->list, &blk_ops->devs);
blk_ops->last_dev_index++;
gd->major = blk_ops->major;
gd->first_minor = (dev->devnum) << blk_ops->minorbits;
gd->fops = &rkflash_blktrans_ops;
if (part->name[0]) {
snprintf(gd->disk_name,
sizeof(gd->disk_name),
"%s",
part->name);
} else {
gd->flags = GENHD_FL_EXT_DEVT;
gd->minors = 255;
snprintf(gd->disk_name,
sizeof(gd->disk_name),
"%s%d",
blk_ops->name,
dev->devnum);
}
set_capacity(gd, dev->size);
gd->private_data = dev;
dev->blkcore_priv = gd;
gd->queue = blk_ops->rq;
gd->queue->bypass_depth = 1;
if (part->type == PART_NO_ACCESS)
dev->disable_access = 1;
if (part->type == PART_READONLY)
dev->readonly = 1;
if (part->type == PART_WRITEONLY)
dev->writeonly = 1;
if (dev->readonly)
set_disk_ro(gd, 1);
add_disk(gd);
return 0;
}
static int rkflash_remove_dev(struct flash_blk_dev *dev)
{
struct gendisk *gd;
gd = dev->blkcore_priv;
list_del(&dev->list);
gd->queue = NULL;
del_gendisk(gd);
put_disk(gd);
kfree(dev);
return 0;
}
static int rkflash_blk_register(struct flash_blk_ops *blk_ops)
{
int i, ret;
u64 offset;
rknand_req_do = 0;
blk_ops->quit = 0;
blk_ops->flash_th_quited = 0;
mtd_read_temp_buffer = kmalloc(MTD_RW_SECTORS * 512,
GFP_KERNEL | GFP_DMA);
ret = register_blkdev(blk_ops->major, blk_ops->name);
if (ret)
return -1;
spin_lock_init(&blk_ops->queue_lock);
init_completion(&blk_ops->thread_exit);
init_waitqueue_head(&blk_ops->thread_wq);
blk_ops->rq = blk_init_queue(rkflash_blk_request, &blk_ops->queue_lock);
if (!blk_ops->rq) {
unregister_blkdev(blk_ops->major, blk_ops->name);
return -1;
}
blk_queue_max_hw_sectors(blk_ops->rq, MTD_RW_SECTORS);
blk_queue_max_segments(blk_ops->rq, MTD_RW_SECTORS);
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, blk_ops->rq);
blk_queue_max_discard_sectors(blk_ops->rq, UINT_MAX >> 9);
blk_ops->rq->queuedata = blk_ops;
INIT_LIST_HEAD(&blk_ops->devs);
kthread_run(rkflash_blktrans_thread, (void *)blk_ops, "rkflash");
g_max_part_num = rk_partition_init(disk_array);
if (g_max_part_num) {
/* partition 0 is save vendor data, need hidden */
blk_ops->last_dev_index = 0;
for (i = 1; i < g_max_part_num; i++) {
offset = (u64)disk_array[i].offset;
pr_info("%10s: 0x%09llx -- 0x%09llx (%llu MB)\n",
disk_array[i].name,
offset * 512,
(u64)(offset + disk_array[i].size) * 512,
(u64)disk_array[i].size / 2048);
rkflash_add_dev(blk_ops, &disk_array[i]);
}
rkflash_add_dev(blk_ops, &fw_header_p);
} else {
struct flash_part part;
part.offset = 0;
part.size = g_boot_ops[g_flash_type]->get_capacity();
part.type = 0;
part.name[0] = 0;
rkflash_add_dev(&mytr, &part);
}
rkflash_create_procfs();
return 0;
}
static void rkflash_blk_unregister(struct flash_blk_ops *blk_ops)
{
struct list_head *this, *next;
blk_ops->quit = 1;
wake_up(&blk_ops->thread_wq);
wait_for_completion(&blk_ops->thread_exit);
list_for_each_safe(this, next, &blk_ops->devs) {
struct flash_blk_dev *dev =
list_entry(this, struct flash_blk_dev, list);
rkflash_remove_dev(dev);
}
blk_cleanup_queue(blk_ops->rq);
unregister_blkdev(blk_ops->major, blk_ops->name);
}
int rkflash_dev_init(void __iomem *reg_addr, enum flash_con_type con_type)
{
int ret;
int tmp_id, start_id, end_id;
pr_err("%s\n", __func__);
if (rkflash_dev_initialised) {
pr_err("rkflash has already inited as id[%d]\n", g_flash_type);
return -1;
}
if (con_type == FLASH_CON_TYPE_NANDC) {
start_id = FLASH_TYPE_NANDC_NAND;
end_id = FLASH_TYPE_NANDC_NAND;
} else {
start_id = FLASH_TYPE_SFC_NOR;
end_id = FLASH_TYPE_SFC_NAND;
}
for (tmp_id = start_id; tmp_id <= end_id; tmp_id++) {
pr_info("init rkflash[%d]\n", tmp_id);
if (g_boot_ops[tmp_id]->id == -1) {
pr_err("rkflash[%d] is invalid\n", tmp_id);
if (tmp_id == end_id)
return -1;
continue;
}
ret = g_boot_ops[tmp_id]->init(reg_addr);
if (ret) {
pr_err("rkflash[%d] init fail\n", tmp_id);
if (tmp_id == end_id)
return -1;
continue;
} else {
break;
}
}
pr_info("rkflash[%d] init success\n", tmp_id);
g_flash_type = tmp_id;
mytr.quit = 1;
flash_vendor_dev_ops_register(g_boot_ops[g_flash_type]->vendor_read,
g_boot_ops[g_flash_type]->vendor_write);
#ifdef CONFIG_RK_SFC_NOR_MTD
if (g_flash_type == FLASH_TYPE_SFC_NOR) {
pr_info("sfc_nor flash registered as a mtd device\n");
rkflash_dev_initialised = 1;
return 0;
}
#endif
ret = rkflash_blk_register(&mytr);
if (ret) {
pr_err("rkflash_blk_register fail\n");
g_flash_type = -1;
return -1;
}
rkflash_dev_initialised = 1;
return ret;
}
int rkflash_dev_exit(void)
{
if (rkflash_dev_initialised) {
g_flash_type = -1;
rkflash_dev_initialised = 0;
rkflash_blk_unregister(&mytr);
pr_info("%s:OK\n", __func__);
}
return 0;
}
int rkflash_dev_suspend(void)
{
mutex_lock(&g_flash_ops_mutex);
return 0;
}
int rkflash_dev_resume(void __iomem *reg_addr)
{
g_boot_ops[g_flash_type]->resume(reg_addr);
mutex_unlock(&g_flash_ops_mutex);
return 0;
}
void rkflash_dev_shutdown(void)
{
pr_info("rkflash_shutdown...\n");
if (mytr.quit == 0) {
mytr.quit = 1;
wake_up(&mytr.thread_wq);
wait_for_completion(&mytr.thread_exit);
}
g_boot_ops[g_flash_type]->deinit();
pr_info("rkflash_shutdown:OK\n");
}

View File

@ -0,0 +1,126 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef __RKFLASH_BLK_H
#define __RKFLASH_BLK_H
#include <linux/semaphore.h>
#define MAX_PART_COUNT 32
#define RK_PARTITION_TAG 0x50464B52
enum flash_con_type {
FLASH_CON_TYPE_NANDC = 0,
FLASH_CON_TYPE_SFC,
FLASH_CON_TYPE_MAX,
};
enum flash_type {
FLASH_TYPE_NANDC_NAND = 0,
FLASH_TYPE_SFC_NOR,
FLASH_TYPE_SFC_NAND,
FLASH_TYPE_MAX,
};
struct flash_boot_ops {
int id;
int (*init)(void __iomem *reg_addr);
int (*read)(u32 sec, u32 n_sec, void *p_data);
int (*write)(u32 sec, u32 n_sec, void *p_data);
u32 (*get_capacity)(void);
void (*deinit)(void);
int (*resume)(void __iomem *reg_addr);
int (*vendor_read)(u32 sec, u32 n_sec, void *p_data);
int (*vendor_write)(u32 sec, u32 n_sec, void *p_data);
};
struct flash_part {
unsigned char name[32];
unsigned int offset;
unsigned int size;
unsigned char type;
};
struct flash_blk_ops {
char *name;
int major;
int minorbits;
int last_dev_index;
struct completion thread_exit;
int quit;
int flash_th_quited;
wait_queue_head_t thread_wq; /* thread wait queue */
struct request_queue *rq;
spinlock_t queue_lock; /* queue lock */
struct list_head devs;
struct module *owner;
};
struct flash_blk_dev {
struct flash_blk_ops *blk_ops;
struct list_head list;
int devnum;
unsigned int size;
unsigned int off_size;
int readonly;
int writeonly;
int disable_access;
void *blkcore_priv;
};
enum ENUM_PARTITION_TYPE {
PART_VENDOR = 1 << 0,
PART_IDBLOCK = 1 << 1,
PART_KERNEL = 1 << 2,
PART_BOOT = 1 << 3,
PART_USER = 1 << 31
};
struct STRUCT_DATETIME {
unsigned short year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char min;
unsigned char sec;
unsigned char reserve;
};
struct STRUCT_FW_HEADER {
unsigned int ui_fw_tag; /* "RKFP" */
struct STRUCT_DATETIME dt_release_data_time;
unsigned int ui_fw_ver;
unsigned int ui_size; /* size of sturct,unit of u8 */
unsigned int ui_part_entry_offset; /* unit of sector */
unsigned int ui_backup_part_entry_offset;
unsigned int ui_part_entry_size; /* unit of u8 */
unsigned int ui_part_entry_count;
unsigned int ui_fw_size; /* unit of u8 */
unsigned char reserved[464];
unsigned int ui_part_entry_crc;
unsigned int ui_header_crc;
};
struct STRUCT_PART_ENTRY {
unsigned char sz_name[32];
enum ENUM_PARTITION_TYPE em_part_type;
unsigned int ui_pt_off; /* unit of sector */
unsigned int ui_pt_sz; /* unit of sector */
unsigned int ui_data_length; /* unit of u8 */
unsigned int ui_part_property;
unsigned char reserved[76];
};
struct STRUCT_PART_INFO {
struct STRUCT_FW_HEADER hdr; /* 0.5KB */
struct STRUCT_PART_ENTRY part[12]; /* 1.5KB */
} __packed;
int rkflash_dev_suspend(void);
int rkflash_dev_resume(void __iomem *reg_addr);
void rkflash_dev_shutdown(void);
void rkflash_dev_flush(void);
int rkflash_dev_init(void __iomem *reg_addr, enum flash_con_type type);
int rkflash_dev_exit(void);
#endif

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include "rkflash_debug.h"
#include "sfc_nor.h"
void rkflash_print_hex(char *s, void *buf, u32 width, u32 len)
{
print_hex_dump(KERN_WARNING, s, DUMP_PREFIX_OFFSET,
16, width, buf, len * width, 0);
}

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _RKFLASH_DEBUG_H
#define _RKFLASH_DEBUG_H
/*
* Print switch, set to 1 if needed
* I - info
* E - error
* HEX - multiline print
*/
#define PRINT_SWI_SFC_I 0
#define PRINT_SWI_SFC_E 1
#define PRINT_SWI_SFC_HEX 1
/*
* Test switch
*/
#if (PRINT_SWI_SFC_I)
#define PRINT_SFC_I(...) pr_info(__VA_ARGS__)
#else
#define PRINT_SFC_I(...)
#endif
#if (PRINT_SWI_SFC_E)
#define PRINT_SFC_E(...) pr_err(__VA_ARGS__)
#else
#define PRINT_SFC_E(...)
#endif
#if (PRINT_SWI_SFC_HEX)
#define PRINT_SFC_HEX(s, buf, width, len)\
rkflash_print_hex(s, buf, width, len)
#else
#define PRINT_SFC_HEX(s, buf, width, len)
#endif
void rkflash_print_hex(char *s, void *buf, u32 width, u32 len);
#endif

View File

@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <asm/cacheflush.h>
#include <linux/bootmem.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#endif
#include "nandc.h"
#include "rkflash_api.h"
#include "rkflash_blk.h"
#define RKNANDC_VERSION_AND_DATE "rknandc_base v1.1 2017-01-11"
#define RKNANDC_CLK_SET_RATE (150 * 1000 * 1000)
struct rknandc_info {
void __iomem *reg_base;
int irq;
int clk_rate;
struct clk *clk; /* controller's clk*/
struct clk *ahb_clk; /* ahb clk gate*/
struct clk *g_clk; /* clk_src_en gate*/
};
static struct rknandc_info g_nandc_info;
static struct device *g_nandc_dev;
static struct completion nandc_irq_complete;
unsigned long rknandc_dma_map_single(unsigned long ptr, int size, int dir)
{
#ifdef CONFIG_ARM64
__dma_map_area((void *)ptr, size, dir);
return ((unsigned long)virt_to_phys((void *)ptr));
#else
return dma_map_single(NULL, (void *)ptr, size
, dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
#endif
}
void rknandc_dma_unmap_single(unsigned long ptr, int size, int dir)
{
#ifdef CONFIG_ARM64
__dma_unmap_area(phys_to_virt(ptr), size, dir);
#else
dma_unmap_single(NULL, (dma_addr_t)ptr, size
, dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
#endif
}
static irqreturn_t rknandc_interrupt(int irq, void *dev_id)
{
nandc_clean_irq();
complete(&nandc_irq_complete);
return IRQ_HANDLED;
}
static int rknandc_irq_config(int mode, void *pfun)
{
int ret = 0;
int irq = g_nandc_info.irq;
if (mode)
ret = request_irq(irq, pfun, 0, "rknandc",
g_nandc_info.reg_base);
else
free_irq(irq, NULL);
return ret;
}
static int rknandc_irq_init(void)
{
init_completion(&nandc_irq_complete);
rknandc_irq_config(1, rknandc_interrupt);
return 0;
}
static int rknandc_irq_deinit(void)
{
rknandc_irq_config(0, rknandc_interrupt);
return 0;
}
static int rknandc_probe(struct platform_device *pdev)
{
int irq;
struct resource *mem;
void __iomem *membase;
int ret;
g_nandc_dev = &pdev->dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
membase = devm_ioremap_resource(&pdev->dev, mem);
if (!membase) {
dev_err(&pdev->dev, "no reg resource?\n");
return -1;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return irq;
}
g_nandc_info.irq = irq;
g_nandc_info.reg_base = membase;
g_nandc_info.ahb_clk = devm_clk_get(&pdev->dev, "hclk_nandc");
g_nandc_info.clk = devm_clk_get(&pdev->dev, "clk_nandc");
g_nandc_info.g_clk = devm_clk_get(&pdev->dev, "g_clk_nandc");
if (unlikely(IS_ERR(g_nandc_info.clk)) ||
unlikely(IS_ERR(g_nandc_info.ahb_clk))) {
dev_err(&pdev->dev, "%s get clk error\n", __func__);
return -1;
}
clk_prepare_enable(g_nandc_info.g_clk);
clk_prepare_enable(g_nandc_info.ahb_clk);
clk_set_rate(g_nandc_info.clk, RKNANDC_CLK_SET_RATE);
g_nandc_info.clk_rate = clk_get_rate(g_nandc_info.clk);
clk_prepare_enable(g_nandc_info.clk);
dev_info(&pdev->dev,
"%s clk rate = %d\n",
__func__,
g_nandc_info.clk_rate);
rknandc_irq_init();
ret = rkflash_dev_init(g_nandc_info.reg_base, FLASH_CON_TYPE_NANDC);
return ret;
}
static int rknandc_suspend(struct platform_device *pdev, pm_message_t state)
{
return rkflash_dev_suspend();
}
static int rknandc_resume(struct platform_device *pdev)
{
return rkflash_dev_resume(g_nandc_info.reg_base);
}
static void rknandc_shutdown(struct platform_device *pdev)
{
rkflash_dev_shutdown();
}
#ifdef CONFIG_OF
static const struct of_device_id of_rknandc_match[] = {
{.compatible = "rockchip,nandc"},
{}
};
#endif
static struct platform_driver rknandc_driver = {
.probe = rknandc_probe,
.suspend = rknandc_suspend,
.resume = rknandc_resume,
.shutdown = rknandc_shutdown,
.driver = {
.name = "rknandc",
#ifdef CONFIG_OF
.of_match_table = of_rknandc_match,
#endif
},
};
static void __exit rknandc_driver_exit(void)
{
rkflash_dev_exit();
rknandc_irq_deinit();
platform_driver_unregister(&rknandc_driver);
}
static int __init rknandc_driver_init(void)
{
int ret = 0;
pr_err("%s\n", RKNANDC_VERSION_AND_DATE);
ret = platform_driver_register(&rknandc_driver);
return ret;
}
module_init(rknandc_driver_init);
module_exit(rknandc_driver_exit);
MODULE_ALIAS(DRIVER_NAME);

View File

@ -0,0 +1,207 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <asm/cacheflush.h>
#include <linux/bootmem.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#endif
#include "sfc.h"
#include "rkflash_api.h"
#include "rkflash_blk.h"
#define RKSFC_VERSION_AND_DATE "rksfc_base v1.1 2016-01-08"
#define RKSFC_CLK_SET_RATE (100 * 1000 * 1000)
struct rksfc_info {
void __iomem *reg_base;
int irq;
int clk_rate;
struct clk *clk; /* sfc clk*/
struct clk *ahb_clk; /* ahb clk gate*/
};
static struct rksfc_info g_sfc_info;
static struct device *g_sfc_dev;
static struct completion sfc_irq_complete;
unsigned long rksfc_dma_map_single(unsigned long ptr, int size, int dir)
{
#ifdef CONFIG_ARM64
__dma_map_area((void *)ptr, size, dir);
return ((unsigned long)virt_to_phys((void *)ptr));
#else
return dma_map_single(NULL, (void *)ptr, size
, dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
#endif
}
void rksfc_dma_unmap_single(unsigned long ptr, int size, int dir)
{
#ifdef CONFIG_ARM64
__dma_unmap_area(phys_to_virt(ptr), size, dir);
#else
dma_unmap_single(NULL, (dma_addr_t)ptr, size
, dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
#endif
}
int rksfc_get_reg_addr(unsigned long *p_sfc_addr)
{
*p_sfc_addr = (unsigned long)g_sfc_info.reg_base;
return 0;
}
static irqreturn_t rksfc_interrupt(int irq, void *dev_id)
{
sfc_clean_irq();
complete(&sfc_irq_complete);
return IRQ_HANDLED;
}
void rksfc_irq_flag_init(void)
{
init_completion(&sfc_irq_complete);
}
void rksfc_wait_for_irq_completed(void)
{
wait_for_completion_timeout(&sfc_irq_complete,
msecs_to_jiffies(10));
}
static int rksfc_irq_config(int mode, void *pfun)
{
int ret = 0;
int irq = g_sfc_info.irq;
if (mode)
ret = request_irq(irq, pfun, 0, "rksfc",
g_sfc_info.reg_base);
else
free_irq(irq, NULL);
return ret;
}
static int rksfc_irq_init(void)
{
init_completion(&sfc_irq_complete);
rksfc_irq_config(1, rksfc_interrupt);
return 0;
}
static int rksfc_irq_deinit(void)
{
rksfc_irq_config(0, rksfc_interrupt);
return 0;
}
static int rksfc_probe(struct platform_device *pdev)
{
int irq;
struct resource *mem;
void __iomem *membase;
int ret;
g_sfc_dev = &pdev->dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
membase = devm_ioremap_resource(&pdev->dev, mem);
if (!membase) {
dev_err(&pdev->dev, "no reg resource?\n");
return -1;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return irq;
}
g_sfc_info.irq = irq;
g_sfc_info.reg_base = membase;
g_sfc_info.ahb_clk = devm_clk_get(&pdev->dev, "hclk_sfc");
g_sfc_info.clk = devm_clk_get(&pdev->dev, "clk_sfc");
if (unlikely(IS_ERR(g_sfc_info.clk)) ||
unlikely(IS_ERR(g_sfc_info.ahb_clk))) {
dev_err(&pdev->dev, "%s get clk error\n", __func__);
return -1;
}
clk_prepare_enable(g_sfc_info.ahb_clk);
clk_set_rate(g_sfc_info.clk, RKSFC_CLK_SET_RATE);
g_sfc_info.clk_rate = clk_get_rate(g_sfc_info.clk);
clk_prepare_enable(g_sfc_info.clk);
dev_info(&pdev->dev,
"%s clk rate = %d\n",
__func__,
g_sfc_info.clk_rate);
rksfc_irq_init();
ret = rkflash_dev_init(g_sfc_info.reg_base, FLASH_CON_TYPE_SFC);
return ret;
}
static int rksfc_suspend(struct platform_device *pdev, pm_message_t state)
{
return rkflash_dev_suspend();
}
static int rksfc_resume(struct platform_device *pdev)
{
return rkflash_dev_resume(g_sfc_info.reg_base);
}
static void rksfc_shutdown(struct platform_device *pdev)
{
rkflash_dev_shutdown();
}
#ifdef CONFIG_OF
static const struct of_device_id of_rksfc_match[] = {
{.compatible = "rockchip,sfc"},
{}
};
#endif
static struct platform_driver rksfc_driver = {
.probe = rksfc_probe,
.suspend = rksfc_suspend,
.resume = rksfc_resume,
.shutdown = rksfc_shutdown,
.driver = {
.name = "rksfc",
#ifdef CONFIG_OF
.of_match_table = of_rksfc_match,
#endif
},
};
static void __exit rksfc_driver_exit(void)
{
rkflash_dev_exit();
rksfc_irq_deinit();
platform_driver_unregister(&rksfc_driver);
}
static int __init rksfc_driver_init(void)
{
int ret = 0;
pr_err("%s\n", RKSFC_VERSION_AND_DATE);
ret = platform_driver_register(&rksfc_driver);
return ret;
}
module_init(rksfc_driver_init);
module_exit(rksfc_driver_exit);
MODULE_ALIAS(DRIVER_NAME);

191
drivers/rkflash/sfc.c Normal file
View File

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include "sfc.h"
static void __iomem *g_sfc_reg;
static void sfc_reset(void)
{
int timeout = 10000;
writel(SFC_RESET, g_sfc_reg + SFC_RCVR);
while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) {
sfc_delay(1);
timeout--;
}
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
}
u16 sfc_get_version(void)
{
return (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff);
}
int sfc_init(void __iomem *reg_addr)
{
g_sfc_reg = reg_addr;
sfc_reset();
writel(0, g_sfc_reg + SFC_CTRL);
return SFC_OK;
}
void sfc_clean_irq(void)
{
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR);
}
int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data)
{
int ret = SFC_OK;
union SFCCMD_DATA cmd;
int reg;
int timeout = 0;
reg = readl(g_sfc_reg + SFC_FSR);
if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) ||
(readl(g_sfc_reg + SFC_SR) & SFC_BUSY))
sfc_reset();
cmd.d32 = sfcmd;
if (cmd.b.addrbits == SFC_ADDR_XBITS) {
union SFCCTRL_DATA ctrl;
ctrl.d32 = sfctrl;
if (!ctrl.b.addrbits)
return SFC_PARAM_ERR;
/* Controller plus 1 automatically */
writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT);
}
/* shift in the data at negedge sclk_out */
sfctrl |= 0x2;
writel(sfctrl, g_sfc_reg + SFC_CTRL);
writel(sfcmd, g_sfc_reg + SFC_CMD);
if (cmd.b.addrbits)
writel(addr, g_sfc_reg + SFC_ADDR);
if (!cmd.b.datasize)
goto exit_wait;
if (SFC_ENABLE_DMA & sfctrl) {
unsigned long dma_addr;
u8 direction = (cmd.b.rw == SFC_WRITE) ? 1 : 0;
dma_addr = rksfc_dma_map_single((unsigned long)data,
cmd.b.datasize,
direction);
rksfc_irq_flag_init();
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
writel(~(FINISH_INT), g_sfc_reg + SFC_IMR);
writel((u32)dma_addr, g_sfc_reg + SFC_DMA_ADDR);
writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER);
timeout = cmd.b.datasize * 10;
rksfc_wait_for_irq_completed();
while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) &&
(timeout-- > 0))
sfc_delay(1);
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
if (timeout <= 0)
ret = SFC_WAIT_TIMEOUT;
direction = (cmd.b.rw == SFC_WRITE) ? 1 : 0;
rksfc_dma_unmap_single(dma_addr,
cmd.b.datasize,
direction);
} else {
u32 i, words, count, bytes;
union SFCFSR_DATA fifostat;
u32 *p_data = (u32 *)data;
if (cmd.b.rw == SFC_WRITE) {
words = (cmd.b.datasize + 3) >> 2;
while (words) {
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
if (fifostat.b.txlevel > 0) {
count = words < fifostat.b.txlevel ?
words : fifostat.b.txlevel;
for (i = 0; i < count; i++) {
writel(*p_data++,
g_sfc_reg + SFC_DATA);
words--;
}
if (words == 0)
break;
timeout = 0;
} else {
sfc_delay(1);
if (timeout++ > 10000) {
ret = SFC_TX_TIMEOUT;
break;
}
}
}
} else {
/* SFC_READ == cmd.b.rw */
bytes = cmd.b.datasize & 0x3;
words = cmd.b.datasize >> 2;
while (words) {
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
if (fifostat.b.rxlevel > 0) {
u32 count;
count = words < fifostat.b.rxlevel ?
words : fifostat.b.rxlevel;
for (i = 0; i < count; i++) {
*p_data++ = readl(g_sfc_reg +
SFC_DATA);
words--;
}
if (words == 0)
break;
timeout = 0;
} else {
sfc_delay(1);
if (timeout++ > 10000) {
ret = SFC_RX_TIMEOUT;
break;
}
}
}
timeout = 0;
while (bytes) {
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
if (fifostat.b.rxlevel > 0) {
u8 *p_data1 = (u8 *)p_data;
words = readl(g_sfc_reg + SFC_DATA);
for (i = 0; i < bytes; i++)
p_data1[i] =
(u8)((words >> (i * 8)) & 0xFF);
break;
}
sfc_delay(1);
if (timeout++ > 10000) {
ret = SFC_RX_TIMEOUT;
break;
}
}
}
}
exit_wait:
timeout = 0; /* wait cmd or data send complete */
while (!(readl(g_sfc_reg + SFC_FSR) & SFC_TXEMPTY)) {
sfc_delay(1);
if (timeout++ > 100000) { /* wait 100ms */
ret = SFC_TX_TIMEOUT;
break;
}
}
sfc_delay(1); /* CS# High Time (read/write) >100ns */
return ret;
}

179
drivers/rkflash/sfc.h Normal file
View File

@ -0,0 +1,179 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _SFC_H
#define _SFC_H
#define SFC_VER_3 0x3 /* ver 3, else ver 1 */
#define SFC_MAX_IOSIZE (1024 * 8) /* 8K byte */
#define SFC_EN_INT (0) /* enable interrupt */
#define SFC_EN_DMA (1) /* enable dma */
#define SFC_FIFO_DEPTH (0x10) /* 16 words */
/* FIFO watermark */
#define SFC_RX_WMARK (SFC_FIFO_DEPTH) /* RX watermark level */
#define SFC_TX_WMARK (SFC_FIFO_DEPTH) /* TX watermark level */
#define SFC_RX_WMARK_SHIFT (8)
#define SFC_TX_WMARK_SHIFT (0)
/*return value*/
#define SFC_OK (0)
#define SFC_ERROR (-1)
#define SFC_PARAM_ERR (-2)
#define SFC_TX_TIMEOUT (-3)
#define SFC_RX_TIMEOUT (-4)
#define SFC_WAIT_TIMEOUT (-5)
#define SFC_BUSY_TIMEOUT (-6)
#define SFC_ECC_FAIL (-7)
#define SFC_PROG_FAIL (-8)
#define SFC_ERASE_FAIL (-9)
/* SFC_CMD Register */
#define SFC_ADDR_0BITS (0)
#define SFC_ADDR_24BITS (1)
#define SFC_ADDR_32BITS (2)
#define SFC_ADDR_XBITS (3)
#define SFC_WRITE (1)
#define SFC_READ (0)
/* SFC_CTRL Register */
#define SFC_1BITS_LINE (0)
#define SFC_2BITS_LINE (1)
#define SFC_4BITS_LINE (2)
#define SFC_ENABLE_DMA BIT(14)
#define sfc_delay(us) udelay(us)
#define DMA_INT BIT(7) /* dma interrupt */
#define NSPIERR_INT BIT(6) /* Nspi error interrupt */
#define AHBERR_INT BIT(5) /* Ahb bus error interrupt */
#define FINISH_INT BIT(4) /* Transfer finish interrupt */
#define TXEMPTY_INT BIT(3) /* Tx fifo empty interrupt */
#define TXOF_INT BIT(2) /* Tx fifo overflow interrupt */
#define RXUF_INT BIT(1) /* Rx fifo underflow interrupt */
#define RXFULL_INT BIT(0) /* Rx fifo full interrupt */
/* SFC_FSR Register*/
#define SFC_RXFULL BIT(3) /* rx fifo full */
#define SFC_RXEMPTY BIT(2) /* rx fifo empty */
#define SFC_TXEMPTY BIT(1) /* tx fifo empty */
#define SFC_TXFULL BIT(0) /* tx fifo full */
/* SFC_RCVR Register */
#define SFC_RESET BIT(0) /* controller reset */
/* SFC_SR Register */
/* sfc busy flag. When busy, don't try to set the control register */
#define SFC_BUSY BIT(0)
/* SFC_DMA_TRIGGER Register */
/* Dma start trigger signal. Auto cleared after write */
#define SFC_DMA_START BIT(0)
#define SFC_CTRL 0x00
#define SFC_IMR 0x04
#define SFC_ICLR 0x08
#define SFC_FTLR 0x0C
#define SFC_RCVR 0x10
#define SFC_AX 0x14
#define SFC_ABIT 0x18
#define SFC_MASKISR 0x1C
#define SFC_FSR 0x20
#define SFC_SR 0x24
#define SFC_RAWISR 0x28
#define SFC_VER 0x2C
#define SFC_QOP 0x30
#define SFC_DMA_TRIGGER 0x80
#define SFC_DMA_ADDR 0x84
#define SFC_CMD 0x100
#define SFC_ADDR 0x104
#define SFC_DATA 0x108
union SFCFSR_DATA {
u32 d32;
struct {
unsigned txempty : 1;
unsigned txfull : 1;
unsigned rxempty : 1;
unsigned rxfull : 1;
unsigned reserved7_4 : 4;
unsigned txlevel : 5;
unsigned reserved15_13 : 3;
unsigned rxlevel : 5;
unsigned reserved31_21 : 11;
} b;
};
/*------------------------------ Global Typedefs -----------------------------*/
enum SFC_DATA_LINES {
DATA_LINES_X1 = 0,
DATA_LINES_X2,
DATA_LINES_X4
};
union SFCCTRL_DATA {
/* raw register data */
u32 d32;
/* register bits */
struct {
/* spi mode select */
unsigned mode : 1;
/*
* Shift in phase selection
* 0: shift in the flash data at posedge sclk_out
* 1: shift in the flash data at negedge sclk_out
*/
unsigned sps : 1;
unsigned reserved3_2 : 2;
/* sclk_idle_level_cycles */
unsigned scic : 4;
/* Cmd bits number */
unsigned cmdlines : 2;
/* Address bits number */
unsigned addrlines : 2;
/* Data bits number */
unsigned datalines : 2;
/* this bit is not exit in regiseter, just use for code param */
unsigned enbledma : 1;
unsigned reserved15 : 1;
unsigned addrbits : 5;
unsigned reserved31_21 : 11;
} b;
};
union SFCCMD_DATA {
/* raw register data */
u32 d32;
/* register bits */
struct {
/* Command that will send to Serial Flash */
unsigned cmd : 8;
/* Dummy bits number */
unsigned dummybits : 4;
/* 0: read, 1: write */
unsigned rw : 1;
/* Continuous read mode */
unsigned readmode : 1;
/* Address bits number */
unsigned addrbits : 2;
/* Transferred bytes number */
unsigned datasize : 14;
/* Chip select */
unsigned cs : 2;
} b;
};
int sfc_init(void __iomem *reg_addr);
int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data);
u16 sfc_get_version(void);
void sfc_clean_irq(void);
int rksfc_get_reg_addr(unsigned long *p_sfc_addr);
void sfc_handle_irq(void);
unsigned long rksfc_dma_map_single(unsigned long ptr, int size, int dir);
void rksfc_dma_unmap_single(unsigned long ptr, int size, int dir);
void rksfc_irq_flag_init(void);
void rksfc_wait_for_irq_completed(void);
#endif

584
drivers/rkflash/sfc_nand.c Normal file
View File

@ -0,0 +1,584 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include "flash.h"
#include "flash_com.h"
#include "sfc.h"
#include "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)
struct SFC_NAND_DEV_T {
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;
};
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 SFC_NAND_DEV_T 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;
ret = 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;
ret = sfc_request(sfcmd.d32, 0, addr, p_data);
ret = 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_E("%s[0x%x], ret=0x%x\n", __func__, addr, ret);
if (p_data)
rknand_print_hex("data:", p_data, 4, 8);
if (p_spare)
rknand_print_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_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_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_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_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_E("ERR:page%x, ret=%x\n", page_addr, ret);
rknand_print_hex("data:", pread, 4, 8);
rknand_print_hex("spare:", pspare_read, 4, 2);
}
}
sfc_nand_erase_block(0, blk * blk_addr);
if (is_bad_blk)
bad_blk_num++;
}
PRINT_E("bad_blk_num = %d, bad_page_num = %d\n",
bad_blk_num, bad_page_num);
PRINT_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 __iomem *sfc_addr)
{
PRINT_E("%s\n", __func__);
sfc_init(sfc_addr);
sfc_nand_read_id_raw(id_byte);
PRINT_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_E("sfc_nand A0 = 0x%x\n", status);
sfc_nand_read_feature(0xB0, &status);
PRINT_E("sfc_nand B0 = 0x%x\n", status);
sfc_nand_read_feature(0xC0, &status);
PRINT_E("sfc_nand C0 = 0x%x\n", status);
PRINT_E("read_lines = %x\n", sfc_nand_dev.read_lines);
PRINT_E("prog_lines = %x\n", sfc_nand_dev.prog_lines);
PRINT_E("page_read_cmd = %x\n", sfc_nand_dev.page_read_cmd);
PRINT_E("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;
}
void sfc_nand_deinit(void)
{
}
int sfc_nand_read_id(u8 *data)
{
memcpy(data, id_byte, 3);
return 0;
}

View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef __SFC_NAND_H
#define __SFC_NAND_H
/* Manufactory ID */
#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)
u32 sfc_nand_init(void __iomem *sfc_addr);
void sfc_nand_deinit(void);
int sfc_nand_read_id(u8 *buf);
#endif

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include <linux/mutex.h>
#include "rk_sftl.h"
#include "sfc_nand.h"
#include "sfc_nand_boot.h"
#include "typedef.h"
int snand_init(void __iomem *reg_addr)
{
int ret;
ret = sfc_nand_init(reg_addr);
if (ret == 0)
ret = sftl_init();
return ret;
}
EXPORT_SYMBOL_GPL(snand_init);
unsigned int snand_get_capacity(void)
{
return sftl_get_density();
}
int snand_write(u32 sec, u32 n_sec, void *p_data)
{
return sftl_write(sec, n_sec, p_data);
}
int snand_read(u32 sec, u32 n_sec, void *p_data)
{
return sftl_read(sec, n_sec, p_data);
}
void snand_deinit(void)
{
sftl_deinit();
sfc_nand_deinit();
}
int snand_resume(void __iomem *reg_addr)
{
return sfc_nand_init(reg_addr);
}

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _SFC_NOR_BOOT_H
#define _SFC_NOR_BOOT_H
int snand_init(void __iomem *reg_addr);
int snand_read(unsigned int sec, unsigned int n_sec, void *p_data);
int snand_write(unsigned int sec, unsigned int n_sec, void *p_data);
unsigned int snand_get_capacity(void);
void snand_deinit(void);
int snand_resume(void __iomem *reg_addr);
void sfc_clean_irq(void);
#endif

656
drivers/rkflash/sfc_nor.c Normal file
View File

@ -0,0 +1,656 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include "sfc_nor.h"
#include "rkflash_debug.h"
static struct flash_info spi_flash_tbl[] = {
/* GD25Q32B */
{0xc84016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 13, 9, 0},
/* GD25Q64B */
{0xc84017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0},
/* GD25Q127C and GD25Q128C*/
{0xc84018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0},
/* GD25Q256B */
{0xc84019, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1C, 16, 6, 0},
/* GD25Q512MC */
{0xc84020, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1C, 17, 6, 0},
/* 25Q128FV */
{0xef4018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0},
/* 25Q256FV */
{0xef4019, 128, 8, 0x13, 0x02, 0x6C, 0x32, 0x20, 0xD8, 0x3C, 16, 9, 0},
/* XT25F128A */
{0x207018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x00, 15, 0, 0},
/* MX25L25635E/F */
{0xc22019, 128, 8, 0x03, 0x02, 0x6B, 0x38, 0x20, 0xD8, 0x30, 16, 6, 0},
};
static const u8 sfnor_dev_code[] = {
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19
};
static const u32 sfnor_capacity[] = {
0x20000, /* 128k-byte */
0x40000, /* 256k-byte */
0x80000, /* 512k-byte */
0x100000, /* 1M-byte */
0x200000, /* 2M-byte */
0x400000, /* 4M-byte */
0x800000, /* 8M-byte */
0x1000000, /* 16M-byte */
0x2000000 /* 32M-byte */
};
struct SFNOR_DEV sfnor_dev;
struct flash_info *g_spi_flash_info;
static int snor_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;
}
int snor_reset_device(void)
{
int ret;
union SFCCMD_DATA sfcmd;
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_ENABLE_RESER;
sfc_request(sfcmd.d32, 0, 0, NULL);
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_RESET_DEVICE;
ret = sfc_request(sfcmd.d32, 0, 0, NULL);
/* tRST=30us , delay 1ms here */
mdelay(1);
return ret;
}
static int snor_enter_4byte_mode(void)
{
int ret;
union SFCCMD_DATA sfcmd;
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_ENTER_4BYTE_MODE;
ret = sfc_request(sfcmd.d32, 0, 0, NULL);
return ret;
}
static int snor_read_status(u32 reg_index, u8 *status)
{
int ret;
union SFCCMD_DATA sfcmd;
u8 read_stat_cmd[] = {CMD_READ_STATUS,
CMD_READ_STATUS2, CMD_READ_STATUS3};
sfcmd.d32 = 0;
sfcmd.b.cmd = read_stat_cmd[reg_index];
sfcmd.b.datasize = 1;
ret = sfc_request(sfcmd.d32, 0, 0, status);
return ret;
}
static int snor_wait_busy(int timeout)
{
int ret;
union SFCCMD_DATA sfcmd;
int i;
u32 status;
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_READ_STATUS;
sfcmd.b.datasize = 1;
for (i = 0; i < timeout; i++) {
ret = sfc_request(sfcmd.d32, 0, 0, &status);
if (ret != SFC_OK)
return ret;
if ((status & 0x01) == 0)
return SFC_OK;
sfc_delay(1);
}
PRINT_SFC_E("%s error %x\n", __func__, timeout);
return SFC_BUSY_TIMEOUT;
}
static int snor_write_status2(u32 reg_index, u8 status)
{
int ret;
union SFCCMD_DATA sfcmd;
u8 status2[2];
u8 read_index;
status2[reg_index] = status;
read_index = (reg_index == 0) ? 1 : 0;
ret = snor_read_status(read_index, &status2[read_index]);
if (ret != SFC_OK)
return ret;
snor_write_en();
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_WRITE_STATUS;
sfcmd.b.datasize = 2;
sfcmd.b.rw = SFC_WRITE;
ret = sfc_request(sfcmd.d32, 0, 0, &status2[0]);
if (ret != SFC_OK)
return ret;
ret = snor_wait_busy(10000); /* 10ms */
return ret;
}
static int snor_write_status(u32 reg_index, u8 status)
{
int ret;
union SFCCMD_DATA sfcmd;
u8 write_stat_cmd[] = {CMD_WRITE_STATUS,
CMD_WRITE_STATUS2, CMD_WRITE_STATUS3};
snor_write_en();
sfcmd.d32 = 0;
sfcmd.b.cmd = write_stat_cmd[reg_index];
sfcmd.b.datasize = 1;
sfcmd.b.rw = SFC_WRITE;
ret = sfc_request(sfcmd.d32, 0, 0, &status);
if (ret != SFC_OK)
return ret;
ret = snor_wait_busy(10000); /* 10ms */
return ret;
}
int snor_erase(struct SFNOR_DEV *p_dev,
u32 addr,
enum NOR_ERASE_TYPE erase_type)
{
int ret;
union SFCCMD_DATA sfcmd;
int timeout[] = {400, 2000, 40000}; /* ms */
if (erase_type > ERASE_CHIP)
return SFC_PARAM_ERR;
sfcmd.d32 = 0;
if (erase_type == ERASE_BLOCK64K)
sfcmd.b.cmd = p_dev->blk_erase_cmd;
else if (erase_type == ERASE_SECTOR)
sfcmd.b.cmd = p_dev->sec_erase_cmd;
else
sfcmd.b.cmd = CMD_CHIP_ERASE;
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)
sfcmd.b.addrbits = SFC_ADDR_32BITS;
snor_write_en();
ret = sfc_request(sfcmd.d32, 0, addr, NULL);
if (ret != SFC_OK)
return ret;
ret = snor_wait_busy(timeout[erase_type] * 1000);
return ret;
}
int snor_prog_page(struct SFNOR_DEV *p_dev,
u32 addr,
void *p_data,
u32 size)
{
int ret;
union SFCCMD_DATA sfcmd;
union SFCCTRL_DATA sfctrl;
sfcmd.d32 = 0;
sfcmd.b.cmd = p_dev->prog_cmd;
sfcmd.b.addrbits = SFC_ADDR_24BITS;
sfcmd.b.datasize = size;
sfcmd.b.rw = SFC_WRITE;
sfctrl.d32 = 0;
sfctrl.b.datalines = p_dev->prog_lines;
sfctrl.b.enbledma = 0;
if (p_dev->prog_cmd == CMD_PAGE_PROG_A4)
sfctrl.b.addrlines = SFC_4BITS_LINE;
if (p_dev->addr_mode == ADDR_MODE_4BYTE)
sfcmd.b.addrbits = SFC_ADDR_32BITS;
snor_write_en();
ret = sfc_request(sfcmd.d32, sfctrl.d32, addr, p_data);
if (ret != SFC_OK)
return ret;
ret = snor_wait_busy(10000);
return ret;
}
static int snor_prog(struct SFNOR_DEV *p_dev, u32 addr, void *p_data, u32 size)
{
int ret = SFC_OK;
u32 page_size, len;
u8 *p_buf = (u8 *)p_data;
page_size = NOR_PAGE_SIZE;
while (size) {
len = page_size < size ? page_size : size;
ret = snor_prog_page(p_dev, addr, p_buf, len);
if (ret != SFC_OK)
return ret;
size -= len;
addr += len;
p_buf += len;
}
return ret;
}
static int snor_enable_QE(struct SFNOR_DEV *p_dev)
{
int ret = SFC_OK;
int reg_index;
int bit_offset;
u8 status;
if (p_dev->manufacturer == MID_GIGADEV ||
p_dev->manufacturer == MID_WINBOND) {
reg_index = p_dev->QE_bits >> 3;
bit_offset = p_dev->QE_bits & 0x7;
ret = snor_read_status(reg_index, &status);
if (ret != SFC_OK)
return ret;
if (status & (1 << bit_offset)) /* is QE bit set */
return SFC_OK;
status |= (1 << bit_offset);
return p_dev->write_status(reg_index, status);
}
return ret;
}
int snor_disable_QE(struct SFNOR_DEV *p_dev)
{
int ret = SFC_OK;
int reg_index;
int bit_offset;
u8 status;
if (p_dev->manufacturer == MID_GIGADEV ||
p_dev->manufacturer == MID_WINBOND) {
reg_index = p_dev->QE_bits >> 3;
bit_offset = p_dev->QE_bits & 0x7;
ret = snor_read_status(reg_index, &status);
if (ret != SFC_OK)
return ret;
if (!(status & (1 << bit_offset)))
return SFC_OK;
status &= ~(1 << bit_offset);
return p_dev->write_status(reg_index, status);
}
return ret;
}
#if (SNOR_4BIT_DATA_DETECT_EN)
static int snor_set_dlines(struct SFNOR_DEV *p_dev, enum SFC_DATA_LINES lines)
{
int ret;
u8 read_cmd[] = {CMD_FAST_READ_X1, CMD_FAST_READ_X2, CMD_FAST_READ_X4};
if (lines == DATA_LINES_X4) {
ret = snor_enable_QE(p_dev);
if (ret != SFC_OK)
return ret;
}
p_dev->read_lines = lines;
p_dev->read_cmd = read_cmd[lines];
if (p_dev->manufacturer == MID_GIGADEV ||
p_dev->manufacturer == MID_WINBOND ||
p_dev->manufacturer == MID_MACRONIX) {
p_dev->prog_lines = (lines != DATA_LINES_X2) ?
lines : DATA_LINES_X1;
if (lines == DATA_LINES_X1) {
p_dev->prog_cmd = CMD_PAGE_PROG;
} else {
if (p_dev->manufacturer == MID_GIGADEV ||
p_dev->manufacturer == MID_WINBOND)
p_dev->prog_cmd = CMD_PAGE_PROG_X4;
else
p_dev->prog_cmd = CMD_PAGE_PROG_A4;
}
}
return SFC_OK;
}
#endif
int snor_read_data(struct SFNOR_DEV *p_dev,
u32 addr,
void *p_data,
u32 size)
{
int ret;
union SFCCMD_DATA sfcmd;
union SFCCTRL_DATA sfctrl;
sfcmd.d32 = 0;
sfcmd.b.cmd = p_dev->read_cmd;
sfcmd.b.datasize = size;
sfcmd.b.addrbits = SFC_ADDR_24BITS;
sfctrl.d32 = 0;
sfctrl.b.datalines = p_dev->read_lines;
if (!(size & 0x3) && size >= 4)
sfctrl.b.enbledma = 0;
if (p_dev->read_cmd == CMD_FAST_READ_X1 ||
p_dev->read_cmd == CMD_FAST_READ_X4 ||
p_dev->read_cmd == CMD_FAST_READ_X2 ||
p_dev->read_cmd == CMD_FAST_4READ_X4) {
sfcmd.b.dummybits = 8;
} else if (p_dev->read_cmd == CMD_FAST_READ_A4) {
sfcmd.b.addrbits = SFC_ADDR_32BITS;
addr = (addr << 8) | 0xFF; /* Set M[7:0] = 0xFF */
sfcmd.b.dummybits = 4;
sfctrl.b.addrlines = SFC_4BITS_LINE;
}
if (p_dev->addr_mode == ADDR_MODE_4BYTE)
sfcmd.b.addrbits = SFC_ADDR_32BITS;
ret = sfc_request(sfcmd.d32, sfctrl.d32, addr, p_data);
return ret;
}
int snor_read(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data)
{
int ret = SFC_OK;
u32 addr, size, len;
u8 *p_buf = (u8 *)p_data;
if ((sec + n_sec) > p_dev->capacity)
return SFC_PARAM_ERR;
mutex_lock(&p_dev->lock);
addr = sec << 9;
size = n_sec << 9;
while (size) {
len = size < SFC_MAX_IOSIZE ? size : SFC_MAX_IOSIZE;
ret = snor_read_data(p_dev, addr, p_buf, len);
if (ret != SFC_OK) {
PRINT_SFC_E("snor_read_data %x ret= %x\n",
addr >> 9, ret);
goto out;
}
size -= len;
addr += len;
p_buf += len;
}
out:
mutex_unlock(&p_dev->lock);
if (!ret)
ret = n_sec;
return ret;
}
int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data)
{
int ret = SFC_OK;
u32 len, blk_size, offset;
u8 *p_buf = (u8 *)p_data;
if ((sec + n_sec) > p_dev->capacity)
return SFC_PARAM_ERR;
mutex_lock(&p_dev->lock);
while (n_sec) {
if (sec < 512 || sec >= p_dev->capacity - 512)
blk_size = 8;
else
blk_size = p_dev->blk_size;
offset = (sec & (blk_size - 1));
if (!offset) {
ret = snor_erase(p_dev, sec << 9, (blk_size == 8) ?
ERASE_SECTOR : ERASE_BLOCK64K);
if (ret != SFC_OK) {
PRINT_SFC_E("snor_erase %x ret= %x\n",
sec, ret);
goto out;
}
}
len = (blk_size - offset) < n_sec ?
(blk_size - offset) : n_sec;
ret = snor_prog(p_dev, sec << 9, p_buf, len << 9);
if (ret != SFC_OK) {
PRINT_SFC_E("snor_prog %x ret= %x\n", sec, ret);
goto out;
}
n_sec -= len;
sec += len;
p_buf += len << 9;
}
out:
mutex_unlock(&p_dev->lock);
if (!ret)
ret = n_sec;
return ret;
}
int snor_read_id(u8 *data)
{
int ret;
union SFCCMD_DATA sfcmd;
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_READ_JEDECID;
sfcmd.b.datasize = 3;
ret = sfc_request(sfcmd.d32, 0, 0, data);
return ret;
}
static int snor_read_parameter(u32 addr, u8 *data)
{
int ret;
union SFCCMD_DATA sfcmd;
sfcmd.d32 = 0;
sfcmd.b.cmd = CMD_READ_PARAMETER;
sfcmd.b.datasize = 1;
sfcmd.b.addrbits = SFC_ADDR_24BITS;
sfcmd.b.dummybits = 8;
ret = sfc_request(sfcmd.d32, 0, addr, data);
return ret;
}
u32 snor_get_capacity(struct SFNOR_DEV *p_dev)
{
return p_dev->capacity;
}
static void snor_print_spi_chip_info(struct SFNOR_DEV *p_dev)
{
PRINT_SFC_I("addr_mode: %x\n", p_dev->addr_mode);
PRINT_SFC_I("read_lines: %x\n", p_dev->read_lines);
PRINT_SFC_I("prog_lines: %x\n", p_dev->prog_lines);
PRINT_SFC_I("read_cmd: %x\n", p_dev->read_cmd);
PRINT_SFC_I("prog_cmd: %x\n", p_dev->prog_cmd);
PRINT_SFC_I("blk_erase_cmd: %x\n", p_dev->blk_erase_cmd);
PRINT_SFC_I("sec_erase_cmd: %x\n", p_dev->sec_erase_cmd);
}
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 < ARRAY_SIZE(spi_flash_tbl); i++) {
if (spi_flash_tbl[i].id == id)
return &spi_flash_tbl[i];
}
return NULL;
}
/* Adjust flash info in ram base on parameter */
static void *snor_flash_info_adjust(struct flash_info *spi_flash_info)
{
u32 addr;
u8 para_version;
if (spi_flash_info->id == 0xc84019) {
addr = 0x09;
snor_read_parameter(addr, &para_version);
if (para_version == 0x06) {
spi_flash_info->QE_bits = 9;
spi_flash_info->prog_cmd_4 = 0x34;
}
}
return 0;
}
int snor_init(struct SFNOR_DEV *p_dev)
{
u32 i;
u8 id_byte[5];
int err;
memset(p_dev, 0, sizeof(struct SFNOR_DEV));
snor_read_id(id_byte);
PRINT_SFC_E("sfc nor id: %x %x %x\n",
id_byte[0], id_byte[1], id_byte[2]);
if (0xFF == id_byte[0] || 0x00 == id_byte[0]) {
err = SFC_ERROR;
goto err_out;
}
p_dev->manufacturer = id_byte[0];
p_dev->mem_type = id_byte[1];
mutex_init(&p_dev->lock);
g_spi_flash_info = snor_get_flash_info(id_byte);
if (g_spi_flash_info) {
snor_flash_info_adjust(g_spi_flash_info);
p_dev->capacity = 1 << g_spi_flash_info->density;
p_dev->blk_size = g_spi_flash_info->block_size;
p_dev->page_size = NOR_SECS_PAGE;
p_dev->read_cmd = g_spi_flash_info->read_cmd;
p_dev->prog_cmd = g_spi_flash_info->prog_cmd;
p_dev->sec_erase_cmd = g_spi_flash_info->sector_erase_cmd;
p_dev->blk_erase_cmd = g_spi_flash_info->block_erase_cmd;
p_dev->prog_lines = DATA_LINES_X1;
p_dev->read_lines = DATA_LINES_X1;
p_dev->QE_bits = g_spi_flash_info->QE_bits;
i = g_spi_flash_info->feature & FEA_READ_STATUE_MASK;
if (i == 0)
p_dev->write_status = snor_write_status;
else
p_dev->write_status = snor_write_status2;
if (g_spi_flash_info->feature & FEA_4BIT_READ) {
if (snor_enable_QE(p_dev) == SFC_OK) {
p_dev->read_lines = DATA_LINES_X4;
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) {
p_dev->prog_lines = DATA_LINES_X4;
p_dev->prog_cmd = g_spi_flash_info->prog_cmd_4;
}
if (g_spi_flash_info->feature & FEA_4BYTE_ADDR)
p_dev->addr_mode = ADDR_MODE_4BYTE;
if ((g_spi_flash_info->feature & FEA_4BYTE_ADDR_MODE))
snor_enter_4byte_mode();
#ifdef CONFIG_RK_SFC_NOR_MTD
err = sfc_nor_mtd_init(p_dev);
if (err)
goto err_out;
#endif
goto normal_out;
}
for (i = 0; i < sizeof(sfnor_dev_code); i++) {
if (id_byte[2] == sfnor_dev_code[i]) {
p_dev->capacity = sfnor_capacity[i] >> 9;
break;
}
}
if (i >= sizeof(sfnor_dev_code)) {
err = SFC_ERROR;
goto err_out;
}
p_dev->QE_bits = 9;
p_dev->blk_size = NOR_SECS_BLK;
p_dev->page_size = NOR_SECS_PAGE;
p_dev->read_cmd = CMD_READ_DATA;
p_dev->prog_cmd = CMD_PAGE_PROG;
p_dev->sec_erase_cmd = CMD_SECTOR_ERASE;
p_dev->blk_erase_cmd = CMD_BLOCK_ERASE;
p_dev->write_status = snor_write_status2;
#if (SNOR_4BIT_DATA_DETECT_EN)
snor_set_dlines(p_dev, DATA_LINES_X4);
#endif
normal_out:
snor_print_spi_chip_info(p_dev);
return SFC_OK;
err_out:
return err;
}

171
drivers/rkflash/sfc_nor.h Normal file
View File

@ -0,0 +1,171 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _SFNOR_H
#define _SFNOR_H
#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include "sfc.h"
/* Four line data transmission detection */
#define SNOR_4BIT_DATA_DETECT_EN 0
#define NOR_PAGE_SIZE 256
#define NOR_BLOCK_SIZE (64 * 1024)
#define NOR_SECS_BLK (NOR_BLOCK_SIZE / 512)
#define NOR_SECS_PAGE 4
#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)
/*Manufactory ID*/
#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)
/* X1 cmd, X1 addr, X1 data */
#define CMD_FAST_READ_X1 (0x0B)
/* X1 cmd, X1 addr, X2 data */
#define CMD_FAST_READ_X2 (0x3B)
/* 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)
#define CMD_READ_PARAMETER (0x5A)
enum NOR_ERASE_TYPE {
ERASE_SECTOR = 0,
ERASE_BLOCK64K,
ERASE_CHIP
};
enum SNOR_IO_MODE {
IO_MODE_SPI = 0,
IO_MODE_QPI
};
enum SNOR_READ_MODE {
READ_MODE_NOMAL = 0,
READ_MODE_FAST
};
enum SNOR_ADDR_MODE {
ADDR_MODE_3BYTE = 0,
ADDR_MODE_4BYTE
};
typedef int (*SNOR_WRITE_STATUS)(u32 reg_index, u8 status);
struct SFNOR_DEV {
u32 capacity;
u8 manufacturer;
u8 mem_type;
u16 page_size;
u32 blk_size;
u8 read_cmd;
u8 prog_cmd;
u8 sec_erase_cmd;
u8 blk_erase_cmd;
u8 QE_bits;
enum SNOR_READ_MODE read_mode;
enum SNOR_ADDR_MODE addr_mode;
enum SNOR_IO_MODE io_mode;
enum SFC_DATA_LINES read_lines;
enum SFC_DATA_LINES prog_lines;
SNOR_WRITE_STATUS write_status;
struct mutex lock; /* to lock this object */
#ifdef CONFIG_RK_SFC_NOR_MTD
struct mtd_info mtd;
u8 *dma_buf;
#endif
};
struct flash_info {
u32 id;
u8 block_size;
u8 sector_size;
u8 read_cmd;
u8 prog_cmd;
u8 read_cmd_4;
u8 prog_cmd_4;
u8 sector_erase_cmd;
u8 block_erase_cmd;
u8 feature;
u8 density; /* (1 << density) sectors*/
u8 QE_bits;
u8 reserved2;
};
int snor_init(struct SFNOR_DEV *p_dev);
u32 snor_get_capacity(struct SFNOR_DEV *p_dev);
int snor_read(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data);
int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data);
int snor_erase(struct SFNOR_DEV *p_dev,
u32 addr,
enum NOR_ERASE_TYPE erase_type);
int snor_read_id(u8 *data);
int snor_prog_page(struct SFNOR_DEV *p_dev, u32 addr, void *p_data, u32 size);
int snor_read_data(struct SFNOR_DEV *p_dev, u32 addr, void *p_data, u32 size);
int snor_reset_device(void);
int snor_disable_QE(struct SFNOR_DEV *p_dev);
extern struct flash_info *g_spi_flash_info;
extern struct SFNOR_DEV sfnor_dev;
int sfc_nor_mtd_init(struct SFNOR_DEV *p_dev);
#endif

View File

@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include <linux/mutex.h>
#include "sfc.h"
#include "sfc_nor.h"
#include "sfc_nor_boot.h"
#include "typedef.h"
#define VENDOR_PART_NUM 4
#define FLASH_VENDOR_PART_START 8
#define FLASH_VENDOR_PART_SIZE 8
#define FLASH_VENDOR_ITEM_NUM 62
#define FLASH_VENDOR_PART_END \
(FLASH_VENDOR_PART_START +\
FLASH_VENDOR_PART_SIZE * VENDOR_PART_NUM - 1)
/* SFNOR_DEV sfnor_dev is in the sfc_nor.h */
int spi_flash_init(void __iomem *reg_addr)
{
int ret;
sfc_init(reg_addr);
ret = snor_init(&sfnor_dev);
return ret;
}
EXPORT_SYMBOL_GPL(spi_flash_init);
void spi_flash_read_id(u8 chip_sel, void *buf)
{
snor_read_id(buf);
}
EXPORT_SYMBOL_GPL(spi_flash_read_id);
int snor_read_lba(u32 sec, u32 n_sec, void *p_data)
{
int ret = 0;
u32 count, offset;
char *buf;
if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
sec > FLASH_VENDOR_PART_END) {
ret = snor_read(&sfnor_dev, sec, n_sec, p_data);
} else {
memset(p_data, 0, 512 * n_sec);
if (sec < FLASH_VENDOR_PART_START) {
count = FLASH_VENDOR_PART_START - sec;
buf = p_data;
ret = snor_read(&sfnor_dev, sec, count, buf);
}
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 = p_data + offset * 512;
ret = snor_read(&sfnor_dev,
FLASH_VENDOR_PART_END + 1,
count, buf);
}
}
return (u32)ret == n_sec ? 0 : ret;
}
int snor_write_lba(u32 sec, u32 n_sec, void *p_data)
{
int ret = 0;
ret = snor_write(&sfnor_dev, sec, n_sec, p_data);
return (u32)ret == n_sec ? 0 : ret;
}
int snor_vendor_read(u32 sec, u32 n_sec, void *p_data)
{
int ret = 0;
ret = snor_read(&sfnor_dev, sec, n_sec, p_data);
return (u32)ret == n_sec ? 0 : ret;
}
int snor_vendor_write(u32 sec, u32 n_sec, void *p_data)
{
int ret = 0;
ret = snor_write(&sfnor_dev, sec, n_sec, p_data);
return (u32)ret == n_sec ? 0 : ret;
}
unsigned int snor_capacity(void)
{
return snor_get_capacity(&sfnor_dev);
}
void snor_deinit(void)
{
snor_disable_QE(&sfnor_dev);
snor_reset_device();
}
int snor_resume(void __iomem *reg_addr)
{
return snor_init(&sfnor_dev);
}

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef _SFC_NOR_BOOT_H
#define _SFC_NOR_BOOT_H
int spi_flash_init(void __iomem *reg_addr);
void spi_flash_read_id(u8 chip_sel, void *buf);
int snor_read_lba(unsigned int sec, unsigned int n_sec, void *p_data);
int snor_write_lba(unsigned int sec, unsigned int n_sec, void *p_data);
unsigned int snor_capacity(void);
void snor_deinit(void);
int snor_resume(void __iomem *reg_addr);
int snor_vendor_read(unsigned int sec, unsigned int n_sec, void *p_data);
int snor_vendor_write(unsigned int sec, unsigned int n_sec, void *p_data);
#endif

View File

@ -0,0 +1,224 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#include <linux/kernel.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include "sfc_nor.h"
#include "rkflash_blk.h"
#include "rkflash_debug.h"
static struct mtd_partition nor_parts[MAX_PART_COUNT];
static inline struct SFNOR_DEV *mtd_to_sfc(struct mtd_info *ptr_mtd)
{
return (struct SFNOR_DEV *)((char *)ptr_mtd -
offsetof(struct SFNOR_DEV, mtd));
}
static int sfc_erase_mtd(struct mtd_info *mtd, struct erase_info *instr)
{
int ret;
struct SFNOR_DEV *p_dev = mtd_to_sfc(mtd);
u32 addr, len;
u32 rem;
if ((instr->addr + instr->len) > p_dev->capacity << 9)
return -EINVAL;
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
mutex_lock(&p_dev->lock);
addr = instr->addr;
len = instr->len;
if (len == p_dev->mtd.size) {
ret = snor_erase(p_dev, 0, CMD_CHIP_ERASE);
if (ret) {
PRINT_SFC_E("snor_erase CHIP 0x%x ret=%d\n",
addr, ret);
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&p_dev->lock);
return -EIO;
}
} else {
while (len > 0) {
ret = snor_erase(p_dev, addr, ERASE_BLOCK64K);
if (ret) {
PRINT_SFC_E("snor_erase 0x%x ret=%d\n",
addr, ret);
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&p_dev->lock);
return -EIO;
}
addr += mtd->erasesize;
len -= mtd->erasesize;
}
}
mutex_unlock(&p_dev->lock);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static int sfc_write_mtd(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
int status;
u32 addr, size, chunk, padding;
u32 page_align;
struct SFNOR_DEV *p_dev = mtd_to_sfc(mtd);
if ((to + len) > p_dev->capacity << 9)
return -EINVAL;
mutex_lock(&p_dev->lock);
addr = to;
size = len;
while (size > 0) {
page_align = addr & (NOR_PAGE_SIZE - 1);
chunk = size;
if (chunk > (NOR_PAGE_SIZE - page_align))
chunk = NOR_PAGE_SIZE - page_align;
memcpy(p_dev->dma_buf, buf, chunk);
padding = 0;
if (chunk < NOR_PAGE_SIZE) {
/* 4 bytes algin */
padding = ((chunk + 3) & 0xFFFC) - chunk;
memset(p_dev->dma_buf + chunk, 0xFF, padding);
}
status = snor_prog_page(p_dev, addr, p_dev->dma_buf,
chunk + padding);
if (status != SFC_OK) {
PRINT_SFC_E("snor_prog_page %x ret= %d\n",
addr, status);
*retlen = len - size;
mutex_unlock(&p_dev->lock);
return status;
}
size -= chunk;
addr += chunk;
buf += chunk;
}
*retlen = len;
mutex_unlock(&p_dev->lock);
return 0;
}
static int sfc_read_mtd(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
u32 addr, size, chunk;
u8 *p_buf = (u8 *)buf;
int ret = SFC_OK;
struct SFNOR_DEV *p_dev = mtd_to_sfc(mtd);
if ((from + len) > p_dev->capacity << 9)
return -EINVAL;
mutex_lock(&p_dev->lock);
addr = from;
size = len;
while (size > 0) {
chunk = (size < NOR_PAGE_SIZE) ? size : NOR_PAGE_SIZE;
ret = snor_read_data(p_dev, addr, p_dev->dma_buf, chunk);
if (ret != SFC_OK) {
PRINT_SFC_E("snor_read_data %x ret=%d\n", addr, ret);
*retlen = len - size;
mutex_unlock(&p_dev->lock);
return ret;
}
memcpy(p_buf, p_dev->dma_buf, chunk);
size -= chunk;
addr += chunk;
p_buf += chunk;
}
*retlen = len;
mutex_unlock(&p_dev->lock);
return 0;
}
int sfc_nor_mtd_init(struct SFNOR_DEV *p_dev)
{
int ret, i, part_num = 0;
int capacity;
struct STRUCT_PART_INFO *g_part; /* size 2KB */
capacity = p_dev->capacity;
p_dev->mtd.name = "sfc_nor";
p_dev->mtd.type = MTD_NORFLASH;
p_dev->mtd.writesize = 1;
p_dev->mtd.flags = MTD_CAP_NORFLASH;
/* see snor_write */
p_dev->mtd.size = capacity << 9;
p_dev->mtd._erase = sfc_erase_mtd;
p_dev->mtd._read = sfc_read_mtd;
p_dev->mtd._write = sfc_write_mtd;
p_dev->mtd.erasesize = g_spi_flash_info->block_size << 9;
p_dev->mtd.writebufsize = NOR_PAGE_SIZE;
p_dev->dma_buf = kmalloc(NOR_PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!p_dev->dma_buf) {
PRINT_SFC_E("kmalloc size=0x%x failed\n", NOR_PAGE_SIZE);
ret = -ENOMEM;
goto out;
}
g_part = kmalloc(sizeof(*g_part), GFP_KERNEL | GFP_DMA);
if (!g_part) {
ret = -ENOMEM;
goto free_dma_buf;
}
part_num = 0;
if (snor_read(p_dev, 0, 4, g_part) == 0) {
if (g_part->hdr.ui_fw_tag == RK_PARTITION_TAG) {
part_num = g_part->hdr.ui_part_entry_count;
for (i = 0; i < part_num; i++) {
nor_parts[i].name =
kstrdup(g_part->part[i].sz_name,
GFP_KERNEL);
if (g_part->part[i].ui_pt_sz == 0xFFFFFFFF)
g_part->part[i].ui_pt_sz = capacity -
g_part->part[i].ui_pt_off;
nor_parts[i].offset =
(u64)g_part->part[i].ui_pt_off << 9;
nor_parts[i].size =
(u64)g_part->part[i].ui_pt_sz << 9;
nor_parts[i].mask_flags = 0;
}
}
}
kfree(g_part);
if (part_num == 0) {
ret = -1;
goto free_dma_buf;
}
ret = mtd_device_register(&p_dev->mtd, nor_parts, part_num);
if (ret != 0)
goto free_dma_buf;
return ret;
free_dma_buf:
kfree(p_dev->dma_buf);
out:
return ret;
}

41
drivers/rkflash/typedef.h Normal file
View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2018 Rockchip Electronics Co. Ltd. */
#ifndef __TYPE_DEF_H
#define __TYPE_DEF_H
#include <linux/kernel.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, int size);
void rknand_print_hex(char *s, void *buf, int width, int len);
#endif /*__TYPEDEF_H */