From patchwork Tue Jan 18 04:09:11 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Chou X-Patchwork-Id: 79247 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 82738B7139 for ; Tue, 18 Jan 2011 15:07:40 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 519CE28119; Tue, 18 Jan 2011 05:07:38 +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 EK-5HXj4OkxI; Tue, 18 Jan 2011 05:07:38 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3D56028105; Tue, 18 Jan 2011 05:07:35 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2873E28105 for ; Tue, 18 Jan 2011 05:07:32 +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 YVjqbatNO50Z for ; Tue, 18 Jan 2011 05:07:29 +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 05506280DF for ; Tue, 18 Jan 2011 05:07:26 +0100 (CET) Received: from [192.168.1.15] (helo=darkstar.wytron.com.tw) by www.wytron.com.tw with esmtp (Exim 4.69) (envelope-from ) id 1Pf2ql-0008E1-Dh; Tue, 18 Jan 2011 12:07:19 +0800 From: Thomas Chou To: Wolfgang Denk Date: Tue, 18 Jan 2011 12:09:11 +0800 Message-Id: <1295323751-12085-1-git-send-email-thomas@wytron.com.tw> X-Mailer: git-send-email 1.7.3.4 In-Reply-To: <20110117212322.46153D1CAD5@gemini.denx.de> References: <20110117212322.46153D1CAD5@gemini.denx.de> X-SA-Exim-Connect-IP: 192.168.1.15 X-SA-Exim-Mail-From: thomas@wytron.com.tw X-SA-Exim-Scanned: No (on www.wytron.com.tw); SAEximRunCond expanded to false Cc: Scott McNutt , u-boot@lists.denx.de, nios2-dev@sopc.et.ntust.edu.tw Subject: [U-Boot] [PATCH v3] 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 --- for u-boot v2, use const and clean up as Mike suggested. v3, use struct instead of base+offset as Wolfgang suggested. drivers/spi/Makefile | 1 + drivers/spi/oc_tiny_spi.c | 248 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 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..f379da1 --- /dev/null +++ b/drivers/spi/oc_tiny_spi.c @@ -0,0 +1,248 @@ +/* + * 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_STATUS_TXE 0x1 +#define TINY_SPI_STATUS_TXR 0x2 + +struct tiny_spi_regs { + volatile unsigned rxdata; /* Rx data reg */ + volatile unsigned txdata; /* Tx data reg */ + volatile unsigned status; /* Status reg */ + volatile unsigned control; /* Control reg */ + volatile unsigned baud; /* Baud reg */ +}; + +struct tiny_spi_host { + uint base; + uint freq; + uint baudwidth; +}; +static const struct tiny_spi_host tiny_spi_host_list[] = + CONFIG_SYS_TINY_SPI_LIST; + +struct tiny_spi_slave { + struct spi_slave slave; + const 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) + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs); +} + +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)); +} + +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); + const struct tiny_spi_host *host = tiny_spi->host; + + tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2), + (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_regs *regs = (void *)tiny_spi->host->base; + + debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + gpio_direction_output(slave->cs, !tiny_spi->flg); + writel(tiny_spi->mode, ®s->control); + writel(tiny_spi->baud, ®s->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_slave *tiny_spi = to_tiny_spi_slave(slave); + struct tiny_spi_regs *regs = (void *)tiny_spi->host->base; + 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++, ®s->txdata); + if (bytes > 1) { + writeb(*txp++, ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 rx, tx = *txp++; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(®s->txdata); + writeb(tx, ®s->txdata); + *rxp++ = rx; + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(®s->txdata); + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(®s->rxdata); + } else if (rxp) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 rx; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + rx = readb(®s->txdata); + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + *rxp++ = rx; + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + *rxp++ = readb(®s->txdata); + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + *rxp++ = readb(®s->rxdata); + } else if (txp) { + writeb(*txp++, ®s->txdata); + if (bytes > 1) { + writeb(*txp++, ®s->txdata); + for (i = 2; i < bytes; i++) { + u8 tx = *txp++; + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + writeb(tx, ®s->txdata); + } + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + } else { + writeb(CONFIG_TINY_SPI_IDLE_VAL, ®s->txdata); + if (bytes > 1) { + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + for (i = 2; i < bytes; i++) { + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXR)) + ; + writeb(CONFIG_TINY_SPI_IDLE_VAL, + ®s->txdata); + } + } + while (!(readb(®s->status) & + TINY_SPI_STATUS_TXE)) + ; + } + + done: + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + return 0; +}