Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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");
344 
  This page was automatically generated by the LXR engine.