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#define T_SKH 1 /* Clock time high (us) */ 83#define T_SKL 1 /* Clock time low (us) */ 84#define T_CS 1 /* Minimum chip select low time (us) */ 85#define T_CSS 1 /* Minimum chip select setup time (us) */ 86#define T_DIS 1 /* Data setup time (us) */ 87 88 /* EEPROM SPI bits */ 89#define P_START (1<<9) 90#define P_WRITE (1<<7) 91#define P_READ (2<<7) 92#define P_ERASE (3<<7) 93#define P_EWDS (0<<7) 94#define P_WRAL (0<<7) 95#define P_ERAL (0<<7) 96#define P_EWEN (0<<7) 97#define P_A_EWDS (0<<5) 98#define P_A_WRAL (1<<5) 99#define P_A_ERAL (2<<5) 100#define P_A_EWEN (3<<5) 101 102struct ssp_configuration { 103 int device; 104 int mode; 105 int speed; 106 int frame_size_write; 107 int frame_size_read; 108}; 109 110static struct ssp_configuration ssp_configuration; 111static spinlock_t ssp_lock; 112 113static void enable_cs (void) 114{ 115 switch (ssp_configuration.device) { 116 case DEVICE_EEPROM: 117 CPLD_SPI |= CPLD_SPI_CS_EEPROM; 118 break; 119 } 120 udelay (T_CSS); 121} 122 123static void disable_cs (void) 124{ 125 switch (ssp_configuration.device) { 126 case DEVICE_EEPROM: 127 CPLD_SPI &= ~CPLD_SPI_CS_EEPROM; 128 break; 129 } 130 udelay (T_CS); 131} 132 133static void pulse_clock (void) 134{ 135 CPLD_SPI |= CPLD_SPI_SCLK; 136 udelay (T_SKH); 137 CPLD_SPI &= ~CPLD_SPI_SCLK; 138 udelay (T_SKL); 139} 140 141 142/* execute_spi_command 143 144 sends an spi command to a device. It first sends cwrite bits from 145 v. If cread is greater than zero it will read cread bits 146 (discarding the leading 0 bit) and return them. If cread is less 147 than zero it will check for completetion status and return 0 on 148 success or -1 on timeout. If cread is zero it does nothing other 149 than sending the command. 150 151 On the LPD7A400, we can only read or write multiples of 8 bits on 152 the codec and the touch screen device. Here, we round up. 153 154*/ 155 156static int execute_spi_command (int v, int cwrite, int cread) 157{ 158 unsigned long l = 0; 159 160#if defined(CONFIG_MACH_LPD7A400) 161 /* The codec and touch devices cannot be bit-banged. Instead, 162 * the CPLD provides an eight-bit shift register and a crude 163 * interface. */ 164 if ( ssp_configuration.device == DEVICE_CODEC 165 || ssp_configuration.device == DEVICE_TOUCH) { 166 int select = 0; 167 168 PRINTK ("spi(%d %d.%d) 0x%04x", 169 ssp_configuration.device, cwrite, cread, 170 v); 171#if defined(TALK) 172 if (ssp_configuration.device == DEVICE_CODEC) 173 PRINTK (" 0x%03x -> %2d", v & 0x1ff, (v >> 9) & 0x7f); 174#endif 175 PRINTK ("\n"); 176 177 if (ssp_configuration.device == DEVICE_CODEC) 178 select = CPLD_SPIC_CS_CODEC; 179 if (ssp_configuration.device == DEVICE_TOUCH) 180 select = CPLD_SPIC_CS_TOUCH; 181 if (cwrite) { 182 for (cwrite = (cwrite + 7)/8; cwrite-- > 0; ) { 183 CPLD_SPID = (v >> (8*cwrite)) & 0xff; 184 CPLD_SPIC = select | CPLD_SPIC_LOAD; 185 while (!(CPLD_SPIC & CPLD_SPIC_LOADED)) 186 ; 187 CPLD_SPIC = select; 188 while (!(CPLD_SPIC & CPLD_SPIC_DONE)) 189 ; 190 } 191 v = 0; 192 } 193 if (cread) { 194 mdelay (2); 195 v = 0; 196 for (cread = (cread + 7)/8; cread-- > 0;) { 197 CPLD_SPID = 0; 198 CPLD_SPIC = select | CPLD_SPIC_READ 199 | CPLD_SPIC_START; 200 while (!(CPLD_SPIC & CPLD_SPIC_LOADED)) 201 ; 202 CPLD_SPIC = select | CPLD_SPIC_READ; 203 while (!(CPLD_SPIC & CPLD_SPIC_DONE)) 204 ; 205 v = (v << 8) | CPLD_SPID; 206 } 207 } 208 return v; 209 } 210#endif 211 212 PRINTK ("spi(%d) 0x%04x -> 0x%x\r\n", ssp_configuration.device, 213 v & 0x1ff, (v >> 9) & 0x7f); 214 215 enable_cs (); 216 217 v <<= CPLD_SPI_TX_SHIFT; /* Correction for position of SPI_TX bit */ 218 while (cwrite--) { 219 CPLD_SPI 220 = (CPLD_SPI & ~CPLD_SPI_TX) 221 | ((v >> cwrite) & CPLD_SPI_TX); 222 udelay (T_DIS); 223 pulse_clock (); 224 } 225 226 if (cread < 0) { 227 int delay = 10; 228 disable_cs (); 229 udelay (1); 230 enable_cs (); 231 232 l = -1; 233 do { 234 if (CPLD_SPI & CPLD_SPI_RX) { 235 l = 0; 236 break; 237 } 238 } while (udelay (1), --delay); 239 } 240 else 241 /* We pulse the clock before the data to skip the leading zero. */ 242 while (cread-- > 0) { 243 pulse_clock (); 244 l = (l<<1) 245 | (((CPLD_SPI & CPLD_SPI_RX) 246 >> CPLD_SPI_RX_SHIFT) & 0x1); 247 } 248 249 disable_cs (); 250 return l; 251} 252 253static int ssp_init (void) 254{ 255 spin_lock_init (&ssp_lock); 256 memset (&ssp_configuration, 0, sizeof (ssp_configuration)); 257 return 0; 258} 259 260 261/* ssp_chip_select 262 263 drops the chip select line for the CPLD shift-register controlled 264 devices. It doesn't enable chip 265 266*/ 267 268static void ssp_chip_select (int enable) 269{ 270#if defined(CONFIG_MACH_LPD7A400) 271 int select; 272 273 if (ssp_configuration.device == DEVICE_CODEC) 274 select = CPLD_SPIC_CS_CODEC; 275 else if (ssp_configuration.device == DEVICE_TOUCH) 276 select = CPLD_SPIC_CS_TOUCH; 277 else 278 return; 279 280 if (enable) 281 CPLD_SPIC = select; 282 else 283 CPLD_SPIC = 0; 284#endif 285} 286 287static void ssp_acquire (void) 288{ 289 spin_lock (&ssp_lock); 290} 291 292static void ssp_release (void) 293{ 294 ssp_chip_select (0); /* just in case */ 295 spin_unlock (&ssp_lock); 296} 297 298static int ssp_configure (int device, int mode, int speed, 299 int frame_size_write, int frame_size_read) 300{ 301 ssp_configuration.device = device; 302 ssp_configuration.mode = mode; 303 ssp_configuration.speed = speed; 304 ssp_configuration.frame_size_write = frame_size_write; 305 ssp_configuration.frame_size_read = frame_size_read; 306 307 return 0; 308} 309 310static int ssp_read (void) 311{ 312 return execute_spi_command (0, 0, ssp_configuration.frame_size_read); 313} 314 315static int ssp_write (u16 data) 316{ 317 execute_spi_command (data, ssp_configuration.frame_size_write, 0); 318 return 0; 319} 320 321static int ssp_write_read (u16 data) 322{ 323 return execute_spi_command (data, ssp_configuration.frame_size_write, 324 ssp_configuration.frame_size_read); 325} 326 327struct ssp_driver lh7a40x_cpld_ssp_driver = { 328 .init = ssp_init, 329 .acquire = ssp_acquire, 330 .release = ssp_release, 331 .configure = ssp_configure, 332 .chip_select = ssp_chip_select, 333 .read = ssp_read, 334 .write = ssp_write, 335 .write_read = ssp_write_read, 336}; 337 338 339MODULE_AUTHOR("Marc Singer"); 340MODULE_DESCRIPTION("LPD7A40X CPLD SPI driver"); 341MODULE_LICENSE("GPL"); 342