[RFC] spi: add demo bitbang driver based on code from linux

Antony Pavlov antonynpavlov at gmail.com
Thu Sep 6 03:53:15 EDT 2012


It not the real driver but demonstration of the conception.
The code was tested on real hardware, but the code for
this hardware can't be published just now.

Usage of bitbang framework is very easy.
You must declare the functions for setting CLK and MOSI signals,
and the function for reading MISO signal:

 * void setsck(struct spi_device *, int is_on);
 * void setmosi(struct spi_device *, int is_on);
 * int getmiso(struct spi_device *);

Next include the "spi-bitbang-txrx.h" header. The header
will give you the functions for reading/writing u32 words from/to spi.

Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
---
 arch/mips/boards/qemu-malta/init.c |   31 +++++++
 drivers/spi/Kconfig                |    4 +
 drivers/spi/Makefile               |    1 +
 drivers/spi/bitbang_demo_spi.c     |  176 ++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-bitbang-txrx.h     |   93 +++++++++++++++++++
 5 files changed, 305 insertions(+)
 create mode 100644 drivers/spi/bitbang_demo_spi.c
 create mode 100644 drivers/spi/spi-bitbang-txrx.h

diff --git a/arch/mips/boards/qemu-malta/init.c b/arch/mips/boards/qemu-malta/init.c
index 45f66f2..314ca88 100644
--- a/arch/mips/boards/qemu-malta/init.c
+++ b/arch/mips/boards/qemu-malta/init.c
@@ -30,6 +30,8 @@
 #include <partition.h>
 #include <sizes.h>
 #include <asm/common.h>
+#include <spi/spi.h>
+#include <spi/flash.h>
 
 static int malta_mem_init(void)
 {
@@ -64,3 +66,32 @@ static int malta_console_init(void)
 	return 0;
 }
 console_initcall(malta_console_init);
+
+static struct flash_platform_data malta_spi_demo_flash_data = {
+	.name		= "spi",
+	.type		= "s25sl004a",
+};
+
+static struct spi_board_info malta_spi_demo_devs[] __initdata = {
+	{
+		/* Spansion S25FL004A SPI flash */
+		.name		= "m25p",
+		.max_speed_hz	= 50000000,
+		.bus_num	= 0,
+		.chip_select	= 0,
+		.mode		= SPI_MODE_3,
+		.platform_data	= &malta_spi_demo_flash_data,
+	},
+};
+
+static int malta_spi_demo_init(void)
+{
+
+	spi_register_board_info(malta_spi_demo_devs,
+		ARRAY_SIZE(malta_spi_demo_devs));
+	add_generic_device("bitbang_demo_spi", -1, NULL,
+		0, 0, IORESOURCE_MEM, NULL);
+
+	return 0;
+}
+device_initcall(malta_spi_demo_init);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b5c55a4..d883cd4 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -34,4 +34,8 @@ config DRIVER_SPI_ATMEL
 	depends on ARCH_AT91
 	depends on SPI
 
+config DRIVER_SPI_BITBANG_DEMO
+	bool "BITBANG DEMO SPI controller"
+	depends on SPI
+
 endmenu
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 101652f..087a982 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_SPI) += spi.o
 obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
 obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o
 obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o
