/* * 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 */ #include <common.h> #include <spi.h> #include <malloc.h> #include <asm/io.h> #include <asm/arch/clk.h> #include <asm/arch/memory-map.h> #include "atmel_spi.h" 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 (cs > 3 || !spi_cs_is_valid(bus, cs)) return NULL; switch (bus) { case 0: regs = (void *)SPI0_BASE; break; #ifdef SPI1_BASE case 1: regs = (void *)SPI1_BASE; break; #endif #ifdef SPI2_BASE case 2: regs = (void *)SPI2_BASE; break; #endif #ifdef SPI3_BASE case 3: regs = (void *)SPI3_BASE; 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 = malloc(sizeof(struct atmel_spi_slave)); if (!as) return NULL; as->slave.bus = bus; as->slave.cs = cs; as->regs = regs; as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); 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; int ret; u32 status; const u8 *txp = dout; u8 *rxp = din; u8 value; ret = 0; 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); 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; }