[RFC] spi: add demo bitbang driver based on code from linux
Antony Pavlov
antonynpavlov at gmail.com
Thu Sep 6 06:45:43 EDT 2012
On 6 September 2012 13:09, Jean-Christophe PLAGNIOL-VILLARD
<plagnioj at jcrosoft.com> wrote:
> On 11:53 Thu 06 Sep , Antony Pavlov wrote:
>> 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>
> can you implement the gpio driver base on it
>
> so we can test it and ack it
Do you mean "to make spi-bitbang work over gpio driver"?
Just now I has no gpio driver for my hardware; first I must write a gpio driver.
I think I can do the work in a one or two weeks.
>
> and does this work owith mmc-spi?
I have not checked it.
I have a board with bitbang-capable SPI controller and MMC-card
connected to it, so I can make the checks in the near future.
>
> Best Regards,
> J.
>> ---
>> 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
>>
--
Best regards,
Antony Pavlov
More information about the barebox
mailing list