u-boo davinci spi 驱动及提速补丁

转载至: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
* by Atmel Corporation
*
* Copyright (C) 2007 Atmel Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
 
#include<common.h>
#include<spi.h>
#include<malloc.h>
#include<asm/io.h>
#include<asm/arch/hardware.h>
 
/* SPIGCR0*/
#defineSPIGCR0_SPIENA_MASK 0x1
#defineSPIGCR0_SPIRST_MASK 0x0
 
/* SPIGCR0*/
#defineSPIGCR1_CLKMOD_MASK BIT(1)
#defineSPIGCR1_MASTER_MASK BIT(0)
#defineSPIGCR1_SPIENA_MASK BIT(24)
 
/* SPIPC0*/
#defineSPIPC0_DIFUN_MASK BIT(11)/* SIMO */
#defineSPIPC0_DOFUN_MASK BIT(10)/* SOMI */
#defineSPIPC0_CLKFUN_MASK BIT(9)/* CLK */
#defineSPIPC0_EN0FUN_MASK BIT(0)
 
/* SPIFMT0*/
#defineSPIFMT_SHIFTDIR_SHIFT 20
#defineSPIFMT_POLARITY_SHIFT 17
#defineSPIFMT_PHASE_SHIFT 16
#defineSPIFMT_PRESCALE_SHIFT 8
 
/* SPIDAT1*/
#defineSPIDAT1_CSHOLD_SHIFT 28
#defineSPIDAT1_CSNR_SHIFT 16
 
/* SPIDELAY*/
#defineSPI_C2TDELAY_SHIFT 24
#defineSPI_T2CDELAY_SHIFT 16
 
/* SPIBUF*/
#defineSPIBUF_RXEMPTY_MASK BIT(31)
#defineSPIBUF_TXFULL_MASK BIT(29)
 
/* SPIDEF*/
#defineSPIDEF_CSDEF0_MASK BIT(0)
 
#defineSPI0_BUS 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.
*/
#ifndef CONFIG_SYS_SPI0
#defineSPI0_NUM_CS 1
#else
#defineSPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS
#endif
 
/*
* 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
*/
#ifdef CONFIG_SYS_SPI1
#defineSPI1_BUS 1
#defineSPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS
#defineSPI1_BASE CONFIG_SYS_SPI1_BASE
#endif
 
/*
* 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
*/
#ifdef CONFIG_SYS_SPI2
#defineSPI2_BUS 2
#defineSPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS
#defineSPI2_BASE CONFIG_SYS_SPI2_BASE
#endif
 
/* davinci spi register set*/
struct davinci_spi_regs {
dv_reg gcr0; /* 0x00 */
dv_reg gcr1; /* 0x04 */
dv_reg int0; /* 0x08 */
dv_reg lvl; /* 0x0c */
dv_reg flg; /* 0x10 */
dv_reg pc0; /* 0x14 */
dv_reg pc1; /* 0x18 */
dv_reg pc2; /* 0x1c */
dv_reg pc3; /* 0x20 */
dv_reg pc4; /* 0x24 */
dv_reg pc5; /* 0x28 */
dv_reg rsvd[3];
dv_reg dat0; /* 0x38 */
dv_reg dat1; /* 0x3c */
dv_reg buf; /* 0x40 */
dv_reg emu; /* 0x44 */
dv_reg delay; /* 0x48 */
dv_reg def; /* 0x4c */
dv_reg fmt0; /* 0x50 */
dv_reg fmt1; /* 0x54 */
dv_reg fmt2; /* 0x58 */
dv_reg fmt3; /* 0x5c */
dv_reg intvec0; /* 0x60 */
dv_reg intvec1; /* 0x64 */
};
 
/* davinci spi slave*/
struct davinci_spi_slave {
struct spi_slave slave;
struct davinci_spi_regs *regs;
unsignedint freq;
};
 
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)
{
u32 buf_reg_val;
 
/* send out 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)
;
 
return buf_reg_val;
}
 
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*/
while ((len--) >1)
*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);
 
/* read the last byte*/
*rxp = davinci_spi_xfer_data(ds,data1_reg_val);
 
return0;
}
 
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*/
if (len >2) {
writel(data1_reg_val | *txp++,&ds->regs->dat1);
len--;
}
 
/* keep writing 1 byte until only 1 byte left*/
while ((len--) >1)
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);
 
/* write the last byte*/
davinci_spi_xfer_data(ds,data1_reg_val | *txp);
 
