sparc: cleanup math-emu
[linux-2.6] / arch / arm / mach-lh7a40x / ssp-cpld.c
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 #include <linux/io.h>
47
48 #include <asm/irq.h>
49 #include <mach/hardware.h>
50
51 #include <mach/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
104 struct ssp_configuration {
105         int device;
106         int mode;
107         int speed;
108         int frame_size_write;
109         int frame_size_read;
110 };
111
112 static struct ssp_configuration ssp_configuration;
113 static spinlock_t ssp_lock;
114
115 static 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
125 static 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
135 static 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
158 static 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
255 static 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
270 static 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
289 static void ssp_acquire (void)
290 {
291         spin_lock (&ssp_lock);
292 }
293
294 static void ssp_release (void)
295 {
296         ssp_chip_select (0);    /* just in case */
297         spin_unlock (&ssp_lock);
298 }
299
300 static 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
312 static int ssp_read (void)
313 {
314         return execute_spi_command (0, 0, ssp_configuration.frame_size_read);
315 }
316
317 static int ssp_write (u16 data)
318 {
319         execute_spi_command (data, ssp_configuration.frame_size_write, 0);
320         return 0;
321 }
322
323 static 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
329 struct 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
341 MODULE_AUTHOR("Marc Singer");
342 MODULE_DESCRIPTION("LPD7A40X CPLD SPI driver");
343 MODULE_LICENSE("GPL");