转载至:https://github.com/TheBlueMatt/u-boot/blob/master/drivers/spi/davinci_spi.c
google: davinci u-boot davinci_spi.c
/* |
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ |
* Driver for SPI controller on DaVinci. Based on atmel_spi.c |
* Copyright (C) 2007 Atmel Corporation |
* SPDX-License-Identifier: GPL-2.0+ |
#include<asm/arch/hardware.h>
|
#defineSPIGCR0_SPIENA_MASK 0x1 |
#defineSPIGCR0_SPIRST_MASK 0x0 |
#defineSPIGCR1_CLKMOD_MASK BIT(1) |
#defineSPIGCR1_MASTER_MASK BIT(0) |
#defineSPIGCR1_SPIENA_MASK BIT(24) |
#defineSPIPC0_DIFUN_MASK BIT(11)/* SIMO */
|
#defineSPIPC0_DOFUN_MASK BIT(10)/* SOMI */
|
#defineSPIPC0_CLKFUN_MASK BIT(9)/* CLK */
|
#defineSPIPC0_EN0FUN_MASK BIT(0) |
#defineSPIFMT_SHIFTDIR_SHIFT 20
|
#defineSPIFMT_POLARITY_SHIFT 17
|
#defineSPIFMT_PHASE_SHIFT 16
|
#defineSPIFMT_PRESCALE_SHIFT 8
|
#defineSPIDAT1_CSHOLD_SHIFT 28
|
#defineSPIDAT1_CSNR_SHIFT 16
|
#defineSPI_C2TDELAY_SHIFT 24
|
#defineSPI_T2CDELAY_SHIFT 16
|
#defineSPIBUF_RXEMPTY_MASK BIT(31) |
#defineSPIBUF_TXFULL_MASK BIT(29) |
#defineSPIDEF_CSDEF0_MASK BIT(0) |
#defineSPI0_BASE CONFIG_SYS_SPI_BASE |
* Define default SPI0_NUM_CS as 1 for existing platforms that uses this |
* driver. Platform can configure number of CS using CONFIG_SYS_SPI0_NUM_CS |
* if more than one CS is supported and by defining CONFIG_SYS_SPI0. |
#defineSPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS |
* define CONFIG_SYS_SPI1 when platform has spi-1 device (bus #1) and |
* CONFIG_SYS_SPI1_NUM_CS defines number of CS on this bus |
#defineSPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS |
#defineSPI1_BASE CONFIG_SYS_SPI1_BASE |
* define CONFIG_SYS_SPI2 when platform has spi-2 device (bus #2) and |
* CONFIG_SYS_SPI2_NUM_CS defines number of CS on this bus |
#defineSPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS |
#defineSPI2_BASE CONFIG_SYS_SPI2_BASE |
/* davinci spi register set*/ |
struct davinci_spi_regs { |
dv_reg intvec0; /* 0x60 */
|
dv_reg intvec1; /* 0x64 */
|
struct davinci_spi_slave { |
struct davinci_spi_regs *regs; |
staticinline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) |
returncontainer_of(slave,struct davinci_spi_slave,slave); |
* This functions needs to act like a macro to avoid pipeline reloads in the |
* loops below. Use always_inline. This gains us about 160KiB/s and the bloat |
* appears to be zero bytes (da830). |
__attribute__((always_inline)) |
static inline u32 davinci_spi_xfer_data(struct davinci_spi_slave *ds,u32 data) |
writel(data,&ds->regs->dat1); |
/* wait for the data to clock in/out*/ |
while ((buf_reg_val =readl(&ds->regs->buf)) & SPIBUF_RXEMPTY_MASK) |
staticint davinci_spi_read(struct spi_slave *slave,unsigned int len, |
u8 *rxp, unsigned long flags) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
unsignedint data1_reg_val; |
/* enable CS hold,CS[n] and clear the data bits*/ |
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | |
(slave->cs << SPIDAT1_CSNR_SHIFT)); |
/* wait till TXFULL is deasserted*/ |
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) |
/* preload the TX buffer to avoid clock starvation*/ |
writel(data1_reg_val,&ds->regs->dat1); |
/* keep reading 1 byte until only 1 byte left*/ |
*rxp++ = davinci_spi_xfer_data(ds,data1_reg_val); |
/* clear CS hold when we reach the end*/ |
if (flags & SPI_XFER_END) |
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); |
*rxp = davinci_spi_xfer_data(ds,data1_reg_val); |
staticint davinci_spi_write(struct spi_slave *slave, |
const u8 *txp,unsigned long flags) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
unsignedint data1_reg_val; |
/* enable CS hold and clear the data bits*/ |
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | |
(slave->cs << SPIDAT1_CSNR_SHIFT)); |
/* wait till TXFULL is deasserted*/ |
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) |
/* preload the TX buffer to avoid clock starvation*/ |
writel(data1_reg_val | *txp++,&ds->regs->dat1); |
/* keep writing 1 byte until only 1 byte left*/ |
davinci_spi_xfer_data(ds,data1_reg_val | *txp++); |
/* clear CS hold when we reach the end*/ |
if (flags & SPI_XFER_END) |
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); |
davinci_spi_xfer_data(ds,data1_reg_val | *txp); |
#ifndef CONFIG_SPI_HALF_DUPLEX |
staticint davinci_spi_read_write(struct spi_slave *slave, |
u8 *rxp, const u8 *txp,unsigned long flags) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
unsignedint data1_reg_val; |
/* enable CS hold and clear the data bits*/ |
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) | |
(slave->cs << SPIDAT1_CSNR_SHIFT)); |
/* wait till TXFULL is deasserted*/ |
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK) |
/* keep reading and writing 1 byte until only 1 byte left*/ |
*rxp++ = davinci_spi_xfer_data(ds,data1_reg_val | *txp++); |
/* clear CS hold when we reach the end*/ |
if (flags & SPI_XFER_END) |
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT); |
/* read and write the last byte*/ |
*rxp = davinci_spi_xfer_data(ds,data1_reg_val | *txp); |
intspi_cs_is_valid(unsigned int bus,unsigned int cs) |
/* Invalid bus number. Do nothing*/ |
voidspi_cs_activate(struct spi_slave *slave) |
voidspi_cs_deactivate(struct spi_slave *slave) |
struct spi_slave *spi_setup_slave(unsignedint bus,unsigned int cs, |
unsignedint max_hz,unsigned int mode) |
struct davinci_spi_slave *ds; |
if (!spi_cs_is_valid(bus,cs)) |
ds = spi_alloc_slave(struct davinci_spi_slave,bus,cs); |
ds->regs = (struct davinci_spi_regs *)SPI0_BASE; |
ds->regs = (struct davinci_spi_regs *)SPI1_BASE; |
ds->regs = (struct davinci_spi_regs *)SPI2_BASE; |
default:/* Invalid bus number */
|
voidspi_free_slave(struct spi_slave *slave) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
intspi_claim_bus(struct spi_slave *slave) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
/* Enable the SPI hardware*/ |
writel(SPIGCR0_SPIRST_MASK,&ds->regs->gcr0); |
writel(SPIGCR0_SPIENA_MASK,&ds->regs->gcr0); |
/* Set master mode,powered up and not activated*/ |
writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK,&ds->regs->gcr1); |
/* CS,CLK,SIMO and SOMI are functional pins*/ |
writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK | |
SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK),&ds->regs->pc0); |
scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) -1) & 0xFF; |
* clock signal delayed by half clk cycle, |
* clock low in idle state - Mode 0, |
writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | |
(1 << SPIFMT_PHASE_SHIFT),&ds->regs->fmt0); |
* Including a minor delay. No science here. Should be good even with |
writel((50 << SPI_C2TDELAY_SHIFT) | |
(50 << SPI_T2CDELAY_SHIFT),&ds->regs->delay); |
/* default chip select register*/ |
writel(SPIDEF_CSDEF0_MASK,&ds->regs->def); |
writel(0,&ds->regs->int0); |
writel(0,&ds->regs->lvl); |
writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK),&ds->regs->gcr1); |
voidspi_release_bus(struct spi_slave *slave) |
struct davinci_spi_slave *ds =to_davinci_spi(slave); |
/* Disable the SPI hardware*/ |
writel(SPIGCR0_SPIRST_MASK,&ds->regs->gcr0); |
intspi_xfer(struct spi_slave *slave,unsigned int bitlen, |
constvoid *dout,void *din,unsigned long flags) |
/* Finish any previously submitted transfers*/ |
* It's not clear how non-8-bit-aligned transfers are supposed to be |
* represented as a stream of bytes...this is a limitation of |
* the current SPI interface - here we terminate on receiving such a |
/* Errors always terminate an ongoing transfer*/ |
returndavinci_spi_read(slave,len,din,flags); |
returndavinci_spi_write(slave,dout,flags); |
#ifndef CONFIG_SPI_HALF_DUPLEX |
returndavinci_spi_read_write(slave,flags); |
printf("SPI full duplex transaction requested with"
|
"CONFIG_SPI_HALF_DUPLEX defined.\n"); |
if (flags & SPI_XFER_END) { |
davinci_spi_write(slave,1,&dummy,flags); |
}
提速补丁:
转载至:https://lists.denx.de/pipermail/u-boot/2010-May/071609.html
Reduce the number of reads per byte transferred on the BUF register from 2 to 1 and
take advantage of the TX buffer in the SPI module. On LogicPD OMAP-L138 EVM,SPI read throughput goes up from ~0.8Mbyte/s to ~1.3Mbyte/s. Tested with a 2Mbyte image file.
Signed-off-by: Delio Brignoli <dbrignoli at audioscience.com>
---
drivers/spi/davinci_spi.c | 67 +++++++++++++++++++++++---------------------
1 files changed,35 insertions(+),32 deletions(-)
diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c
index 60ba007..ee0fc84 100644
--- a/drivers/spi/davinci_spi.c
+++ b/drivers/spi/davinci_spi.c
@@ -131,6 +131,7 @@ int spi_xfer(struct spi_slave *slave,unsigned int bitlen,{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
unsigned int len,data1_reg_val = readl(&ds->regs->dat1);
+ unsigned int i_cnt = 0,o_cnt = 0,buf_reg_val;
int ret,i;
const u8 *txp = dout; /* dout can be NULL for read operation */
u8 *rxp = din; /* din can be NULL for write operation */
@@ -159,41 +160,43 @@ int spi_xfer(struct spi_slave *slave,readl(&ds->regs->buf);
/* keep writing and reading 1 byte until done */
- for (i = 0; i < len; i++) {
- /* wait till TXFULL is asserted */
- while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK);
-
- /* write the data */
- data1_reg_val &= ~0xFFFF;
- if (txp) {
- data1_reg_val |= *txp;
- txp++;
+ while((i_cnt < len) || (o_cnt < len)) {
+ /* read RX buffer and flags */
+ buf_reg_val = readl(&ds->regs->buf);
+
+ /* if data is available */
+ if ((i_cnt < len) && (buf_reg_val & SPIBUF_RXEMPTY_MASK) == 0) {
+ /* if there is no read buffer simply ignore the read character */
+ if(rxp) {
+ *rxp = buf_reg_val & 0xFF;
+ rxp++;
+ }
+ /* increment read words count */
+ i_cnt++;
}
- /*
- * Write to DAT1 is required to keep the serial transfer going.
- * We just terminate when we reach the end.
- */
- if ((i == (len - 1)) && (flags & SPI_XFER_END)) {
- /* clear CS hold */
- writel(data1_reg_val &
- ~(1 << SPIDAT1_CSHOLD_SHIFT),&ds->regs->dat1);
- } else {
- /* enable CS hold */
- data1_reg_val |= ((1 << SPIDAT1_CSHOLD_SHIFT) |
+ /* if the tx buffer is empty and there is still data to transmit */
+ if ((o_cnt < len) && ((buf_reg_val & SPIBUF_TXFULL_MASK) == 0)) {
+ /* write the data */
+ data1_reg_val &= ~0xFFFF;
+ if(txp) {
+ data1_reg_val |= *txp;
+ txp++;
+ }
+ /* write to DAT1 is required to keep the serial transfer going */
+ /* we just terminate when we reach the end */
+ if((o_cnt == (len - 1)) && (flags & SPI_XFER_END)) {
+ /* clear CS hold */
+ writel(data1_reg_val &
+ ~(1 << SPIDAT1_CSHOLD_SHIFT),&ds->regs->dat1);
+ } else {
+ /* enable CS hold and write TX register */
+ data1_reg_val |= ((1 << SPIDAT1_CSHOLD_SHIFT) |
(slave->cs << SPIDAT1_CSNR_SHIFT));
- writel(data1_reg_val,&ds->regs->dat1);
- }
-
- /* read the data - wait for data availability */
- while (readl(&ds->regs->buf) & SPIBUF_RXEMPTY_MASK);
-
- if (rxp) {
- *rxp = readl(&ds->regs->buf) & 0xFF;
- rxp++;
- } else {
- /* simply drop the read character */
- readl(&ds->regs->buf);
+ writel(data1_reg_val,&ds->regs->dat1);
+ }
+ /* increment written words count */
+ o_cnt++;
}
}
return 0;
--
1.6.3.3