X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fspi%2Fdavinci_spi.c;h=0ec5b9d85920d4aca224fc91925925d5ca25703a;hb=eef67029d664d75766f0b8b88d065a4c3b1c8335;hp=08f837b66f61ad753aba9816df8cabe1b6838ac9;hpb=23911740486c59851df57521c49bfd81ce1865ec;p=u-boot diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 08f837b66f..0ec5b9d859 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,13 +28,31 @@ 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; + + 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 */ + return NULL; + } + ds->freq = max_hz; return &ds->slave; @@ -66,7 +68,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); @@ -77,7 +79,7 @@ int spi_claim_bus(struct spi_slave *slave) writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1); /* CS, CLK, SIMO and SOMI are functional pins */ - writel((SPIPC0_EN0FUN_MASK | SPIPC0_CLKFUN_MASK | + writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0); /* setup format */ @@ -93,11 +95,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 @@ -113,8 +110,7 @@ int spi_claim_bus(struct spi_slave *slave) writel(0, &ds->regs->lvl); /* enable SPI */ - writel((readl(&ds->regs->gcr1) | - SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); + writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); return 0; } @@ -127,14 +123,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); - unsigned int i_cnt = 0, o_cnt = 0, buf_reg_val; - 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++); + + /* 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 */ @@ -154,70 +261,53 @@ 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 */ - 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; - /* increment read words count */ - i_cnt++; - } - - /* - * 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++; - /* - * 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); - } - /* increment written words count */ - o_cnt++; - } - } - 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; } int spi_cs_is_valid(unsigned int bus, unsigned int cs) { - return bus == 0 && cs == 0; + 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; } void spi_cs_activate(struct spi_slave *slave)