505 lines
12 KiB
C
Executable File
505 lines
12 KiB
C
Executable File
/*
|
|
* (C) Copyright 2007-2013
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* Jerry Wang <wangflord@allwinnertech.com>
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <spi.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/spi.h>
|
|
#include <asm/arch/ccmu.h>
|
|
//#include <asm/arch/ccmu.h>
|
|
#include <asm/arch/dma.h>
|
|
#define SUNXI_NOR_FLASH_DEBUG 1
|
|
#ifdef SUNXI_NOR_FLASH_DEBUG
|
|
#define SUNXI_DEBUG(fmt,args...) printf(fmt ,##args)
|
|
#else
|
|
#define SUNXI_DEBUG(fmt,args...) do {} while(0)
|
|
#endif
|
|
|
|
static u32 g_cfg_mclk = 0;
|
|
static sunxi_dma_setting_t *spi_tx_dma;
|
|
static sunxi_dma_setting_t *spi_rx_dma;
|
|
static uint spi_tx_dma_hd;
|
|
static uint spi_rx_dma_hd;
|
|
|
|
void ccm_module_disable_bak(u32 clk_id)
|
|
{
|
|
switch(clk_id>>8) {
|
|
case AHB1_BUS0:
|
|
clr_wbit(CCM_AHB1_RST_REG0, 0x1U<<(clk_id&0xff));
|
|
SUNXI_DEBUG("\nread CCM_AHB1_RST_REG0[0x%x]\n",readl(CCM_AHB1_RST_REG0));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ccm_module_enable_bak(u32 clk_id)
|
|
{
|
|
switch(clk_id>>8) {
|
|
case AHB1_BUS0:
|
|
set_wbit(CCM_AHB1_RST_REG0, 0x1U<<(clk_id&0xff));
|
|
SUNXI_DEBUG("\nread enable CCM_AHB1_RST_REG0[0x%x]\n",readl(CCM_AHB1_RST_REG0));
|
|
break;
|
|
}
|
|
}
|
|
void ccm_clock_enable_bak(u32 clk_id)
|
|
{
|
|
switch(clk_id>>8) {
|
|
case AXI_BUS:
|
|
set_wbit(CCM_AXI_GATE_CTRL, 0x1U<<(clk_id&0xff));
|
|
break;
|
|
case AHB1_BUS0:
|
|
set_wbit(CCM_AHB1_GATE0_CTRL, 0x1U<<(clk_id&0xff));
|
|
SUNXI_DEBUG("read s CCM_AHB1_GATE0_CTRL[0x%x]\n",readl(CCM_AHB1_GATE0_CTRL));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ccm_clock_disable_bak(u32 clk_id)
|
|
{
|
|
switch(clk_id>>8) {
|
|
case AXI_BUS:
|
|
clr_wbit(CCM_AXI_GATE_CTRL, 0x1U<<(clk_id&0xff));
|
|
break;
|
|
case AHB1_BUS0:
|
|
clr_wbit(CCM_AHB1_GATE0_CTRL, 0x1U<<(clk_id&0xff));
|
|
SUNXI_DEBUG("read dis CCM_AHB1_GATE0_CTRL[0x%x]\n",readl(CCM_AHB1_GATE0_CTRL));
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static int spi_dma_recv_start(uint spi_no, uchar* pbuf, uint byte_cnt)
|
|
{
|
|
flush_cache((uint)pbuf, byte_cnt);
|
|
|
|
sunxi_dma_start(spi_rx_dma_hd, SPI_RXD, (uint)pbuf, byte_cnt);
|
|
|
|
return 0;
|
|
}
|
|
static int spi_wait_dma_recv_over(uint spi_no)
|
|
{
|
|
//int ret = 0;
|
|
//count_recv ++;
|
|
//ret = sunxi_dma_querystatus(spi_rx_dma_hd);
|
|
//if(ret == 0)
|
|
// printf("dma recv end \n");
|
|
//return ret;
|
|
return sunxi_dma_querystatus(spi_rx_dma_hd);
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static int spi_dma_send_start(uint spi_no, uchar* pbuf, uint byte_cnt)
|
|
{
|
|
flush_cache((uint)pbuf, byte_cnt);
|
|
|
|
sunxi_dma_start(spi_tx_dma_hd, (uint)pbuf, SPI_TXD, byte_cnt);
|
|
|
|
return 0;
|
|
}
|
|
static int spi_wait_dma_send_over(uint spi_no)
|
|
{
|
|
//int ret = 0;
|
|
//count_send++;
|
|
//ret = sunxi_dma_querystatus(spi_tx_dma_hd);
|
|
//if(ret == 0)
|
|
// printf("dma send end \n");
|
|
//return ret ;
|
|
return sunxi_dma_querystatus(spi_tx_dma_hd);
|
|
}
|
|
|
|
#define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v))
|
|
#define clr_wbit(addr, v) (*((volatile unsigned long *)(addr)) &= ~(unsigned long)(v))
|
|
|
|
void ccm_module_reset_bak(u32 clk_id)
|
|
{
|
|
ccm_module_disable_bak(clk_id);
|
|
ccm_module_disable_bak(clk_id);
|
|
ccm_module_enable_bak(clk_id);
|
|
}
|
|
|
|
u32 ccm_get_pll_periph_clk(void)
|
|
{
|
|
u32 rval = 0;
|
|
u32 n, k;
|
|
rval = readl(CCM_PLL6_MOD_CTRL);
|
|
n = (0x1f & (rval >> 8)) + 1;
|
|
k = (0x3 & (rval >> 4)) + 1;
|
|
return (24000000 * n * k)>>1;
|
|
}
|
|
|
|
void pattern_goto(int pos)
|
|
{
|
|
//SUNXI_DEBUG("pos =%d\n",pos);
|
|
}
|
|
|
|
u32 spi_cfg_mclk(u32 spi_no, u32 src, u32 mclk)
|
|
{
|
|
#ifdef CONFIG_FPGA
|
|
g_cfg_mclk = 24000000;
|
|
return g_cfg_mclk;
|
|
#else
|
|
u32 mclk_base = CCM_SPI0_SCLK_CTRL;
|
|
u32 source_clk;
|
|
u32 rval;
|
|
u32 m, n, div;
|
|
src = 1;
|
|
|
|
switch (src) {
|
|
case 0:
|
|
source_clk = 24000000;
|
|
break;
|
|
case 1:
|
|
source_clk = ccm_get_pll_periph_clk();
|
|
break;
|
|
default :
|
|
SUNXI_DEBUG("Wrong SPI clock source :%x\n", src);
|
|
}
|
|
SUNXI_DEBUG("SPI clock source :0x%x\n", source_clk);
|
|
div = (source_clk + mclk - 1) / mclk;
|
|
div = div==0 ? 1 : div;
|
|
if (div > 128) {
|
|
m = 1;
|
|
n = 0;
|
|
SUNXI_DEBUG("Source clock is too high\n");
|
|
} else if (div > 64) {
|
|
n = 3;
|
|
m = div >> 3;
|
|
} else if (div > 32) {
|
|
n = 2;
|
|
m = div >> 2;
|
|
} else if (div > 16) {
|
|
n = 1;
|
|
m = div >> 1;
|
|
} else {
|
|
n = 0;
|
|
m = div;
|
|
}
|
|
|
|
rval = (1U << 31) | (src << 24) | (n << 16) | (m - 1);
|
|
writel(rval, mclk_base);
|
|
g_cfg_mclk = source_clk / (1 << n) / (m - 1);
|
|
SUNXI_DEBUG("spi spic->sclk =0x%x\n",g_cfg_mclk );
|
|
return g_cfg_mclk;
|
|
#endif
|
|
}
|
|
|
|
u32 spi_get_mlk(u32 spi_no)
|
|
{
|
|
return g_cfg_mclk ;//spicinfo[spi_no].sclk;
|
|
}
|
|
|
|
void spi_gpio_cfg(int spi_no)
|
|
{
|
|
uint reg_val = 0;
|
|
writel(0x3333, (0x1c20800 + 0x48)); // PIO SETTING,PortC SPI0
|
|
reg_val = readl(0x1c20800 + 0x64); // PIO SETTING,PortC SPI0 pull reg
|
|
reg_val &= ~(0x03 << 4);
|
|
reg_val |= (0x01 << 4);
|
|
writel(reg_val, (0x1c20800 + 0x64));
|
|
|
|
SUNXI_DEBUG("Reg pull reg_val=0x%x,read=0x%x\n",reg_val,readl((0x1c20800 + 0x64)));
|
|
}
|
|
|
|
void spi_onoff(u32 spi_no, u32 onoff)
|
|
{
|
|
u32 clkid[] = {SPI0_CKID, SPI1_CKID};
|
|
//u32 reg_val = 0;
|
|
spi_no = 0;
|
|
switch (spi_no) {
|
|
case 0:
|
|
spi_gpio_cfg(0);
|
|
break;
|
|
}
|
|
ccm_module_reset_bak(clkid[spi_no]);
|
|
if (onoff)
|
|
ccm_clock_enable_bak(clkid[spi_no]);
|
|
else
|
|
ccm_clock_disable_bak(clkid[spi_no]);
|
|
|
|
}
|
|
|
|
void spic_set_clk(u32 spi_no, u32 clk)
|
|
{
|
|
u32 mclk = spi_get_mlk(spi_no);
|
|
u32 div;
|
|
u32 cdr1 = 0;
|
|
u32 cdr2 = 0;
|
|
u32 cdr_sel = 0;
|
|
|
|
div = mclk/(clk<<1);
|
|
|
|
if (div==0) {
|
|
cdr1 = 0;
|
|
|
|
cdr2 = 0;
|
|
cdr_sel = 0;
|
|
} else if (div<=0x100) {
|
|
cdr1 = 0;
|
|
|
|
cdr2 = div-1;
|
|
cdr_sel = 1;
|
|
} else {
|
|
div = 0;
|
|
while (mclk > clk) {
|
|
div++;
|
|
mclk >>= 1;
|
|
}
|
|
cdr1 = div;
|
|
|
|
cdr2 = 0;
|
|
cdr_sel = 0;
|
|
}
|
|
|
|
writel((cdr_sel<<12)|(cdr1<<8)|cdr2, SPI_CCR);
|
|
SUNXI_DEBUG("spic_set_clk:mclk=0x%x\n",mclk);
|
|
}
|
|
|
|
int spic_init(u32 spi_no)
|
|
{
|
|
u32 rval;
|
|
//uint reg_val, div;
|
|
spi_rx_dma = malloc_noncache(sizeof(sunxi_dma_setting_t));
|
|
spi_tx_dma = malloc_noncache(sizeof(sunxi_dma_setting_t));
|
|
if(!(spi_rx_dma) || !(spi_tx_dma))
|
|
{
|
|
printf("no enough memory to malloc \n");
|
|
return -1;
|
|
}
|
|
memset(spi_tx_dma , 0 , sizeof(sunxi_dma_setting_t));
|
|
memset(spi_rx_dma , 0 , sizeof(sunxi_dma_setting_t));
|
|
spi_rx_dma_hd = sunxi_dma_request(DMAC_DMATYPE_NORMAL);
|
|
spi_tx_dma_hd = sunxi_dma_request(DMAC_DMATYPE_NORMAL);
|
|
|
|
|
|
if((spi_tx_dma_hd == 0) || (spi_rx_dma_hd == 0))
|
|
{
|
|
printf("spi request dma failed\n");
|
|
|
|
return -1;
|
|
}
|
|
//配置spi rx dma资源
|
|
//spi_rx_dma->pgsz = 0;
|
|
//spi_rx_dma->pgstp = 0;
|
|
//spi_rx_dma->cmt_blk_cnt = 0;
|
|
//config recv(from spi fifo to dram)
|
|
spi_rx_dma->cfg.src_drq_type = DMAC_CFG_TYPE_SPI0; //SPI0
|
|
spi_rx_dma->cfg.src_addr_mode = DMAC_CFG_SRC_ADDR_TYPE_IO_MODE;
|
|
spi_rx_dma->cfg.src_burst_length = DMAC_CFG_SRC_1_BURST;
|
|
spi_rx_dma->cfg.src_data_width = DMAC_CFG_SRC_DATA_WIDTH_8BIT;
|
|
|
|
spi_rx_dma->cfg.dst_drq_type = DMAC_CFG_TYPE_DRAM; //DRAM
|
|
spi_rx_dma->cfg.dst_addr_mode = DMAC_CFG_DEST_ADDR_TYPE_LINEAR_MODE;
|
|
spi_rx_dma->cfg.dst_burst_length = DMAC_CFG_DEST_1_BURST;
|
|
spi_rx_dma->cfg.dst_data_width = DMAC_CFG_DEST_DATA_WIDTH_8BIT;
|
|
|
|
//spi_rx_dma->cfg.wait_state = 4;
|
|
//spi_rx_dma->cfg.continuous_mode = 0;
|
|
//配置spi tx dma资源
|
|
//spi_tx_dma->pgsz = 0;
|
|
//spi_tx_dma->pgstp = 0;
|
|
//spi_tx_dma->cmt_blk_cnt = 0;
|
|
//spi_tx_dma.
|
|
//config send(from dram to spi fifo)
|
|
spi_tx_dma->cfg.src_drq_type = DMAC_CFG_TYPE_SRAM; //
|
|
spi_tx_dma->cfg.src_addr_mode = DMAC_CFG_SRC_ADDR_TYPE_LINEAR_MODE;
|
|
spi_tx_dma->cfg.src_burst_length = DMAC_CFG_SRC_1_BURST;
|
|
spi_tx_dma->cfg.src_data_width = DMAC_CFG_SRC_DATA_WIDTH_8BIT;
|
|
|
|
spi_tx_dma->cfg.dst_drq_type = DMAC_CFG_TYPE_SPI0; //SPI0
|
|
spi_tx_dma->cfg.dst_addr_mode = DMAC_CFG_DEST_ADDR_TYPE_IO_MODE;
|
|
spi_tx_dma->cfg.dst_burst_length = DMAC_CFG_DEST_1_BURST;
|
|
spi_tx_dma->cfg.dst_data_width = DMAC_CFG_DEST_DATA_WIDTH_8BIT;
|
|
spi_tx_dma->wait_cyc = 0x10;
|
|
//spi_tx_dma->cfg.wait_state = 4;
|
|
//spi_tx_dma->cfg.continuous_mode = 0;
|
|
|
|
sunxi_dma_setting(spi_rx_dma_hd, (void *)spi_rx_dma);
|
|
sunxi_dma_setting(spi_tx_dma_hd, (void *)spi_tx_dma);
|
|
spi_no = 0;
|
|
spi_onoff(spi_no, 1);
|
|
|
|
spi_cfg_mclk(spi_no, SPI_CLK_SRC, SPI_MCLK);
|
|
spic_set_clk(spi_no, SPI_DEFAULT_CLK);
|
|
|
|
rval = SPI_SOFT_RST|SPI_TXPAUSE_EN|SPI_MASTER|SPI_ENABLE;
|
|
writel(rval, SPI_GCR);
|
|
rval = SPI_SET_SS_1|SPI_DHB|SPI_SS_ACTIVE0; //set ss to high,discard unused burst,SPI select signal polarity(low,1=idle)
|
|
writel(rval, SPI_TCR);
|
|
writel(SPI_TXFIFO_RST|(SPI_TX_WL<<16)|(SPI_RX_WL), SPI_FCR);
|
|
return 0;
|
|
}
|
|
|
|
int spic_rw( u32 tcnt, void* txbuf, u32 rcnt, void* rxbuf)
|
|
{
|
|
u32 i = 0,fcr;
|
|
int timeout = 0xfffff;
|
|
//uint ret = 0;
|
|
|
|
u8 *tx_buffer = txbuf ;
|
|
u8 *rx_buffer = rxbuf;
|
|
writel(SPI_IER, 0);
|
|
writel(SPI_ISR, 0xffffffff);//clear status register
|
|
|
|
writel(tcnt, SPI_MTC);
|
|
writel(tcnt+rcnt, SPI_MBC);
|
|
writel(readl(SPI_TCR)|SPI_EXCHANGE, SPI_TCR);
|
|
if(tcnt)
|
|
{
|
|
if(tcnt < 64 )
|
|
{
|
|
i = 0;
|
|
while (i < tcnt)
|
|
{
|
|
//send data
|
|
while(((readl(SPI_FSR)>>16)==SPI_FIFO_SIZE) );
|
|
writeb(*(tx_buffer+i),SPI_TXD);
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//spi_no = 0;
|
|
writel((readl(SPI_FCR)|SPI_TXDMAREQ_EN), SPI_FCR);
|
|
spi_dma_send_start(0, txbuf, tcnt);
|
|
/* wait DMA finish */
|
|
while ((timeout-- > 0) && spi_wait_dma_send_over(0));
|
|
if (timeout <= 0)
|
|
{
|
|
printf("tx wait_dma_send_over fail\n");
|
|
return -1;
|
|
}
|
|
// printf("timeout %d,count_send %d \n",timeout,count_send);
|
|
}
|
|
}
|
|
timeout = 0xfffff;
|
|
/* start transmit */
|
|
//writel(readl(SPI_TCR)|SPI_EXCHANGE, SPI_TCR);
|
|
if(rcnt)
|
|
{
|
|
if(rcnt < 64)
|
|
{
|
|
i = 0;
|
|
while(i < rcnt)
|
|
{
|
|
//receive valid data
|
|
while(((readl(SPI_FSR))&0x7f)==0);
|
|
*(rx_buffer+i)=readb(SPI_RXD);
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pattern_goto(115);
|
|
timeout = 0xfffff;
|
|
writel((readl(SPI_FCR)|SPI_RXDMAREQ_EN), SPI_FCR);
|
|
spi_dma_recv_start(0, rxbuf, rcnt);
|
|
/* wait DMA finish */
|
|
while ((timeout-- > 0) && spi_wait_dma_recv_over(0));
|
|
if (timeout <= 0)
|
|
{
|
|
printf("rx wait_dma_recv_over fail\n");
|
|
return -1;
|
|
}
|
|
// printf("count_recv %d \n",count_recv);
|
|
}
|
|
}
|
|
#if 1
|
|
timeout = 0xfffff;
|
|
while(!(readl(SPI_ISR)&(0x1<<12)))//wait transfer complete
|
|
{
|
|
timeout--;
|
|
if (!timeout)
|
|
{
|
|
printf("SPI_ISR time_out \n");
|
|
break;
|
|
}
|
|
}
|
|
timeout = 0xfffff;
|
|
while(DMA_CHAN_STA_REG & 0x1)
|
|
{
|
|
timeout--;
|
|
if (!timeout)
|
|
{
|
|
printf("DMA_CHAN_STA_REG timeout\n");
|
|
break;
|
|
}
|
|
}
|
|
pattern_goto(121);
|
|
#endif
|
|
|
|
fcr = readl(SPI_FCR);
|
|
fcr &= ~(SPI_TXDMAREQ_EN|SPI_RXDMAREQ_EN);
|
|
writel(fcr, SPI_FCR);
|
|
if ((readl(SPI_ISR) & (0xf << 8))|| (timeout==0)) /* (1U << 11) | (1U << 10) | (1U << 9) | (1U << 8)) */
|
|
return RET_FAIL;
|
|
|
|
if(readl(SPI_TCR)&SPI_EXCHANGE)
|
|
{
|
|
printf("XCH Control Error!!\n");
|
|
}
|
|
|
|
writel(0xfffff,SPI_ISR); /* clear flag */
|
|
return RET_OK;
|
|
|
|
}
|
|
|
|
int spic_exit(u32 spi_no)
|
|
{
|
|
if(spi_tx_dma)
|
|
free(spi_tx_dma);
|
|
if(spi_rx_dma)
|
|
free(spi_rx_dma);
|
|
sunxi_dma_release(spi_tx_dma_hd);
|
|
sunxi_dma_release(spi_rx_dma_hd);
|
|
return 0;
|
|
}
|
|
|