• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
enable_cs(void)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 
disable_cs(void)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 
pulse_clock(void)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 
execute_spi_command(int v,int cwrite,int cread)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 
ssp_init(void)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 
ssp_chip_select(int enable)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 
ssp_acquire(void)289 static void ssp_acquire (void)
290 {
291 	spin_lock (&ssp_lock);
292 }
293 
ssp_release(void)294 static void ssp_release (void)
295 {
296 	ssp_chip_select (0);	/* just in case */
297 	spin_unlock (&ssp_lock);
298 }
299 
ssp_configure(int device,int mode,int speed,int frame_size_write,int frame_size_read)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 
ssp_read(void)312 static int ssp_read (void)
313 {
314 	return execute_spi_command (0, 0, ssp_configuration.frame_size_read);
315 }
316 
ssp_write(u16 data)317 static int ssp_write (u16 data)
318 {
319 	execute_spi_command (data, ssp_configuration.frame_size_write, 0);
320 	return 0;
321 }
322 
ssp_write_read(u16 data)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