Patchwork [U-Boot,v2] spi: add new driver for OpenCores tiny_spi

login
register
mail settings
Submitter Thomas Chou
Date Jan. 10, 2011, 2:24 a.m.
Message ID <1294626279-8601-1-git-send-email-thomas@wytron.com.tw>
Download mbox | patch
Permalink /patch/78064/
State Changes Requested
Headers show

Comments

Thomas Chou - Jan. 10, 2011, 2:24 a.m.
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.

 drivers/spi/Makefile      |    1 +
 drivers/spi/oc_tiny_spi.c |  240 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 241 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/oc_tiny_spi.c
Wolfgang Denk - Jan. 17, 2011, 9:23 p.m.
Dear Thomas Chou,

In message <1294626279-8601-1-git-send-email-thomas@wytron.com.tw> you wrote:
> 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.
...
> +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));
> +}

Please separate declartations and code with a blank line. Please fix
globally.

...
> +int spi_claim_bus(struct spi_slave *slave)
> +{
> +	struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
> +	const 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);

Please do not use base + offset notation.  Use a proper C struct
instead.  Please fix globally.


Best regards,

Wolfgang Denk
Thomas Chou - Jan. 18, 2011, 2:08 a.m.
Dear Wolfgang,

On 01/18/2011 05:23 AM, Wolfgang Denk wrote:
> Please separate declartations and code with a blank line. Please fix
> globally.

> Please do not use base + offset notation.  Use a proper C struct
> instead.  Please fix globally.

Thank you very much for the coding style correction. I will follow them.

Best regards,
Thomas

Patch

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..b61cb7a
--- /dev/null
+++ b/drivers/spi/oc_tiny_spi.c
@@ -0,0 +1,240 @@ 
+/*
+ * 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_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 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);
+	const 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)
+{
+	const 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;
+}