402 lines
11 KiB
C
402 lines
11 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
#include <amlogic/secure_storage.h>
|
|
#include <asm/arch/secure_apb.h>
|
|
|
|
struct amlnand_chip *aml_nand_chip = NULL;
|
|
|
|
extern int boot_dev_init(struct amlnand_chip *aml_chip);
|
|
|
|
static void show_nand_driver_version(void)
|
|
{
|
|
aml_nand_msg("Nand PHY Ver:%d.%02d.%03d.%04d (c) 2013 Amlogic Inc.",
|
|
(DRV_PHY_VERSION >> 24)&0xff,
|
|
(DRV_PHY_VERSION >> 16)&0xff,
|
|
(DRV_PHY_VERSION >> 8)&0xff,
|
|
(DRV_PHY_VERSION)&0xff);
|
|
}
|
|
static int amlnf_phy_resource(struct amlnand_chip *aml_chip, struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
if (aml_chip == NULL) {
|
|
aml_nand_msg("%s() %d: invalid param!", __FUNCTION__, __LINE__);
|
|
ret = -1;
|
|
goto _out;
|
|
}
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
aml_chip->device = pdev->dev;
|
|
|
|
aml_nand_msg("%s() %d: amlnf init flag %d",
|
|
__FUNCTION__, __LINE__, aml_chip->init_flag);
|
|
|
|
#ifdef CONFIG_OF
|
|
aml_chip->nand_pinctrl = devm_pinctrl_get(&aml_chip->device);
|
|
if (IS_ERR(aml_chip->nand_pinctrl)) {
|
|
aml_nand_msg("get pinctrl error");
|
|
return PTR_ERR(aml_chip->nand_pinctrl);
|
|
}
|
|
aml_chip->nand_rbstate = pinctrl_lookup_state(aml_chip->nand_pinctrl,
|
|
"nand_rb_mod");
|
|
if (IS_ERR(aml_chip->nand_rbstate)) {
|
|
devm_pinctrl_put(aml_chip->nand_pinctrl);
|
|
return PTR_ERR(aml_chip->nand_rbstate);
|
|
}
|
|
aml_chip->nand_norbstate = pinctrl_lookup_state(aml_chip->nand_pinctrl,
|
|
"nand_norb_mod");
|
|
if (IS_ERR(aml_chip->nand_norbstate)) {
|
|
devm_pinctrl_put(aml_chip->nand_pinctrl);
|
|
return PTR_ERR(aml_chip->nand_norbstate);
|
|
}
|
|
aml_chip->nand_idlestate = pinctrl_lookup_state(aml_chip->nand_pinctrl,
|
|
"nand_cs_pins_only");
|
|
if (IS_ERR(aml_chip->nand_idlestate)) {
|
|
devm_pinctrl_put(aml_chip->nand_pinctrl);
|
|
return PTR_ERR(aml_chip->nand_idlestate);
|
|
}
|
|
#endif /* CONFIG_OF */
|
|
#endif /* AML_NAND_UBOOT */
|
|
_out:
|
|
return ret;
|
|
}
|
|
|
|
extern int dbg_amlnf_erase_ops(u64 off, u64 erase_len, unsigned char scrub_flag);
|
|
extern void amlnf_get_chip_size(u64 *size);
|
|
void dbg_erase_whole_chip(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
u64 chipsize;
|
|
|
|
PHY_NAND_LINE
|
|
//amlnf_get_chip_size(&chipsize);
|
|
chipsize = flash->blocksize * 4; //erase 1st 60 blocks.
|
|
PHY_NAND_LINE
|
|
|
|
dbg_amlnf_erase_ops(0, chipsize, 1);
|
|
|
|
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------
|
|
extern void _dump_mem(u32 * buf, u32 len);
|
|
#define DBG_OOB_LEN (8)
|
|
static void dbg_buffer_free(struct amlnand_chip *aml_chip)
|
|
{
|
|
if (aml_chip->user_oob_buf != NULL)
|
|
kfree(aml_chip->user_oob_buf);
|
|
|
|
if (aml_chip->user_page_buf != NULL)
|
|
kfree(aml_chip->user_page_buf);
|
|
return;
|
|
}
|
|
|
|
static void dbg_buffer_alloc(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
u32 ret = 0, buf_size;
|
|
|
|
buf_size = flash->oobsize * controller->chip_num;
|
|
if (flash->option & NAND_MULTI_PLANE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
aml_chip->user_oob_buf = aml_nand_malloc(buf_size);
|
|
if (aml_chip->user_oob_buf == NULL) {
|
|
aml_nand_msg("malloc failed for user_oob_buf ");
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto _out;
|
|
}
|
|
memset(aml_chip->user_oob_buf, 0x0, buf_size);
|
|
buf_size = (flash->pagesize + flash->oobsize) * controller->chip_num;
|
|
if (flash->option & NAND_MULTI_PLANE_MODE)
|
|
buf_size <<= 1;
|
|
|
|
aml_chip->user_page_buf = aml_nand_malloc(buf_size);
|
|
if (aml_chip->user_page_buf == NULL) {
|
|
aml_nand_msg("malloc failed for user_page_buf ");
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto _out;
|
|
}
|
|
memset(aml_chip->user_page_buf, 0x0, buf_size);
|
|
return;
|
|
if (ret) {
|
|
aml_nand_msg("%s() ret %d", __func__, ret);
|
|
}
|
|
|
|
_out:
|
|
dbg_buffer_free(aml_chip);
|
|
return;
|
|
}
|
|
|
|
void dbg_calcaddress(u8 chip, u32 blk, u32 page, struct amlnand_chip *aml_chip)
|
|
{
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
u8 phys_erase_shift, phys_page_shift;
|
|
u32 pages_per_blk;
|
|
|
|
phys_erase_shift = ffs(flash->blocksize) - 1;
|
|
phys_page_shift = ffs(flash->pagesize) - 1;
|
|
pages_per_blk = (1 << (phys_erase_shift - phys_page_shift));
|
|
|
|
ops_para->chipnr = chip;
|
|
ops_para->page_addr = blk * pages_per_blk + page;
|
|
ops_para->ooblen = DBG_OOB_LEN;
|
|
|
|
printk("%s(%d, %d, %d) - %x\n", __func__, chip, blk, page, ops_para->page_addr);
|
|
}
|
|
|
|
void dbg_ereaeblk(u8 chip, u32 blk, struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct chip_operation *operation = &aml_chip->operation;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
int ret;
|
|
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
dbg_calcaddress(chip, blk, 0, aml_chip);
|
|
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
nand_get_chip(aml_chip);
|
|
ret = operation->erase_block(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand erased failed", __func__, __LINE__);
|
|
}
|
|
nand_release_chip(aml_chip);
|
|
|
|
}
|
|
|
|
void dbg_readpage(u8 chip, u32 blk, u32 page, struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_operation *operation = &aml_chip->operation;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
int ret;
|
|
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
memset(ops_para->data_buf, 0x0, flash->pagesize);
|
|
memset(ops_para->oob_buf, 0x0, DBG_OOB_LEN);
|
|
dbg_calcaddress(chip, blk, page, aml_chip);
|
|
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
nand_get_chip(aml_chip);
|
|
ret = operation->read_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand read failed", __func__, __LINE__);
|
|
}
|
|
nand_release_chip(aml_chip);
|
|
_dump_mem((u32 *)ops_para->oob_buf, DBG_OOB_LEN);
|
|
_dump_mem((u32 *)ops_para->data_buf, 512);
|
|
|
|
}
|
|
|
|
void dbg_writepage(u8 chip, u32 blk, u32 page, struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
struct chip_operation *operation = &aml_chip->operation;
|
|
struct chip_ops_para *ops_para = &aml_chip->ops_para;
|
|
int ret;
|
|
|
|
memset((u8 *)ops_para, 0x0, sizeof(struct chip_ops_para));
|
|
dbg_calcaddress(chip, blk, page, aml_chip);
|
|
|
|
ops_para->data_buf = aml_chip->user_page_buf;
|
|
ops_para->oob_buf = aml_chip->user_oob_buf;
|
|
|
|
memset(ops_para->data_buf, 0xAA, flash->pagesize);
|
|
memset(ops_para->oob_buf, 0x55, DBG_OOB_LEN);
|
|
|
|
controller->select_chip(controller, ops_para->chipnr);
|
|
nand_get_chip(aml_chip);
|
|
ret = operation->write_page(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("%s() %d: nand write failed", __func__, __LINE__);
|
|
}
|
|
nand_release_chip(aml_chip);
|
|
|
|
}
|
|
|
|
void dbg_phyop( void )
|
|
{
|
|
/* u64 blksize; */
|
|
struct amlnand_chip *aml_chip = aml_nand_chip;
|
|
/* struct hw_controller *controller = &aml_chip->controller; */
|
|
/* struct nand_flash *flash = &aml_chip->flash; */
|
|
|
|
PHY_NAND_LINE
|
|
dbg_buffer_alloc(aml_chip);
|
|
PHY_NAND_LINE
|
|
#if 0
|
|
blksize = (u64) flash->blocksize;
|
|
PHY_NAND_LINE
|
|
printk("erase\n");
|
|
amlnf_erase_ops(0, blksize * 2, 1);
|
|
|
|
printk("erase again\n");
|
|
dbg_ereaeblk(0, 0, aml_chip);
|
|
#endif
|
|
PHY_NAND_LINE //write page here
|
|
//printk("read after erase!\n");
|
|
dbg_readpage(0, 80, 0, aml_chip);
|
|
dbg_readpage(0, 81, 0, aml_chip);
|
|
PHY_NAND_LINE //read page here.
|
|
#if 0
|
|
printk("write!\n");
|
|
dbg_writepage(0, 0, 0, aml_chip);
|
|
printk("read!\n");
|
|
dbg_readpage(0, 0, 0, aml_chip);
|
|
PHY_NAND_LINE
|
|
#endif //0
|
|
dbg_buffer_free(aml_chip);
|
|
}
|
|
|
|
void amlnand_clear_pinmux(struct amlnand_chip *aml_chip)
|
|
{
|
|
amlnf_clr_reg32_mask(P_PERIPHS_PIN_MUX_4,(0x7ff<<20));
|
|
return;
|
|
}
|
|
|
|
extern int get_flash_type(struct amlnand_chip *aml_chip);
|
|
|
|
int amlnf_phy_init(u8 flag, struct platform_device *pdev)
|
|
{
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
int ret = 0;
|
|
|
|
/*show nand phy version here.*/
|
|
show_nand_driver_version();
|
|
|
|
aml_chip = aml_nand_malloc(sizeof(struct amlnand_chip));
|
|
if (aml_chip == NULL) {
|
|
aml_nand_msg("malloc failed for aml_chip:%x",
|
|
(uint32_t)sizeof(struct amlnand_chip));
|
|
ret = -NAND_MALLOC_FAILURE;
|
|
goto exit_error0;
|
|
}
|
|
memset(aml_chip , 0, sizeof(struct amlnand_chip));
|
|
memset(aml_chip->reserved_blk, 0xff, RESERVED_BLOCK_CNT);
|
|
aml_chip->init_flag = flag;
|
|
aml_chip->nand_status = NAND_STATUS_NORMAL;
|
|
aml_nand_chip = aml_chip;
|
|
|
|
PHY_NAND_LINE
|
|
ret = amlnf_phy_resource(aml_chip, pdev);
|
|
if (ret)
|
|
goto exit_error1;
|
|
|
|
PHY_NAND_LINE
|
|
/* Step 1: init hw controller */
|
|
ret = amlnand_hwcontroller_init(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("aml_hw_controller_init failed");
|
|
ret = -NAND_FAILED;
|
|
goto exit_error1;
|
|
}
|
|
PHY_NAND_LINE
|
|
/* Step 2: init aml_chip and store interface operation */
|
|
ret = amlnand_init_operation(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("chip detect failed and ret:%x", ret);
|
|
ret = -NAND_FAILED;
|
|
goto exit_error1;
|
|
}
|
|
|
|
PHY_NAND_LINE
|
|
/* Step 3: get nand id and get hw flash information */
|
|
ret = amlnand_chip_init(aml_chip);
|
|
if (ret < 0) {
|
|
aml_nand_msg("chip detect failed and ret:%x", ret);
|
|
amlnand_clear_pinmux(aml_chip);
|
|
ret = -NAND_FAILED;
|
|
goto exit_error1;
|
|
}
|
|
PHY_NAND_LINE
|
|
/* write 2 gp2*/
|
|
#if 0
|
|
secure_storage_set_info(STORAGE_DEV_NAND);
|
|
#endif
|
|
|
|
PHY_NAND_LINE
|
|
if (aml_chip->init_flag == NAND_SCAN_ID_INIT)
|
|
goto exit_error0;
|
|
PHY_NAND_LINE
|
|
|
|
//fixme, debug code
|
|
//dbg_phyop(aml_chip);
|
|
//dbg_erase_whole_chip(aml_chip);
|
|
|
|
/* step 3.5 init boot phydev 1st, then we can operate uboot no matter what happens further.*/
|
|
boot_dev_init(aml_chip);
|
|
if (aml_chip->init_flag == 6) { //NAND_PHY_INIT
|
|
PHY_NAND_LINE
|
|
goto exit_error0;
|
|
}
|
|
|
|
|
|
//Step 4: get device configs
|
|
ret = amlnand_get_dev_configs(aml_chip);
|
|
if (ret < 0) {
|
|
if ((ret == -NAND_CONFIGS_FAILED) || (ret == -NAND_SHIPPED_BADBLOCK_FAILED)
|
|
|| (ret == -NAND_DETECT_DTB_FAILED)) {
|
|
aml_nand_msg("get device configs failed and ret:%x", ret);
|
|
goto exit_error0;
|
|
}else{
|
|
aml_nand_msg("get device configs failed and ret:%x", ret);
|
|
ret = -NAND_READ_FAILED;
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
PHY_NAND_LINE
|
|
if (aml_chip->init_flag >= NAND_BOOT_ERASE_PROTECT_CACHE) {
|
|
struct hw_controller *controller = &aml_chip->controller;
|
|
#ifndef AML_NAND_UBOOT
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
|
|
amlnf_dma_free(controller->data_buf, (flash->pagesize + flash->oobsize), 0);
|
|
amlnf_dma_free(controller->user_buf, (flash->pagesize /controller->ecc_bytes)*sizeof(int), 1);
|
|
#else /* AML_NAND_UBOOT */
|
|
aml_nand_free(controller->data_buf);
|
|
aml_nand_free(controller->user_buf);
|
|
#endif /* AML_NAND_UBOOT */
|
|
//aml_nand_free(controller->page_buf);
|
|
aml_nand_free(controller->oob_buf);
|
|
|
|
//nand_buf_free(aml_chip);
|
|
/*exit with NAND_SUCCESS while we are erasing.*/
|
|
ret = NAND_SUCCESS;
|
|
goto exit_error0;
|
|
}else{
|
|
|
|
//Step 5: register nand device, and config device information
|
|
PHY_NAND_LINE
|
|
ret = amlnand_phydev_init(aml_chip);
|
|
PHY_NAND_LINE
|
|
if (ret < 0) {
|
|
aml_nand_msg("register nand device failed and ret:%x", ret);
|
|
ret = -NAND_READ_FAILED;
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
return ret;
|
|
|
|
exit_error1:
|
|
if (aml_chip)
|
|
aml_nand_free(aml_chip);
|
|
exit_error0:
|
|
|
|
return ret;
|
|
}
|
|
|