[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