sdk-hwV1.3/lichee/linux-4.9/drivers/mtd/awnand/rawnand/aw_rawnand_spl.c

978 lines
26 KiB
C

/**
* spdx-license-identifier: gpl-2.0+
* aw_rawnand_spl.c
*
* (c) copyright 2020 - 2021
* allwinner technology co., ltd. <www.allwinnertech.com>
* cuizhikui <cuizhikui@allwinnertech.com>
*
*/
#include <linux/mtd/aw-rawnand.h>
#include "aw_rawnand_nfc.h"
#include <linux/sunxi-boot.h>
#define TOC0_MAGIC "TOC0.GLH"
#define TOC_MAIN_INFO_MAGIC 0x89119800
#define STAMP_VALUE 0x5F0A6C39
typedef struct sbrom_toc1_head_info {
char name[16] ; //user can modify
u32 magic ; //must equal TOC_U32_MAGIC
u32 add_sum ;
u32 serial_num ; //user can modify
u32 status ; //user can modify,such as TOC_MAIN_INFO_STATUS_ENCRYP_NOT_USED
u32 items_nr; //total entry number
u32 valid_len;
u32 version_main; //only one byte
u32 version_sub; //two bytes
u32 reserved[3]; //reserved for future
u32 end;
} sbrom_toc1_head_info_t;
#if 0
/*keep it the same with driver/sunxi_flash/nand/nand_bsp.h*/
typedef struct {
__u32 ChannelCnt;
__u32 ChipCnt; //the count of the total nand flash chips are currently connecting on the CE pin
__u32 ChipConnectInfo; //chip connect information, bit == 1 means there is a chip connecting on the CE pin
__u32 RbCnt;
__u32 RbConnectInfo; //the connect information of the all rb chips are connected
__u32 RbConnectMode; //the rb connect mode
__u32 BankCntPerChip; //the count of the banks in one nand chip, multiple banks can support Inter-Leave
__u32 DieCntPerChip; //the count of the dies in one nand chip, block management is based on Die
__u32 PlaneCntPerDie; //the count of planes in one die, multiple planes can support multi-plane operation
__u32 SectorCntPerPage; //the count of sectors in one single physic page, one sector is 0.5k
__u32 PageCntPerPhyBlk; //the count of physic pages in one physic block
__u32 BlkCntPerDie; //the count of the physic blocks in one die, include valid block and invalid block
__u32 OperationOpt; //the mask of the operation types which current nand flash can support support
__u32 FrequencePar; //the parameter of the hardware access clock, based on 'MHz'
__u32 EccMode; //the Ecc Mode for the nand flash chip, 0: bch-16, 1:bch-28, 2:bch_32
__u8 NandChipId[8]; //the nand chip id of current connecting nand chip
__u32 ValidBlkRatio; //the ratio of the valid physical blocks, based on 1024
__u32 good_block_ratio; //good block ratio get from hwscan
__u32 ReadRetryType; //the read retry type
__u32 DDRType;
__u32 uboot_start_block;
__u32 uboot_next_block;
__u32 logic_start_block;
__u32 nand_specialinfo_page;
__u32 nand_specialinfo_offset;
__u32 physic_block_reserved;
/*special nand cmd for some nand in batch cmd, only for write*/
__u32 random_cmd2_send_flag;
/*random col addr num in batch cmd*/
__u32 random_addr_num;
/*real physic page size*/
__u32 nand_real_page_size;
__u32 Reserved[13];
} boot_nand_para_t;
#endif
#define NAND_VERSION_0 0x03
#define NAND_VERSION_1 0x01
struct aw_rawnand_boot0 {
struct aw_nand_chip *chip;
/*
*#define SLC_NAND (0)
*#define MLC_NAND (1)
*/
uint8_t nand_type;
uint8_t ecc_mode;
uint8_t reserve[2];
#define SLC_MDATA_IO (1024)
#define MLC_MDATA_IO (4096)
/*mdata_len equal to (pagesize - ecc code), which >= MDATA_IO*/
int mdata_len;
uint8_t *mdata;
#define BOOT0_OOB_LEN (8)
uint8_t oob[BOOT0_OOB_LEN];
int init_flag;
/* according to boot0 sram size,
* minimum one page 1K boot0img*/
#define BOOT0_PAGE_CNT_PER_COPY (128)
int page_cnt_per_copy;
int copys_per_blk;
/*uboot start block*/
int boundary;
int writen_copy;
};
struct aw_rawnand_boot0 g_boot0;
static inline struct aw_rawnand_boot0 *get_rawnand_boot0(void)
{
return &g_boot0;
}
static uint32_t sunxi_generate_checksum(void *buffer, uint32_t length, uint32_t div, uint32_t src_sum)
{
uint32_t *buf;
int count;
uint32_t sum;
count = length >> 2;
sum = 0;
buf = (__u32 *)buffer;
do {
sum += *buf++;
sum += *buf++;
sum += *buf++;
sum += *buf++;
} while ((count -= (4*div)) > (4 - 1));
while (count-- > 0)
sum += *buf++;
sum = sum - src_sum + STAMP_VALUE;
return sum;
}
static uint32_t sunxi_sprite_generate_checksum(void *buffer, uint32_t length, uint32_t src_sum)
{
return sunxi_generate_checksum(buffer, length, 1, src_sum);
}
/*usually small capacity nand is slc nand*/
static int get_smallnand_uboot_start_block_num(void)
{
return UBOOT_START_BLOCK_SMALLNAND;
}
/*usually big capacity nand is mlc nand*/
static int get_bignand_uboot_start_block_num(void)
{
return UBOOT_START_BLOCK_BIGNAND;
}
void rawnand_uboot_blknum(unsigned int *start, unsigned int *end)
{
uint32_t uboot_block_size = 0;
uint32_t uboot_start_block = 0;
uint32_t uboot_next_block = 0;
uint32_t page_cnt_per_blk = 0;
struct aw_nand_chip *chip = get_rawnand();
page_cnt_per_blk = 1 << chip->pages_per_blk_shift;
uboot_block_size = chip->erasesize;
/*small nand:block size < 1MB; reserve 4M for uboot*/
if (uboot_block_size <= 0x20000) { //128K
uboot_start_block = get_smallnand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 32;
} else if (uboot_block_size <= 0x40000) { //256k
uboot_start_block = get_smallnand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 16;
} else if (uboot_block_size <= 0x80000) { //512k
uboot_start_block = get_smallnand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 8;
} else if (uboot_block_size <= 0x100000 && page_cnt_per_blk <= 128) { //1M
uboot_start_block = get_smallnand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 4;
}
/* big nand; reserve at least 20M for uboot */
else if (uboot_block_size <= 0x100000 && page_cnt_per_blk > 128) { //BIGNAND 1M
uboot_start_block = get_bignand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 20;
} else if (uboot_block_size <= 0x200000) { //BIGNAND 2M
uboot_start_block = get_bignand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 10;
} else {
uboot_start_block = get_bignand_uboot_start_block_num();
uboot_next_block = uboot_start_block + 8;
}
if (start) {
*start = uboot_start_block;
awrawnand_dbg("uboot_start@%u\n", *start);
}
if (end) {
*end = uboot_next_block;
awrawnand_dbg("uboot_end@%u\n", *end);
}
}
int rawslcnand_write_boot0_page(struct mtd_info *mtd, struct aw_nand_chip *chip,
uint8_t *mdata, int mlen, uint8_t *sdata, int slen, int page)
{
struct aw_nand_host *host = awnand_chip_to_host(chip);
int ret = 0;
uint8_t status = 0;
int row_cycles = chip->row_cycles;
BATCH_REQ_WRITE_SEQ(req, page, row_cycles, mdata, mlen, sdata, slen);
chip->operate_boot0 = 1;
chip->boot0_ecc_mode = MAX_ECC_BCH_80;
if (!chip->dev_ready_wait(mtd)) {
awrawnand_err("dev is busy write boot0 page@%d fail\n", page);
ret = -ETIMEDOUT;
goto out;
}
ret = host->batch_op(chip, &req);
if (ret == ECC_ERR) {
awrawnand_err("%s write boot0 page@%d fail\n", __func__, 0);
goto out;
}
if (!chip->dev_ready_wait(mtd)) {
awrawnand_err("dev is busy write boot0 page@%d fail\n", page);
ret = -ETIMEDOUT;
goto out;
}
status = chip->dev_status(mtd);
if (status & RAWNAND_STATUS_FAIL) {
awrawnand_err("write boot0 page@%d fail\n", page);
ret = -EIO;
}
chip->operate_boot0 = 0;
chip->boot0_ecc_mode = 0;
out:
return ret;
}
int rawslcnand_read_boot0_page(struct mtd_info *mtd, struct aw_nand_chip *chip,
uint8_t *mdata, int mlen, uint8_t *sdata, int slen, int page)
{
struct aw_nand_host *host = awnand_chip_to_host(chip);
int row_cycles = chip->row_cycles;
int ret = 0;
BATCH_REQ_READ_SEQ(req, page, row_cycles, mdata, mlen, sdata, slen);
chip->operate_boot0 = 1;
chip->boot0_ecc_mode = MAX_ECC_BCH_80;
if (!chip->dev_ready_wait(mtd)) {
awrawnand_err("dev is busy read page@%d fail\n", page);
ret = -EIO;
goto out;
}
ret = host->batch_op(chip, &req);
if (ret == ECC_ERR)
awrawnand_err("read page@%d fail\n", page);
chip->operate_boot0 = 0;
chip->boot0_ecc_mode = 0;
out:
return ret;
}
static int rawnand_calc_page_valid_main_data_len(int pagesize, int boot0_ecc_mode, int ecc_mode)
{
int boot0_ecc_bits = 0, normal_ecc_bits = 0;
int increase_ecc_size = 0;
int cnt = 1;
int i = 0;
/*ecc block size 1024Bytes*/
int ecc_block_cnts = (pagesize >> 10);
boot0_ecc_bits = ecc_bits_tbl[boot0_ecc_mode];
normal_ecc_bits = ecc_bits_tbl[ecc_mode];
increase_ecc_size = (14 * (boot0_ecc_bits - normal_ecc_bits) / 8);
/* data_min_io is ecc block size,
* so the increased overhead(increase_ecc_size)
* is calculate in terms of the data_min_io*/
for (i = (ecc_block_cnts - 1); i > 0; i--) {
if ((increase_ecc_size * i) < (cnt << 10))
break;
cnt++;
}
return ((ecc_block_cnts - cnt) << 10);
}
/**
* rawslcnand_write_boot0_one - write one or more copy boot0
* @len: boot0 len
* @buf: boot0 img
* @count: range from 0 to n(rawnand_uboot_blknum(&n, NULL))
*/
static int rawslcnand_write_boot0_one(struct aw_rawnand_boot0 *boot0,
unsigned int len, void *buf, int count)
{
struct aw_nand_chip *chip = boot0->chip;
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
int ret = 0;
int start = 0;
int blks_per_copy = 0;
int blk = 0;
int page = 0;
int pages_per_blk = (1 << chip->pages_per_blk_shift);
uint8_t *mdata = buf;
int mlen = boot0->mdata_len;
uint8_t *sdata = boot0->oob;
int slen = BOOT0_OOB_LEN;
int writen_len = 0;
uint8_t *mbuf = kzalloc(chip->pagesize, GFP_KERNEL);
if (mbuf == NULL) {
awrawnand_err("write boot0 kzalloc mbuf fail\n");
return -ENOMEM;
}
blks_per_copy = boot0->page_cnt_per_copy >> chip->pages_per_blk_shift;
if (boot0->page_cnt_per_copy & chip->pages_per_blk_mask)
blks_per_copy++;
start = count * blks_per_copy;
if ((start + blks_per_copy) > boot0->boundary)
return 0;
mutex_lock(&chip->lock);
chip->select_chip(mtd, 0);
for (blk = start; blk < start + blks_per_copy; blk++) {
page = blk << chip->pages_per_blk_shift;
ret = chip->erase(mtd, page);
if (ret) {
awrawnand_err("erase block@%d fail when download boot0 count@%d\n",
blk, count);
awrawnand_err("skip boot0 count@%d\n", count);
break;
}
for (; page < ((blk << chip->pages_per_blk_shift) + pages_per_blk);
page++) {
/*boot0 align to 4K, so it would not cross the border*/
memcpy(mbuf, mdata, SLC_MDATA_IO);
ret = chip->write_boot0_page(mtd, chip, mbuf, mlen, sdata, slen, page);
if (ret) {
awrawnand_err("when write boot0 fail in page@%d\n\n", page);
/*the info is no mean*/
awrawnand_dbg("ecc mode@%d ecc limit@%d\n", ecc_bits_tbl[boot0->ecc_mode],
ecc_limit_tab[boot0->ecc_mode]);
goto out;
}
writen_len += SLC_MDATA_IO;
if (writen_len == len) {
writen_len = 0;
awrawnand_print("W boot0 cp@%d suc\n", boot0->writen_copy++);
}
mdata = buf + writen_len;
}
}
out:
chip->select_chip(mtd, -1);
mutex_unlock(&chip->lock);
return ret;
}
/**
* rawslcnand_read_boot0_one - read a right boot0 in one boot0 area
* @len: boot0 size(buf len)
* @buf: boot0 buffer
* @count: 0 from n(rawnand_uboot_blknum(&n, NULL))
* */
static int rawslcnand_read_boot0_one(struct aw_rawnand_boot0 *boot0,
unsigned int len, void *buf, int count)
{
struct aw_nand_chip *chip = boot0->chip;
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
int ret = 0;
int start = 0;
int blks_per_copy = 0;
int blk = 0;
int page = 0;
int pages_per_blk = (1 << chip->pages_per_blk_shift);
uint8_t *mdata = buf;
int mlen = boot0->mdata_len;
uint8_t *sdata = boot0->oob;
int slen = BOOT0_OOB_LEN;
int read_len = 0;
uint8_t *mbuf = kzalloc(chip->pagesize, GFP_KERNEL);
if (mbuf == NULL) {
awrawnand_err("read boot0 kzalloc mbuf fail\n");
return -ENOMEM;
}
blks_per_copy = boot0->page_cnt_per_copy >> chip->pages_per_blk_shift;
if (boot0->page_cnt_per_copy & chip->pages_per_blk_mask)
blks_per_copy++;
start = count * blks_per_copy;
if ((start + blks_per_copy) > boot0->boundary)
return 0;
mutex_lock(&chip->lock);
chip->select_chip(mtd, 0);
for (blk = start; blk < start + blks_per_copy; blk++) {
page = blk << chip->pages_per_blk_shift;
for (; page < ((blk << chip->pages_per_blk_shift) + pages_per_blk);
page++) {
ret = chip->read_boot0_page(mtd, chip, mbuf, mlen, sdata, slen, page);
if (ret) {
awrawnand_err("when read boot0 fail in page@%d\n\n", page);
/*the info is no mean*/
awrawnand_dbg("ecc mode@%d ecc limit@%d\n",
ecc_bits_tbl[boot0->ecc_mode],
ecc_limit_tab[boot0->ecc_mode]);
goto out;
}
memcpy(mdata, mbuf, SLC_MDATA_IO);
read_len += SLC_MDATA_IO;
if (read_len == len) {
awrawnand_info("R boot0 suc\n");
goto out;
}
mdata = buf + read_len;
}
}
out:
chip->select_chip(mtd, -1);
mutex_unlock(&chip->lock);
return ret;
}
/**
* rawslcnand_mtd_download_boot0 - download boot0
* @boot0:boot0 structure
* @len: boot size
* @buf: boot0 img
* */
static int rawslcnand_mtd_download_boot0(struct aw_rawnand_boot0 *boot0,
unsigned int len, void *buf)
{
int ret = 0;
int count = 0;
unsigned int start = 0;
unsigned int end = 0;
rawnand_uboot_blknum(&end, NULL);
for (count = start; count < end; count++) {
ret &= rawslcnand_write_boot0_one(boot0, len, buf, count);
}
if (ret)
awrawnand_err("download boot0 fail\n");
return ret;
}
/**
* rawslcnand_mtd_upload_boot0 - read boot0
* @boot0: boot0 structure
* @len: boot0 size
* @buf: buffer to store boot0 img
* */
static int rawslcnand_mtd_upload_boot0(struct aw_rawnand_boot0 *boot0,
unsigned int len, void *buf)
{
int ret = 0;
int count = 0;
unsigned int start = 0;
unsigned int end = 0;
rawnand_uboot_blknum(&end, NULL);
for (count = start; count < end; count++) {
ret = rawslcnand_read_boot0_one(boot0, len, buf, count);
if (!ret)
break;
}
if (count == end)
awrawnand_err("upload boot0 fail\n");
return ret;
}
/**
* rawnand_mtd_download_boot0_init - init boot0 structure parameter
* */
static inline void rawnand_mtd_download_boot0_init(void)
{
struct aw_nand_chip *chip = get_rawnand();
unsigned int uboot_start;
rawnand_uboot_blknum(&uboot_start, NULL);
if (!g_boot0.init_flag) {
g_boot0.chip = chip;
g_boot0.nand_type = SLC_NAND;
g_boot0.page_cnt_per_copy = BOOT0_PAGE_CNT_PER_COPY;
g_boot0.ecc_mode = MAX_ECC_BCH_80;
g_boot0.mdata_len = rawnand_calc_page_valid_main_data_len(chip->pagesize,
g_boot0.ecc_mode, chip->ecc_mode);
memset(g_boot0.oob, 0xff, BOOT0_OOB_LEN);
g_boot0.oob[0] = 0xff;
g_boot0.oob[1] = 0x00;
g_boot0.oob[2] = NAND_VERSION_0;
g_boot0.oob[3] = NAND_VERSION_1;
g_boot0.init_flag = 1;
g_boot0.boundary = uboot_start;
g_boot0.writen_copy = 0;
awrawnand_info("boot0:nand_type@%s\n", g_boot0.nand_type ? "mlc" : "slc");
awrawnand_info("boot0:ecc_mode@%d\n", g_boot0.ecc_mode);
awrawnand_info("boot0:mdata_len@%d\n", g_boot0.mdata_len);
awrawnand_info("boot0:doundary@%d\n", g_boot0.boundary);
}
}
/**
* rawnand_mtd_download_boot0_exit - destory process var
* */
static inline void rawnand_mtd_download_boot0_exit(void)
{
g_boot0.init_flag = 0;
g_boot0.writen_copy = 0;
}
/**
* fill boot0 header
*/
int rawnand_mtd_get_flash_info(void *data, unsigned int len)
{
struct aw_nand_chip *chip = get_rawnand();
boot_nand_para_t *boot_info = data;
unsigned int uboot_start, uboot_end;
rawnand_uboot_blknum(&uboot_start, &uboot_end);
/* nand information */
boot_info->ChipCnt = chip->chips;
boot_info->DieCntPerChip = chip->dies;
boot_info->SectorCntPerPage = (1 << (chip->pagesize_shift - 9));
boot_info->PageCntPerPhyBlk = (1 << chip->pages_per_blk_shift);
/*OperationOpt must be match to boot0 define*/
if (chip->options & RAWNAND_NFC_RANDOM)
boot_info->OperationOpt |= (1 << 7);
if (chip->options & RAWNAND_TOGGLE_DDR_TO_SDR)
boot_info->OperationOpt |= (1 << 27);
if (chip->type == SLC_NAND)
boot_info->OperationOpt &= ~(0xff << 12);
else {
boot_info->OperationOpt &= ~(0xff << 12);
awrawnand_err("don't support nand type, default slc nand\n");
}
boot_info->FrequencePar = chip->clk_rate;
boot_info->EccMode = chip->ecc_mode;
memcpy(boot_info->NandChipId, chip->id, RAWNAND_MAX_ID_LEN);
/* others */
boot_info->uboot_start_block = uboot_start;
boot_info->uboot_next_block = uboot_end;
boot_info->logic_start_block = uboot_end + AW_RAWNAND_RESERVED_PHY_BLK_FOR_SECURE_STORAGE;
boot_info->physic_block_reserved = 0;
awrawnand_info("flash id@%02x %02x %02x %02x %02x %02x %02x %02x\n",
boot_info->NandChipId[0], boot_info->NandChipId[1],
boot_info->NandChipId[2], boot_info->NandChipId[3],
boot_info->NandChipId[4], boot_info->NandChipId[5],
boot_info->NandChipId[6], boot_info->NandChipId[7]);
awrawnand_info("uboot_start_block@%u\n", boot_info->uboot_start_block);
awrawnand_info("uboot_end_block@%u\n", boot_info->uboot_next_block);
return 0;
}
static int rawnand_mtd_download_uboot_fill_remain_pages_in_block(struct aw_nand_chip *chip, int page)
{
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
int ret = 0;
int block_end_page = (((page >> chip->pages_per_blk_shift) + 1) << chip->pages_per_blk_shift);
int p = 0;
/*uboot spare require*/
int slen = chip->pagesize > SZ_2K ? 16 : 8;
uint8_t sdata[slen];
uint8_t *mdata = kzalloc(chip->pagesize, GFP_KERNEL);
if (mdata == NULL) {
awrawnand_err("fill remain page: kzalloc mdata fail\n");
return -ENOMEM;
}
memset(mdata, 0x55, chip->pagesize);
memset(sdata, 0xA5, slen);
/*bad block mark flag position*/
sdata[0] = 0xff;
for (p = page; p < block_end_page; p++) {
ret = chip->write_page(mtd, chip, mdata, chip->pagesize,
sdata, slen, p);
if (ret) {
awrawnand_err("fill remain data in block@%d page@%d fail chip page@%d\n",
page >> chip->pages_per_blk_shift, page & chip->pages_per_blk_mask, page);
}
}
kfree(mdata);
return 0;
}
static int rawslcnand_mtd_upload_uboot(struct aw_nand_chip *chip,
unsigned int len, void *buf)
{
unsigned int start = 0;
unsigned int end = 0;
int blk = 0;
int ret = -1;
uint8_t *mdata = buf;
/*uboot spare require*/
int slen = chip->pagesize > SZ_2K ? 16 : 8;
uint8_t sdata[slen];
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
int pages_per_blk = (1 << chip->pages_per_blk_shift);
int had_read_len = 0;
sbrom_toc1_head_info_t *head = buf;
unsigned int uboot_len = 0;
uint32_t check_sum = 0;
int page = 0;
int copy_size = chip->pagesize;
uint8_t *mbuf = kzalloc(chip->pagesize, GFP_KERNEL);
if (mbuf == NULL) {
awrawnand_err("upload uboot: kzalloc fail\n");
goto out1;
}
rawnand_uboot_blknum(&start, &end);
mutex_lock(&chip->lock);
chip->select_chip(mtd, 0);
for (blk = start; blk < end; blk++) {
if (chip->block_bad(mtd, blk)) {
awrawnand_err("block@%d is bad,skip it\n", blk);
continue;
}
if (had_read_len == 0)
mdata = buf;
page = blk << chip->pages_per_blk_shift;
for (; page < ((blk << chip->pages_per_blk_shift) + pages_per_blk);
page++) {
/*ret = chip->read_page(mtd, chip, mdata, chip->pagesize, sdata, slen, page);*/
ret = chip->read_page(mtd, chip, mbuf, chip->pagesize, sdata, slen, page);
if (ret) {
awrawnand_err("read page@%d fail\n", page);
break;
}
memcpy(mdata, mbuf, copy_size);
had_read_len += chip->pagesize;
mdata += chip->pagesize;
if (head->magic != TOC_MAIN_INFO_MAGIC) {
had_read_len = 0;
break;
}
uboot_len = head->valid_len;
if (len < uboot_len) {
awrawnand_err("upload uboot buffer size(len)@%d is less the"
"real ubootsize@%d, pls check\n", len,
uboot_len);
goto out;
}
/*last page data align to pagesize read, prevent illegal access buf*/
if ((uboot_len - had_read_len) <= chip->pagesize) {
copy_size = uboot_len - had_read_len;
}
if (had_read_len >= uboot_len) {
awrawnand_info("to check sum\n");
check_sum = sunxi_sprite_generate_checksum(buf,
uboot_len, head->add_sum);
had_read_len = 0;
if (check_sum == head->add_sum) {
awrawnand_print("upload uboot success\n");
ret = 0;
goto out;
} else {
copy_size = chip->pagesize;
break;
}
}
} /*for (; ; page++*/
} /*for (blk = start; blk < end; blk++)*/
out:
chip->select_chip(mtd, -1);
mutex_unlock(&chip->lock);
kfree(mbuf);
return ret;
out1:
return -ENOMEM;
}
static int rawslcnand_mtd_download_uboot(struct aw_nand_chip *chip,
unsigned int len, void *buf)
{
unsigned int start = 0;
unsigned int end = 0;
int blk = 0;
int ret = 0;
uint8_t *mdata = buf;
/*uboot spare require*/
int slen = chip->pagesize > SZ_2K ? 16 : 8;
uint8_t sdata[slen];
struct mtd_info *mtd = awnand_chip_to_mtd(chip);
int pages_per_blk = (1 << chip->pages_per_blk_shift);
int writen_len = 0;
int copy = 0;
int page = 0;
int fail_flag = 0;
uint8_t *mbuf = kzalloc(chip->pagesize, GFP_KERNEL);
if (mbuf == NULL) {
awrawnand_err("download uboot: kzalloc fail\n");
goto out;
}
/*uboot oob layout*/
sdata[0] = 0xff;
sdata[1] = 0;
sdata[2] = NAND_VERSION_0;
sdata[3] = NAND_VERSION_1;
memset(sdata + 4, 0xff, slen - 4);
rawnand_uboot_blknum(&start, &end);
awrawnand_info("download uboot len@0x%x [%d to%d]\n", len, start, end);
mutex_lock(&chip->lock);
chip->select_chip(mtd, 0);
for (blk = start; blk < end; blk++) {
if (chip->block_bad(mtd, blk)) {
awrawnand_err("block@%d is bad,skip it\n", blk);
continue;
}
ret = chip->erase(mtd, (blk << chip->pages_per_blk_shift));
if (ret) {
awrawnand_err("erase block@%d fail, skip it\n", blk);
continue;
}
if (writen_len == 0)
mdata = buf;
page = blk << chip->pages_per_blk_shift;
for (; page < ((blk << chip->pages_per_blk_shift) + pages_per_blk);
page++) {
memcpy(mbuf, mdata, chip->pagesize);
ret = chip->write_page(mtd, chip, mbuf, chip->pagesize, sdata, slen, page);
if (ret) {
awrawnand_err("write page@%d fail\n", page);
fail_flag = 1;
}
writen_len += chip->pagesize;
mdata += chip->pagesize;
/*prevent len is not align to pagesize*/
if (((len - writen_len) < chip->pagesize) &&
((len - writen_len) > 0)) {
memcpy(mbuf, mdata, (len - writen_len));
mdata = mbuf;
}
if (writen_len >= len) {
rawnand_mtd_download_uboot_fill_remain_pages_in_block(chip,
(page + 1));
if (fail_flag == 0)
awrawnand_print("W uboot cp@%d ok\n", copy++);
writen_len = 0;
break;
}
} /*for (; ; page++*/
} /*for (blk = start; blk < end; blk++)*/
chip->select_chip(mtd, -1);
mutex_unlock(&chip->lock);
/*at least one copy is ok*/
kfree(mbuf);
return copy ? 0 : -1;
out:
return -ENOMEM;
}
/**
* rawnand_mtd_upload_uboot - upload uboot img interface
* @len: uboot size
* @buf: uboot img
**/
int rawnand_mtd_upload_uboot(unsigned int len, void *buf)
{
int ret = 0;
struct aw_nand_chip *chip = get_rawnand();
if (chip->type == SLC_NAND) {
ret = rawslcnand_mtd_upload_uboot(chip, len, buf);
} else {
awrawnand_err("upload uboot don't support nand type@%s\n",
chip->type == SLC_NAND ? "slc" : "mlc");
}
return ret;
}
EXPORT_SYMBOL_GPL(rawnand_mtd_upload_uboot);
/**
* rawnand_mtd_download_uboot - download uboot img interface
* @len: uboot size
* @buf: uboot img
**/
int rawnand_mtd_download_uboot(unsigned int len, void *buf)
{
int ret = 0;
struct aw_nand_chip *chip = get_rawnand();
#ifdef CONFIG_AW_RAWNAND_BURN_CHECK_UBOOT
uint8_t *uboot = vmalloc(len);
if (uboot == NULL) {
awrawnand_err("kzalloc buffer for read uboot after write fail\n");
goto out;
} else {
memset(uboot, 0x00, len);
}
#endif
if (chip->type == SLC_NAND) {
ret = rawslcnand_mtd_download_uboot(chip, len, buf);
} else {
awrawnand_err("download uboot don't support nand type@%s\n",
chip->type == SLC_NAND ? "slc" : "mlc");
ret = -EPERM;
goto out;
}
#ifdef CONFIG_AW_RAWNAND_BURN_CHECK_UBOOT
rawnand_mtd_upload_uboot(len, uboot);
if (!memcmp(buf, uboot, len))
awrawnand_print("write & read the uboot is same\n");
else
awrawnand_print("write & read the uboot is diff\n");
vfree(uboot);
#endif
out:
return ret;
}
EXPORT_SYMBOL_GPL(rawnand_mtd_download_uboot);
/**
* rawnand_mtd_upload_boot0 - read boot0 interface
* @len: boot0 size
* @buf: boot0 img
**/
int rawnand_mtd_upload_boot0(unsigned int len, void *buf)
{
int ret = 0;
struct aw_rawnand_boot0 *boot0 = get_rawnand_boot0();
rawnand_mtd_download_boot0_init();
if (g_boot0.nand_type == SLC_NAND) {
ret = rawslcnand_mtd_upload_boot0(boot0, len, buf);
} else {
awrawnand_err("don't support nand to down boot0\n");
ret = -EINVAL;
}
rawnand_mtd_download_boot0_exit();
return ret;
}
EXPORT_SYMBOL_GPL(rawnand_mtd_upload_boot0);
/**
* rawnand_mtd_download_boot0 - download boot0 interface
* @len: boot0 size
* @buf: boot0 img
* */
int rawnand_mtd_download_boot0(unsigned int len, void *buf)
{
int ret = 0;
struct aw_rawnand_boot0 *boot0 = get_rawnand_boot0();
#ifdef CONFIG_AW_RAWNAND_BURN_CHECK_BOOT0
uint8_t *rbuf = vmalloc(len);
if (!rbuf) {
awrawnand_err("malloc read buf fail\n");
ret = -ENOMEM;
goto out;
}
#endif
rawnand_mtd_download_boot0_init();
if (g_boot0.nand_type == SLC_NAND) {
ret = rawslcnand_mtd_download_boot0(boot0, len, buf);
} else {
awrawnand_err("don't support nand to down boot0\n");
ret = -EINVAL;
goto out;
}
#ifdef CONFIG_AW_RAWNAND_BURN_CHECK_BOOT0
rawnand_mtd_upload_boot0(len, rbuf);
if (memcmp(rbuf, buf, len))
awrawnand_err("read boot0 fail\n");
else
awrawnand_info("read&write is the same\n");
vfree(rbuf);
#endif
rawnand_mtd_download_boot0_exit();
out:
return ret;
}
EXPORT_SYMBOL_GPL(rawnand_mtd_download_boot0);
int aw_rawnand_mtd_download_uboot(unsigned int len, void *buf)
{
return rawnand_mtd_download_uboot(len, buf);
}
EXPORT_SYMBOL_GPL(aw_rawnand_mtd_download_uboot);
int aw_rawnand_mtd_download_boot0(unsigned int len, void *buf)
{
boot0_file_head_t *boot0 = (boot0_file_head_t *)buf;
rawnand_mtd_get_flash_info((void *)boot0->prvt_head.storage_data,
sizeof(boot0->prvt_head.storage_data));
boot0->boot_head.check_sum =
sunxi_sprite_generate_checksum(buf, boot0->boot_head.length, boot0->boot_head.check_sum);
return rawnand_mtd_download_boot0(len, buf);
}
EXPORT_SYMBOL_GPL(aw_rawnand_mtd_download_boot0);