| /* |
| * Driver for simulating ssp as spi device for pxa |
| * |
| * Copyright (c) 2009 Marvell Inc. |
| * Lei Wen <leiwen@marvell.com> |
| * |
| * 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/pxa2xx_spi.h> |
| #include <asm/gpio.h> |
| #include <asm/arch/pxa_dma.h> |
| #include <asm/arch/cpu.h> |
| |
| #define to_pxa_spi_slave(s) container_of(s, struct pxa_spi_slave, slave) |
| |
| int cs_continuous; |
| |
| #ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS |
| void spi_cs_activate(struct spi_slave *slave) |
| { |
| struct pxa_spi_slave *pss = to_pxa_spi_slave(slave); |
| gpio_set_value(slave->cs, pss->gpio_cs_inverted); |
| } |
| |
| void spi_cs_deactivate(struct spi_slave *slave) |
| { |
| struct pxa_spi_slave *pss = to_pxa_spi_slave(slave); |
| gpio_set_value(slave->cs, !pss->gpio_cs_inverted); |
| } |
| #endif |
| |
| static int spi_pxa_write(struct pxa_spi_slave *pss, unsigned int bitlen) |
| { |
| int wait_timeout = SSP_FLUSH_NUM; |
| int bytes = bitlen / pss->slave.wordlen; |
| |
| while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_TNF)) |
| ; |
| if (!wait_timeout) { |
| debug("%s: timeout error\n", __func__); |
| return -1; |
| } |
| |
| while (bytes--) { |
| if (pss->tx != NULL) { |
| if (pss->slave.wordlen > 16) { |
| writel(*(u32 *)pss->tx, &pss->spi_reg->ssdr); |
| pss->tx += 4; |
| } else if (pss->slave.wordlen > 8) { |
| writel(*(u16 *)pss->tx, &pss->spi_reg->ssdr); |
| pss->tx += 2; |
| } else { |
| writel(*(u8 *)pss->tx, &pss->spi_reg->ssdr); |
| ++pss->tx; |
| } |
| } else |
| writel(0, &pss->spi_reg->ssdr); |
| |
| while ((readl(&pss->spi_reg->sssr)&0xF10) && wait_timeout--) { |
| nop(); |
| nop(); |
| } |
| if (!wait_timeout) { |
| debug("%s: timeout error\n", __func__); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int spi_pxa_read(struct pxa_spi_slave *pss, unsigned int bitlen) |
| { |
| int wait_timeout = SSP_FLUSH_NUM; |
| int bytes = bitlen / pss->slave.wordlen; |
| |
| while (--wait_timeout && !(readl(&pss->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, &pss->spi_reg->ssdr); |
| while ((readl(&pss->spi_reg->sssr)&0xF10) && wait_timeout--) { |
| nop(); |
| nop(); |
| } |
| if (!wait_timeout) { |
| printf("%s: timeout error, sssr:0x%x. byte:0x%x.\n", |
| __func__, readl(&pss->spi_reg->sssr), bytes); |
| return -1; |
| } |
| |
| wait_timeout = SSP_FLUSH_NUM; |
| while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_RNE)) |
| ; |
| if (!wait_timeout) { |
| printf("%s: timeout error sssr:0x%x.\n", __func__, |
| readl(&pss->spi_reg->sssr)); |
| return -1; |
| } |
| |
| if (pss->rx != NULL) { |
| if (pss->slave.wordlen > 16) { |
| *(u32 *)pss->rx = readl(&pss->spi_reg->ssdr); |
| pss->rx += 4; |
| } else if (pss->slave.wordlen > 8) { |
| *(u16 *)pss->rx = readl(&pss->spi_reg->ssdr); |
| pss->rx += 2; |
| } else { |
| *(u8 *)pss->rx = readl(&pss->spi_reg->ssdr); |
| ++pss->rx; |
| } |
| } else |
| readl(&pss->spi_reg->ssdr); |
| } |
| |
| return 0; |
| } |
| |
| static int spi_pxa_flush(struct pxa_spi_slave *pss) |
| { |
| unsigned long limit = SSP_FLUSH_NUM; |
| |
| do { |
| while (readl(&pss->spi_reg->sssr) & SSSR_RNE) |
| readl(&pss->spi_reg->ssdr); |
| } while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--); |
| |
| writel(SSSR_ROR, &pss->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 pxa_spi_slave *pss; |
| |
| pss = spi_alloc_slave(struct pxa_spi_slave, bus, cs); |
| if (!pss) |
| return NULL; |
| |
| pss->slave.bus = bus; |
| #ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS |
| pss->slave.cs = cs; |
| #endif |
| pss->spi_reg = (struct ssp_reg *)CONFIG_SYS_SSP_BASE; |
| #ifdef CONFIG_SPI_52M_ENABLE |
| pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) |
| | SSCR0_SSE | SSCR0_52MM; |
| #else |
| pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) |
| | SSCR0_SSE; |
| #endif |
| |
| pss->cr1 = SSCR1_TTE | SSCR1_TTELP | |
| (SSCR1_RXTRESH(RX_THRESH_DEF) & SSCR1_RFT) | |
| (SSCR1_TXTRESH(TX_THRESH_DEF) & SSCR1_TFT); |
| |
| pss->cr1 &= ~(SSCR1_SPO | SSCR1_SPH); |
| pss->cr1 |= (((mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) |
| | (((mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); |
| |
| pss->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; |
| pss->clear_sr = SSSR_ROR | SSSR_TINT; |
| |
| if (cs) { |
| #ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS |
| gpio_direction_output(cs, !pss->gpio_cs_inverted); |
| pss->gpio_cs_inverted = mode & SPI_CS_HIGH; |
| gpio_set_value(cs, !pss->gpio_cs_inverted); |
| #endif |
| } |
| cs_continuous = 0; |
| |
| return &pss->slave; |
| } |
| |
| void spi_free_slave(struct spi_slave *slave) |
| { |
| struct pxa_spi_slave *pss = to_pxa_spi_slave(slave); |
| free(pss); |
| } |
| #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 + SSCR0); |
| writel(SSCR1_RXTRESH(RX_THRESH_DEF) | |
| SSCR1_TXTRESH(TX_THRESH_DEF), CONFIG_SYS_SSP_BASE + SSCR1); |
| writel(SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) |
| , CONFIG_SYS_SSP_BASE + SSCR0); |
| writel(0, CONFIG_SYS_SSP_BASE + SSTO); |
| writel(0, CONFIG_SYS_SSP_BASE + SSPSP); |
| |
| __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 pxa_spi_slave *pss = to_pxa_spi_slave(slave); |
| slave->wordlen = 8; /* set default wordlen */ |
| |
| if (spi_pxa_flush(pss) == 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 pxa_spi_slave *pss = to_pxa_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; |
| |
| pss->rx = din; |
| pss->tx = dout; |
| |
| writel(0, &pss->spi_reg->sscr0); |
| pss->cr0 &= ~(0xf); |
| if (slave->wordlen > 16) |
| pss->cr0 |= SSCR0_EDSS | (slave->wordlen - 1); |
| else |
| pss->cr0 |= 0xc00000 | (slave->wordlen - 1); |
| writel(pss->cr1, &pss->spi_reg->sscr1); |
| writel(TIMEOUT_DEF, &pss->spi_reg->ssto); |
| writel(pss->cr0, &pss->spi_reg->sscr0); |
| |
| 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(pss, bitlen - slave->wordlen); |
| if(ret) |
| return -1; |
| |
| ret = spi_pxa_read(pss, slave->wordlen); |
| if(ret) |
| return -1; |
| } else if (dout != NULL) { |
| ret = spi_pxa_write(pss, bitlen); |
| if(ret) |
| return -1; |
| } else if (din != NULL) { |
| ret = spi_pxa_read(pss, bitlen); |
| if(ret) |
| return -1; |
| } |
| |
| if (flags & SPI_XFER_END) { |
| writel(pss->clear_sr, &pss->spi_reg->sssr); |
| writel(0, &pss->spi_reg->ssto); |
| writel(0, &pss->spi_reg->sscr0); |
| #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) { |
| if (cpu_is_pxa1826_z3() || cpu_is_pxa1826_a0()) { |
| dmac_map_device_to_channel(DMAC_SSP2_RX, |
| SSP_RX_CHANNEL); |
| dmac_map_device_to_channel(DMAC_SSP2_TX, |
| SSP_TX_CHANNEL); |
| } else { |
| dmac_map_device_to_channel(DMAC_SSP2_RX_Z12, |
| SSP_RX_CHANNEL); |
| dmac_map_device_to_channel(DMAC_SSP2_TX_Z12, |
| 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) { |
| if (cpu_is_pxa1826_z3() || cpu_is_pxa1826_a0()) |
| dmac_map_device_to_channel(DMAC_SSP2_TX, |
| SSP_TX_CHANNEL); |
| else |
| dmac_map_device_to_channel(DMAC_SSP2_TX_Z12, |
| 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 pxa_spi_slave *pss = to_pxa_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, &pss->spi_reg->ssto); |
| #ifdef CONFIG_SPI_52M_ENABLE |
| writel(SSCR1_TTELP | SSCR1_TTE | SSCR1_RXTRESH(0x3) |
| | SSCR1_TRAIL | SSCR1_TSRE | SSCR1_RSRE | |
| SSCR1_TXTRESH(0x4), &pss->spi_reg->sscr1); |
| writel(SSCR0_52MM | SSCR0_SSE | SSCR0_EDSS | |
| SSCR0_DATASIZE(0x10), &pss->spi_reg->sscr0); |
| #else |
| writel(SSCR1_TTELP | SSCR1_TTE | SSCR1_RXTRESH(0x9) |
| | SSCR1_TRAIL | SSCR1_TSRE | SSCR1_RSRE | |
| SSCR1_TXTRESH(0x4), &pss->spi_reg->sscr1); |
| |
| writel(SSCR0_SSE | SSCR0_EDSS | |
| SSCR0_DATASIZE(0x10), &pss->spi_reg->sscr0); |
| #endif |
| } |
| |
| if (dout) |
| ret = spi_dma_write((void *)dout, data_len); |
| if (din) |
| ret = spi_dma_read((void *)din, data_len); |
| |
| while (readl(&pss->spi_reg->sssr) & 0xF10) |
| ; |
| if (dout || din) { |
| writel(pss->clear_sr, &pss->spi_reg->sssr); |
| clrbits_le32(&pss->spi_reg->sscr1, pss->int_cr1); |
| writel(0, &pss->spi_reg->sscr0); |
| #ifndef CONFIG_PXA_SPI_ENABLE_AUTO_CS |
| spi_cs_deactivate(slave); |
| #endif |
| } |
| cs_continuous = 0; |
| return ret; |
| } |
| #endif |