aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorMarc Singer <elf@buici.com>2006-05-16 06:41:32 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-06-18 11:16:49 -0400
commitc97898614bf0ac9605333a2c99bdbcf4276a22bd (patch)
tree9b947f1f4dd0f1e14ef0579cf4e48078ac7a2009 /arch/arm
parent903e2bbda92e5a14f8050154046a14230abb800b (diff)
[ARM] 3405/1: lpd7a40x: CPLD ssp driver
Patch from Marc Singer Driver for operating SSP devices through LPD7A40X CPLD chip. This driver is used by the audio codecs. Signed-off-by: Marc Singer <elf@buici.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-lh7a40x/Kconfig5
-rw-r--r--arch/arm/mach-lh7a40x/ssp-cpld.c343
2 files changed, 348 insertions, 0 deletions
diff --git a/arch/arm/mach-lh7a40x/Kconfig b/arch/arm/mach-lh7a40x/Kconfig
index 8a17867a6a24..558a34f53b1c 100644
--- a/arch/arm/mach-lh7a40x/Kconfig
+++ b/arch/arm/mach-lh7a40x/Kconfig
@@ -14,6 +14,7 @@ config MACH_LPD7A400
14 bool "LPD7A400 Card Engine" 14 bool "LPD7A400 Card Engine"
15 select ARCH_LH7A400 15 select ARCH_LH7A400
16# select IDE_POLL 16# select IDE_POLL
17 select HAS_TOUCHSCREEN_ADS7843_LH7
17 help 18 help
18 Say Y here if you are using Logic Product Development's 19 Say Y here if you are using Logic Product Development's
19 LPD7A400 CardEngine. For the time being, the LPD7A400 and 20 LPD7A400 CardEngine. For the time being, the LPD7A400 and
@@ -23,6 +24,7 @@ config MACH_LPD7A404
23 bool "LPD7A404 Card Engine" 24 bool "LPD7A404 Card Engine"
24 select ARCH_LH7A404 25 select ARCH_LH7A404
25# select IDE_POLL 26# select IDE_POLL
27 select HAS_TOUCHSCREEN_ADC_LH7
26 help 28 help
27 Say Y here if you are using Logic Product Development's 29 Say Y here if you are using Logic Product Development's
28 LPD7A404 CardEngine. For the time being, the LPD7A400 and 30 LPD7A404 CardEngine. For the time being, the LPD7A400 and
@@ -34,6 +36,9 @@ config ARCH_LH7A400
34config ARCH_LH7A404 36config ARCH_LH7A404
35 bool 37 bool
36 38
39config LPD7A40X_CPLD_SSP
40 bool
41
37config LH7A40X_CONTIGMEM 42config LH7A40X_CONTIGMEM
38 bool "Disable NUMA Support" 43 bool "Disable NUMA Support"
39 depends on ARCH_LH7A40X 44 depends on ARCH_LH7A40X
diff --git a/arch/arm/mach-lh7a40x/ssp-cpld.c b/arch/arm/mach-lh7a40x/ssp-cpld.c
new file mode 100644
index 000000000000..a10830186dac
--- /dev/null
+++ b/arch/arm/mach-lh7a40x/ssp-cpld.c
@@ -0,0 +1,343 @@
1/* arch/arm/mach-lh7a40x/ssp-cpld.c
2 *
3 * Copyright (C) 2004,2005 Marc Singer
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
8 *
9 * SSP/SPI driver for the CardEngine CPLD.
10 *
11 */
12
13/* NOTES
14 -----
15
16 o *** This driver is cribbed from the 7952x implementation.
17 Some comments may not apply.
18
19 o This driver contains sufficient logic to control either the
20 serial EEPROMs or the audio codec. It is included in the kernel
21 to support the codec. The EEPROMs are really the responsibility
22 of the boot loader and should probably be left alone.
23
24 o The code must be augmented to cope with multiple, simultaneous
25 clients.
26 o The audio codec writes to the codec chip whenever playback
27 starts.
28 o The touchscreen driver writes to the ads chip every time it
29 samples.
30 o The audio codec must write 16 bits, but the touch chip writes
31 are 8 bits long.
32 o We need to be able to keep these configurations separate while
33 simultaneously active.
34
35 */
36
37#include <linux/module.h>
38#include <linux/kernel.h>
39//#include <linux/sched.h>
40#include <linux/errno.h>
41#include <linux/interrupt.h>
42//#include <linux/ioport.h>
43#include <linux/init.h>
44#include <linux/delay.h>
45#include <linux/spinlock.h>
46
47#include <asm/io.h>
48#include <asm/irq.h>
49#include <asm/hardware.h>
50
51#include <asm/arch/ssp.h>
52
53//#define TALK
54
55#if defined (TALK)
56#define PRINTK(f...) printk (f)
57#else
58#define PRINTK(f...) do {} while (0)
59#endif
60
61#if defined (CONFIG_ARCH_LH7A400)
62# define CPLD_SPID __REGP16(CPLD06_VIRT) /* SPI data */
63# define CPLD_SPIC __REGP16(CPLD08_VIRT) /* SPI control */
64# define CPLD_SPIC_CS_CODEC (1<<0)
65# define CPLD_SPIC_CS_TOUCH (1<<1)
66# define CPLD_SPIC_WRITE (0<<2)
67# define CPLD_SPIC_READ (1<<2)
68# define CPLD_SPIC_DONE (1<<3) /* r/o */
69# define CPLD_SPIC_LOAD (1<<4)
70# define CPLD_SPIC_START (1<<4)
71# define CPLD_SPIC_LOADED (1<<5) /* r/o */
72#endif
73
74#define CPLD_SPI __REGP16(CPLD0A_VIRT) /* SPI operation */
75#define CPLD_SPI_CS_EEPROM (1<<3)
76#define CPLD_SPI_SCLK (1<<2)
77#define CPLD_SPI_TX_SHIFT (1)
78#define CPLD_SPI_TX (1<<CPLD_SPI_TX_SHIFT)
79#define CPLD_SPI_RX_SHIFT (0)
80#define CPLD_SPI_RX (1<<CPLD_SPI_RX_SHIFT)
81
82/* *** FIXME: these timing values are substantially larger than the
83 *** chip requires. We may implement an nsleep () function. */
84#define T_SKH 1 /* Clock time high (us) */
85#define T_SKL 1 /* Clock time low (us) */
86#define T_CS 1 /* Minimum chip select low time (us) */
87#define T_CSS 1 /* Minimum chip select setup time (us) */
88#define T_DIS 1 /* Data setup time (us) */
89
90 /* EEPROM SPI bits */
91#define P_START (1<<9)
92#define P_WRITE (1<<7)
93#define P_READ (2<<7)
94#define P_ERASE (3<<7)
95#define P_EWDS (0<<7)
96#define P_WRAL (0<<7)
97#define P_ERAL (0<<7)
98#define P_EWEN (0<<7)
99#define P_A_EWDS (0<<5)
100#define P_A_WRAL (1<<5)
101#define P_A_ERAL (2<<5)
102#define P_A_EWEN (3<<5)
103
104struct ssp_configuration {
105 int device;
106 int mode;
107 int speed;
108 int frame_size_write;
109 int frame_size_read;
110};
111
112static struct ssp_configuration ssp_configuration;
113static spinlock_t ssp_lock;
114
115static void enable_cs (void)
116{
117 switch (ssp_configuration.device) {
118 case DEVICE_EEPROM:
119 CPLD_SPI |= CPLD_SPI_CS_EEPROM;
120 break;
121 }
122 udelay (T_CSS);
123}
124
125static void disable_cs (void)
126{
127 switch (ssp_configuration.device) {
128 case DEVICE_EEPROM:
129 CPLD_SPI &= ~CPLD_SPI_CS_EEPROM;
130 break;
131 }
132 udelay (T_CS);
133}
134
135static void pulse_clock (void)
136{
137 CPLD_SPI |= CPLD_SPI_SCLK;
138 udelay (T_SKH);
139 CPLD_SPI &= ~CPLD_SPI_SCLK;
140 udelay (T_SKL);
141}
142
143
144/* execute_spi_command
145
146 sends an spi command to a device. It first sends cwrite bits from
147 v. If cread is greater than zero it will read cread bits
148 (discarding the leading 0 bit) and return them. If cread is less
149 than zero it will check for completetion status and return 0 on
150 success or -1 on timeout. If cread is zero it does nothing other
151 than sending the command.
152
153 On the LPD7A400, we can only read or write multiples of 8 bits on
154 the codec and the touch screen device. Here, we round up.
155
156*/
157
158static int execute_spi_command (int v, int cwrite, int cread)
159{
160 unsigned long l = 0;
161
162#if defined (CONFIG_MACH_LPD7A400)
163 /* The codec and touch devices cannot be bit-banged. Instead,
164 * the CPLD provides an eight-bit shift register and a crude
165 * interface. */
166 if ( ssp_configuration.device == DEVICE_CODEC
167 || ssp_configuration.device == DEVICE_TOUCH) {
168 int select = 0;
169
170 PRINTK ("spi(%d %d.%d) 0x%04x",
171 ssp_configuration.device, cwrite, cread,
172 v);
173#if defined (TALK)
174 if (ssp_configuration.device == DEVICE_CODEC)
175 PRINTK (" 0x%03x -> %2d", v & 0x1ff, (v >> 9) & 0x7f);
176#endif
177 PRINTK ("\n");
178
179 if (ssp_configuration.device == DEVICE_CODEC)
180 select = CPLD_SPIC_CS_CODEC;
181 if (ssp_configuration.device == DEVICE_TOUCH)
182 select = CPLD_SPIC_CS_TOUCH;
183 if (cwrite) {
184 for (cwrite = (cwrite + 7)/8; cwrite-- > 0; ) {
185 CPLD_SPID = (v >> (8*cwrite)) & 0xff;
186 CPLD_SPIC = select | CPLD_SPIC_LOAD;
187 while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
188 ;
189 CPLD_SPIC = select;
190 while (!(CPLD_SPIC & CPLD_SPIC_DONE))
191 ;
192 }
193 v = 0;
194 }
195 if (cread) {
196 mdelay (2); /* *** FIXME: required by ads7843? */
197 v = 0;
198 for (cread = (cread + 7)/8; cread-- > 0;) {
199 CPLD_SPID = 0;
200 CPLD_SPIC = select | CPLD_SPIC_READ
201 | CPLD_SPIC_START;
202 while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
203 ;
204 CPLD_SPIC = select | CPLD_SPIC_READ;
205 while (!(CPLD_SPIC & CPLD_SPIC_DONE))
206 ;
207 v = (v << 8) | CPLD_SPID;
208 }
209 }
210 return v;
211 }
212#endif
213
214 PRINTK ("spi(%d) 0x%04x -> 0x%x\r\n", ssp_configuration.device,
215 v & 0x1ff, (v >> 9) & 0x7f);
216
217 enable_cs ();
218
219 v <<= CPLD_SPI_TX_SHIFT; /* Correction for position of SPI_TX bit */
220 while (cwrite--) {
221 CPLD_SPI
222 = (CPLD_SPI & ~CPLD_SPI_TX)
223 | ((v >> cwrite) & CPLD_SPI_TX);
224 udelay (T_DIS);
225 pulse_clock ();
226 }
227
228 if (cread < 0) {
229 int delay = 10;
230 disable_cs ();
231 udelay (1);
232 enable_cs ();
233
234 l = -1;
235 do {
236 if (CPLD_SPI & CPLD_SPI_RX) {
237 l = 0;
238 break;
239 }
240 } while (udelay (1), --delay);
241 }
242 else
243 /* We pulse the clock before the data to skip the leading zero. */
244 while (cread-- > 0) {
245 pulse_clock ();
246 l = (l<<1)
247 | (((CPLD_SPI & CPLD_SPI_RX)
248 >> CPLD_SPI_RX_SHIFT) & 0x1);
249 }
250
251 disable_cs ();
252 return l;
253}
254
255static int ssp_init (void)
256{
257 spin_lock_init (&ssp_lock);
258 memset (&ssp_configuration, 0, sizeof (ssp_configuration));
259 return 0;
260}
261
262
263/* ssp_chip_select
264
265 drops the chip select line for the CPLD shift-register controlled
266 devices. It doesn't enable chip
267
268*/
269
270static void ssp_chip_select (int enable)
271{
272#if defined (CONFIG_MACH_LPD7A400)
273 int select;
274
275 if (ssp_configuration.device == DEVICE_CODEC)
276 select = CPLD_SPIC_CS_CODEC;
277 else if (ssp_configuration.device == DEVICE_TOUCH)
278 select = CPLD_SPIC_CS_TOUCH;
279 else
280 return;
281
282 if (enable)
283 CPLD_SPIC = select;
284 else
285 CPLD_SPIC = 0;
286#endif
287}
288
289static void ssp_acquire (void)
290{
291 spin_lock (&ssp_lock);
292}
293
294static void ssp_release (void)
295{
296 ssp_chip_select (0); /* just in case */
297 spin_unlock (&ssp_lock);
298}
299
300static int ssp_configure (int device, int mode, int speed,
301 int frame_size_write, int frame_size_read)
302{
303 ssp_configuration.device = device;
304 ssp_configuration.mode = mode;
305 ssp_configuration.speed = speed;
306 ssp_configuration.frame_size_write = frame_size_write;
307 ssp_configuration.frame_size_read = frame_size_read;
308
309 return 0;
310}
311
312static int ssp_read (void)
313{
314 return execute_spi_command (0, 0, ssp_configuration.frame_size_read);
315}
316
317static int ssp_write (u16 data)
318{
319 execute_spi_command (data, ssp_configuration.frame_size_write, 0);
320 return 0;
321}
322
323static int ssp_write_read (u16 data)
324{
325 return execute_spi_command (data, ssp_configuration.frame_size_write,
326 ssp_configuration.frame_size_read);
327}
328
329struct ssp_driver lh7a40x_cpld_ssp_driver = {
330 .init = ssp_init,
331 .acquire = ssp_acquire,
332 .release = ssp_release,
333 .configure = ssp_configure,
334 .chip_select = ssp_chip_select,
335 .read = ssp_read,
336 .write = ssp_write,
337 .write_read = ssp_write_read,
338};
339
340
341MODULE_AUTHOR("Marc Singer");
342MODULE_DESCRIPTION("LPD7A40X CPLD SPI driver");
343MODULE_LICENSE("GPL");