123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /* arch/arm/mach-lh7a40x/ssp-cpld.c
- *
- * Copyright (C) 2004,2005 Marc Singer
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * SSP/SPI driver for the CardEngine CPLD.
- *
- */
- /* NOTES
- -----
- o *** This driver is cribbed from the 7952x implementation.
- Some comments may not apply.
- o This driver contains sufficient logic to control either the
- serial EEPROMs or the audio codec. It is included in the kernel
- to support the codec. The EEPROMs are really the responsibility
- of the boot loader and should probably be left alone.
- o The code must be augmented to cope with multiple, simultaneous
- clients.
- o The audio codec writes to the codec chip whenever playback
- starts.
- o The touchscreen driver writes to the ads chip every time it
- samples.
- o The audio codec must write 16 bits, but the touch chip writes
- are 8 bits long.
- o We need to be able to keep these configurations separate while
- simultaneously active.
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- //#include <linux/sched.h>
- #include <linux/errno.h>
- #include <linux/interrupt.h>
- //#include <linux/ioport.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/spinlock.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <asm/hardware.h>
- #include <asm/arch/ssp.h>
- //#define TALK
- #if defined (TALK)
- #define PRINTK(f...) printk (f)
- #else
- #define PRINTK(f...) do {} while (0)
- #endif
- #if defined (CONFIG_ARCH_LH7A400)
- # define CPLD_SPID __REGP16(CPLD06_VIRT) /* SPI data */
- # define CPLD_SPIC __REGP16(CPLD08_VIRT) /* SPI control */
- # define CPLD_SPIC_CS_CODEC (1<<0)
- # define CPLD_SPIC_CS_TOUCH (1<<1)
- # define CPLD_SPIC_WRITE (0<<2)
- # define CPLD_SPIC_READ (1<<2)
- # define CPLD_SPIC_DONE (1<<3) /* r/o */
- # define CPLD_SPIC_LOAD (1<<4)
- # define CPLD_SPIC_START (1<<4)
- # define CPLD_SPIC_LOADED (1<<5) /* r/o */
- #endif
- #define CPLD_SPI __REGP16(CPLD0A_VIRT) /* SPI operation */
- #define CPLD_SPI_CS_EEPROM (1<<3)
- #define CPLD_SPI_SCLK (1<<2)
- #define CPLD_SPI_TX_SHIFT (1)
- #define CPLD_SPI_TX (1<<CPLD_SPI_TX_SHIFT)
- #define CPLD_SPI_RX_SHIFT (0)
- #define CPLD_SPI_RX (1<<CPLD_SPI_RX_SHIFT)
- /* *** FIXME: these timing values are substantially larger than the
- *** chip requires. We may implement an nsleep () function. */
- #define T_SKH 1 /* Clock time high (us) */
- #define T_SKL 1 /* Clock time low (us) */
- #define T_CS 1 /* Minimum chip select low time (us) */
- #define T_CSS 1 /* Minimum chip select setup time (us) */
- #define T_DIS 1 /* Data setup time (us) */
- /* EEPROM SPI bits */
- #define P_START (1<<9)
- #define P_WRITE (1<<7)
- #define P_READ (2<<7)
- #define P_ERASE (3<<7)
- #define P_EWDS (0<<7)
- #define P_WRAL (0<<7)
- #define P_ERAL (0<<7)
- #define P_EWEN (0<<7)
- #define P_A_EWDS (0<<5)
- #define P_A_WRAL (1<<5)
- #define P_A_ERAL (2<<5)
- #define P_A_EWEN (3<<5)
- struct ssp_configuration {
- int device;
- int mode;
- int speed;
- int frame_size_write;
- int frame_size_read;
- };
- static struct ssp_configuration ssp_configuration;
- static spinlock_t ssp_lock;
- static void enable_cs (void)
- {
- switch (ssp_configuration.device) {
- case DEVICE_EEPROM:
- CPLD_SPI |= CPLD_SPI_CS_EEPROM;
- break;
- }
- udelay (T_CSS);
- }
- static void disable_cs (void)
- {
- switch (ssp_configuration.device) {
- case DEVICE_EEPROM:
- CPLD_SPI &= ~CPLD_SPI_CS_EEPROM;
- break;
- }
- udelay (T_CS);
- }
- static void pulse_clock (void)
- {
- CPLD_SPI |= CPLD_SPI_SCLK;
- udelay (T_SKH);
- CPLD_SPI &= ~CPLD_SPI_SCLK;
- udelay (T_SKL);
- }
- /* execute_spi_command
- sends an spi command to a device. It first sends cwrite bits from
- v. If cread is greater than zero it will read cread bits
- (discarding the leading 0 bit) and return them. If cread is less
- than zero it will check for completetion status and return 0 on
- success or -1 on timeout. If cread is zero it does nothing other
- than sending the command.
- On the LPD7A400, we can only read or write multiples of 8 bits on
- the codec and the touch screen device. Here, we round up.
- */
- static int execute_spi_command (int v, int cwrite, int cread)
- {
- unsigned long l = 0;
- #if defined (CONFIG_MACH_LPD7A400)
- /* The codec and touch devices cannot be bit-banged. Instead,
- * the CPLD provides an eight-bit shift register and a crude
- * interface. */
- if ( ssp_configuration.device == DEVICE_CODEC
- || ssp_configuration.device == DEVICE_TOUCH) {
- int select = 0;
- PRINTK ("spi(%d %d.%d) 0x%04x",
- ssp_configuration.device, cwrite, cread,
- v);
- #if defined (TALK)
- if (ssp_configuration.device == DEVICE_CODEC)
- PRINTK (" 0x%03x -> %2d", v & 0x1ff, (v >> 9) & 0x7f);
- #endif
- PRINTK ("\n");
- if (ssp_configuration.device == DEVICE_CODEC)
- select = CPLD_SPIC_CS_CODEC;
- if (ssp_configuration.device == DEVICE_TOUCH)
- select = CPLD_SPIC_CS_TOUCH;
- if (cwrite) {
- for (cwrite = (cwrite + 7)/8; cwrite-- > 0; ) {
- CPLD_SPID = (v >> (8*cwrite)) & 0xff;
- CPLD_SPIC = select | CPLD_SPIC_LOAD;
- while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
- ;
- CPLD_SPIC = select;
- while (!(CPLD_SPIC & CPLD_SPIC_DONE))
- ;
- }
- v = 0;
- }
- if (cread) {
- mdelay (2); /* *** FIXME: required by ads7843? */
- v = 0;
- for (cread = (cread + 7)/8; cread-- > 0;) {
- CPLD_SPID = 0;
- CPLD_SPIC = select | CPLD_SPIC_READ
- | CPLD_SPIC_START;
- while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
- ;
- CPLD_SPIC = select | CPLD_SPIC_READ;
- while (!(CPLD_SPIC & CPLD_SPIC_DONE))
- ;
- v = (v << 8) | CPLD_SPID;
- }
- }
- return v;
- }
- #endif
- PRINTK ("spi(%d) 0x%04x -> 0x%x\r\n", ssp_configuration.device,
- v & 0x1ff, (v >> 9) & 0x7f);
- enable_cs ();
- v <<= CPLD_SPI_TX_SHIFT; /* Correction for position of SPI_TX bit */
- while (cwrite--) {
- CPLD_SPI
- = (CPLD_SPI & ~CPLD_SPI_TX)
- | ((v >> cwrite) & CPLD_SPI_TX);
- udelay (T_DIS);
- pulse_clock ();
- }
- if (cread < 0) {
- int delay = 10;
- disable_cs ();
- udelay (1);
- enable_cs ();
- l = -1;
- do {
- if (CPLD_SPI & CPLD_SPI_RX) {
- l = 0;
- break;
- }
- } while (udelay (1), --delay);
- }
- else
- /* We pulse the clock before the data to skip the leading zero. */
- while (cread-- > 0) {
- pulse_clock ();
- l = (l<<1)
- | (((CPLD_SPI & CPLD_SPI_RX)
- >> CPLD_SPI_RX_SHIFT) & 0x1);
- }
- disable_cs ();
- return l;
- }
- static int ssp_init (void)
- {
- spin_lock_init (&ssp_lock);
- memset (&ssp_configuration, 0, sizeof (ssp_configuration));
- return 0;
- }
- /* ssp_chip_select
- drops the chip select line for the CPLD shift-register controlled
- devices. It doesn't enable chip
- */
- static void ssp_chip_select (int enable)
- {
- #if defined (CONFIG_MACH_LPD7A400)
- int select;
- if (ssp_configuration.device == DEVICE_CODEC)
- select = CPLD_SPIC_CS_CODEC;
- else if (ssp_configuration.device == DEVICE_TOUCH)
- select = CPLD_SPIC_CS_TOUCH;
- else
- return;
- if (enable)
- CPLD_SPIC = select;
- else
- CPLD_SPIC = 0;
- #endif
- }
- static void ssp_acquire (void)
- {
- spin_lock (&ssp_lock);
- }
- static void ssp_release (void)
- {
- ssp_chip_select (0); /* just in case */
- spin_unlock (&ssp_lock);
- }
- static int ssp_configure (int device, int mode, int speed,
- int frame_size_write, int frame_size_read)
- {
- ssp_configuration.device = device;
- ssp_configuration.mode = mode;
- ssp_configuration.speed = speed;
- ssp_configuration.frame_size_write = frame_size_write;
- ssp_configuration.frame_size_read = frame_size_read;
- return 0;
- }
- static int ssp_read (void)
- {
- return execute_spi_command (0, 0, ssp_configuration.frame_size_read);
- }
- static int ssp_write (u16 data)
- {
- execute_spi_command (data, ssp_configuration.frame_size_write, 0);
- return 0;
- }
- static int ssp_write_read (u16 data)
- {
- return execute_spi_command (data, ssp_configuration.frame_size_write,
- ssp_configuration.frame_size_read);
- }
- struct ssp_driver lh7a40x_cpld_ssp_driver = {
- .init = ssp_init,
- .acquire = ssp_acquire,
- .release = ssp_release,
- .configure = ssp_configure,
- .chip_select = ssp_chip_select,
- .read = ssp_read,
- .write = ssp_write,
- .write_read = ssp_write_read,
- };
- MODULE_AUTHOR("Marc Singer");
- MODULE_DESCRIPTION("LPD7A40X CPLD SPI driver");
- MODULE_LICENSE("GPL");
|