1001 lines
26 KiB
C
1001 lines
26 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
#include <asm/arch/secure_apb.h>
|
|
static int controller_select_chip(struct hw_controller *controller,
|
|
u8 chipnr)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
switch (chipnr) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
controller->chip_selected = controller->ce_enable[chipnr];
|
|
controller->rb_received = controller->rb_enable[chipnr];
|
|
#ifdef AML_NAND_UBOOT
|
|
for (i=0; i < controller->chip_num; i++) {
|
|
pinmux_select_chip(controller->ce_enable[i], controller->rb_enable[i], ((controller->option & NAND_CTRL_NONE_RB) == 0));
|
|
}
|
|
#endif
|
|
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
break;
|
|
default:
|
|
BUG();
|
|
controller->chip_selected = CE_NOT_SEL;
|
|
ret = -NAND_SELECT_CHIP_FAILURE;
|
|
aml_nand_msg("failed");
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef AML_NAND_DMA_POLLING
|
|
static struct completion controller_dma_completion;
|
|
|
|
static enum hrtimer_restart controller_dma_timerfuc(struct hrtimer *timer)
|
|
{
|
|
struct hw_controller *controller = NULL;
|
|
u32 fifo_cnt = 0;
|
|
|
|
controller = container_of(timer, struct hw_controller, timer);
|
|
fifo_cnt = NFC_CMDFIFO_SIZE(controller);
|
|
|
|
/* */
|
|
smp_rmb();
|
|
/* */
|
|
smp_wmb();
|
|
if (fifo_cnt == 0)
|
|
complete(&controller_dma_completion);
|
|
else
|
|
hrtimer_start(&controller->timer,
|
|
ktime_set(0, DMA_TIME_CNT_20US),
|
|
HRTIMER_MODE_REL);
|
|
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
static int controller_dma_timer_handle(struct hw_controller *controller)
|
|
{
|
|
struct amlnand_chip *aml_chip = controller->aml_chip;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
int timeout, time_start;
|
|
|
|
time_start = (flash->pagesize + flash->oobsize)*50+5000;
|
|
init_completion(&controller_dma_completion);
|
|
hrtimer_start(&controller->timer,
|
|
ktime_set(0, time_start),
|
|
HRTIMER_MODE_REL);
|
|
|
|
/* max 500mS */
|
|
timeout = wait_for_completion_timeout(&controller_dma_completion, 50);
|
|
if (timeout == 0) {
|
|
aml_nand_msg("dma time out");
|
|
return -NAND_BUSY_FAILURE;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* AML_NAND_DMA_POLLING */
|
|
|
|
#ifdef AML_NAND_RB_IRQ
|
|
|
|
static struct completion controller_rb_completion;
|
|
|
|
void controller_open_interrupt(struct hw_controller *controller)
|
|
{
|
|
/* NFC_ENABLE_STS_IRQ(); */
|
|
NFC_ENABLE_IO_IRQ(controller);
|
|
|
|
}
|
|
|
|
void controller_close_interrupt(struct hw_controller *controller)
|
|
{
|
|
/* NFC_DISABLE_STS_IRQ(); */
|
|
NFC_DISABLE_IO_IRQ(controller);
|
|
}
|
|
|
|
static irqreturn_t controller_interrupt_monitor(int irq,
|
|
void *dev_id,
|
|
struct pt_regs *regs)
|
|
{
|
|
struct hw_controller *controller = (struct hw_controller *)dev_id;
|
|
|
|
controller_close_interrupt(controller);
|
|
complete(&controller_rb_completion);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int controller_queue_rb_irq(struct hw_controller *controller,
|
|
u8 chipnr)
|
|
{
|
|
int ret = 0, timeout = 0;
|
|
|
|
if (chipnr != NAND_CHIP_UNDEFINE)/* skip dma operation */
|
|
controller->select_chip(controller, chipnr);
|
|
|
|
init_completion(&controller_rb_completion);
|
|
controller_open_interrupt(controller);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
controller->cmd_ctrl(controller, NAND_CMD_STATUS, NAND_CTRL_CLE);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
/* */
|
|
smp_rmb();
|
|
/* */
|
|
smp_wmb();
|
|
|
|
NFC_SEND_CMD_RB_IRQ(controller, 18);
|
|
/* NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE); */
|
|
|
|
timeout = wait_for_completion_timeout(&controller_rb_completion, 50);
|
|
if (timeout == 0) {
|
|
aml_nand_msg("***nand irq timeout here");
|
|
ret = -NAND_BUSY_FAILURE;
|
|
}
|
|
|
|
controller_close_interrupt(controller);
|
|
return ret;
|
|
}
|
|
#endif /* AML_NAND_RB_IRQ */
|
|
|
|
static int controller_quene_rb(struct hw_controller *controller,
|
|
u8 chipnr)
|
|
{
|
|
u32 time_out_limit, time_out_cnt = 0;
|
|
struct amlnand_chip *aml_chip = controller->aml_chip;
|
|
int ret = 0;
|
|
|
|
if (aml_chip->state == CHIP_RESETING)
|
|
time_out_limit = AML_NAND_ERASE_BUSY_TIMEOUT;
|
|
else if (aml_chip->state == CHIP_WRITING)
|
|
time_out_limit = AML_NAND_WRITE_BUSY_TIMEOUT;
|
|
else
|
|
time_out_limit = AML_NAND_READ_BUSY_TIMEOUT;
|
|
|
|
controller->select_chip(controller, chipnr);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
while (NFC_CMDFIFO_SIZE(controller) > 0);
|
|
|
|
#if 0
|
|
NFC_SEND_CMD_RB(controller, aml_chip->chip_enable, 20);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
} while (time_out_cnt++ <= AML_DMA_BUSY_TIMEOUT);
|
|
#endif
|
|
|
|
/* udelay(2); */
|
|
if (controller->option & NAND_CTRL_NONE_RB) {
|
|
controller->cmd_ctrl(controller,
|
|
NAND_CMD_STATUS, NAND_CTRL_CLE);
|
|
/*
|
|
aml_nand_dbg("controller->chip_selected =%d",
|
|
controller->chip_selected);
|
|
*/
|
|
//NAND_LINE
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWHR_TIME_CYCLE);
|
|
do {
|
|
/* udelay(chip->chip_delay); */
|
|
if ((int)controller->readbyte(controller) &
|
|
NAND_STATUS_READY)
|
|
break;
|
|
udelay(1);
|
|
//NAND_LINE
|
|
} while (time_out_cnt++ <= time_out_limit);/* 200ms max */
|
|
} else {
|
|
do {
|
|
if (NFC_GET_RB_STATUS(controller,
|
|
controller->rb_received))
|
|
break;
|
|
udelay(2);
|
|
} while (time_out_cnt++ <= time_out_limit);
|
|
}
|
|
|
|
if (time_out_cnt >= time_out_limit) {
|
|
NAND_LINE
|
|
ret = -NAND_BUSY_FAILURE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int controller_hwecc_correct(struct hw_controller *controller,
|
|
u32 size,
|
|
u8 *oob_buf)
|
|
{
|
|
u32 ecc_step_num, cur_ecc, usr_info;
|
|
u32 info_times_int_len = PER_INFO_BYTE/sizeof(u32);
|
|
struct amlnand_chip *aml_chip = controller->aml_chip;
|
|
int max_ecc = 0;
|
|
int user_offset = 0;
|
|
u32 tmp_value;
|
|
|
|
if (controller->oob_mod == 1)
|
|
user_offset = 4;
|
|
|
|
if (size % controller->ecc_unit) {
|
|
aml_nand_msg("err para size for ecc correct %x,and ecc_unit:%x",
|
|
size,
|
|
controller->ecc_unit);
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
}
|
|
|
|
controller->ecc_cnt_cur = 0;
|
|
for (ecc_step_num = 0;
|
|
ecc_step_num < (size / controller->ecc_unit);
|
|
ecc_step_num++) {
|
|
/* check if there have uncorrectable sector */
|
|
tmp_value = ecc_step_num*info_times_int_len + user_offset;
|
|
usr_info = (*(u32 *)(&(controller->user_buf[tmp_value])));
|
|
cur_ecc = NAND_ECC_CNT(usr_info);
|
|
/*
|
|
aml_nand_dbg("uncorrected for cur_ecc:%d, usr_buf[%d]:%x",
|
|
cur_ecc,
|
|
ecc_step_num,
|
|
usr_info);
|
|
*/
|
|
if (cur_ecc == 0x3f) {
|
|
controller->zero_cnt = NAND_ZERO_CNT(usr_info);
|
|
if (max_ecc < controller->zero_cnt)
|
|
max_ecc = controller->zero_cnt;
|
|
/*
|
|
aml_nand_dbg("uncorrected for ecc_step_num:%d,
|
|
zero_cnt:%d",
|
|
ecc_step_num,
|
|
controller->zero_cnt);
|
|
*/
|
|
return NAND_ECC_FAILURE;
|
|
} else {
|
|
controller->ecc_cnt_cur =
|
|
(controller->ecc_cnt_cur > cur_ecc) ?
|
|
controller->ecc_cnt_cur : cur_ecc;
|
|
if (max_ecc < controller->ecc_cnt_cur)
|
|
max_ecc = controller->ecc_cnt_cur;
|
|
}
|
|
}
|
|
aml_chip->max_ecc_per_page = max_ecc;
|
|
return 0;
|
|
}
|
|
|
|
void _dump_mem(u32 * buf, u32 len)
|
|
{
|
|
u32 i;
|
|
printk("%s, %p, %d", __func__, buf, len);
|
|
for (i = 0; i < len/sizeof(u32); i++) {
|
|
|
|
if ( i % 4 == 0)
|
|
printk("\n0x%p: ", buf+i);
|
|
printk("%08x ", buf[i]);
|
|
}
|
|
printk("\n");
|
|
return;
|
|
}
|
|
|
|
/* default enable ran mode */
|
|
static int controller_dma_read(struct hw_controller *controller,
|
|
u32 len,
|
|
u8 bch_mode)
|
|
{
|
|
int count, dma_unit_size, info_times_int_len, time_out_cnt, dma_cnt;
|
|
u32 *info_buf = 0;
|
|
u32 tmp_value;
|
|
u32 cmp;
|
|
u64 data64, user64;
|
|
/* volatile int cmp=0; */
|
|
/* int ret = 0; */
|
|
//printk("%s() %d: 0x%p\n", __func__, __LINE__, controller->data_buf);
|
|
dma_unit_size = 0;
|
|
info_times_int_len = PER_INFO_BYTE/sizeof(u32);
|
|
if (bch_mode == NAND_ECC_NONE) {
|
|
if (len > 0x3fff)
|
|
len = 0x3ffe;
|
|
count = 1;
|
|
} else if (bch_mode == NAND_ECC_BCH_SHORT) {
|
|
dma_unit_size = (controller->ecc_unit >> 3);
|
|
count = len/controller->ecc_unit;
|
|
} else
|
|
count = controller->ecc_steps;
|
|
|
|
dma_cnt = count;
|
|
if ((controller->oob_mod == 1) && (bch_mode != NAND_ECC_NONE))
|
|
count += 16 / PER_INFO_BYTE;
|
|
|
|
tmp_value = (count-1)*info_times_int_len;
|
|
info_buf = (u32 *)&(controller->user_buf[tmp_value]);
|
|
memset((u8 *)controller->user_buf, 0, count*PER_INFO_BYTE);
|
|
|
|
/* */
|
|
#ifndef AML_NAND_UBOOT
|
|
smp_wmb();
|
|
wmb();
|
|
|
|
//while(NFC_CMDFIFO_SIZE() > 10);
|
|
NFC_SEND_CMD_ADL(controller, controller->data_dma_addr);
|
|
NFC_SEND_CMD_ADH(controller, controller->data_dma_addr);
|
|
NFC_SEND_CMD_AIL(controller, controller->info_dma_addr);
|
|
NFC_SEND_CMD_AIH(controller, controller->info_dma_addr);
|
|
#else
|
|
|
|
flush_dcache_range((uint64_t)controller->user_buf, (uint64_t)(controller->user_buf + count*PER_INFO_BYTE));
|
|
invalidate_dcache_range((uint64_t)controller->data_buf, (uint64_t)(controller->data_buf + len));
|
|
//while(NFC_CMDFIFO_SIZE() > 10);
|
|
data64 = (u64)controller->data_buf;
|
|
user64 = (u64)controller->user_buf;
|
|
NFC_SEND_CMD_ADL(controller, (u32)data64);
|
|
NFC_SEND_CMD_ADH(controller, (u32)data64);
|
|
NFC_SEND_CMD_AIL(controller, (u32)user64);
|
|
NFC_SEND_CMD_AIH(controller, (u32)user64);
|
|
#endif
|
|
|
|
|
|
/* setting page_addr used for seed */
|
|
aml_nand_dbg("r seed %x, bch %d\n", controller->page_addr, bch_mode);
|
|
NFC_SEND_CMD_SEED(controller, controller->page_addr);
|
|
|
|
if (bch_mode == NAND_ECC_NONE)
|
|
NFC_SEND_CMD_N2M_RAW(controller, controller->ran_mode, len);
|
|
else
|
|
NFC_SEND_CMD_N2M(controller, controller->ran_mode,
|
|
((bch_mode == NAND_ECC_BCH_SHORT)?NAND_ECC_BCH60_1K:bch_mode),
|
|
((bch_mode == NAND_ECC_BCH_SHORT)?1:0), dma_unit_size, dma_cnt);
|
|
|
|
#if 0
|
|
NFC_SEND_CMD_STS(controller, 20, 2);
|
|
#else
|
|
#ifdef AML_NAND_DMA_POLLING
|
|
ret = controller_dma_timer_handle(controller);
|
|
#if 0 /* irq failed here */
|
|
ret = controller_queue_rb_irq(controller, NAND_CHIP_UNDEFINE);
|
|
#endif /* 0 */
|
|
|
|
if (ret) {
|
|
time_out_cnt = AML_DMA_BUSY_TIMEOUT;
|
|
aml_nand_msg("dma timeout here");
|
|
return -NAND_DMA_FAILURE;
|
|
}
|
|
#else /* AML_NAND_DMA_POLLING */
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
time_out_cnt = 0;
|
|
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
} while (time_out_cnt++ <= AML_DMA_BUSY_TIMEOUT);
|
|
|
|
if (time_out_cnt >= AML_DMA_BUSY_TIMEOUT) {
|
|
aml_nand_msg("dma timeout here");
|
|
return -NAND_DMA_FAILURE;
|
|
}
|
|
|
|
#endif /* AML_NAND_DMA_POLLING */
|
|
#endif /* 0 */
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
do {
|
|
smp_rmb();
|
|
}while(NAND_INFO_DONE(*info_buf) == 0);
|
|
|
|
smp_wmb();
|
|
wmb();
|
|
#else /*AML_NAND_UBOOT*/
|
|
do {
|
|
invalidate_dcache_range((unsigned long)controller->user_buf, ((unsigned long)controller->user_buf + count * PER_INFO_BYTE));
|
|
info_buf = (u32 *)&(controller->user_buf[(count-1) * info_times_int_len]);
|
|
cmp = *info_buf;
|
|
}while((cmp)==0);
|
|
|
|
#endif /* AML_NAND_UBOOT */
|
|
/*
|
|
aml_nand_dbg("len:%d, count:%d, bch_mode:%d\n",
|
|
len,
|
|
count,
|
|
bch_mode);
|
|
*/
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
static int controller_dma_write(struct hw_controller *controller,
|
|
u8 *buf,
|
|
u32 len,
|
|
u8 bch_mode)
|
|
{
|
|
int ret = 0, time_out_cnt = 0, oob_fill_cnt = 0;
|
|
u32 dma_unit_size = 0, count = 0;
|
|
u64 data64, user64;
|
|
|
|
data64 = (u64)controller->data_buf;
|
|
user64 = (u64)controller->user_buf;
|
|
|
|
if (bch_mode == NAND_ECC_NONE) {
|
|
if (len > 0x3fff)
|
|
len = 0x3ffe;
|
|
count = 1;
|
|
} else if (bch_mode == NAND_ECC_BCH_SHORT) {
|
|
dma_unit_size = (controller->ecc_unit >> 3);
|
|
count = len / controller->ecc_unit;
|
|
} else
|
|
count = controller->ecc_steps;
|
|
|
|
memcpy(controller->data_buf, buf, len);
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
smp_wmb();
|
|
wmb();
|
|
|
|
NFC_SEND_CMD_ADL(controller, controller->data_dma_addr);
|
|
NFC_SEND_CMD_ADH(controller, controller->data_dma_addr);
|
|
NFC_SEND_CMD_AIL(controller, controller->info_dma_addr);
|
|
NFC_SEND_CMD_AIH(controller, controller->info_dma_addr);
|
|
#else /* AML_NAND_UBOOT */
|
|
flush_dcache_range((unsigned long)controller->user_buf, ((unsigned long)controller->user_buf + count*PER_INFO_BYTE));
|
|
flush_dcache_range((unsigned long)controller->data_buf, ((unsigned long)controller->data_buf +len));
|
|
NFC_SEND_CMD_ADL(controller, (u32)data64);
|
|
NFC_SEND_CMD_ADH(controller, (u32)data64);
|
|
NFC_SEND_CMD_AIL(controller, (u32)user64);
|
|
NFC_SEND_CMD_AIH(controller, (u32)user64);
|
|
#endif /* AML_NAND_UBOOT */
|
|
/* fixme, dbg code */
|
|
aml_nand_dbg("w seed %x, bch %d\n", controller->page_addr, bch_mode);
|
|
NFC_SEND_CMD_SEED(controller, controller->page_addr);
|
|
|
|
if (!bch_mode)
|
|
NFC_SEND_CMD_M2N_RAW(controller, 0, len);
|
|
else
|
|
NFC_SEND_CMD_M2N(controller, controller->ran_mode,
|
|
((bch_mode == NAND_ECC_BCH_SHORT)?NAND_ECC_BCH60_1K:bch_mode),
|
|
((bch_mode == NAND_ECC_BCH_SHORT)?1:0), dma_unit_size, count);
|
|
|
|
if (bch_mode == NAND_ECC_BCH_SHORT)
|
|
oob_fill_cnt = controller->oob_fill_boot;
|
|
else if (bch_mode != NAND_ECC_NONE)
|
|
oob_fill_cnt = controller->oob_fill_data;
|
|
|
|
if (((bch_mode != NAND_ECC_NONE)) && (oob_fill_cnt > 0))
|
|
/*
|
|
aml_nand_dbg("fill oob controller oob_fill_cnt %d",\
|
|
oob_fill_cnt);
|
|
*/
|
|
NFC_SEND_CMD_M2N_RAW(controller,
|
|
controller->ran_mode,
|
|
oob_fill_cnt);
|
|
else if (bch_mode == NAND_ECC_NONE) {
|
|
NFC_SEND_CMD_ADL(controller, (u32)data64);
|
|
NFC_SEND_CMD_ADH(controller, (u32)data64);
|
|
NFC_SEND_CMD_M2N_RAW(controller, 0, controller->oobavail);
|
|
}
|
|
|
|
#ifdef AML_NAND_DMA_POLLING
|
|
ret = controller_dma_timer_handle(controller);
|
|
#if 0/* irq failed here */
|
|
ret = controller_queue_rb_irq(controller, NAND_CHIP_UNDEFINE);
|
|
#endif /* 0 */
|
|
if (ret) {
|
|
time_out_cnt = AML_DMA_BUSY_TIMEOUT;
|
|
aml_nand_msg("dma timeout here");
|
|
return -NAND_DMA_FAILURE;
|
|
}
|
|
#else /* AML_NAND_DMA_POLLING */
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
time_out_cnt = 0;
|
|
do {
|
|
if (NFC_CMDFIFO_SIZE(controller) <= 0)
|
|
break;
|
|
} while (time_out_cnt++ <= AML_DMA_BUSY_TIMEOUT);
|
|
|
|
if (time_out_cnt >= AML_DMA_BUSY_TIMEOUT) {
|
|
aml_nand_msg("dma timeout here");
|
|
return -NAND_DMA_FAILURE;
|
|
}
|
|
#endif /* AML_NAND_DMA_POLLING */
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* aml_nand_hw_init function.
|
|
* init hwcontroller CFG register setting,
|
|
*
|
|
*/
|
|
static int controller_hw_init(struct hw_controller *controller)
|
|
{
|
|
int sys_clk_rate, sys_time, bus_cycle, bus_timing;
|
|
/* int clk_delay; */
|
|
int ret = 0;
|
|
//nand_cfg_t cfg;
|
|
|
|
sys_clk_rate = 200;
|
|
get_sys_clk_rate(controller, &sys_clk_rate);
|
|
|
|
sys_time = (10000 / sys_clk_rate);
|
|
|
|
#if 0
|
|
/* if( get_cpu_type() >=MESON_CPU_MAJOR_ID_M8) */
|
|
/* clk_delay = ; */
|
|
start_cycle = (((NAND_CYCLE_DELAY + T_REA * 10) * 10) / sys_time);
|
|
start_cycle = (start_cycle + 9) / 10;
|
|
|
|
for (bus_cycle = 4; bus_cycle <= MAX_CYCLE_NUM; bus_cycle++) {
|
|
Tcycle = bus_cycle * sys_time;
|
|
end_cycle =
|
|
(((NAND_CYCLE_DELAY + Tcycle/2 + T_RHOH * 10) * 10)/sys_time);
|
|
end_cycle = end_cycle / 10;
|
|
if ((((start_cycle >= 3) && (start_cycle <= (bus_cycle + 1)))
|
|
|| ((end_cycle >= 3) && (end_cycle <= (bus_cycle + 1))))
|
|
&& (start_cycle <= end_cycle)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bus_cycle > MAX_CYCLE_NUM) {
|
|
aml_nand_msg("timming failed bus_cycle:%d", bus_cycle);
|
|
return -NAND_FAILED;
|
|
}
|
|
|
|
bus_timing = (start_cycle + end_cycle) / 2;
|
|
#else
|
|
bus_cycle = 6;
|
|
bus_timing = bus_cycle + 1;
|
|
#endif
|
|
//NFC_SET_CFG(controller, 0);
|
|
NFC_SET_TIMING_ASYC(controller, bus_timing, (bus_cycle - 1));
|
|
NFC_SEND_CMD(controller, 1<<31);
|
|
|
|
aml_nand_msg("init bus_cycle=%d, bus_timing=%d, system=%d.%dns",
|
|
bus_cycle, bus_timing, sys_time/10, sys_time%10);
|
|
aml_nand_dbg("cfg %x\n", p_nand_reg->cfg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void controller_enter_standby(struct hw_controller *controller)
|
|
{
|
|
/* just enter standby status. */
|
|
NFC_SEND_CMD_STANDBY(controller, 5);/* delay for 5 cycle. */
|
|
}
|
|
|
|
static int controller_adjust_timing(struct hw_controller *controller)
|
|
{
|
|
struct amlnand_chip *aml_chip = controller->aml_chip;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
int sys_clk_rate, sys_time, bus_cycle, bus_timing;
|
|
//nand_cfg_t cfg;
|
|
|
|
if (!flash->T_REA || (flash->T_REA < 16))
|
|
flash->T_REA = 16;
|
|
if (!flash->T_RHOH || (flash->T_RHOH < 15))
|
|
flash->T_RHOH = 15;
|
|
|
|
if (flash->T_REA > 16)
|
|
sys_clk_rate = 200;
|
|
else
|
|
sys_clk_rate = 250;
|
|
|
|
get_sys_clk_rate(controller, &sys_clk_rate);
|
|
|
|
sys_time = (10000 / sys_clk_rate);
|
|
/* sys_time = (10000 / (sys_clk_rate / 1000000)); */
|
|
|
|
#if 0
|
|
start_cycle = (((NAND_CYCLE_DELAY + flash->T_REA * 10) * 10)/sys_time);
|
|
start_cycle = (start_cycle + 9) / 10;
|
|
|
|
for (bus_cycle = 6; bus_cycle <= MAX_CYCLE_NUM; bus_cycle++) {
|
|
Tcycle = bus_cycle * sys_time;
|
|
end_cycle =
|
|
(((NAND_CYCLE_DELAY+Tcycle/2+flash->T_RHOH*10)*10)/sys_time);
|
|
end_cycle = end_cycle / 10;
|
|
if ((((start_cycle >= 3) && (start_cycle <= (bus_cycle + 1)))
|
|
|| ((end_cycle >= 3) && (end_cycle <= (bus_cycle + 1))))
|
|
&& (start_cycle <= end_cycle)) {
|
|
break;
|
|
}
|
|
}
|
|
if (bus_cycle > MAX_CYCLE_NUM) {
|
|
aml_nand_msg("timming fail,bus_c:%d,sys_t%d,T_REA:%d,T_RHOH:%d",
|
|
bus_cycle,
|
|
sys_time,
|
|
flash->T_REA,
|
|
flash->T_RHOH);
|
|
return -NAND_FAILED;
|
|
}
|
|
|
|
bus_timing = (start_cycle + end_cycle) / 2;
|
|
#else /* 0 */
|
|
bus_cycle = 6;
|
|
bus_timing = bus_cycle + 1;
|
|
#endif /* 0 */
|
|
|
|
NFC_SET_CFG(controller , 0);
|
|
NFC_SET_TIMING_ASYC(controller, bus_timing, (bus_cycle - 1));
|
|
|
|
/* for encrypt store */
|
|
if (AMLNF_READ_REG(P_AO_SEC_SD_CFG10) & (1 << 15))
|
|
NFC_ENABLE_ENCRYPT(controller);
|
|
|
|
NFC_SEND_CMD(controller, 1<<31);
|
|
aml_nand_msg("bus_c=%d,bus_t=%d,sys=%d.%dns,T_REA=%d,T_RHOH=%d",
|
|
bus_cycle,
|
|
bus_timing,
|
|
sys_time/10,
|
|
sys_time%10,
|
|
flash->T_REA,
|
|
flash->T_RHOH);
|
|
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*options confirm here, including ecc mode
|
|
*/
|
|
static int controller_ecc_confirm(struct hw_controller *controller)
|
|
{
|
|
struct amlnand_chip *aml_chip = controller->aml_chip;
|
|
struct nand_flash *flash = &(aml_chip->flash);
|
|
struct bch_desc *ecc_supports = controller->bch_desc;
|
|
u32 max_bch_mode = controller->max_bch;
|
|
u32 options_support = 0, ecc_bytes, ecc_page_cnt = 0, i;
|
|
u8 bch_index = 0;
|
|
u16 tmp_value;
|
|
|
|
if (controller->option & NAND_ECC_SOFT_MODE) {
|
|
controller->ecc_unit = flash->pagesize + flash->oobsize;
|
|
controller->bch_mode = NAND_ECC_NONE;
|
|
aml_nand_msg("soft ecc mode");
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
for (i = (max_bch_mode-1); i > 0; i--) {
|
|
ecc_page_cnt = flash->pagesize/ecc_supports[i].unit_size;
|
|
ecc_bytes = flash->oobsize / ecc_page_cnt;
|
|
if (ecc_bytes >= ecc_supports[i].bytes +
|
|
ecc_supports[i].usr_mode) {
|
|
options_support = ecc_supports[i].mode;
|
|
bch_index = ecc_supports[i].bch_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
controller->oob_mod = 0;
|
|
#if (AML_CFG_NEWOOB_EN)
|
|
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
|
|
if (flash->oobsize >= (16+ecc_supports[i].bytes*ecc_page_cnt)) {
|
|
/* for backward compatbility 4k page mlc. The code
|
|
we released before like below, which means old oob
|
|
mode will be used when page size < 4k.
|
|
------------------------------------
|
|
if (flash->pagesize > 4096) {
|
|
aml_nand_msg("AML_NAND_NEW_OOB : new oob");
|
|
NFC_SET_OOB_MODE(3<<26);
|
|
controller->oob_mod = 1;
|
|
}else{
|
|
controller->oob_mod = 0;
|
|
}
|
|
------------------------------------*/
|
|
if ((flash->pagesize == 4096)
|
|
&& (flash->chipsize > 2048))
|
|
controller->oob_mod = 0;
|
|
else {
|
|
aml_nand_msg("new oob mode");
|
|
NFC_SET_OOB_MODE(controller, 3<<26);
|
|
controller->oob_mod = 1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
switch (options_support) {
|
|
case NAND_ECC_BCH8_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_SIZE;
|
|
controller->ecc_bytes = NAND_BCH8_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 6;
|
|
controller->ecc_max = 8;
|
|
break;
|
|
|
|
case NAND_ECC_BCH8_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH8_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 6;
|
|
controller->ecc_max = 8;
|
|
break;
|
|
|
|
case NAND_ECC_BCH16_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH16_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 14;
|
|
controller->ecc_max = 16;
|
|
break;
|
|
|
|
case NAND_ECC_BCH24_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH24_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 22;
|
|
controller->ecc_max = 24;
|
|
break;
|
|
|
|
case NAND_ECC_BCH30_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH30_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 26;
|
|
controller->ecc_max = 30;
|
|
break;
|
|
|
|
case NAND_ECC_BCH40_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH40_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 34;
|
|
controller->ecc_max = 40;
|
|
break;
|
|
|
|
case NAND_ECC_BCH50_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH50_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 45;
|
|
controller->ecc_max = 50;
|
|
break;
|
|
|
|
case NAND_ECC_BCH60_1K_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_1KSIZE;
|
|
controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 55;
|
|
controller->ecc_max = 60;
|
|
break;
|
|
|
|
case NAND_ECC_SHORT_MODE:
|
|
controller->ecc_unit = NAND_ECC_UNIT_SHORT;
|
|
controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
|
|
controller->ecc_cnt_limit = 55;
|
|
controller->ecc_max = 60;
|
|
break;
|
|
|
|
default:
|
|
aml_nand_msg("no match ecc mode here");
|
|
return -NAND_ARGUMENT_FAILURE;
|
|
break;
|
|
}
|
|
|
|
controller->bch_mode = bch_index;
|
|
if (controller->oob_mod)
|
|
controller->user_mode = 16 / (flash->pagesize / controller->ecc_unit);
|
|
else
|
|
controller->user_mode = 2;
|
|
tmp_value = controller->ecc_unit + controller->ecc_bytes +
|
|
controller->user_mode;
|
|
controller->ecc_steps = (flash->pagesize+flash->oobsize)/tmp_value;
|
|
controller->oobavail = controller->ecc_steps*controller->user_mode;
|
|
|
|
controller->oobtail = flash->pagesize - controller->ecc_steps*tmp_value;
|
|
controller->oob_fill_data = (flash->oobsize -
|
|
(controller->ecc_steps*(controller->ecc_bytes+controller->user_mode)));
|
|
controller->oob_fill_boot = (flash->pagesize+flash->oobsize) - 512;
|
|
controller->ran_mode = 1;
|
|
aml_nand_dbg("ecc_unit:%d, ecc_bytes:%d, ecc_steps:%d, ecc_max:%d",
|
|
controller->ecc_unit,
|
|
controller->ecc_bytes,
|
|
controller->ecc_steps,
|
|
controller->ecc_max);
|
|
aml_nand_dbg("bch_mode:%d,user_mode:%d, oobavail:%d,oobtail:%d",
|
|
controller->bch_mode,
|
|
controller->user_mode,
|
|
controller->oobavail,
|
|
controller->oobtail);
|
|
aml_nand_dbg("oob_fill_data %d,controller->oob_fill_boot %d",
|
|
controller->oob_fill_data,
|
|
controller->oob_fill_boot);
|
|
|
|
return NAND_SUCCESS;
|
|
}
|
|
|
|
static void controller_cmd_ctrl(struct hw_controller *controller,
|
|
u32 cmd,
|
|
u32 ctrl)
|
|
{
|
|
if (cmd == NAND_CMD_NONE)
|
|
return;
|
|
|
|
if (ctrl & NAND_CLE)
|
|
cmd = NFC_CMD_CLE(controller->chip_selected, cmd);
|
|
else
|
|
cmd = NFC_CMD_ALE(controller->chip_selected, cmd);
|
|
|
|
NFC_SEND_CMD(controller, cmd);
|
|
}
|
|
|
|
static void controller_write_byte(struct hw_controller *controller,
|
|
u8 data)
|
|
{
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
NFC_SEND_CMD_DWR(controller, controller->chip_selected, data);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
while (NFC_CMDFIFO_SIZE(controller) > 0)
|
|
;
|
|
|
|
}
|
|
|
|
static u8 controller_read_byte(struct hw_controller *controller)
|
|
{
|
|
NFC_SEND_CMD_DRD(controller, controller->chip_selected, 0);
|
|
NFC_SEND_CMD_IDLE(controller, NAND_TWB_TIME_CYCLE);
|
|
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
NFC_SEND_CMD_IDLE(controller, 0);
|
|
|
|
while (NFC_CMDFIFO_SIZE(controller) > 0)
|
|
;
|
|
|
|
return amlnf_read_reg32(controller->reg_base + P_NAND_BUF);
|
|
|
|
}
|
|
|
|
static void controller_get_user_byte(struct hw_controller *controller,
|
|
u8 *oob_buf,
|
|
u8 byte_num)
|
|
{
|
|
int read_times = 0;
|
|
u32 len = PER_INFO_BYTE/sizeof(u32);
|
|
|
|
if (controller->oob_mod == 1) {
|
|
memcpy(oob_buf,
|
|
(u8 *)controller->user_buf,
|
|
byte_num);
|
|
return;
|
|
}
|
|
while (byte_num > 0) {
|
|
*oob_buf++ = (controller->user_buf[read_times*len] & 0xff);
|
|
byte_num--;
|
|
*oob_buf++ =
|
|
((controller->user_buf[read_times*len] >> 8) & 0xff);
|
|
byte_num--;
|
|
read_times++;
|
|
}
|
|
}
|
|
|
|
static void controller_set_user_byte(struct hw_controller *controller,
|
|
u8 *oob_buf,
|
|
u8 byte_num)
|
|
{
|
|
int write_times = 0;
|
|
u32 len = PER_INFO_BYTE/sizeof(u32);
|
|
#if 0
|
|
u8 *usr_info;
|
|
usr_info = (u8 *)controller->user_buf;
|
|
#endif
|
|
if (controller->oob_mod == 1) {
|
|
memcpy((u8 *)controller->user_buf,
|
|
oob_buf,
|
|
byte_num);
|
|
return;
|
|
}
|
|
while (byte_num > 0) {
|
|
controller->user_buf[write_times*len] = *oob_buf++;
|
|
byte_num--;
|
|
controller->user_buf[write_times*len] |=
|
|
(*oob_buf++ << 8);
|
|
byte_num--;
|
|
write_times++;
|
|
}
|
|
}
|
|
nand_reg_t *p_nand_reg;
|
|
/*
|
|
* fill hw_controller struct.
|
|
* including hw init, option setting and operation function.
|
|
*
|
|
*/
|
|
int amlnand_hwcontroller_init(struct amlnand_chip *aml_chip)
|
|
{
|
|
struct hw_controller *controller = &(aml_chip->controller);
|
|
|
|
int i, tmp_num = 0, ret = 0;
|
|
|
|
/*IO base mapping address*/
|
|
controller->reg_base = (void *)aml_nand_dev->platform_data->nf_reg_base;
|
|
/*external register mapping address for nand clock cfg*/
|
|
controller->nand_clk_reg = (void *) aml_nand_dev->platform_data->ext_clk_reg;
|
|
controller->irq = aml_nand_dev->platform_data->irq;
|
|
|
|
p_nand_reg = (nand_reg_t *) controller->reg_base;
|
|
|
|
aml_nand_dbg("controller->reg_base %p\n", controller->reg_base);
|
|
if (!controller->init)
|
|
controller->init = controller_hw_init;
|
|
if (!controller->adjust_timing)
|
|
controller->adjust_timing = controller_adjust_timing;
|
|
if (!controller->ecc_confirm)
|
|
controller->ecc_confirm = controller_ecc_confirm;
|
|
if (!controller->cmd_ctrl)
|
|
controller->cmd_ctrl = controller_cmd_ctrl;
|
|
if (!controller->select_chip)
|
|
controller->select_chip = controller_select_chip;
|
|
if (!controller->quene_rb)
|
|
controller->quene_rb = controller_quene_rb;
|
|
#ifdef AML_NAND_RB_IRQ
|
|
if (!controller->quene_rb_irq)
|
|
controller->quene_rb_irq = controller_queue_rb_irq;
|
|
#endif
|
|
if (!controller->dma_read)
|
|
controller->dma_read = controller_dma_read;
|
|
if (!controller->dma_write)
|
|
controller->dma_write = controller_dma_write;
|
|
if (!controller->hwecc_correct)
|
|
controller->hwecc_correct = controller_hwecc_correct;
|
|
if (!controller->readbyte)
|
|
controller->readbyte = controller_read_byte;
|
|
if (!controller->writebyte)
|
|
controller->writebyte = controller_write_byte;
|
|
if (!controller->get_usr_byte)
|
|
controller->get_usr_byte = controller_get_user_byte;
|
|
if (!controller->set_usr_byte)
|
|
controller->set_usr_byte = controller_set_user_byte;
|
|
if (!controller->enter_standby)
|
|
controller->enter_standby = controller_enter_standby;
|
|
|
|
for (i = 0; i < MAX_CHIP_NUM; i++) {
|
|
controller->ce_enable[i] =
|
|
(((CE_PAD_DEFAULT >> i*4) & 0xf) << 10);
|
|
controller->rb_enable[i] =
|
|
(((RB_PAD_DEFAULT >> i*4) & 0xf) << 10);
|
|
}
|
|
|
|
/*setting default value for option.*/
|
|
controller->option |= NAND_CTRL_NONE_RB;
|
|
controller->option |= NAND_ECC_BCH60_1K_MODE;
|
|
|
|
controller->aml_chip = aml_chip;
|
|
|
|
#ifdef AML_NAND_RB_IRQ
|
|
aml_nand_msg("######STS IRQ mode for nand driver");
|
|
if (request_irq(controller->irq,
|
|
(irq_handler_t)controller_interrupt_monitor,
|
|
0,
|
|
"aml_nand",
|
|
controller)) {
|
|
aml_nand_msg("request nand status irq error!!!");
|
|
return -1;
|
|
}
|
|
#endif /* AML_NAND_RB_IRQ */
|
|
|
|
#ifdef AML_NAND_DMA_POLLING
|
|
aml_nand_msg("######timer mode for nand driver");
|
|
hrtimer_init(&controller->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
controller->timer.function = controller_dma_timerfuc;
|
|
#endif /* AML_NAND_DMA_POLLING */
|
|
#ifndef AML_NAND_UBOOT
|
|
amlphy_prepare(0);
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
ret = controller->init(controller);
|
|
if (ret)
|
|
aml_nand_msg("controller hw init failed");
|
|
/* do not need change unless I/F changed...*/
|
|
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
|
|
controller->bch_desc = (struct bch_desc *)&bch_list_m8[0];
|
|
else
|
|
controller->bch_desc = (struct bch_desc *)&bch_list[0];
|
|
for (i = 0; i < MAX_ECC_MODE_NUM; i++) {
|
|
if (controller->bch_desc[i].name == NULL)
|
|
break;
|
|
tmp_num++;
|
|
}
|
|
controller->max_bch = tmp_num;
|
|
/*
|
|
controller->max_bch = sizeof(bch_list) / sizeof(bch_list[0]);
|
|
*/
|
|
return ret;
|
|
}
|
|
|