|
@@ -0,0 +1,210 @@
|
|
|
+/*
|
|
|
+ * 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;
|
|
|
+}
|