+obj-$(CONFIG_DRIVER_SPI_BITBANG_DEMO) += bitbang_demo_spi.o
diff --git a/drivers/spi/bitbang_demo_spi.c b/drivers/spi/bitbang_demo_spi.c
new file mode 100644
index 0000000..76abf98
--- /dev/null
+++ b/drivers/spi/bitbang_demo_spi.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 Antony Pavlov <antonynpavlov at gmail.com>
+ *
+ * This file is part of barebox.
+ * 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
+ *
+ */
+
+#define DEBUG
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <spi/spi.h>
+#include <io.h>
+#include <clock.h>
+
+struct spi_bitbang_demo_master {
+	int	num_chipselect;
+	int	spi_mode;
+	int	databits;
+	int	speed;
+	int	bus_num;
+};
+
+struct bitbang_demo_spi {
+	struct spi_master	master;
+	int			databits;
+	int			speed;
+	int			mode;
+};
+
+static inline void setsck(struct spi_device *spi, int on)
+{
+	struct bitbang_demo_spi *sc = container_of(spi->master, struct bitbang_demo_spi, master);
+
+	/* do something ... */
+	(void)sc;
+}
+
+static inline void setmosi(struct spi_device *spi, int on)
+{
+	struct bitbang_demo_spi *sc = container_of(spi->master, struct bitbang_demo_spi, master);
+
+	/* do something ... */
+	(void)sc;
+}
+
+static inline u32 getmiso(struct spi_device *spi)
+{
+	struct bitbang_demo_spi *sc = container_of(spi->master, struct bitbang_demo_spi, master);
+
+	/* do something ... */
+	(void)sc;
+
+	return 0;
+}
+
+#include "spi-bitbang-txrx.h"
+
+static int bitbang_demo_spi_setup(struct spi_device *spi)
+{
+	struct spi_master *master = spi->master;
+	struct bitbang_demo_spi *sc = container_of(master, struct bitbang_demo_spi, master);
+
+	dev_dbg(master->dev, "%s\n", __func__);
+	printf("%s chipsel %d mode 0x%08x bits_per_word: %d speed: %d\n",
+			__FUNCTION__, spi->chip_select, spi->mode, spi->bits_per_word,
+			spi->max_speed_hz);
+
+	/* do something ... */
+	(void) sc;
+
+	return 0;
+}
+
+static int bitbang_demo_read(struct spi_device *spi, void *buf, size_t nbyte)
+{
+	ssize_t cnt = 0;
+	volatile u8 *rxf_buf = buf;
+
+	/* SPI_MODE_3 only */
+	while (cnt < nbyte) {
+		*rxf_buf = bitbang_txrx_be_cpha1(spi, 1000, 1, 0, 8);
+		rxf_buf++;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+static int bitbang_demo_write(struct spi_device *spi, const void *buf, size_t nbyte)
+{
+	ssize_t cnt = 0;
+	const u8 *txf_buf = buf;
+
+	/* SPI_MODE_3 only */
+	while (cnt < nbyte) {
+		bitbang_txrx_be_cpha1(spi, 1000, 1, (u32)*txf_buf, 8);
+		txf_buf++;
+		cnt++;
+	}
+
+	return 0;
+}
+
+static int bitbang_demo_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
+{
+	struct spi_master *master = spi->master;
+	struct spi_transfer *t;
+
+	dev_dbg(master->dev, "%s\n", __func__);
+
+	mesg->actual_length = 0;
+
+	/* chip select and mode select stuff skipped */
+
+	list_for_each_entry(t, &mesg->transfers, transfer_list) {
+
+		if (t->tx_buf)
+			bitbang_demo_write(spi, t->tx_buf, t->len);
+
+		if (t->rx_buf)
+			bitbang_demo_read(spi, t->rx_buf, t->len);
+
+		mesg->actual_length += t->len;
+	}
+
+	return 0;
+}
+
+static int bitbang_demo_spi_probe(struct device_d *dev)
+{
+	struct spi_master *master;
+	struct bitbang_demo_spi *bitbang_demo_spi;
+
+	bitbang_demo_spi = xzalloc(sizeof(*bitbang_demo_spi));
+
+	master = &bitbang_demo_spi->master;
+	master->dev = dev;
+
+	master->setup = bitbang_demo_spi_setup;
+	master->transfer = bitbang_demo_spi_transfer;
+	master->num_chipselect = 1;
+	master->bus_num = 0;
+
+	spi_register_master(master);
+
+	return 0;
+}
+
+static struct driver_d bitbang_demo_spi_driver = {
+	.name  = "bitbang_demo_spi",
+	.probe = bitbang_demo_spi_probe,
+};
+
+static int bitbang_demo_spi_driver_init(void)
+{
+	return register_driver(&bitbang_demo_spi_driver);
+}
+device_initcall(bitbang_demo_spi_driver_init);
diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h
new file mode 100644
index 0000000..e86eec2
--- /dev/null
+++ b/drivers/spi/spi-bitbang-txrx.h
@@ -0,0 +1,93 @@
+/*
+ * Mix this utility code with some glue code to get one of several types of
+ * simple SPI master driver.  Two do polled word-at-a-time I/O:
+ *
+ *   -	GPIO/parport bitbangers.  Provide chipselect() and txrx_word[](),
+ *	expanding the per-word routines from the inline templates below.
+ *
+ *   -	Drivers for controllers resembling bare shift registers.  Provide
+ *	chipselect() and txrx_word[](), with custom setup()/cleanup() methods
+ *	that use your controller's clock and chipselect registers.
+ *
+ * Some hardware works well with requests at spi_transfer scope:
+ *
+ *   -	Drivers leveraging smarter hardware, with fifos or DMA; or for half
+ *	duplex (MicroWire) controllers.  Provide chipselect() and txrx_bufs(),
+ *	and custom setup()/cleanup() methods.
+ */
+
+/*
+ * The code that knows what GPIO pins do what should have declared four
+ * functions, ideally as inlines, before including this header:
+ *
+ *  void setsck(struct spi_device *, int is_on);
+ *  void setmosi(struct spi_device *, int is_on);
+ *  int getmiso(struct spi_device *);
+ *  void spidelay(unsigned);
+ *
+ * setsck()'s is_on parameter is a zero/nonzero boolean.
+ *
+ * setmosi()'s is_on parameter is a zero/nonzero boolean.
+ *
+ * getmiso() is required to return 0 or 1 only. Any other value is invalid
+ * and will result in improper operation.
+ *
+ * A non-inlined routine would call bitbang_txrx_*() routines.  The
+ * main loop could easily compile down to a handful of instructions,
+ * especially if the delay is a NOP (to run at peak speed).
+ *
+ * Since this is software, the timings may not be exactly what your board's
+ * chips need ... there may be several reasons you'd need to tweak timings
+ * in these routines, not just make to make it faster or slower to match a
+ * particular CPU clock rate.
+ */
+
+#define spidelay(nsecs) udelay(nsecs/1000)
+
+static inline u32
+bitbang_txrx_be_cpha0(struct spi_device *spi,
+		unsigned nsecs, unsigned cpol, u32 word, u8 bits)
+{
+	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
+
+	/* clock starts at inactive polarity */
+	for (word <<= (32 - bits); likely(bits); bits--) {
+
+		/* setup MSB (to slave) on trailing edge */
+		setmosi(spi, word & (1 << 31));
+		spidelay(nsecs);	/* T(setup) */
+
+		setsck(spi, !cpol);
+		spidelay(nsecs);
+
+		/* sample MSB (from slave) on leading edge */
+		word <<= 1;
+		word |= getmiso(spi);
+		setsck(spi, cpol);
+	}
+	return word;
+}
+
+static inline u32
+bitbang_txrx_be_cpha1(struct spi_device *spi, unsigned nsecs, unsigned cpol,
+		u32 word, u8 bits)
+{
+	/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
+
+	/* clock starts at inactive polarity */
+	for (word <<= (32 - bits); likely(bits); bits--) {
+
+		/* setup MSB (to slave) on leading edge */
+		setsck(spi, !cpol);
+		setmosi(spi, word & (1 << 31));
+		spidelay(nsecs); /* T(setup) */
+
+		setsck(spi, cpol);
+		spidelay(nsecs);
+
+		/* sample MSB (from slave) on trailing edge */
+		word <<= 1;
+		word |= getmiso(spi);
+	}
+	return word;
+}
-- 
1.7.10.4




More information about the barebox mailing list