diff options
author | Tom Rini <trini@konsulko.com> | 2018-04-07 09:15:50 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-04-07 09:19:00 -0400 |
commit | e80fa2c2c0870f7c17e233ecc07552e1082c1513 (patch) | |
tree | 8274e5382c7b6240899ded2b6c0b3c9440c7d839 /drivers/spi | |
parent | 5270df2836761909864d9b548bf4b7f7f3a51484 (diff) |
Revert "spi: atmel: Drop non-dm code"
As we aren't quite able to convert some platforms with a very small size
limit in SPL yet, we need to revert this for now.
This reverts commit 7b0947787358c6b277431d6b76ce043d8bec641d.
Signed-off-by: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/atmel_spi.c | 201 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.h | 16 |
2 files changed, 217 insertions, 0 deletions
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 122c6d107d1..3cdfd366ab8 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -26,6 +26,206 @@ DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_DM_SPI + +static int spi_has_wdrbt(struct atmel_spi_slave *slave) +{ + unsigned int ver; + + ver = spi_readl(slave, VERSION); + + return (ATMEL_SPI_VERSION_REV(ver) >= 0x210); +} + +void spi_init() +{ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct atmel_spi_slave *as; + unsigned int scbr; + u32 csrx; + void *regs; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + switch (bus) { + case 0: + regs = (void *)ATMEL_BASE_SPI0; + break; +#ifdef ATMEL_BASE_SPI1 + case 1: + regs = (void *)ATMEL_BASE_SPI1; + break; +#endif +#ifdef ATMEL_BASE_SPI2 + case 2: + regs = (void *)ATMEL_BASE_SPI2; + break; +#endif +#ifdef ATMEL_BASE_SPI3 + case 3: + regs = (void *)ATMEL_BASE_SPI3; + break; +#endif + default: + return NULL; + } + + + scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; + if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) + /* Too low max SCK rate */ + return NULL; + if (scbr < 1) + scbr = 1; + + csrx = ATMEL_SPI_CSRx_SCBR(scbr); + csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); + if (!(mode & SPI_CPHA)) + csrx |= ATMEL_SPI_CSRx_NCPHA; + if (mode & SPI_CPOL) + csrx |= ATMEL_SPI_CSRx_CPOL; + + as = spi_alloc_slave(struct atmel_spi_slave, bus, cs); + if (!as) + return NULL; + + as->regs = regs; + as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS + | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); + if (spi_has_wdrbt(as)) + as->mr |= ATMEL_SPI_MR_WDRBT; + + spi_writel(as, CSR(cs), csrx); + + return &as->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + free(as); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Enable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); + + /* + * Select the slave. This should set SCK to the correct + * initial state, etc. + */ + spi_writel(as, MR, as->mr); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Disable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + unsigned int len_tx; + unsigned int len_rx; + unsigned int len; + u32 status; + const u8 *txp = dout; + u8 *rxp = din; + u8 value; + + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + /* + * TODO: The controller can do non-multiple-of-8 bit + * transfers, but this driver currently doesn't support it. + * + * It's also not clear how such transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + /* + * The controller can do automatic CS control, but it is + * somewhat quirky, and it doesn't really buy us much anyway + * in the context of U-Boot. + */ + if (flags & SPI_XFER_BEGIN) { + spi_cs_activate(slave); + /* + * sometimes the RDR is not empty when we get here, + * in theory that should not happen, but it DOES happen. + * Read it here to be on the safe side. + * That also clears the OVRES flag. Required if the + * following loop exits due to OVRES! + */ + spi_readl(as, RDR); + } + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + status = spi_readl(as, SR); + + if (status & ATMEL_SPI_SR_OVRES) + return -1; + + if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + spi_writel(as, TDR, value); + len_tx++; + } + if (status & ATMEL_SPI_SR_RDRF) { + value = spi_readl(as, RDR); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + /* + * Wait until the transfer is completely done before + * we deactivate CS. + */ + do { + status = spi_readl(as, SR); + } while (!(status & ATMEL_SPI_SR_TXEMPTY)); + + spi_cs_deactivate(slave); + } + + return 0; +} + +#else + #define MAX_CS_COUNT 4 struct atmel_spi_platdata { @@ -315,3 +515,4 @@ U_BOOT_DRIVER(atmel_spi) = { .priv_auto_alloc_size = sizeof(struct atmel_spi_priv), .probe = atmel_spi_probe, }; +#endif diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h index 685eeed99e5..76b8556c98a 100644 --- a/drivers/spi/atmel_spi.h +++ b/drivers/spi/atmel_spi.h @@ -79,6 +79,22 @@ #define ATMEL_SPI_BITS_16 8 struct atmel_spi_slave { + struct spi_slave slave; void *regs; u32 mr; }; + +static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave) +{ + return container_of(slave, struct atmel_spi_slave, slave); +} + +/* Register access macros */ +#define spi_readl(as, reg) \ + readl(as->regs + ATMEL_SPI_##reg) +#define spi_writel(as, reg, value) \ + writel(value, as->regs + ATMEL_SPI_##reg) + +#if !defined(CONFIG_SYS_SPI_WRITE_TOUT) +#define CONFIG_SYS_SPI_WRITE_TOUT (5 * CONFIG_SYS_HZ) +#endif |