📄 dm355_spi_bitbang.c
字号:
/* * dm355_spi_bitbang.c - polling/bitbanging TI dm355 SPI master controller driver utilities * * 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 */#include <linux/init.h>#include <linux/spinlock.h>#include <linux/workqueue.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/device.h>#include <linux/device.h>#include <linux/spi/spi.h>#include <linux/spi/dm355_spi_bitbang.h>#if defined(CONFIG_SPI_DEBUG)#define DEBUG_BB(fmt,arg...) printk(KERN_EMERG fmt , ##arg);#else#define DEBUG_BB(fmt,arg...)#endif#define ENTER DEBUG_BB("[Ei %s-%d] \n", __FUNCTION__, __LINE__);#define EXIT DEBUG_BB("[Ex %s-%d] \n", __FUNCTION__, __LINE__);/*----------------------------------------------------------------------*//* * FIRST PART (OPTIONAL): word-at-a-time spi_transfer support. * Use this for GPIO or shift-register level hardware APIs. * * spi_bitbang_cs is in spi_device->controller_state, which is unavailable * to glue code. These bitbang setup() and cleanup() routines are always * used, though maybe they're called from controller-aware code. * * chipselect() and friends may use use spi_device->controller_data and * controller registers as appropriate. * * * NOTE: SPI controller pins can often be used as GPIO pins instead, * which means you could use a bitbang driver either to get hardware * working quickly, or testing for differences that aren't speed related. */struct spi_bitbang_cs { unsigned nsecs; /* (clock cycle time)/2 */ u32(*txrx_word) (struct spi_device * spi, unsigned nsecs, u32 word, u8 bits); unsigned (*txrx_bufs) (struct spi_device *, u32(*txrx_word) (struct spi_device * spi, unsigned nsecs, u32 word, u8 bits), unsigned, struct spi_transfer *);};static unsigned bitbang_txrx_8(struct spi_device *spi, u32(*txrx_word) (struct spi_device * spi, unsigned nsecs, u32 word, u8 bits), unsigned ns, struct spi_transfer *t){ unsigned bits = spi->bits_per_word; unsigned count = t->len; const u8 *tx = t->tx_buf; u8 *rx = t->rx_buf; while (likely(count > 0)) { u8 word = 0; if (tx) word = *tx++; word = txrx_word(spi, ns, word, bits); if (rx) *rx++ = word; count -= 1; } return t->len - count;}static unsigned bitbang_txrx_16(struct spi_device *spi, u32(*txrx_word) (struct spi_device * spi, unsigned nsecs, u32 word, u8 bits), unsigned ns, struct spi_transfer *t){ unsigned bits = spi->bits_per_word; unsigned count = t->len; const u16 *tx = t->tx_buf; u16 *rx = t->rx_buf; while (likely(count > 1)) { u16 word = 0; if (tx) word = *tx++; word = txrx_word(spi, ns, word, bits); if (rx) *rx++ = word; count -= 2; } return t->len - count;}int dm355_spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t){ struct spi_bitbang_cs *cs = spi->controller_state; u8 bits_per_word; u32 hz; ENTER if (t) { bits_per_word = t->bits_per_word; hz = t->speed_hz; } else { bits_per_word = 0; hz = 0; } /* spi_transfer level calls that work per-word */ if (!bits_per_word) bits_per_word = spi->bits_per_word; if (bits_per_word <= 8 && bits_per_word >= 2) { cs->txrx_bufs = bitbang_txrx_8; DEBUG_BB("word = %d\n", bits_per_word); } else if (bits_per_word <= 16 && bits_per_word >= 2) { cs->txrx_bufs = bitbang_txrx_16; DEBUG_BB("word = %d\n", bits_per_word); } /*else if (bits_per_word <= 32) cs->txrx_bufs = bitbang_txrx_32; */ else return -EINVAL; /* nsecs = (clock period)/2 */ if (!hz) hz = spi->max_speed_hz; if (hz) { cs->nsecs = (1000000000 / 2) / hz; if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000)) return -EINVAL; } EXIT return 0;}EXPORT_SYMBOL_GPL(dm355_spi_bitbang_setup_transfer);/** * dm355_spi_bitbang_setup - default setup for per-word I/O loops */int dm355_spi_bitbang_setup(struct spi_device *spi){ struct spi_bitbang_cs *cs = spi->controller_state; struct spi_bitbang *bitbang; int retval; bitbang = spi_master_get_devdata(spi->master); /* REVISIT: some systems will want to support devices using lsb-first * bit encodings on the wire. In pure software that would be trivial, * just bitbang_txrx_le_cphaX() routines shifting the other way, and * some hardware controllers also have this support. */ if ((spi->mode & SPI_LSB_FIRST) != 0) return -EINVAL; if (!cs) { cs = kzalloc(sizeof *cs, SLAB_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; } if (!spi->bits_per_word) spi->bits_per_word = 8; /* per-word shift register access, in hardware or bitbanging */ cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL | SPI_CPHA)]; if (!cs->txrx_word) return -EINVAL; retval = dm355_spi_bitbang_setup_transfer(spi, NULL); if (retval < 0) return retval; dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", __FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA), spi->bits_per_word, 2 * cs->nsecs); /* NOTE we _need_ to call chipselect() early, ideally with adapter * setup, unless the hardware defaults cooperate to avoid confusion * between normal (active low) and inverted chipselects. */ /* deselect chip (low or high) */ spin_lock(&bitbang->lock); if (!bitbang->busy) { bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(cs->nsecs); } spin_unlock(&bitbang->lock); return 0;}EXPORT_SYMBOL_GPL(dm355_spi_bitbang_setup);/** * dm355_spi_bitbang_cleanup - default cleanup for per-word I/O loops */void dm355_spi_bitbang_cleanup(const struct spi_device *spi){ kfree(spi->controller_state);}EXPORT_SYMBOL_GPL(dm355_spi_bitbang_cleanup);static int dm355_spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t){ struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);}/*----------------------------------------------------------------------*//* * SECOND PART ... simple transfer queue runner. * * This costs a task context per controller, running the queue by * performing each transfer in sequence. Smarter hardware can queue * several DMA transfers at once, and process several controller queues * in parallel; this driver doesn't match such hardware very well. * * Drivers can provide word-at-a-time i/o primitives, or provide * transfer-at-a-time ones to leverage dma or fifo hardware. */static void dm355_bitbang_work(void *_bitbang){ struct spi_bitbang *bitbang = _bitbang; unsigned long flags; ENTER spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; while (!list_empty(&bitbang->queue)) { struct spi_message *m; struct spi_device *spi; unsigned nsecs; struct spi_transfer *t = NULL; unsigned tmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -