diff options
Diffstat (limited to 'arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c')
-rw-r--r-- | arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c b/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c new file mode 100644 index 000000000000..fae3136f462d --- /dev/null +++ b/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c @@ -0,0 +1,159 @@ +/* + * linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c + * Copyright (C) 2000-2001 Toshiba Corporation + * + * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com) + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <asm/tx4938/spi.h> +#include <asm/tx4938/tx4938.h> + +static int (*txx9_spi_cs_func)(int chipid, int on); +static DEFINE_SPINLOCK(txx9_spi_lock); + +extern unsigned int txx9_gbus_clock; + +#define SPI_FIFO_SIZE 4 + +void __init txx9_spi_init(unsigned long base, int (*cs_func)(int chipid, int on)) +{ + txx9_spi_cs_func = cs_func; + /* enter config mode */ + tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR; +} + +static DECLARE_WAIT_QUEUE_HEAD(txx9_spi_wait); +static void txx9_spi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* disable rx intr */ + tx4938_spiptr->cr0 &= ~TXx9_SPCR0_RBSIE; + wake_up(&txx9_spi_wait); +} +static struct irqaction txx9_spi_action = { + txx9_spi_interrupt, 0, 0, "spi", NULL, NULL, +}; + +void __init txx9_spi_irqinit(int irc_irq) +{ + setup_irq(irc_irq, &txx9_spi_action); +} + +int txx9_spi_io(int chipid, struct spi_dev_desc *desc, + unsigned char **inbufs, unsigned int *incounts, + unsigned char **outbufs, unsigned int *outcounts, + int cansleep) +{ + unsigned int incount, outcount; + unsigned char *inp, *outp; + int ret; + unsigned long flags; + + spin_lock_irqsave(&txx9_spi_lock, flags); + if ((tx4938_spiptr->mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE) { + spin_unlock_irqrestore(&txx9_spi_lock, flags); + return -EBUSY; + } + /* enter config mode */ + tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR; + tx4938_spiptr->cr0 = + (desc->byteorder ? TXx9_SPCR0_SBOS : 0) | + (desc->polarity ? TXx9_SPCR0_SPOL : 0) | + (desc->phase ? TXx9_SPCR0_SPHA : 0) | + 0x08; + tx4938_spiptr->cr1 = + (((TXX9_IMCLK + desc->baud) / (2 * desc->baud) - 1) << 8) | + 0x08 /* 8 bit only */; + /* enter active mode */ + tx4938_spiptr->mcr = TXx9_SPMCR_ACTIVE; + spin_unlock_irqrestore(&txx9_spi_lock, flags); + + /* CS ON */ + if ((ret = txx9_spi_cs_func(chipid, 1)) < 0) { + spin_unlock_irqrestore(&txx9_spi_lock, flags); + return ret; + } + udelay(desc->tcss); + + /* do scatter IO */ + inp = inbufs ? *inbufs : NULL; + outp = outbufs ? *outbufs : NULL; + incount = 0; + outcount = 0; + while (1) { + unsigned char data; + unsigned int count; + int i; + if (!incount) { + incount = incounts ? *incounts++ : 0; + inp = (incount && inbufs) ? *inbufs++ : NULL; + } + if (!outcount) { + outcount = outcounts ? *outcounts++ : 0; + outp = (outcount && outbufs) ? *outbufs++ : NULL; + } + if (!inp && !outp) + break; + count = SPI_FIFO_SIZE; + if (incount) + count = min(count, incount); + if (outcount) + count = min(count, outcount); + + /* now tx must be idle... */ + while (!(tx4938_spiptr->sr & TXx9_SPSR_SIDLE)) + ; + + tx4938_spiptr->cr0 = + (tx4938_spiptr->cr0 & ~TXx9_SPCR0_RXIFL_MASK) | + ((count - 1) << 12); + if (cansleep) { + /* enable rx intr */ + tx4938_spiptr->cr0 |= TXx9_SPCR0_RBSIE; + } + /* send */ + for (i = 0; i < count; i++) + tx4938_spiptr->dr = inp ? *inp++ : 0; + /* wait all rx data */ + if (cansleep) { + wait_event(txx9_spi_wait, + tx4938_spiptr->sr & TXx9_SPSR_SRRDY); + } else { + while (!(tx4938_spiptr->sr & TXx9_SPSR_RBSI)) + ; + } + /* receive */ + for (i = 0; i < count; i++) { + data = tx4938_spiptr->dr; + if (outp) + *outp++ = data; + } + if (incount) + incount -= count; + if (outcount) + outcount -= count; + } + + /* CS OFF */ + udelay(desc->tcsh); + txx9_spi_cs_func(chipid, 0); + udelay(desc->tcsr); + + spin_lock_irqsave(&txx9_spi_lock, flags); + /* enter config mode */ + tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR; + spin_unlock_irqrestore(&txx9_spi_lock, flags); + + return 0; +} |