ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/drivers/spi/asr_spi.c b/marvell/uboot/drivers/spi/asr_spi.c
new file mode 100644
index 0000000..0f34056
--- /dev/null
+++ b/marvell/uboot/drivers/spi/asr_spi.c
@@ -0,0 +1,448 @@
+/*
+ * Driver for simulating ssp as spi device for asr1802s
+ *
+ * (C) Copyright 2018
+ * ASR Microelectronics (Shanghai) Co., Ltd.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <watchdog.h>
+#include <asm/io.h>
+#include <asm/arch/asr_spi.h>
+#include <asm/gpio.h>
+#include <asm/arch/pxa_dma.h>
+#include <asm/arch/cpu.h>
+
+#define to_asr_spi_slave(s) container_of(s, struct asr_spi_slave, slave)
+
+int cs_continuous;
+
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ gpio_set_value(slave->cs, slv->gpio_cs_inverted);
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ gpio_set_value(slave->cs, !slv->gpio_cs_inverted);
+}
+#endif
+
+static int spi_pxa_write(struct asr_spi_slave *slv, unsigned int bitlen)
+{
+ int wait_timeout = SSP_FLUSH_NUM;
+ int bytes = bitlen / slv->slave.wordlen;
+
+ while (--wait_timeout && !(readl(&slv->spi_reg->sssr) & SSSR_TNF))
+ ;
+ if (!wait_timeout) {
+ debug("%s: timeout error\n", __func__);
+ return -1;
+ }
+
+ while (bytes--) {
+ if (slv->tx != NULL) {
+ if (slv->slave.wordlen > 16) {
+ writel(*(u32 *)slv->tx, &slv->spi_reg->ssdr);
+ slv->tx += 4;
+ } else if (slv->slave.wordlen > 8) {
+ writel(*(u16 *)slv->tx, &slv->spi_reg->ssdr);
+ slv->tx += 2;
+ } else {
+ writel(*(u8 *)slv->tx, &slv->spi_reg->ssdr);
+ ++slv->tx;
+ }
+ } else
+ writel(0, &slv->spi_reg->ssdr);
+
+ while (((readl(&slv->spi_reg->sssr)&0xFC1) != 0x40) && wait_timeout--) {
+ nop();
+ nop();
+ }
+ if (!wait_timeout) {
+ debug("%s: timeout error\n", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int spi_pxa_read(struct asr_spi_slave *slv, unsigned int bitlen)
+{
+ int wait_timeout = SSP_FLUSH_NUM;
+ int bytes = bitlen / slv->slave.wordlen;
+
+ while (--wait_timeout && !(readl(&slv->spi_reg->sssr) & SSSR_TNF))
+ ;
+ if (!wait_timeout) {
+ debug("%s: timeout error\n", __func__);
+ return -1;
+ }
+
+ while (bytes--) {
+ wait_timeout = SSP_FLUSH_NUM;
+ writel(0xff, &slv->spi_reg->ssdr);
+ while (((readl(&slv->spi_reg->sssr)&0xFC1) != 0x40) && wait_timeout--) {
+ nop();
+ nop();
+ }
+ if (!wait_timeout) {
+ printf("%s: timeout error, sssr:0x%x. byte:0x%x.\n",
+ __func__, readl(&slv->spi_reg->sssr), bytes);
+ return -1;
+ }
+
+ wait_timeout = SSP_FLUSH_NUM;
+ while (--wait_timeout && !(readl(&slv->spi_reg->sssr) & SSSR_RNE))
+ ;
+ if (!wait_timeout) {
+ printf("%s: timeout error sssr:0x%x.\n", __func__,
+ readl(&slv->spi_reg->sssr));
+ return -1;
+ }
+
+ if (slv->rx != NULL) {
+ if (slv->slave.wordlen > 16) {
+ *(u32 *)slv->rx = readl(&slv->spi_reg->ssdr);
+ slv->rx += 4;
+ } else if (slv->slave.wordlen > 8) {
+ *(u16 *)slv->rx = readl(&slv->spi_reg->ssdr);
+ slv->rx += 2;
+ } else {
+ *(u8 *)slv->rx = readl(&slv->spi_reg->ssdr);
+ ++slv->rx;
+ }
+ } else
+ readl(&slv->spi_reg->ssdr);
+ }
+
+ return 0;
+}
+
+static int spi_pxa_flush(struct asr_spi_slave *slv)
+{
+ unsigned long limit = SSP_FLUSH_NUM;
+
+ do {
+ while (readl(&slv->spi_reg->sssr) & SSSR_RNE)
+ readl(&slv->spi_reg->ssdr);
+ } while ((readl(&slv->spi_reg->sssr) & SSSR_BSY) && limit--);
+
+ writel(SSSR_ROR, &slv->spi_reg->sssr);
+
+ return limit;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct asr_spi_slave *slv;
+
+ slv = spi_alloc_slave(struct asr_spi_slave, bus, cs);
+ if (!slv)
+ return NULL;
+
+ slv->slave.bus = bus;
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ slv->slave.cs = cs;
+#endif
+ slv->spi_reg = (struct ssp_reg *)CONFIG_SYS_SSP_BASE;
+ slv->tcr = SSTCR_TTE | SSTCR_TTELP | SSTCR_MOTO |
+ SSTCR_DSS(DEFAULT_WORD_LEN) | SSTCR_SSE;
+ slv->tcr &= ~(SSTCR_SPO | SSTCR_SPH);
+ slv->tcr |= (((mode & SPI_CPHA) != 0) ? SSTCR_SPH : 0)
+ | (((mode & SPI_CPOL) != 0) ? SSTCR_SPO : 0);
+
+ slv->fcr = (SSFCR_RXTRESH(RX_THRESH_DEF) & SSFCR_RFT) |
+ (SSFCR_TXTRESH(TX_THRESH_DEF) & SSFCR_TFT);
+
+ slv->ier = SSIER_TIE | SSIER_RIE | SSIER_TINTE;
+ slv->clear_sr = SSSR_ROR | SSSR_TINT;
+
+ if (cs) {
+ //In case that device nHold and nWP is connected to 1802s
+ gpio_direction_output(CONFIG_SPINAND_nHOLD, 1);
+ gpio_direction_output(CONFIG_SPINAND_nWP, 1);
+
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ gpio_direction_output(cs, !slv->gpio_cs_inverted);
+ slv->gpio_cs_inverted = mode & SPI_CS_HIGH;
+ gpio_set_value(cs, !slv->gpio_cs_inverted);
+#endif
+ }
+ cs_continuous = 0;
+
+ return &slv->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ free(slv);
+}
+#ifdef CONFIG_PXA_DMA
+__attribute__ ((aligned(32))) unsigned int dummy[0x400];
+#endif
+void spi_init(void)
+{
+ unsigned int reg;
+ int clk = SSP_26M; // default 26M
+ if (CONFIG_SYS_SSP_BASE == SSP0_BASE)
+ reg = APBC_SSP0_CLK_RST;
+ else if (CONFIG_SYS_SSP_BASE == SSP1_BASE)
+ reg = APBC_SSP1_CLK_RST;
+ else if (CONFIG_SYS_SSP_BASE == SSP2_BASE)
+ reg = APBC_SSP2_CLK_RST;
+ else {
+ printf("Fatal error: unsupportted ssp base: 0x%x.\n", CONFIG_SYS_SSP_BASE);
+ return;
+ }
+
+#ifdef CONFIG_SPI_52M_ENABLE
+ clk = SSP_52M;
+#else
+ #ifdef CONFIG_SPI_26M_ENABLE
+ clk = SSP_26M;
+ #else
+ #ifdef CONFIG_SPI_13M_ENABLE
+ clk = SSP_13M;
+ #else
+ #ifdef CONFIG_SPI_6P5M_ENABLE
+ clk = SSP_6P5M;
+ #endif
+ #endif
+ #endif
+#endif
+
+ __raw_writel(APBC_SSP_BCLKEN | APBC_SSP_FNCLKEN |
+ (clk << APBC_SSP_FNCLKSEL_SHIFT), reg);
+
+#ifdef CONFIG_PXA_DMA
+ memset(dummy, 0x0, 0x400*4);
+#endif
+ /* Load default SSP configuration */
+ writel(0, CONFIG_SYS_SSP_BASE + SSTCR);
+ writel(SSFCR_RXTRESH(RX_THRESH_DEF) |
+ SSFCR_TXTRESH(TX_THRESH_DEF), CONFIG_SYS_SSP_BASE + SSFCR);
+ writel(SSTCR_MOTO | SSTCR_DSS(DEFAULT_WORD_LEN)
+ , CONFIG_SYS_SSP_BASE + SSTCR);
+ writel(0, CONFIG_SYS_SSP_BASE + SSTO);
+
+ __raw_writel(DMA_CLK_AXICLK_EN | DMA_CLK_AXI_RST,
+ PMUA_DMA_CLK_RES_CTRL); /* enable DMA clock */
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ slave->wordlen = 8; /* set default wordlen */
+
+ if (spi_pxa_flush(slv) == 0)
+ return -1;
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ int ret = 0;
+
+ if (slave->wordlen < 4 || slave->wordlen > 32) {
+ printf("pxa2xx_spi: invalid wordlen %d\n", slave->wordlen);
+ return -1;
+ }
+
+ if (bitlen % slave->wordlen)
+ return -1;
+
+ slv->rx = din;
+ slv->tx = dout;
+
+ writel(0, &slv->spi_reg->sstcr);
+ slv->tcr &= ~(SSTCR_DSS_MASK);
+ slv->tcr |= SSTCR_DSS(slave->wordlen);
+
+ writel(slv->fcr, &slv->spi_reg->ssfcr);
+ writel(slv->ier, &slv->spi_reg->ssier);
+ writel(TIMEOUT_DEF, &slv->spi_reg->ssto);
+ writel(slv->tcr, &slv->spi_reg->sstcr);
+
+ if (flags & SPI_XFER_BEGIN) {
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ if (!cs_continuous)
+ spi_cs_activate(slave);
+#endif
+ }
+
+ if (dout != NULL && din != NULL) {
+ ret = spi_pxa_write(slv, bitlen - slave->wordlen);
+ if(ret)
+ return -1;
+
+ ret = spi_pxa_read(slv, slave->wordlen);
+ if(ret)
+ return -1;
+ } else if (dout != NULL) {
+ ret = spi_pxa_write(slv, bitlen);
+ if(ret)
+ return -1;
+ } else if (din != NULL) {
+ ret = spi_pxa_read(slv, bitlen);
+ if(ret)
+ return -1;
+ }
+
+ if (flags & SPI_XFER_END) {
+ writel(slv->clear_sr, &slv->spi_reg->sssr);
+ writel(0, &slv->spi_reg->ssto);
+ writel(0, &slv->spi_reg->sstcr);
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ if (!cs_continuous)
+ spi_cs_deactivate(slave);
+#endif
+ }
+ return ret;
+}
+#ifdef CONFIG_PXA_DMA
+static int spi_dma_read(void *din, unsigned int len)
+{
+ int i, init = 0;
+ if (!init) {
+ dmac_map_device_to_channel(DMAC_SSP2_RX,
+ SSP_RX_CHANNEL);
+ dmac_map_device_to_channel(DMAC_SSP2_TX,
+ SSP_TX_CHANNEL);
+
+ dmac_user_aligment(SSP_RX_CHANNEL);
+ dmac_user_aligment(SSP_TX_CHANNEL);
+ init = 1;
+ }
+
+ pxa_dma_read((unsigned int)din, SSP_SSDR, len, SSP_RX_CHANNEL);
+ pxa_dma_write(SSP_SSDR, (unsigned int)dummy, len, SSP_TX_CHANNEL);
+
+ dmac_start_transfer(SSP_RX_CHANNEL);
+ dmac_start_transfer(SSP_TX_CHANNEL);
+
+ do {
+ for (i = 0; i < 500; i++)
+ nop();
+ if (dmac_read_dcsr(SSP_TX_CHANNEL) & DCSR_STOPSTATE)
+ break;
+
+ } while (1);
+
+ do {
+ if (dmac_read_dcsr(SSP_RX_CHANNEL) & DCSR_STOPSTATE)
+ break;
+ } while (1);
+
+
+ for (i = 0; i < len; i += 4)
+ swab32s(din+i);
+
+ if (!(dmac_read_dcsr(SSP_TX_CHANNEL) & DCSR_STOPSTATE)) {
+ printf("Error:DMA write operate timeout.\n");
+ return 1;
+ }
+ if (!(dmac_read_dcsr(SSP_RX_CHANNEL) & DCSR_STOPSTATE)) {
+ printf("Error:DMA read operate timeout.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int spi_dma_write(void *dout, unsigned int len)
+{
+ int i, init = 0;
+
+ for (i = 0; i < len; i += 4)
+ swab32s(dout+i);
+
+ flush_dcache_all();
+ if (!init) {
+ dmac_map_device_to_channel(DMAC_SSP2_TX,
+ SSP_TX_CHANNEL);
+
+ dmac_user_aligment(SSP_TX_CHANNEL);
+ init = 1;
+ }
+
+ pxa_dma_write(SSP_SSDR, (unsigned int)dout, len, SSP_TX_CHANNEL);
+ dmac_start_transfer(SSP_TX_CHANNEL);
+
+ do {
+ for (i = 0; i < 500; i++)
+ nop();
+ if (dmac_read_dcsr(SSP_TX_CHANNEL) & DCSR_STOPSTATE)
+ break;
+ } while (1);
+
+ if (!(dmac_read_dcsr(SSP_TX_CHANNEL) & DCSR_STOPSTATE)) {
+ printf("Error:DMA write operate timeout.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int spi_dma_xfer(struct spi_slave *slave, const u8 *cmd, size_t cmd_len,
+ const void *dout, const void *din, size_t data_len)
+{
+ struct asr_spi_slave *slv = to_asr_spi_slave(slave);
+ int ret = 0;
+
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ spi_cs_activate(slave);
+#endif
+
+ if (cmd) {
+ cs_continuous = 1;
+ spi_xfer(slave, cmd_len*8, cmd, NULL,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+ }
+
+ if (dout || din) {
+ writel(TIMEOUT_DEF, &slv->spi_reg->ssto);
+ writel(SSFCR_RXTRESH(0x5) | SSFCR_TSRE | SSFCR_RSRE |
+ SSFCR_TXTRESH(0x4), &slv->spi_reg->ssfcr);
+
+ writel(SSTCR_TTELP | SSTCR_TTE | SSTCR_SSE |SSTCR_TRAIL |
+ SSTCR_DSS(32), &slv->spi_reg->sstcr);
+ }
+
+ if (dout)
+ ret = spi_dma_write((void *)dout, data_len);
+ if (din)
+ ret = spi_dma_read((void *)din, data_len);
+
+ while ( (readl(&slv->spi_reg->sssr) & 0xFC1) != 0x40 )
+ ;
+ if (dout || din) {
+ writel(slv->clear_sr, &slv->spi_reg->sssr);
+ clrbits_le32(&slv->spi_reg->ssier, slv->ier);
+ writel(0, &slv->spi_reg->sstcr);
+#ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS
+ spi_cs_deactivate(slave);
+#endif
+ }
+ cs_continuous = 0;
+ return ret;
+}
+#endif