rt-thread/bsp/x1000/drivers/sfc/drv_sfc.c

1503 lines
37 KiB
C
Raw Normal View History

2017-11-11 13:51:56 +08:00
/*
* drv_sfc.c
*
* Created on: 2016<EFBFBD><EFBFBD>4<EFBFBD><EFBFBD>5<EFBFBD><EFBFBD>
* Author: Urey
*/
/*********************************************************************************************************
** Include Files
*********************************************************************************************************/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <cache.h>
#include <sys/types.h>
#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;
}