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

    xiaoxiao2021-04-15  41

    转载至: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,unsigned int len, 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,unsigned int len, 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, len, dout, flags); #ifndef CONFIG_SPI_HALF_DUPLEX else returndavinci_spi_read_write(slave, len, din, dout, 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, unsigned int bitlen, 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

    转载请注明原文地址: https://ju.6miu.com/read-670847.html

    最新回复(0)