Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[pandora-kernel.git] / arch / mips / tx4938 / toshiba_rbtx4938 / spi_txx9.c
1 /*
2  * linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c
3  * Copyright (C) 2000-2001 Toshiba Corporation
4  *
5  * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
6  * terms of the GNU General Public License version 2. This program is
7  * licensed "as is" without any warranty of any kind, whether express
8  * or implied.
9  *
10  * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
11  */
12 #include <linux/init.h>
13 #include <linux/delay.h>
14 #include <linux/errno.h>
15 #include <linux/interrupt.h>
16 #include <linux/module.h>
17 #include <linux/sched.h>
18 #include <linux/spinlock.h>
19 #include <linux/wait.h>
20 #include <asm/tx4938/spi.h>
21 #include <asm/tx4938/tx4938.h>
22
23 static int (*txx9_spi_cs_func)(int chipid, int on);
24 static DEFINE_SPINLOCK(txx9_spi_lock);
25
26 extern unsigned int txx9_gbus_clock;
27
28 #define SPI_FIFO_SIZE   4
29
30 void __init txx9_spi_init(unsigned long base, int (*cs_func)(int chipid, int on))
31 {
32         txx9_spi_cs_func = cs_func;
33         /* enter config mode */
34         tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
35 }
36
37 static DECLARE_WAIT_QUEUE_HEAD(txx9_spi_wait);
38
39 static void txx9_spi_interrupt(int irq, void *dev_id)
40 {
41         /* disable rx intr */
42         tx4938_spiptr->cr0 &= ~TXx9_SPCR0_RBSIE;
43         wake_up(&txx9_spi_wait);
44 }
45 static struct irqaction txx9_spi_action = {
46         txx9_spi_interrupt, 0, 0, "spi", NULL, NULL,
47 };
48
49 void __init txx9_spi_irqinit(int irc_irq)
50 {
51         setup_irq(irc_irq, &txx9_spi_action);
52 }
53
54 int txx9_spi_io(int chipid, struct spi_dev_desc *desc,
55                 unsigned char **inbufs, unsigned int *incounts,
56                 unsigned char **outbufs, unsigned int *outcounts,
57                 int cansleep)
58 {
59         unsigned int incount, outcount;
60         unsigned char *inp, *outp;
61         int ret;
62         unsigned long flags;
63
64         spin_lock_irqsave(&txx9_spi_lock, flags);
65         if ((tx4938_spiptr->mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE) {
66                 spin_unlock_irqrestore(&txx9_spi_lock, flags);
67                 return -EBUSY;
68         }
69         /* enter config mode */
70         tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
71         tx4938_spiptr->cr0 =
72                 (desc->byteorder ? TXx9_SPCR0_SBOS : 0) |
73                 (desc->polarity ? TXx9_SPCR0_SPOL : 0) |
74                 (desc->phase ? TXx9_SPCR0_SPHA : 0) |
75                 0x08;
76         tx4938_spiptr->cr1 =
77                 (((TXX9_IMCLK + desc->baud) / (2 * desc->baud) - 1) << 8) |
78                 0x08 /* 8 bit only */;
79         /* enter active mode */
80         tx4938_spiptr->mcr = TXx9_SPMCR_ACTIVE;
81         spin_unlock_irqrestore(&txx9_spi_lock, flags);
82
83         /* CS ON */
84         if ((ret = txx9_spi_cs_func(chipid, 1)) < 0) {
85                 spin_unlock_irqrestore(&txx9_spi_lock, flags);
86                 return ret;
87         }
88         udelay(desc->tcss);
89
90         /* do scatter IO */
91         inp = inbufs ? *inbufs : NULL;
92         outp = outbufs ? *outbufs : NULL;
93         incount = 0;
94         outcount = 0;
95         while (1) {
96                 unsigned char data;
97                 unsigned int count;
98                 int i;
99                 if (!incount) {
100                         incount = incounts ? *incounts++ : 0;
101                         inp = (incount && inbufs) ? *inbufs++ : NULL;
102                 }
103                 if (!outcount) {
104                         outcount = outcounts ? *outcounts++ : 0;
105                         outp = (outcount && outbufs) ? *outbufs++ : NULL;
106                 }
107                 if (!inp && !outp)
108                         break;
109                 count = SPI_FIFO_SIZE;
110                 if (incount)
111                         count = min(count, incount);
112                 if (outcount)
113                         count = min(count, outcount);
114
115                 /* now tx must be idle... */
116                 while (!(tx4938_spiptr->sr & TXx9_SPSR_SIDLE))
117                         ;
118
119                 tx4938_spiptr->cr0 =
120                         (tx4938_spiptr->cr0 & ~TXx9_SPCR0_RXIFL_MASK) |
121                         ((count - 1) << 12);
122                 if (cansleep) {
123                         /* enable rx intr */
124                         tx4938_spiptr->cr0 |= TXx9_SPCR0_RBSIE;
125                 }
126                 /* send */
127                 for (i = 0; i < count; i++)
128                         tx4938_spiptr->dr = inp ? *inp++ : 0;
129                 /* wait all rx data */
130                 if (cansleep) {
131                         wait_event(txx9_spi_wait,
132                                    tx4938_spiptr->sr & TXx9_SPSR_SRRDY);
133                 } else {
134                         while (!(tx4938_spiptr->sr & TXx9_SPSR_RBSI))
135                                 ;
136                 }
137                 /* receive */
138                 for (i = 0; i < count; i++) {
139                         data = tx4938_spiptr->dr;
140                         if (outp)
141                                 *outp++ = data;
142                 }
143                 if (incount)
144                         incount -= count;
145                 if (outcount)
146                         outcount -= count;
147         }
148
149         /* CS OFF */
150         udelay(desc->tcsh);
151         txx9_spi_cs_func(chipid, 0);
152         udelay(desc->tcsr);
153
154         spin_lock_irqsave(&txx9_spi_lock, flags);
155         /* enter config mode */
156         tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
157         spin_unlock_irqrestore(&txx9_spi_lock, flags);
158
159         return 0;
160 }