All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thomas Chou <thomas@wytron.com.tw>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v4] spi: add new driver for OpenCores tiny_spi
Date: Tue, 12 Apr 2011 13:48:47 +0800	[thread overview]
Message-ID: <1302587327-4699-1-git-send-email-thomas@wytron.com.tw> (raw)
In-Reply-To: <20110411195446.5428D14F02E@gemini.denx.de>

This patch adds support for OpenCores tiny_spi.

http://opencores.org/project,tiny_spi

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
for u-boot
v2, use const and clean up as Mike suggested.
v3, use struct instead of base+offset as Wolfgang suggested.
v4, remove volatile in regs def.

 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 d582fbb..a9fcfe7 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
 COBJS-$(CONFIG_SH_SPI) += sh_spi.o
diff --git a/drivers/spi/oc_tiny_spi.c b/drivers/spi/oc_tiny_spi.c
new file mode 100644
index 0000000..fc01fb8
--- /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 <thomas@wytron.com.tw>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/gpio.h>
+
+#define TINY_SPI_STATUS_TXE 0x1
+#define TINY_SPI_STATUS_TXR 0x2
+
+struct tiny_spi_regs {
+	unsigned rxdata;	/* Rx data reg */
+	unsigned txdata;	/* Tx data reg */
+	unsigned status;	/* Status reg */
+	unsigned control;	/* Control reg */
+	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, &regs->control);
+	writel(tiny_spi->baud, &regs->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++, &regs->txdata);
+		if (bytes > 1) {
+			writeb(*txp++, &regs->txdata);
+			for (i = 2; i < bytes; i++) {
+				u8 rx, tx = *txp++;
+				while (!(readb(&regs->status) &
+					 TINY_SPI_STATUS_TXR))
+					;
+				rx = readb(&regs->txdata);
+				writeb(tx, &regs->txdata);
+				*rxp++ = rx;
+			}
+			while (!(readb(&regs->status) &
+				 TINY_SPI_STATUS_TXR))
+				;
+			*rxp++ = readb(&regs->txdata);
+		}
+		while (!(readb(&regs->status) &
+			 TINY_SPI_STATUS_TXE))
+			;
+		*rxp++ = readb(&regs->rxdata);
+	} else if (rxp) {
+		writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
+		if (bytes > 1) {
+			writeb(CONFIG_TINY_SPI_IDLE_VAL,
+			       &regs->txdata);
+			for (i = 2; i < bytes; i++) {
+				u8 rx;
+				while (!(readb(&regs->status) &
+					 TINY_SPI_STATUS_TXR))
+					;
+				rx = readb(&regs->txdata);
+				writeb(CONFIG_TINY_SPI_IDLE_VAL,
+				       &regs->txdata);
+				*rxp++ = rx;
+			}
+			while (!(readb(&regs->status) &
+				 TINY_SPI_STATUS_TXR))
+				;
+			*rxp++ = readb(&regs->txdata);
+		}
+		while (!(readb(&regs->status) &
+			 TINY_SPI_STATUS_TXE))
+			;
+		*rxp++ = readb(&regs->rxdata);
+	} else if (txp) {
+		writeb(*txp++, &regs->txdata);
+		if (bytes > 1) {
+			writeb(*txp++, &regs->txdata);
+			for (i = 2; i < bytes; i++) {
+				u8 tx = *txp++;
+				while (!(readb(&regs->status) &
+					 TINY_SPI_STATUS_TXR))
+					;
+				writeb(tx, &regs->txdata);
+			}
+		}
+		while (!(readb(&regs->status) &
+			 TINY_SPI_STATUS_TXE))
+			;
+	} else {
+		writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
+		if (bytes > 1) {
+			writeb(CONFIG_TINY_SPI_IDLE_VAL,
+			       &regs->txdata);
+			for (i = 2; i < bytes; i++) {
+				while (!(readb(&regs->status) &
+					 TINY_SPI_STATUS_TXR))
+					;
+				writeb(CONFIG_TINY_SPI_IDLE_VAL,
+				       &regs->txdata);
+			}
+		}
+		while (!(readb(&regs->status) &
+			 TINY_SPI_STATUS_TXE))
+			;
+	}
+
+ done:
+	if (flags & SPI_XFER_END)
+		spi_cs_deactivate(slave);
+
+	return 0;
+}
-- 
1.7.4.2

  reply	other threads:[~2011-04-12  5:48 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-08 23:56 [U-Boot] [PATCH] spi: add new driver for OpenCores tiny_spi Thomas Chou
2011-01-09  1:44 ` Mike Frysinger
2011-01-10  2:24   ` [U-Boot] [PATCH v2] " Thomas Chou
2011-01-17 21:23     ` Wolfgang Denk
2011-01-18  2:08       ` Thomas Chou
2011-01-18  4:09       ` [U-Boot] [PATCH v3] " Thomas Chou
2011-04-11 19:54         ` Wolfgang Denk
2011-04-12  5:48           ` Thomas Chou [this message]
2011-04-30 19:02             ` [U-Boot] [PATCH v4] " Wolfgang Denk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1302587327-4699-1-git-send-email-thomas@wytron.com.tw \
    --to=thomas@wytron.com.tw \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.