sdk-hwV1.3/lichee/linux-4.9/modules/nand/sun8iw15p1/phy-nand/physic/spic_op.c

827 lines
17 KiB
C
Raw Blame History

/*
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
/**
* spi_hal.c
* date: 2012/2/12 22:34:41
* author: Aaron<leafy.myeh@allwinnertech.com>
* history: V0.1
*/
//#include "include.h"
#include "nand_type_spinand.h"
#include "spic.h"
#include "spinand_drv_cfg.h"
#include "spinand_interface_for_common.h"
#define MAX_SPI_NUM 2
#if 0
struct spic_info {
__u32 spi_no;
__s32 irq;
__u32 sclk;
__u32 master;
u8 *txbuf;
__u32 txptr;
__u32 txnum;
u8 *rxbuf;
__u32 rxptr;
__u32 rxnum;
__u32 usedma;
__s32 txdma_ch;
__s32 rxdma_ch;
__u32 error;
volatile __u32 done;
volatile __u32 txdma_done;
volatile __u32 rxdma_done;
} spicinfo[MAX_SPI_NUM];
#endif
// extern void *SPIC_IO_BASE;
#if 0
__u32 spi_cfg_mclk(__u32 spi_no, __u32 src, __u32 mclk)
{
struct spic_info *spic = &spicinfo[spi_no];
__u32 mclk_base = CCM_SPI_CLK_REG + 4 * spi_no;
__u32 source_clk;
__u32 rval;
__u32 m, n, div;
#ifdef FPGA_PLATFORM
spic->sclk = 24000000;
return spic->sclk;
#else
switch (src) {
case 0:
source_clk = 24000000;
break;
case 1:
source_clk = ccm_get_pll_periph0_clk();
break;
case 2:
source_clk = ccm_get_pll_periph1_clk();
break;
default:
printk("Wrong SPI clock source :%x\n", src);
break;
}
div = (source_clk + mclk - 1) / mclk;
div = div == 0 ? 1 : div;
if (div > 128) {
m = 1;
n = 0;
printk("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);
writew(rval, mclk_base);
spic->sclk = source_clk / (1 << n) / (m - 1);
return spic->sclk;
#endif
}
/*
*__u32 spi_get_mlk(__u32 spi_no)
*{
* return spicinfo[spi_no].sclk;
*}
*/
void spi_onoff(__u32 spi_no, __u32 onoff)
{
__u32 clkid[] = {SPI_CKID};
switch (spi_no) {
case 0:
//pc0~3,pc2:cs,muliti3
gpio_set_cfg(GPIO_C(0), 4, 3);
gpio_set_pull(GPIO_C(2), 1, 1);
gpio_set_drv(GPIO_C(0), 4, 1);
break;
default:
break;
}
ccm_module_reset(clkid[spi_no]);
if (onoff)
ccm_clock_enable(clkid[spi_no]);
else
ccm_clock_disable(clkid[spi_no]);
}
#endif
#if 0
void spi_irq_handler(__u32 spi_no)
{
struct spic_info *spic = &spicinfo[spi_no];
__u32 isr = readw(SPI_ISR);
/* check error */
if (isr & (SPI_TXUR_INT|SPI_TXOF_INT|SPI_RXUR_INT|SPI_RXOF_INT)) {
printk("spi %d FIFO run error, isr 0x%x\n", spi_no, isr);
spic->done = 1;
spic->error = isr & (SPI_TXUR_INT|SPI_TXOF_INT|SPI_RXUR_INT|
SPI_RXOF_INT);
goto out;
}
/* check transfer compelete */
if (isr & SPI_TC_INT) {
if (!spic->usedma && (isr & SPI_RXREQ_INT)) {
__u32 rcnt = readw(SPI_FSR) & 0xff;
// if (rcnt > spic->rxnum - spic->rxptr)
// rcnt = spic->rxnum - spic->rxptr;
while (rcnt) {
spic->rxbuf[spic->rxptr++] = readb(SPI_RXD);
rcnt--;
}
if (spic->rxptr != spic->rxnum)
printk("Rx number != total number\n");
}
writew(SPI_ERROR_INT, SPI_IER);
spic->done = 1;
goto out;
}
if (!spic->usedma) {
if (isr & SPI_RXREQ_INT) {
//__u32 rcnt = SPI_RX_WL;
__u32 rcnt = readw(SPI_FSR) & 0xff;
if (rcnt > spic->rxnum - spic->rxptr)
rcnt = spic->rxnum - spic->rxptr;
while (rcnt) {
spic->rxbuf[spic->rxptr++] = readb(SPI_RXD);
rcnt--;
}
}
if (isr & SPI_TXREQ_INT) {
__u32 tcnt = SPI_FIFO_SIZE - SPI_TX_WL;
if (tcnt > spic->txnum - spic->txptr)
tcnt = spic->txnum - spic->txptr;
while (tcnt) {
writeb(spic->txbuf[spic->txptr++], SPI_TXD);
tcnt--;
}
}
}
out:
writew(isr, SPI_ISR);
}
#define SPIx_IRQ_DEFINE(_n) \
static void spi##_n##_irq_hdle(void) \
{ \
spi_irq_handler(_n); \
}
SPIx_IRQ_DEFINE(0)
SPIx_IRQ_DEFINE(1)
static void (*spi_irq_hdle[])(void) = {
spi0_irq_hdle,
spi1_irq_hdle,
};
#endif
#if 0
#if 0
void spic_set_clk(__u32 spi_no, __u32 clk)
{
// __u32 mclk = spi_get_mlk(spi_no);
__u32 mclk = AHBCLK;
__u32 div;
__u32 cdr1 = 0;
__u32 cdr2 = 0;
__u32 cdr_sel = 0;
div = mclk/(clk<<1)-1;
if (mclk < (clk<<1))
div++;
if (mclk%(clk<<1))
div++;
if (div == 0) {
cdr1 = 0;
cdr2 = 0;
cdr_sel = 0;
} else if (div <= 0x100) {
cdr1 = 0;
cdr2 = div;
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);
}
#else
void spic_set_clk(__u32 spi_no, __u32 clk)
{
// __u32 mclk = spi_get_mlk(spi_no);
__u32 mclk = AHBCLK;
__u32 div;
__u32 cdr1 = 0;
__u32 cdr2 = 0;
__u32 cdr_sel = 0;
if (mclk < clk) {
printk("spi %d set_clk failed,%d MHz < %d MHz\n",
spi_no, (mclk / 1000000), (clk / 1000000));
}
div = mclk/(clk<<1);
if ((mclk < (clk<<1)) && (mclk > clk))
div++;
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;
}
writew((cdr_sel<<12)|(cdr1<<8)|cdr2, SPI_CCR);
}
#endif
__u32 spic_get_clk(__u32 spi_no)
{
// __u32 mclk = spi_get_mlk(spi_no);
__u32 mclk = AHBCLK;
__u32 temp;
__u32 temp_clk;
mclk = mclk / 1000000;
temp = readw(SPI_CCR);
if (temp && (1<<12)) {
temp_clk = mclk / 2 / ((temp & 0xff) + 1);
} else {
temp_clk = mclk >> ((temp >> 8) & 0xf);
}
return temp_clk;
}
#endif
__s32 Wait_Tc_Complete(void)
{
__u32 timeout = 0xffffff;
while (!(readw(SPI_ISR) & (0x1 << 12))) {// wait transfer complete
timeout--;
if (!timeout)
break;
}
if (timeout == 0) {
PHY_ERR("TC Complete wait status timeout!\n");
return ERR_TIMEOUT;
}
return 0;
}
__s32 Spic_init(__u32 spi_no)
{
__u32 rval;
// __s32 irq;
// MEMSET(&spicinfo[spi_no], 0, sizeof(struct spic_info));
// init pin
if (0 != NAND_PIORequest(spi_no)) {
PHY_ERR("request spi gpio fail!\n");
return -1;
} else
PHY_DBG("request spi gpio ok!\n");
// SPIC_IO_BASE = NAND_GetIOBaseAddr();
// request general dma channel
if (0 != spinand_request_tx_dma()) {
PHY_ERR("request tx dma fail!\n");
return -1;
} else
PHY_DBG("request general tx dma channel ok!\n");
if (0 != spinand_request_rx_dma()) {
PHY_ERR("request rx dma fail!\n");
return -1;
} else
PHY_DBG("request general rx dma channel ok!\n");
// init clk
NAND_ClkRequest(spi_no);
NAND_SetClk(spi_no, 20, 20 * 2);
rval = SPI_SOFT_RST | SPI_TXPAUSE_EN | SPI_MASTER | SPI_ENABLE;
writew(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)
writew(rval, SPI_TCR);
writew(0x1000,
SPI_CCR); // SPI data clk = source clk / 2, Duty Ratio <20><> 50%
#if 0
irq = irq_request(SPI_IRQNO(spi_no), spi_irq_hdle[spi_no]);
if (irq < 0) {
printk("Request spi %d irq failed\n", spi_no);
return -1;
}
irq_enable(irq);
spicinfo[spi_no].irq = irq;
#endif
// spicinfo[spi_no].master = 1;
writew(SPI_TXFIFO_RST | (SPI_TX_WL << 16) | (SPI_RX_WL), SPI_FCR);
writew(SPI_ERROR_INT, SPI_IER);
return 0;
}
__s32 Spic_exit(__u32 spi_no)
{
__u32 rval;
// __s32 irq;
rval = readw(SPI_GCR);
rval &= (~(SPI_SOFT_RST | SPI_MASTER | SPI_ENABLE));
writew(rval, SPI_GCR);
#if 0
irq = spicinfo[spi_no].irq;
irq_disable(irq);
irq_free(irq);
#endif
NAND_ClkRelease(spi_no);
spinand_releasetxdma();
spinand_releaserxdma();
// init pin
NAND_PIORelease(spi_no);
// MEMSET(&spicinfo[spi_no], 0, sizeof(struct spic_info));
rval = SPI_SET_SS_1 | SPI_DHB |
SPI_SS_ACTIVE0; // set ss to high,discard unused burst,SPI select
// signal polarity(low,1=idle)
writew(rval, SPI_TCR);
return 0;
}
void Spic_set_master_slave(__u32 spi_no, __u32 master)
{
__u32 rval = readw(SPI_GCR) & (~(1 << 1));
rval |= master << 1;
writew(rval, SPI_GCR);
// spicinfo[spi_no].master = master;
}
void Spic_sel_ss(__u32 spi_no, __u32 ssx)
{
__u32 rval = readw(SPI_TCR) & (~(3 << 4));
rval |= ssx << 4;
writew(rval, SPI_TCR);
}
// add for aw1650
void Spic_set_transmit_LSB(__u32 spi_no, __u32 tmod)
{
__u32 rval = readw(SPI_TCR) & (~(1 << 12));
rval |= tmod << 12;
writew(rval, SPI_TCR);
}
void Spic_set_ss_level(__u32 spi_no, __u32 level)
{
__u32 rval = readw(SPI_TCR) & (~(1 << 7));
rval |= level << 7;
writew(rval, SPI_TCR);
}
void Spic_set_rapids(__u32 spi_no, __u32 rapids)
{
__u32 rval = readw(SPI_TCR) & (~(1 << 10));
rval |= rapids << 10;
writew(rval, SPI_TCR);
}
void Spic_set_sample_mode(__u32 spi_no, __u32 smod)
{
__u32 rval = readw(SPI_TCR) & (~(1 << 13));
rval |= smod << 13;
writew(rval, SPI_TCR);
}
void Spic_set_sample(__u32 spi_no, __u32 sample)
{
__u32 rval = readw(SPI_TCR) & (~(1 << 11));
rval |= sample << 11;
writew(rval, SPI_TCR);
}
void Spic_set_trans_mode(__u32 spi_no, __u32 mode)
{
__u32 rval = readw(SPI_TCR) & (~(3 << 0));
rval |= mode << 0;
writew(rval, SPI_TCR);
}
void Spic_set_wait_clk(__u32 spi_no, __u32 swc, __u32 wcc)
{
writew((swc << 16) | (wcc), SPI_WCR);
}
#if 0
void spic_dma_onoff(__u32 spi_no, __u32 dma)
{
spicinfo[spi_no].usedma = dma;
}
void spic_dma_txcb(__u32 data)
{
struct spic_info *spic = (struct spic_info *)data;
spic->txdma_done = 1;
}
void spic_dma_rxcb(__u32 data)
{
struct spic_info *spic = (struct spic_info *)data;
spic->rxdma_done = 1;
}
__s32 spic_dma_config(__u32 spi_no, __u32 tx_mode, __u32 buff_addr, __u32 len)
{
struct spic_info *spic = &spicinfo[spi_no];
__s32 chan;
__u32 param = 0;
__u32 config;
__u32 dmatype[] = {DMA_TYPE_SPI0};
__u32 drqno_tx[] = {DMA_CFG_DST_DRQ_SPI0};
__u32 drqno_rx[] = {DMA_CFG_SRC_DRQ_SPI0};
if (tx_mode == 1) {
chan = dma_request(dmatype[spi_no], spic_dma_txcb,
DMA_QUEUE_END_IRQFLAG, (__u32)spic);
if (chan < 0) {
printk("Request spi %d dma channel failed\n", spi_no);
return -1;
}
spic->txdma_ch = chan;
param = 0x10;
config = DMA_CFG_SRC_BST1_WIDTH8 | DMA_CFG_SRC_LINEAR |
drqno_tx[spi_no] | DMA_CFG_DST_BST1_WIDTH8 |
DMA_CFG_DST_IO;
config |= DMA_CFG_SRC_DRQ_SDRAM;
dma_setup(chan, config, param);
dma_enqueue(chan, (__u32)buff_addr, SPI_TXD, len);
NAND_CleanFlushDCacheRegion(buff_addr, len);
nand_dma_config_start(tx_mode, buff_addr, len);
} else {
chan = dma_request(dmatype[spi_no], spic_dma_rxcb,
DMA_QUEUE_END_IRQFLAG, (__u32)spic);
if (chan < 0) {
printk("Request spi %d dma channel failed\n", spi_no);
return -1;
}
spic->rxdma_ch = chan;
param = 0x10;
config = DMA_CFG_DST_BST1_WIDTH8 | DMA_CFG_DST_LINEAR |
drqno_rx[spi_no] | DMA_CFG_SRC_BST1_WIDTH8 |
DMA_CFG_SRC_IO;
config |= DMA_CFG_DST_DRQ_SDRAM;
dma_setup(chan, config, param);
dma_enqueue(chan, SPI_RXD, (__u32)buff_addr, len);
}
dma_start(chan);
// NAND_CleanFlushDCacheRegion(buff_addr, len);
// nand_dma_config_start( rw, nand_dma_addr[NandIndex][0], len);
return 0;
}
#endif
void Spic_config_io_mode(__u32 spi_no, __u32 rxmode, __u32 dbc, __u32 stc)
{
if (rxmode == 0)
writew((dbc << 24) | (stc), SPI_BCC);
else if (rxmode == 1)
writew((1 << 28) | (dbc << 24) | (stc), SPI_BCC);
else if (rxmode == 2)
writew((1 << 29) | (dbc << 24) | (stc), SPI_BCC);
}
/*
* spi txrx
* _ _______ ______________
* |_______|/_/_/_/_/_/_/_|
*/
#if 0
__s32 spic_rw(__u32 spi_no, __u32 txlen, void *txbuff, __u32 rxlen,
void *rxbuff, __u32 dummy_cnt)
{
struct spic_info *spic = &spicinfo[spi_no];
__u32 ier;
//__u32 tmp;
spic->txdma_done = 0;
spic->rxdma_done = 0;
spic->done = 0;
/* config transmit counters */
ier = readl(SPI_IER)|SPI_TC_INT; //transmit complete enable
writel(ier, SPI_IER);
pattern_goto(20);
writel(txlen, SPI_MTC);
pattern_goto(21);
writel(txlen+rxlen+dummy_cnt, SPI_MBC);
pattern_goto(22);
/* start transmit */
writel(readl(SPI_TCR)|SPI_EXCHANGE, SPI_TCR);
pattern_goto(23);
/* config dma */
if (spic->usedma) {
__u32 fcr = readl(SPI_FCR);
if (rxlen) {
fcr |= SPI_RXDMAREQ_EN;
writel(fcr, SPI_FCR);
pattern_goto(24);
spic_dma_config(spi_no, 0, rxbuff, rxlen);
pattern_goto(25);
}
if (txlen) {
/* flush cache */
#ifdef SYS_ENABLE_DCACHE
FlushDcacheRegion((void *)txbuff, txlen);
#endif
fcr |= SPI_TXDMAREQ_EN;
writel(fcr, SPI_FCR);
pattern_goto(26);
spic_dma_config(spi_no, 1, txbuff, txlen);
pattern_goto(27);
}
/* wait dma done */
while (rxlen && !spic->rxdma_done) {
};
while (txlen && !spic->txdma_done) {
};
pattern_goto(28);
if (rxlen) {
/* flush cache */
#ifdef SYS_ENABLE_DCACHE
FlushDcacheRegion((void *)rxbuff, rxlen);
#endif
}
spic->rxdma_done = 0;
spic->txdma_done = 0;
fcr &= ~(SPI_TXDMAREQ_EN|SPI_RXDMAREQ_EN);
writel(fcr, SPI_FCR);
pattern_goto(29);
} else {
spic->txbuf = txbuff;
spic->txptr = 0;
spic->txnum = txlen;
spic->rxbuf = rxbuff;
spic->rxptr = 0;
spic->rxnum = rxlen;
if (rxlen)
ier |= SPI_RXREQ_INT;
pattern_goto(30);
if (txlen)
ier |= SPI_TXREQ_INT;
pattern_goto(31);
writel(ier, SPI_IER);
pattern_goto(32);
}
pattern_goto(33);
/* wait transfer compelete */
while (!spic->done) {
};
pattern_goto(34);
if (spic->error) {
printk("spi %d transfer failed\n", spi_no);
writel(readl(SPI_FCR)|SPI_TXFIFO_RST, SPI_FCR);
pattern_goto(35);
return -1;
}
pattern_goto(36);
writel(readl(SPI_IER)&(~(SPI_RXREQ_INT|SPI_TXREQ_INT|SPI_TC_INT)),
SPI_IER);
pattern_goto(39);
return 0;
}
#else
__s32 Spic_rw(__u32 spi_no, __u32 tcnt, __u8 *txbuf, __u32 rcnt, __u8 *rxbuf,
__u32 dummy_cnt)
{
__u32 i = 0, fcr;
__u32 tx_dma_flag = 0;
__u32 rx_dma_flag = 0;
__s32 timeout = 0xffff;
writew(0, SPI_IER);
writew(0xffffffff, SPI_ISR); // clear status register
writew(tcnt, SPI_MTC);
writew(tcnt + rcnt + dummy_cnt, SPI_MBC);
// read and write by cpu operation
if (tcnt) {
if (tcnt < 64) {
i = 0;
while (i < tcnt) {
// send data
// while ((readw(SPI_FSR)>>16)==SPI_FIFO_SIZE);
if (((readw(SPI_FSR) >> 16) & 0x7f) ==
SPI_FIFO_SIZE)
PHY_ERR("TX FIFO size error!\n");
writeb(*(txbuf + i), SPI_TXD);
i++;
}
} else {
tx_dma_flag = 1;
writew((readw(SPI_FCR) | SPI_TXDMAREQ_EN), SPI_FCR);
nand_dma_config_start(1, (__u32)txbuf, tcnt);
#if 0
timeout = 0xffffff;
while (readw(DMA_IRQ_PEND_REG)&0x7 != 7) {
timeout--;
if (!timeout)
break;
}
if (timeout == 0) {
PHY_ERR("TX DMA wait status timeout!\n");
return ERR_TIMEOUT;
}
#endif
}
}
/* start transmit */
writew(readw(SPI_TCR) | SPI_EXCHANGE, SPI_TCR);
if (rcnt) {
if (rcnt < 64) {
i = 0;
#if 1
timeout = 0xfffff;
while (1) {
if (((readw(SPI_FSR)) & 0x7f) == rcnt)
break;
if (timeout < 0) {
PHY_ERR(
"RX FIFO size error,timeout!\n");
break;
}
timeout--;
}
#endif
while (i < rcnt) {
// receive valid data
// while(((readw(SPI_FSR))&0x7f)==0);
// while((((readw(SPI_FSR))&0x7f)!=rcnt)||
// timeout < 0))
// PHY_ERR("RX FIFO size error!\n");
*(rxbuf + i) = readb(SPI_RXD);
i++;
}
} else {
rx_dma_flag = 1;
writew((readw(SPI_FCR) | SPI_RXDMAREQ_EN), SPI_FCR);
nand_dma_config_start(0, (__u32)rxbuf, rcnt);
#if 0
timeout = 0xffffff;
while (readw(DMA_IRQ_PEND_REG)&0x7 != 7) {
timeout--;
if (!timeout)
break;
}
if (timeout == 0) {
PHY_ERR("RX DMA wait status timeout!\n");
return ERR_TIMEOUT;
}
//while (readw(SPI_FSR)&0x7f);
if (readw(SPI_FSR)&0x7f) {
PHY_ERR("RX FIFO not empty after DMA finish!\n");
}
#endif
}
}
if (NAND_WaitDmaFinish(tx_dma_flag, rx_dma_flag)) {
PHY_ERR("DMA wait status timeout!\n");
return ERR_TIMEOUT;
}
if (Wait_Tc_Complete()) {
PHY_ERR("wait tc complete timeout!\n");
return ERR_TIMEOUT;
}
if (tx_dma_flag)
Nand_Dma_End(1, (__u32)txbuf, tcnt);
if (rx_dma_flag)
Nand_Dma_End(0, (__u32)rxbuf, rcnt);
fcr = readw(SPI_FCR);
fcr &= ~(SPI_TXDMAREQ_EN | SPI_RXDMAREQ_EN);
writew(fcr, SPI_FCR);
if (readw(SPI_ISR) &
(0xf << 8)) {/* (1U << 11) | (1U << 10) | (1U << 9) | (1U << 8)) */
PHY_ERR("FIFO status error: 0x%x!\n", readw(SPI_ISR));
return NAND_OP_FALSE;
}
if (readw(SPI_TCR) & SPI_EXCHANGE)
PHY_ERR("XCH Control Error!!\n");
writew(0xffffffff, SPI_ISR); /* clear flag */
return NAND_OP_TRUE;
}
#endif
__s32 spi_nand_rdid(__u32 spi_no, __u32 chip, __u32 id_addr, __u32 addr_cnt,
__u32 id_cnt, void *id)
{
__s32 ret = NAND_OP_TRUE;
__u8 sdata[2];
__u32 txnum;
__u32 rxnum;
txnum = 1 + addr_cnt;
rxnum = id_cnt; // mira nand:rxnum=5
sdata[0] = SPI_NAND_RDID;
sdata[1] = id_addr; // add 00H:Maunufation ID,01H:Device ID
Spic_sel_ss(spi_no, chip);
Spic_config_io_mode(spi_no, 0, 0, txnum);
ret = Spic_rw(spi_no, txnum, (void *)sdata, rxnum, (void *)id, 0);
return ret;
}