X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fspi%2Fdavinci_spi.c;h=e3fb3212aa5c2073dedb72dc73f6648d74829568;hb=a891601ce51edbafa1a2750c96a618e4fcbca1c2;hp=60ba007aaabf2c83f902ec6e244337a0c98f0fde;hpb=93910edb595a88d394da3eb2cf5148096155dfe9;p=u-boot diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 60ba007aaa..e3fb3212aa 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -6,23 +6,7 @@ * * Copyright (C) 2007 Atmel Corporation * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include #include @@ -44,12 +28,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, if (!spi_cs_is_valid(bus, cs)) return NULL; - ds = malloc(sizeof(*ds)); + ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs); if (!ds) return NULL; - ds->slave.bus = bus; - ds->slave.cs = cs; ds->regs = (struct davinci_spi_regs *)CONFIG_SYS_SPI_BASE; ds->freq = max_hz; @@ -66,7 +48,7 @@ void spi_free_slave(struct spi_slave *slave) int spi_claim_bus(struct spi_slave *slave) { struct davinci_spi_slave *ds = to_davinci_spi(slave); - unsigned int scalar, data1_reg_val = 0; + unsigned int scalar; /* Enable the SPI hardware */ writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); @@ -93,11 +75,6 @@ int spi_claim_bus(struct spi_slave *slave) writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | (1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0); - /* hold cs active at end of transfer until explicitly de-asserted */ - data1_reg_val = (1 << SPIDAT1_CSHOLD_SHIFT) | - (slave->cs << SPIDAT1_CSNR_SHIFT); - writel(data1_reg_val, &ds->regs->dat1); - /* * Including a minor delay. No science here. Should be good even with * no delay @@ -126,16 +103,125 @@ void spi_release_bus(struct spi_slave *slave) writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) +/* + * 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; +} + +static int davinci_spi_read(struct spi_slave *slave, unsigned int len, + u8 *rxp, unsigned long flags) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int 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); + + return 0; +} + +static int 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); + unsigned int 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); + + return 0; +} + +#ifndef CONFIG_SPI_HALF_DUPLEX +static int 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); - unsigned int len, data1_reg_val = readl(&ds->regs->dat1); - int ret, i; - const u8 *txp = dout; /* dout can be NULL for read operation */ - u8 *rxp = din; /* din can be NULL for write operation */ + unsigned int 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++); - ret = 0; + /* 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); + + return 0; +} +#endif + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + unsigned int len; if (bitlen == 0) /* Finish any previously submitted transfers */ @@ -155,53 +241,23 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, len = bitlen / 8; - /* do an empty read to clear the current contents */ - 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++; - } - - /* - * 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) | - (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); - } - } - return 0; + if (!dout) + return davinci_spi_read(slave, len, din, flags); + else if (!din) + return davinci_spi_write(slave, len, dout, flags); +#ifndef CONFIG_SPI_HALF_DUPLEX + else + return davinci_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) { - writel(data1_reg_val & - ~(1 << SPIDAT1_CSHOLD_SHIFT), &ds->regs->dat1); + u8 dummy = 0; + davinci_spi_write(slave, 1, &dummy, flags); } return 0; }