/* * drv_sfc.c * * Created on: 2016Äê4ÔÂ5ÈÕ * Author: Urey */ /********************************************************************************************************* ** Include Files *********************************************************************************************************/ #include #include #include #include #include #include "board.h" #include "drv_clock.h" #include "drv_gpio.h" #include "drv_sfc.h" //#define SFC_DEBUG #if defined(SFC_DEBUG) #define SFC_DBG(...) rt_kprintf("[SFC]"),rt_kprintf(__VA_ARGS__) #else #define SFC_DBG(...) #endif #define L2CACHE_ALIGN_SIZE 256 #define THRESHOLD 32 #define PAGE_SIZE 4096 /* Max time can take up to 3 seconds! */ #define MAX_READY_WAIT_TIME 3000 /* the time of erase BE(64KB) */ #define STATUS_SUSPND (1<<0) #define tCHSH 5 //hold #define tSLCH 5 //setup #define tSHSL_RD 20 //interval #define tSHSL_WR 30 static void sfc_writel(struct sfc *sfc, uint16_t offset, u32 value) { writel(value, (uint32_t)sfc->iomem + offset); } static uint32_t sfc_readl(struct sfc *sfc, uint16_t offset) { return readl((uint32_t)sfc->iomem + offset); } static void sfc_init(struct sfc *sfc) { uint32_t n; for (n = 0; n < N_MAX; n++) { sfc_writel(sfc, SFC_TRAN_CONF(n), 0); sfc_writel(sfc, SFC_DEV_ADDR(n), 0); sfc_writel(sfc, SFC_DEV_ADDR_PLUS(n), 0); } //sfc_writel(sfc, SFC_GLB, ((1 << 7) | (1 << 3))); sfc_writel(sfc, SFC_DEV_CONF, 0); sfc_writel(sfc, SFC_DEV_STA_EXP, 0); sfc_writel(sfc, SFC_DEV_STA_MSK, 0); sfc_writel(sfc, SFC_TRAN_LEN, 0); sfc_writel(sfc, SFC_MEM_ADDR, 0); sfc_writel(sfc, SFC_TRIG, 0); sfc_writel(sfc, SFC_SCR, 0); sfc_writel(sfc, SFC_INTC, 0); sfc_writel(sfc, SFC_CGE, 0); sfc_writel(sfc, SFC_RM_DR, 0); } static void sfc_stop(struct sfc*sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRIG); tmp |= TRIG_STOP; sfc_writel(sfc, SFC_TRIG, tmp); } static void sfc_start(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRIG); tmp |= TRIG_START; sfc_writel(sfc, SFC_TRIG, tmp); } static void sfc_flush_fifo(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRIG); tmp |= TRIG_FLUSH; sfc_writel(sfc, SFC_TRIG, tmp); } static void sfc_ce_invalid_value(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_CEDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= DEV_CONF_CEDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_hold_invalid_value(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_HOLDDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= DEV_CONF_HOLDDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_wp_invalid_value(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_WPDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= DEV_CONF_WPDL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_clear_end_intc(struct sfc *sfc) { uint32_t tmp = 0; tmp = sfc_readl(sfc, SFC_SCR); tmp |= CLR_END; sfc_writel(sfc, SFC_SCR, tmp); tmp = sfc_readl(sfc, SFC_SCR); } static void sfc_clear_treq_intc(struct sfc *sfc) { uint32_t tmp = 0; tmp = sfc_readl(sfc, SFC_SCR); tmp |= CLR_TREQ; sfc_writel(sfc, SFC_SCR, tmp); } static void sfc_clear_rreq_intc(struct sfc *sfc) { uint32_t tmp = 0; tmp = sfc_readl(sfc, SFC_SCR); tmp |= CLR_RREQ; sfc_writel(sfc, SFC_SCR, tmp); } static void sfc_clear_over_intc(struct sfc *sfc) { uint32_t tmp = 0; tmp = sfc_readl(sfc, SFC_SCR); tmp |= CLR_OVER; sfc_writel(sfc, SFC_SCR, tmp); } static void sfc_clear_under_intc(struct sfc *sfc) { uint32_t tmp = 0; tmp = sfc_readl(sfc, SFC_SCR); tmp |= CLR_UNDER; sfc_writel(sfc, SFC_SCR, tmp); } static void sfc_clear_all_intc(struct sfc *sfc) { sfc_writel(sfc, SFC_SCR, 0x1f); } static void sfc_mask_all_intc(struct sfc *sfc) { sfc_writel(sfc, SFC_INTC, 0x1f); } static void sfc_mode(struct sfc *sfc, uint32_t channel, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~(TRAN_CONF_TRAN_MODE_MSK << TRAN_CONF_TRAN_MODE_OFFSET); tmp |= (value << TRAN_CONF_TRAN_MODE_OFFSET); sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } static void sfc_set_phase_num(struct sfc *sfc,uint32_t num) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp &= ~GLB_PHASE_NUM_MSK; tmp |= num << GLB_PHASE_NUM_OFFSET; sfc_writel(sfc, SFC_GLB, tmp); } static void sfc_clock_phase(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_CPHA; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= DEV_CONF_CPHA; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_clock_polarity(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_CPOL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= DEV_CONF_CPOL; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_threshold(struct sfc *sfc, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp &= ~GLB_THRESHOLD_MSK; tmp |= value << GLB_THRESHOLD_OFFSET; sfc_writel(sfc, SFC_GLB, tmp); } static void sfc_smp_delay(struct sfc *sfc, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_SMP_DELAY_MSK; tmp |= value << DEV_CONF_SMP_DELAY_OFFSET; sfc_writel(sfc, SFC_DEV_CONF, tmp); } static void sfc_hold_delay(struct sfc *sfc, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_THOLD_MSK; tmp |= value << DEV_CONF_THOLD_OFFSET; sfc_writel(sfc, SFC_DEV_CONF, tmp); } static void sfc_setup_delay(struct sfc *sfc, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_TSETUP_MSK; tmp |= value << DEV_CONF_TSETUP_OFFSET; sfc_writel(sfc, SFC_DEV_CONF, tmp); } static void sfc_interval_delay(struct sfc *sfc, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~DEV_CONF_TSH_MSK; tmp |= value << DEV_CONF_TSH_OFFSET; sfc_writel(sfc, SFC_DEV_CONF, tmp); } static void sfc_set_cmd_length(struct sfc *sfc, uint32_t value) { if (value == 1) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp &= ~TRAN_CONF_CMD_LEN; sfc_writel(sfc, SFC_DEV_CONF, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_DEV_CONF); tmp |= TRAN_CONF_CMD_LEN; sfc_writel(sfc, SFC_DEV_CONF, tmp); } } static void sfc_transfer_direction(struct sfc *sfc, uint32_t value) { if (value == GLB_TRAN_DIR_READ) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp &= ~GLB_TRAN_DIR; sfc_writel(sfc, SFC_GLB, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp |= GLB_TRAN_DIR; sfc_writel(sfc, SFC_GLB, tmp); } } static int set_flash_timing(struct sfc *sfc, uint32_t t_hold, uint32_t t_setup, uint32_t t_shslrd, uint32_t t_shslwr) { uint32_t c_hold; uint32_t c_setup; uint32_t t_in, c_in, val; uint64_t cycle; cycle = 1000000000UL / sfc->src_clk; c_hold = t_hold / cycle; if (c_hold > 0) val = c_hold - 1; sfc_hold_delay(sfc, val); c_setup = t_setup / cycle; if(c_setup > 0) val = c_setup - 1; sfc_setup_delay(sfc, val); t_in = max(t_shslrd, t_shslwr); c_in = t_in / cycle; if(c_in > 0) val = c_in - 1; sfc_interval_delay(sfc, val); return 0; } static void sfc_set_length(struct sfc *sfc, uint32_t value) { sfc_writel(sfc, SFC_TRAN_LEN, value); } static void sfc_transfer_mode(struct sfc *sfc, uint32_t value) { if (value == 0) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp &= ~GLB_OP_MODE; sfc_writel(sfc, SFC_GLB, tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_GLB); tmp |= GLB_OP_MODE; sfc_writel(sfc, SFC_GLB, tmp); } } static void sfc_read_data(struct sfc *sfc, uint32_t *value) { *value = sfc_readl(sfc, SFC_RM_DR); } static void sfc_write_data(struct sfc *sfc, const uint32_t value) { sfc_writel(sfc, SFC_RM_DR, value); } uint32_t sfc_fifo_num(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); tmp &= (0x7f << 16); tmp = tmp >> 16; return tmp; } static uint32_t cpu_read_rxfifo(struct sfc *sfc) { uint32_t i; uint32_t align_len = 0; uint32_t fifo_num = 0; uint32_t data[1] = {0}; uint32_t last_word = 0; align_len = RT_ALIGN(sfc->transfer->len, 4); if (((align_len - sfc->transfer->cur_len) / 4) > THRESHOLD) { fifo_num = THRESHOLD; last_word = 0; } else { /* last aligned THRESHOLD data*/ if (sfc->transfer->len % 4) { fifo_num = (align_len - sfc->transfer->cur_len) / 4 - 1; last_word = 1; } else { fifo_num = (align_len - sfc->transfer->cur_len) / 4; last_word = 0; } } for (i = 0; i < fifo_num; i++) { sfc_read_data(sfc, (uint32_t *) sfc->transfer->data); sfc->transfer->data += 4; sfc->transfer->cur_len += 4; } /* last word */ if (last_word == 1) { sfc_read_data(sfc, data); rt_memcpy((void *) sfc->transfer->data, data, sfc->transfer->len % 4); sfc->transfer->data += sfc->transfer->len % 4; sfc->transfer->cur_len += 4; } return 0; } static uint32_t cpu_write_txfifo(struct sfc *sfc) { uint32_t i; uint32_t align_len = 0; uint32_t fifo_num = 0; align_len = RT_ALIGN(sfc->transfer->len , 4); if (((align_len - sfc->transfer->cur_len) / 4) > THRESHOLD){ fifo_num = THRESHOLD; } else { fifo_num = (align_len - sfc->transfer->cur_len) / 4; } for(i = 0; i < fifo_num; i++) { sfc_write_data(sfc, *(uint32_t *)sfc->transfer->data); sfc->transfer->data += 4; sfc->transfer->cur_len += 4; } return 0; } static int ssi_underrun(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); if(tmp & CLR_UNDER) return 1; else return 0; } static int ssi_overrun(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); if(tmp & CLR_OVER) return 1; else return 0; } static int rxfifo_rreq(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); if(tmp & CLR_RREQ) return 1; else return 0; } static int txfifo_treq(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); if(tmp & CLR_TREQ) return 1; else return 0; } static int sfc_end(struct sfc *sfc) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_SR); if(tmp & CLR_END) return 1; else return 0; } static uint32_t sfc_get_sta_rt(struct sfc *sfc) { return sfc_readl(sfc,SFC_DEV_STA_RT); } static uint32_t sfc_get_fsm(struct sfc *sfc) { return sfc_readl(sfc,SFC_FSM); } static void sfc_set_addr_length(struct sfc *sfc, uint32_t channel, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~(ADDR_WIDTH_MSK); tmp |= (value << ADDR_WIDTH_OFFSET); sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } static void sfc_cmd_enble(struct sfc *sfc, uint32_t channel, uint32_t value) { if (value == ENABLE) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp |= TRAN_CONF_CMDEN; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~TRAN_CONF_CMDEN; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } } static void sfc_data_en(struct sfc *sfc, uint32_t channel, uint32_t value) { if (value == 1) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp |= TRAN_CONF_DATEEN; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~TRAN_CONF_DATEEN; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } } static void sfc_phase_format(struct sfc *sfc, uint32_t channel, uint32_t value) { if (value == 1) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp |= TRAN_CONF_FMAT; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } else { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~TRAN_CONF_FMAT; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } } static void sfc_write_cmd(struct sfc *sfc, uint32_t channel, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~TRAN_CONF_CMD_MSK; tmp |= value; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } static void sfc_dev_addr(struct sfc *sfc, uint32_t channel, uint32_t value) { sfc_writel(sfc, SFC_DEV_ADDR(channel), value); } static void sfc_dev_data_dummy_bytes(struct sfc *sfc, uint32_t channel, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); tmp &= ~TRAN_CONF_DMYBITS_MSK; tmp |= value << DMYBITS_OFFSET; sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } static void sfc_dev_addr_plus(struct sfc *sfc, uint32_t channel, uint32_t value) { sfc_writel(sfc, SFC_DEV_ADDR_PLUS(channel), value); } static void sfc_dev_pollen(struct sfc *sfc, uint32_t channel, uint32_t value) { uint32_t tmp; tmp = sfc_readl(sfc, SFC_TRAN_CONF(channel)); if(value == 1) tmp |= TRAN_CONF_POLLEN; else tmp &= ~(TRAN_CONF_POLLEN); sfc_writel(sfc, SFC_TRAN_CONF(channel), tmp); } static void sfc_dev_sta_exp(struct sfc *sfc, uint32_t value) { sfc_writel(sfc, SFC_DEV_STA_EXP, value); } static void sfc_dev_sta_msk(struct sfc *sfc, uint32_t value) { sfc_writel(sfc, SFC_DEV_STA_MSK, value); } static void sfc_enable_all_intc(struct sfc *sfc) { sfc_writel(sfc, SFC_INTC, 0); } static void sfc_set_mem_addr(struct sfc *sfc,uint32_t addr ) { sfc_writel(sfc, SFC_MEM_ADDR, addr); } static int sfc_start_transfer(struct sfc *sfc) { int err; sfc_clear_all_intc(sfc); sfc_enable_all_intc(sfc); sfc_start(sfc); err = rt_completion_wait(&sfc->done,RT_TICK_PER_SECOND * 10); if (RT_EOK != err) { sfc_mask_all_intc(sfc); sfc_clear_all_intc(sfc); SFC_DBG("line:%d Timeout for ACK from SFC device\n", __LINE__); return -RT_ETIMEOUT; } return 0; } static void sfc_phase_transfer(struct sfc *sfc,struct sfc_transfer * transfer,uint32_t channel) { sfc_flush_fifo(sfc); sfc_set_addr_length(sfc,channel,transfer->addr_len); sfc_cmd_enble(sfc,channel,ENABLE); sfc_write_cmd(sfc,channel,transfer->cmd_info->cmd); sfc_dev_data_dummy_bytes(sfc,channel,transfer->data_dummy_bits); sfc_data_en(sfc,channel,transfer->cmd_info->dataen); sfc_dev_addr(sfc, channel,transfer->addr); sfc_dev_addr_plus(sfc,channel,transfer->addr_plus); sfc_mode(sfc,channel,transfer->sfc_mode); sfc_phase_format(sfc,channel,0);/*default 0,dummy bits is blow the addr*/ } static void common_cmd_request_transfer(struct sfc *sfc,struct sfc_transfer *transfer,uint32_t channel) { sfc_phase_transfer(sfc,transfer,channel); sfc_dev_sta_exp(sfc,0); sfc_dev_sta_msk(sfc,0); sfc_dev_pollen(sfc,channel,DISABLE); } static void poll_cmd_request_transfer(struct sfc *sfc,struct sfc_transfer *transfer,uint32_t channel) { struct cmd_info *cmd = transfer->cmd_info; sfc_phase_transfer(sfc,transfer,channel); sfc_dev_sta_exp(sfc,cmd->sta_exp); sfc_dev_sta_msk(sfc,cmd->sta_msk); sfc_dev_pollen(sfc,channel,ENABLE); } static void sfc_glb_info_config(struct sfc *sfc,struct sfc_transfer *transfer) { sfc_transfer_direction(sfc, transfer->direction); if ((transfer->ops_mode == DMA_OPS)) { sfc_set_length(sfc, transfer->len); if (transfer->direction == GLB_TRAN_DIR_READ) r4k_dma_cache_sync((uint32_t) transfer->data, transfer->len, DMA_FROM_DEVICE); else r4k_dma_cache_sync((uint32_t) transfer->data, transfer->len, DMA_TO_DEVICE); sfc_set_mem_addr(sfc, PHYS(transfer->data)); sfc_transfer_mode(sfc, DMA_MODE); } else { sfc_set_length(sfc, transfer->len); sfc_set_mem_addr(sfc, 0); sfc_transfer_mode(sfc, SLAVE_MODE); } } #ifdef DEBUG static void dump_transfer(struct sfc_transfer *xfer,uint32_t num) { rt_kprintf("\n"); rt_kprintf("cmd[%d].cmd = 0x%02x\n",num,xfer->cmd_info->cmd); rt_kprintf("cmd[%d].addr_len = %d\n",num,xfer->addr_len); rt_kprintf("cmd[%d].dummy_byte = %d\n",num,xfer->data_dummy_bits); rt_kprintf("cmd[%d].dataen = %d\n",num,xfer->cmd_info->dataen); rt_kprintf("cmd[%d].sta_exp = %d\n",num,xfer->cmd_info->sta_exp); rt_kprintf("cmd[%d].sta_msk = %d\n",num,xfer->cmd_info->sta_msk); rt_kprintf("transfer[%d].addr = 0x%08x\n",num,xfer->addr); rt_kprintf("transfer[%d].len = %d\n",num,xfer->len); rt_kprintf("transfer[%d].data = 0x%p\n",num,xfer->data); rt_kprintf("transfer[%d].direction = %d\n",num,xfer->direction); rt_kprintf("transfer[%d].sfc_mode = %d\n",num,xfer->sfc_mode); rt_kprintf("transfer[%d].ops_mode = %d\n",num,xfer->ops_mode); } #endif static int sfc_sync(struct sfc *sfc, struct sfc_message *message) { struct sfc_transfer *xfer; int phase_num = 0,ret = 0; sfc_set_length(sfc, 0); rt_list_for_each_entry(xfer, &message->transfers, transfer_list) { if (xfer->cmd_info->sta_msk == 0) { common_cmd_request_transfer(sfc, xfer, phase_num); } else { poll_cmd_request_transfer(sfc, xfer, phase_num); } if (xfer->addr_len || xfer->len) sfc_glb_info_config(sfc, xfer); phase_num++; message->actual_length += xfer->len; if (xfer->len > 0) sfc->transfer = xfer; } sfc_set_phase_num(sfc,phase_num); ret = sfc_start_transfer(sfc); rt_list_remove(&message->transfers); return ret; } static void sfc_transfer_del(struct sfc_transfer *t) { rt_list_remove(&t->transfer_list); } static void sfc_message_add_tail(struct sfc_transfer *t, struct sfc_message *m) { rt_list_insert_before(&m->transfers, &t->transfer_list); } static void sfc_message_init(struct sfc_message *m) { rt_memset(m, 0, sizeof *m); rt_list_init(&m->transfers); } static void jz_sfc_pio_irq(int vector,void *param) { struct sfc *sfc = (struct sfc *)param; if (ssi_underrun(sfc)) { sfc_clear_under_intc(sfc); rt_completion_done(&sfc->done); return ; } if (ssi_overrun(sfc)) { sfc_clear_over_intc(sfc); rt_completion_done(&sfc->done); return ; } if (rxfifo_rreq(sfc)) { sfc_clear_rreq_intc(sfc); cpu_read_rxfifo(sfc); return ; } if (txfifo_treq(sfc)) { sfc_clear_treq_intc(sfc); cpu_write_txfifo(sfc); return ; } if (sfc_end(sfc)) { sfc_mask_all_intc(sfc); sfc_clear_end_intc(sfc); rt_completion_done(&sfc->done); return ; } } static int jz_sfc_init_setup(struct sfc *sfc) { sfc_init(sfc); sfc_stop(sfc); /*set hold high*/ sfc_hold_invalid_value(sfc, 1); /*set wp high*/ sfc_wp_invalid_value(sfc, 1); sfc_clear_all_intc(sfc); sfc_mask_all_intc(sfc); sfc_threshold(sfc, sfc->threshold); /*config the sfc pin init state*/ sfc_clock_phase(sfc, 0); sfc_clock_polarity(sfc, 0); sfc_ce_invalid_value(sfc, 1); sfc_transfer_mode(sfc, SLAVE_MODE); if (sfc->src_clk >= 100000000) { sfc_smp_delay(sfc, DEV_CONF_HALF_CYCLE_DELAY); } return 0; } static struct sfc* jz_sfc_init(void) { struct sfc *sfc = (struct sfc *)rt_malloc(sizeof(struct sfc)); if(sfc == RT_NULL) return RT_NULL; sfc->iomem = (void *)SFC_BASE; sfc->irq = IRQ_SFC; sfc->clk = clk_get("cgu_ssi"); sfc->clk_gate = clk_get("sfc"); sfc->src_clk = 100000000L; if(clk_get_rate(sfc->clk) >= sfc->src_clk) clk_set_rate(sfc->clk, sfc->src_clk); else clk_set_rate(sfc->clk, sfc->src_clk); clk_enable(sfc->clk); clk_enable(sfc->clk_gate); sfc->threshold = THRESHOLD; /* Init IPC */ rt_completion_init(&(sfc->done)); /* Request SFC IRQ */ rt_hw_interrupt_install(sfc->irq,jz_sfc_pio_irq,sfc,"SFC"); rt_hw_interrupt_umask(sfc->irq); /* SFC controller initializations for SFC */ jz_sfc_init_setup(sfc); rt_completion_init(&sfc->done); return sfc; } static int sfc_flash_read_id(struct sfc_flash *flash, uint8_t command, uint32_t addr, uint32_t addr_len, size_t len, uint32_t dummy_byte) { struct sfc_transfer transfer; struct sfc_message message; struct cmd_info cmd; int ret; uint32_t chip_id = 0; sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); cmd.cmd = command; cmd.dataen = ENABLE; transfer.addr_len = addr_len; transfer.data_dummy_bits = dummy_byte; transfer.addr = addr; transfer.len = len; transfer.data =(uint8_t *)&chip_id; transfer.ops_mode = CPU_OPS; transfer.sfc_mode = TM_STD_SPI; transfer.direction = GLB_TRAN_DIR_READ; transfer.cmd_info = &cmd; sfc_message_add_tail(&transfer, &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } return chip_id & 0x00ffffff; } static uint32_t sfc_flash_do_read(struct sfc_flash *flash,uint8_t command,uint32_t addr,uint32_t addr_len,uint8_t *buf,size_t len,uint32_t dummy_byte) { struct sfc_transfer transfer; struct sfc_message message; struct cmd_info cmd; int ret; sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); cmd.cmd = command; cmd.dataen = ENABLE; transfer.addr_len = addr_len; transfer.data_dummy_bits = dummy_byte; transfer.addr = addr; transfer.len = len; transfer.data = buf; transfer.cur_len = 0; if(len >= L2CACHE_ALIGN_SIZE) transfer.ops_mode = DMA_OPS; else transfer.ops_mode = CPU_OPS; transfer.sfc_mode = flash->sfc_mode; transfer.direction = GLB_TRAN_DIR_READ; transfer.cmd_info = &cmd; sfc_message_add_tail(&transfer, &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } /*fix the cache line problem,when use jffs2 filesystem must be flush cache twice*/ if(transfer.ops_mode == DMA_OPS) r4k_dma_cache_sync((rt_base_t)buf, len, DMA_FROM_DEVICE); return message.actual_length; } static unsigned int sfc_flash_do_write(struct sfc_flash *flash,uint8_t command,uint32_t addr,uint32_t addr_len,const uint8_t *buf,size_t len,uint32_t dummy_byte) { struct sfc_transfer transfer[3]; struct sfc_message message; struct cmd_info cmd[3]; int ret; sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); /* write enable */ cmd[0].cmd = CMD_WREN; cmd[0].dataen = DISABLE; transfer[0].cmd_info = &cmd[0]; transfer[0].sfc_mode = flash->sfc_mode; sfc_message_add_tail(&transfer[0], &message); /* write ops */ cmd[1].cmd = command; cmd[1].dataen = ENABLE; transfer[1].addr = addr; transfer[1].addr_len = addr_len; transfer[1].len = len; transfer[1].cur_len = 0; transfer[1].data_dummy_bits = dummy_byte; transfer[1].data = buf; if(len >= L2CACHE_ALIGN_SIZE) transfer[1].ops_mode = DMA_OPS; else transfer[1].ops_mode = CPU_OPS; transfer[1].sfc_mode = flash->sfc_mode; transfer[1].direction = GLB_TRAN_DIR_WRITE; transfer[1].cmd_info = &cmd[1]; sfc_message_add_tail(&transfer[1], &message); cmd[2].cmd = CMD_RDSR; cmd[2].dataen = DISABLE; cmd[2].sta_exp = 0; cmd[2].sta_msk = 0x1; transfer[2].cmd_info = &cmd[2]; sfc_message_add_tail(&transfer[2], &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } return message.actual_length; } #ifdef SFC_USE_QUAD static int sfc_flash_set_quad_mode(struct sfc_flash *flash) { uint8_t command; uint32_t sent_data,len,dummy_byte; int ret; struct sfc_transfer transfer[3]; struct sfc_message message; struct cmd_info cmd[3]; if (flash->quad_mode == NULL) { SFC_DBG("quad info is null, use standard spi mode\n"); flash->sfc_mode = TM_STD_SPI; return -1; } command = flash->quad_mode->WRSR_CMD; sent_data = flash->quad_mode->WRSR_DATE; len = flash->quad_mode->WD_DATE_SIZE; dummy_byte = flash->quad_mode->dummy_byte; sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); /* write enable */ cmd[0].cmd = CMD_WREN; cmd[0].dataen = DISABLE; transfer[0].cmd_info = &cmd[0]; transfer[0].sfc_mode = TM_STD_SPI; sfc_message_add_tail(&transfer[0], &message); /* write ops */ cmd[1].cmd = command; cmd[1].dataen = ENABLE; transfer[1].len = len; transfer[1].data = (const uint8_t *)&sent_data; transfer[1].data_dummy_bits = dummy_byte; transfer[1].ops_mode = CPU_OPS; transfer[1].sfc_mode = TM_STD_SPI; transfer[1].direction = GLB_TRAN_DIR_WRITE; transfer[1].cmd_info = &cmd[1]; sfc_message_add_tail(&transfer[1], &message); cmd[2].cmd = flash->quad_mode->RDSR_CMD; cmd[2].dataen = DISABLE; cmd[2].sta_exp = 0x2; cmd[2].sta_msk = 0x2; transfer[2].data_dummy_bits = 0; transfer[2].cmd_info = &cmd[2]; sfc_message_add_tail(&transfer[2], &message); ret = sfc_sync(flash->sfc, &message); if (ret) { flash->sfc_mode = TM_STD_SPI; SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } else { flash->sfc_mode = flash->quad_mode->sfc_mode; } return 0; } #endif static int sfc_flash_write(struct sfc_flash *flash, rt_off_t to, size_t len, const uint8_t *buf) { uint8_t command; int dummy_byte = 0; uint32_t s_len = 0, f_len = 0, a_len = 0; #ifdef SFC_USE_QUAD if((flash->sfc_mode == TM_QI_QO_SPI) || (flash->sfc_mode == TM_QIO_SPI) || (flash->sfc_mode == TM_FULL_QIO_SPI)) { command = CMD_PP; } else { command = CMD_PP; } #else command = CMD_PP; #endif if (len > L2CACHE_ALIGN_SIZE) { s_len = RT_ALIGN((uint32_t )buf, L2CACHE_ALIGN_SIZE) - (uint32_t)buf; if (s_len) { sfc_flash_do_write(flash, command, (uint32_t) to, flash->addrsize, buf, s_len, dummy_byte); } a_len = (len - s_len) - (len - s_len) % L2CACHE_ALIGN_SIZE; if (a_len) { sfc_flash_do_write(flash, command, (uint32_t) to + s_len, flash->addrsize, &buf[s_len], a_len, dummy_byte); } f_len = len - s_len - a_len; if (f_len) { sfc_flash_do_write(flash, command, (uint32_t) to + s_len + a_len, flash->addrsize, &buf[s_len + a_len], f_len, dummy_byte); } } else { sfc_flash_do_write(flash, command, (uint32_t) to, flash->addrsize, buf, len, dummy_byte); } return len; } static int sfc_flash_read_cacheline_align(struct sfc_flash *flash,uint8_t command,uint32_t addr,int addr_len,uint8_t *buf,size_t len,int dummy_byte) { uint32_t ret = 0; uint32_t s_len = 0, f_len = 0, a_len = 0; /** * s_len : start not align length * a_len : middle align length * f_len : end not align length */ if (len > L2CACHE_ALIGN_SIZE) { s_len = RT_ALIGN((uint32_t )buf, L2CACHE_ALIGN_SIZE) - (uint32_t)buf; if (s_len) { ret += sfc_flash_do_read(flash, command, (uint32_t) addr, flash->addrsize, buf, s_len, dummy_byte); } a_len = (len - s_len) - (len - s_len) % L2CACHE_ALIGN_SIZE; if (a_len) { ret += sfc_flash_do_read(flash, command, (uint32_t) addr + s_len, flash->addrsize, &buf[s_len], a_len, dummy_byte); } f_len = len - s_len - a_len; if (f_len) { ret += sfc_flash_do_read(flash, command, (uint32_t) addr + s_len + a_len, flash->addrsize, &buf[s_len + a_len], f_len, dummy_byte); } } else { ret = sfc_flash_do_read(flash, command, (uint32_t)addr, flash->addrsize, buf, len, dummy_byte); } return ret; } static int sfc_flash_read(struct sfc_flash *flash, rt_off_t from, size_t len, uint8_t *buf) { uint8_t command; int dummy_byte; int tmp_len = 0, current_len = 0; #ifdef SFC_USE_QUAD if((flash->sfc_mode == TM_QI_QO_SPI) || (flash->sfc_mode == TM_QIO_SPI) || (flash->sfc_mode == TM_FULL_QIO_SPI)) { command = flash->quad_mode->cmd_read; dummy_byte = flash->quad_mode->dummy_byte; } else { command = CMD_READ; dummy_byte = 0; } #else command = CMD_READ; dummy_byte = 0; #endif while (len) { tmp_len = sfc_flash_read_cacheline_align(flash, command, (uint32_t) from + current_len, flash->addrsize, &buf[current_len], len, dummy_byte); current_len += tmp_len; len -= tmp_len; } return current_len; } int sfc_norflash_set_addr_width_4byte(struct sfc_flash *flash,int on) { struct sfc_transfer transfer; struct sfc_message message; struct cmd_info cmd; int ret; sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); cmd.cmd = CMD_EN4B; cmd.dataen = DISABLE; transfer.data_dummy_bits = 0; transfer.cmd_info = &cmd; sfc_message_add_tail(&transfer, &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } return 0; } size_t sfc_norflash_read(struct sfc_flash *flash, rt_off_t from, uint8_t *buf, size_t len) { size_t retlen; rt_mutex_take(&flash->lock,RT_WAITING_FOREVER); retlen = sfc_flash_read(flash, from, len, buf); rt_mutex_release(&flash->lock); return retlen; } int sfc_norflash_read_params(struct sfc_flash *flash, rt_off_t from, size_t len, uint8_t *buf) { struct sfc_transfer transfer; struct sfc_message message; struct cmd_info cmd; uint8_t command; int dummy_byte = 0,ret; command = CMD_READ; rt_mutex_take(&flash->lock,RT_WAITING_FOREVER); sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); cmd.cmd = command; cmd.dataen = ENABLE; transfer.addr = (uint32_t)from; transfer.len = len; transfer.data = buf; transfer.addr_len = DEFAULT_ADDRSIZE; transfer.data_dummy_bits = dummy_byte; transfer.ops_mode = CPU_OPS; transfer.direction = GLB_TRAN_DIR_READ; transfer.sfc_mode = TM_STD_SPI; transfer.cmd_info = &cmd; sfc_message_add_tail(&transfer, &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } /*fix the cache line problem,when use jffs2 filesystem must be flush cache twice*/ if(transfer.ops_mode == DMA_OPS) r4k_dma_cache_sync((rt_ubase_t)buf,len, DMA_FROM_DEVICE); rt_mutex_release(&flash->lock); return 0; } int sfc_norflash_erase_sector(struct sfc_flash *flash, uint32_t addr) { uint8_t command; struct sfc_transfer transfer[3]; struct sfc_message message; struct cmd_info cmd[3]; int ret; rt_mutex_take(&flash->lock,RT_WAITING_FOREVER); sfc_message_init(&message); rt_memset(&transfer, 0, sizeof(transfer)); rt_memset(&cmd, 0, sizeof(cmd)); /* write enable */ cmd[0].cmd = CMD_WREN; cmd[0].dataen = DISABLE; transfer[0].sfc_mode = TM_STD_SPI; transfer[0].cmd_info = &cmd[0]; sfc_message_add_tail(&transfer[0], &message); switch (flash->erasesize) { case 0x1000: command = CMD_BE_4K; break; case 0x8000: command = CMD_BE_32K; break; case 0x10000: command = CMD_BE_64K; break; } /* erase ops */ cmd[1].cmd = command; cmd[1].dataen = DISABLE; transfer[1].addr_len = flash->addrsize; transfer[1].data_dummy_bits = 0; transfer[1].addr = addr; transfer[1].sfc_mode = TM_STD_SPI; transfer[1].direction = GLB_TRAN_DIR_WRITE; transfer[1].cmd_info = &cmd[1]; sfc_message_add_tail(&transfer[1], &message); cmd[2].cmd = CMD_RDSR; cmd[2].dataen = DISABLE; cmd[2].sta_exp = 0; cmd[2].sta_msk = 0x1; transfer[2].cmd_info = &cmd[2]; sfc_message_add_tail(&transfer[2], &message); ret = sfc_sync(flash->sfc, &message); if (ret) { SFC_DBG("sfc_sync error ! %s %s %d\n", __FILE__, __func__, __LINE__); ret = -RT_EIO; } rt_mutex_release(&flash->lock); return 0; } size_t sfc_norflash_write(struct sfc_flash *flash, rt_off_t to, const uint8_t *buf, size_t len) { size_t retlen; u32 page_offset, actual_len; int ret; rt_mutex_take(&flash->lock,RT_WAITING_FOREVER); page_offset = to & (flash->pagesize - 1); /* do all the bytes fit onto one page? */ if (page_offset + len <= flash->pagesize) { ret = sfc_flash_write(flash, to, len, buf); retlen = ret; } else { u32 i; /* the size of data remaining on the first page */ actual_len = flash->pagesize - page_offset; ret = sfc_flash_write(flash,to,actual_len,buf); retlen += ret; /* write everything in flash->page_size chunks */ for (i = actual_len; i < len; i += flash->writesize) { actual_len = len - i; if (actual_len >= flash->writesize) actual_len = flash->writesize; ret = sfc_flash_write(flash, to + i, actual_len, buf + i); retlen += ret; } } rt_mutex_release(&flash->lock); return retlen; } int sfc_norflash_probe(struct sfc_flash *flash) { struct sfc *sfc; sfc = flash->sfc = jz_sfc_init(); if(sfc == RT_NULL) return -RT_EIO; /* GPIO Initialize (SFC FUNC1) */ gpio_set_func(GPIO_PORT_A,GPIO_Pin_26,GPIO_FUNC_1); //CLK gpio_set_func(GPIO_PORT_A,GPIO_Pin_27,GPIO_FUNC_1); //CE gpio_set_func(GPIO_PORT_A,GPIO_Pin_28,GPIO_FUNC_1); //DR gpio_set_func(GPIO_PORT_A,GPIO_Pin_29,GPIO_FUNC_1); //DT gpio_set_func(GPIO_PORT_A,GPIO_Pin_30,GPIO_FUNC_1); //WP gpio_set_func(GPIO_PORT_A,GPIO_Pin_31,GPIO_FUNC_1); //HOLD /* init mutex */ if(rt_mutex_init(&(flash->lock),"norLock",RT_IPC_FLAG_FIFO) != RT_EOK) { SFC_DBG("Init mutex error\n"); RT_ASSERT(0); } rt_mutex_take(&(flash->lock),RT_WAITING_FOREVER); //get ID { uint8_t command; int dummy_byte = 0; int addr_len = 0; int len = 3; int addr = 0; int id; int i; struct spi_nor_platform_data *flash_info; struct spi_board_info *binfo; command = CMD_RDID; SFC_DBG("Get ID:\n"); id = sfc_flash_read_id(flash, command, addr, addr_len, len, dummy_byte); id = ((id & 0xff) << 16) | (((id >> 8) & 0xff) << 8) | ((id >> 16) & 0xff); SFC_DBG("id = %06x\n",id); flash->id = id; } #if 0 //get UID { int i; sfc_flash_do_read(flash,CMD_RUID,0,3,flash->uid,8,8); SFC_DBG("uid = "); for (i = 0; i < 8; ++i) { SFC_DBG("%02x ",flash->uid[i]); } SFC_DBG("\n"); } #endif rt_mutex_release(&(flash->lock)); return 0; }