return0;
}
 
#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*/
while ((len--) >1)
*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);
 
return0;
}
#endif
 
intspi_cs_is_valid(unsigned int bus,unsigned int cs)
{
int ret =0;
 
switch (bus) {
case SPI0_BUS:
if (cs < SPI0_NUM_CS)
ret = 1;
break;
#ifdef CONFIG_SYS_SPI1
case SPI1_BUS:
if (cs < SPI1_NUM_CS)
ret = 1;
break;
#endif
#ifdef CONFIG_SYS_SPI2
case SPI2_BUS:
if (cs < SPI2_NUM_CS)
ret = 1;
break;
#endif
default:
/* Invalid bus number. Do nothing*/
break;
}
return ret;
}
 
voidspi_cs_activate(struct spi_slave *slave)
{
/* do nothing*/
}
 
voidspi_cs_deactivate(struct spi_slave *slave)
{
/* do nothing*/
}
 
voidspi_init(void)
{
/* do nothing*/
}
 
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))
returnNULL;
 
ds = spi_alloc_slave(struct davinci_spi_slave,bus,cs);
if (!ds)
returnNULL;
 
switch (bus) {
case SPI0_BUS:
ds->regs = (struct davinci_spi_regs *)SPI0_BASE;
break;
#ifdef CONFIG_SYS_SPI1
case SPI1_BUS:
ds->regs = (struct davinci_spi_regs *)SPI1_BASE;
break;
#endif
#ifdef CONFIG_SYS_SPI2
case SPI2_BUS:
ds->regs = (struct davinci_spi_regs *)SPI2_BASE;
break;
#endif
default:/* Invalid bus number */
returnNULL;
}
 
ds->freq = max_hz;
 
return &ds->slave;
}
 
voidspi_free_slave(struct spi_slave *slave)
{
struct davinci_spi_slave *ds =to_davinci_spi(slave);
 
free(ds);
}
 
intspi_claim_bus(struct spi_slave *slave)
{
struct davinci_spi_slave *ds =to_davinci_spi(slave);
unsignedint scalar;
 
/* Enable the SPI hardware*/
writel(SPIGCR0_SPIRST_MASK,&ds->regs->gcr0);
udelay(1000);
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);
 
/* setup format*/
scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) -1) & 0xFF;
 
/*
* Use following format:
* character length = 8,
* clock signal delayed by half clk cycle,
* clock low in idle state - Mode 0,
* MSB shifted out first
*/
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
* no delay
*/
writel((50 << SPI_C2TDELAY_SHIFT) |
(50 << SPI_T2CDELAY_SHIFT),&ds->regs->delay);
 
/* default chip select register*/
writel(SPIDEF_CSDEF0_MASK,&ds->regs->def);
 
/* no interrupts*/
writel(0,&ds->regs->int0);
writel(0,&ds->regs->lvl);
 
/* enable SPI*/
writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK),&ds->regs->gcr1);
 
return0;
}
 
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)
{
unsignedint len;
 
if (bitlen ==0)
/* Finish any previously submitted transfers*/
goto out;
 
/*
* 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
* transfer request.
*/
if (bitlen %8) {
/* Errors always terminate an ongoing transfer*/
flags |= SPI_XFER_END;
goto out;
}
 
len = bitlen / 8;
 
if (!dout)
returndavinci_spi_read(slave,len,din,flags);
elseif (!din)
returndavinci_spi_write(slave,dout,flags);
#ifndef CONFIG_SPI_HALF_DUPLEX
else
returndavinci_spi_read_write(slave,flags);
#else
printf("SPI full duplex transaction requested with"
"CONFIG_SPI_HALF_DUPLEX defined.\n");
flags |= SPI_XFER_END;
#endif
 
out:
if (flags & SPI_XFER_END) {
u8 dummy = 0;
davinci_spi_write(slave,1,&dummy,flags);
}
return0;

}


提速补丁:

转载至: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

相关文章

  译序:JWMediaPlayer是开源的网页使用的Flash播放器。本...
    Flash编程原理都是只能将1写为0,而不能将0写成1.所...
 上传setenvgatewayip192.168.1.1;setenvserverip192.168.1...
Error:FlashDownloadFailed-"Cortex-M3"出现一般有...
jPlayer是一个用于控制和播放mp3文件的jQuery插件。它在后台...
#ifndef__FONTUPD_H__#define__FONTUPD_H__#include"sy...