From patchwork Sat Jan 8 23:56:03 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Chou X-Patchwork-Id: 77993 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 93957B70DF for ; Sun, 9 Jan 2011 10:56:57 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6ABEB280E9; Sun, 9 Jan 2011 00:56:54 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6sDOaA8LQC7S; Sun, 9 Jan 2011 00:56:54 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8C9F4280FC; Sun, 9 Jan 2011 00:56:52 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 43503280FC for ; Sun, 9 Jan 2011 00:56:47 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id K5ovEfJy-jIU for ; Sun, 9 Jan 2011 00:56:35 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from www.wytron.com.tw (www.wytron.com.tw [211.75.82.101]) by theia.denx.de (Postfix) with ESMTPS id 18F5E280E9 for ; Sun, 9 Jan 2011 00:56:33 +0100 (CET) Received: from c5 ([192.168.1.250] helo=darkstar.wytron.com.tw) by www.wytron.com.tw with esmtp (Exim 4.69) (envelope-from ) id 1Pbidz-0002Tk-PN; Sun, 09 Jan 2011 07:56:24 +0800 From: Thomas Chou To: Scott McNutt Date: Sun, 9 Jan 2011 07:56:03 +0800 Message-Id: <1294530963-8606-1-git-send-email-thomas@wytron.com.tw> X-Mailer: git-send-email 1.7.3.4 X-SA-Exim-Connect-IP: 192.168.1.250 X-SA-Exim-Mail-From: thomas@wytron.com.tw X-SA-Exim-Scanned: No (on www.wytron.com.tw); SAEximRunCond expanded to false Cc: u-boot@lists.denx.de, nios2-dev@sopc.et.ntust.edu.tw Subject: [U-Boot] [PATCH] spi: add new driver for OpenCores tiny_spi X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch adds support for OpenCores tiny_spi. http://opencores.org/project,tiny_spi Signed-off-by: Thomas Chou --- drivers/spi/Makefile | 1 + drivers/spi/oc_tiny_spi.c | 241 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/oc_tiny_spi.c diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e34a124..8ad1d7f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -35,6 +35,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o +COBJS-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c new file mode 100644 index 0000000..c234d90 --- /dev/null +++ b/drivers/spi/oc_tiny_spi.c @@ -0,0 +1,241 @@ +/* + * Opencore tiny_spi driver + * + * http://opencores.org/project,tiny_spi + * + * based on bfin_spi.c + * Copyright (c) 2005-2008 Analog Devices Inc. + * Copyright (C) 2010 Thomas Chou + * + * Licensed under the GPL-2 or later. + */ +#include +#include +#include +#include +#include +#define TINY_SPI_RXDATA 0 +#define TINY_SPI_TXDATA 4 +#define TINY_SPI_STATUS 8 +#define TINY_SPI_CONTROL 12 +#define TINY_SPI_BAUD 16 + +#define TINY_SPI_STATUS_TXE 0x1 +#define TINY_SPI_STATUS_TXR 0x2 + +struct tiny_spi_host { + ulong base; + uint freq; + uint baudwidth; +}; +static struct tiny_spi_host tiny_spi_host_list[] = CONFIG_SYS_TINY_SPI_LIST; + +struct tiny_spi_slave { + struct spi_slave slave; + struct tiny_spi_host *host; + uint mode; + uint baud; + uint flg; +}; +#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave) + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs); +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + unsigned int cs = slave->cs; + gpio_set_value(cs, tiny_spi->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + unsigned int cs = slave->cs; + gpio_set_value(cs, !tiny_spi->flg); + debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); +} + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + struct tiny_spi_host *host = tiny_spi->host; + tiny_spi->baud = DIV_ROUND_UP(host->freq, hz * 2) - 1; + if (tiny_spi->baud > (1 << host->baudwidth) - 1) + tiny_spi->baud = (1 << host->baudwidth) - 1; + debug("%s: speed %u actual %u\n", __func__, hz, + host->freq / ((tiny_spi->baud + 1) * 2)); +} + +void spi_init(void) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int hz, unsigned int mode) +{ + struct tiny_spi_slave *tiny_spi; + + if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi")) + return NULL; + + tiny_spi = malloc(sizeof(*tiny_spi)); + if (!tiny_spi) + return NULL; + memset(tiny_spi, 0, sizeof(*tiny_spi)); + + tiny_spi->slave.bus = bus; + tiny_spi->slave.cs = cs; + tiny_spi->host = &tiny_spi_host_list[bus]; + tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA); + tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0; + spi_set_speed(&tiny_spi->slave, hz); + + debug("%s: bus:%i cs:%i base:%lx\n", __func__, + bus, cs, tiny_spi->host->base); + return &tiny_spi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + gpio_free(slave->cs); + free(tiny_spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave); + struct tiny_spi_host *host = tiny_spi->host; + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + gpio_direction_output(slave->cs, !tiny_spi->flg); + writel(tiny_spi->mode, host->base + TINY_SPI_CONTROL); + writel(tiny_spi->baud, host->base + TINY_SPI_BAUD); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); +} + +#ifndef CONFIG_TINY_SPI_IDLE_VAL +# define CONFIG_TINY_SPI_IDLE_VAL 0xff +#endif + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct tiny_spi_host *host = to_tiny_spi_slave(slave)->host; + const u8 *txp = dout; + u8 *rxp = din; + uint bytes = bitlen / 8; + uint i; + + debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + slave->bus, slave->cs, bitlen, bytes, flags); + if (bitlen == 0) + goto done; + + /* assume to do 8 bits transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* we need to tighten the transfer loop */ + if (txp && rxp) { + writeb(*txp++, host->base + TINY_SPI_TXDATA); + if (bytes > 1) { + writeb(*txp++, host->base + TINY_SPI_TXDATA); + for (i = 2; i < bytes; i++) { + u8 rx, tx = *txp++; + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(host->base + TINY_SPI_TXDATA); + writeb(tx, host->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(host->base + TINY_SPI_TXDATA); + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(host->base + TINY_SPI_RXDATA); + } else if (rxp) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + host->base + TINY_SPI_TXDATA); + for (i = 2; i < bytes; i++) { + u8 rx; + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(host->base + TINY_SPI_TXDATA); + writeb(CONFIG_TINY_SPI_IDLE_VAL, + host->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(host->base + TINY_SPI_TXDATA); + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(host->base + TINY_SPI_RXDATA); + } else if (txp) { + writeb(*txp++, host->base + TINY_SPI_TXDATA); + if (bytes > 1) { + writeb(*txp++, host->base + TINY_SPI_TXDATA); + for (i = 2; i < bytes; i++) { + u8 tx = *txp++; + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + writeb(tx, host->base + TINY_SPI_TXDATA); + } + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + ; + } else { + writeb(CONFIG_TINY_SPI_IDLE_VAL, host->base + TINY_SPI_TXDATA); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + host->base + TINY_SPI_TXDATA); + for (i = 2; i < bytes; i++) { + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + ; + writeb(CONFIG_TINY_SPI_IDLE_VAL, + host->base + TINY_SPI_TXDATA); + } + } + while (!(readb(host->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + ; + } + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +}