| /***************************************************************** |
| ** |
| ** Copyright (C) 2012 Amlogic,Inc. All rights reserved |
| ** |
| ** Filename : hw_controller.c |
| ** Revision : 1.001 |
| ** Author: Benjamin Zhao |
| ** Description: |
| ** hw controller operation function, mainly init nand phy driver. |
| ** |
| ** |
| *****************************************************************/ |
| #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; |
| } |
| |