From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mail-lb0-f177.google.com ([209.85.217.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1T9Wtw-0004Tj-EE for barebox@lists.infradead.org; Thu, 06 Sep 2012 07:53:26 +0000 Received: by lbbgf7 with SMTP id gf7so934032lbb.36 for ; Thu, 06 Sep 2012 00:53:22 -0700 (PDT) From: Antony Pavlov Date: Thu, 6 Sep 2012 11:53:15 +0400 Message-Id: <1346917995-28683-1-git-send-email-antonynpavlov@gmail.com> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: barebox-bounces@lists.infradead.org Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [RFC] spi: add demo bitbang driver based on code from linux To: barebox@lists.infradead.org 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 --- 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 #include #include +#include +#include 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 + * + * 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 +#include +#include +#include +#include +#include + +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 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox