aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/spi/spi_bitbang.h
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2006-01-08 16:34:26 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-01-13 19:29:55 -0500
commit9904f22a7202c6b54e96b0cc9870817013c350a1 (patch)
tree02d526b1bf54b1c64e58a9f903269f9cdc6ec83c /include/linux/spi/spi_bitbang.h
parent2e5a7bd978bf4118a0c8edf2e6ff81d0a72fee47 (diff)
[PATCH] spi: add spi_bitbang driver
This adds a bitbanging spi master, hooking up to board/adapter-specific glue code which knows how to set and read the signals (gpios etc). This code kicks in after the glue code creates a platform_device with the right platform_data. That data includes I/O loops, which will usually come from expanding an inline function (provided in the header). One goal is that the I/O loops should be easily optimized down to a few GPIO register accesses, in common cases, for speed and minimized overhead. This understands all the currently defined protocol tweaking options in the SPI framework, and might eventually serve as as reference implementation. - different word sizes (1..32 bits) - differing clock rates - SPI modes differing by CPOL (affecting chip select and I/O loops) - SPI modes differing by CPHA (affecting I/O loops) - delays (usecs) after transfers - temporarily deselecting chips in mid-transfer A lot of hardware could work with this framework, though common types of controller can't reach peak performance without switching to a driver structure that supports pipelining of transfers (e.g. DMA queues) and maybe controllers (e.g. IRQ driven). Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'include/linux/spi/spi_bitbang.h')
-rw-r--r--include/linux/spi/spi_bitbang.h128
1 files changed, 128 insertions, 0 deletions
diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h
new file mode 100644
index 000000000000..8dfe61a445f4
--- /dev/null
+++ b/include/linux/spi/spi_bitbang.h
@@ -0,0 +1,128 @@
1#ifndef __SPI_BITBANG_H
2#define __SPI_BITBANG_H
3
4/*
5 * Mix this utility code with some glue code to get one of several types of
6 * simple SPI master driver. Two do polled word-at-a-time I/O:
7 *
8 * - GPIO/parport bitbangers. Provide chipselect() and txrx_word[](),
9 * expanding the per-word routines from the inline templates below.
10 *
11 * - Drivers for controllers resembling bare shift registers. Provide
12 * chipselect() and txrx_word[](), with custom setup()/cleanup() methods
13 * that use your controller's clock and chipselect registers.
14 *
15 * Some hardware works well with requests at spi_transfer scope:
16 *
17 * - Drivers leveraging smarter hardware, with fifos or DMA; or for half
18 * duplex (MicroWire) controllers. Provide chipslect() and txrx_bufs(),
19 * and custom setup()/cleanup() methods.
20 */
21struct spi_bitbang {
22 struct workqueue_struct *workqueue;
23 struct work_struct work;
24
25 spinlock_t lock;
26 struct list_head queue;
27 u8 busy;
28 u8 shutdown;
29 u8 use_dma;
30
31 struct spi_master *master;
32
33 void (*chipselect)(struct spi_device *spi, int is_on);
34
35 int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
36 u32 (*txrx_word[4])(struct spi_device *spi,
37 unsigned nsecs,
38 u32 word, u8 bits);
39};
40
41/* you can call these default bitbang->master methods from your custom
42 * methods, if you like.
43 */
44extern int spi_bitbang_setup(struct spi_device *spi);
45extern void spi_bitbang_cleanup(const struct spi_device *spi);
46extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
47
48/* start or stop queue processing */
49extern int spi_bitbang_start(struct spi_bitbang *spi);
50extern int spi_bitbang_stop(struct spi_bitbang *spi);
51
52#endif /* __SPI_BITBANG_H */
53
54/*-------------------------------------------------------------------------*/
55
56#ifdef EXPAND_BITBANG_TXRX
57
58/*
59 * The code that knows what GPIO pins do what should have declared four
60 * functions, ideally as inlines, before #defining EXPAND_BITBANG_TXRX
61 * and including this header:
62 *
63 * void setsck(struct spi_device *, int is_on);
64 * void setmosi(struct spi_device *, int is_on);
65 * int getmiso(struct spi_device *);
66 * void spidelay(unsigned);
67 *
68 * A non-inlined routine would call bitbang_txrx_*() routines. The
69 * main loop could easily compile down to a handful of instructions,
70 * especially if the delay is a NOP (to run at peak speed).
71 *
72 * Since this is software, the timings may not be exactly what your board's
73 * chips need ... there may be several reasons you'd need to tweak timings
74 * in these routines, not just make to make it faster or slower to match a
75 * particular CPU clock rate.
76 */
77
78static inline u32
79bitbang_txrx_be_cpha0(struct spi_device *spi,
80 unsigned nsecs, unsigned cpol,
81 u32 word, u8 bits)
82{
83 /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
84
85 /* clock starts at inactive polarity */
86 for (word <<= (32 - bits); likely(bits); bits--) {
87
88 /* setup MSB (to slave) on trailing edge */
89 setmosi(spi, word & (1 << 31));
90 spidelay(nsecs); /* T(setup) */
91
92 setsck(spi, !cpol);
93 spidelay(nsecs);
94
95 /* sample MSB (from slave) on leading edge */
96 word <<= 1;
97 word |= getmiso(spi);
98 setsck(spi, cpol);
99 }
100 return word;
101}
102
103static inline u32
104bitbang_txrx_be_cpha1(struct spi_device *spi,
105 unsigned nsecs, unsigned cpol,
106 u32 word, u8 bits)
107{
108 /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
109
110 /* clock starts at inactive polarity */
111 for (word <<= (32 - bits); likely(bits); bits--) {
112
113 /* setup MSB (to slave) on leading edge */
114 setsck(spi, !cpol);
115 setmosi(spi, word & (1 << 31));
116 spidelay(nsecs); /* T(setup) */
117
118 setsck(spi, cpol);
119 spidelay(nsecs);
120
121 /* sample MSB (from slave) on trailing edge */
122 word <<= 1;
123 word |= getmiso(spi);
124 }
125 return word;
126}
127
128#endif /* EXPAND_BITBANG_TXRX */