Merge ../linux-2.6-watchdog-mm
[sfrench/cifs-2.6.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 irqreturn_t 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         return IRQ_HANDLED;
46 }
47
48 static struct irqaction txx9_spi_action = {
49         .handler        = txx9_spi_interrupt,
50         .name           = "spi",
51 };
52
53 void __init txx9_spi_irqinit(int irc_irq)
54 {
55         setup_irq(irc_irq, &txx9_spi_action);
56 }
57
58 int txx9_spi_io(int chipid, struct spi_dev_desc *desc,
59                 unsigned char **inbufs, unsigned int *incounts,
60                 unsigned char **outbufs, unsigned int *outcounts,
61                 int cansleep)
62 {
63         unsigned int incount, outcount;
64         unsigned char *inp, *outp;
65         int ret;
66         unsigned long flags;
67
68         spin_lock_irqsave(&txx9_spi_lock, flags);
69         if ((tx4938_spiptr->mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE) {
70                 spin_unlock_irqrestore(&txx9_spi_lock, flags);
71                 return -EBUSY;
72         }
73         /* enter config mode */
74         tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
75         tx4938_spiptr->cr0 =
76                 (desc->byteorder ? TXx9_SPCR0_SBOS : 0) |
77                 (desc->polarity ? TXx9_SPCR0_SPOL : 0) |
78                 (desc->phase ? TXx9_SPCR0_SPHA : 0) |
79                 0x08;
80         tx4938_spiptr->cr1 =
81                 (((TXX9_IMCLK + desc->baud) / (2 * desc->baud) - 1) << 8) |
82                 0x08 /* 8 bit only */;
83         /* enter active mode */
84         tx4938_spiptr->mcr = TXx9_SPMCR_ACTIVE;
85         spin_unlock_irqrestore(&txx9_spi_lock, flags);
86
87         /* CS ON */
88         if ((ret = txx9_spi_cs_func(chipid, 1)) < 0) {
89                 spin_unlock_irqrestore(&txx9_spi_lock, flags);
90                 return ret;
91         }
92         udelay(desc->tcss);
93
94         /* do scatter IO */
95         inp = inbufs ? *inbufs : NULL;
96         outp = outbufs ? *outbufs : NULL;
97         incount = 0;
98         outcount = 0;
99         while (1) {
100                 unsigned char data;
101                 unsigned int count;
102                 int i;
103                 if (!incount) {
104                         incount = incounts ? *incounts++ : 0;
105                         inp = (incount && inbufs) ? *inbufs++ : NULL;
106                 }
107                 if (!outcount) {
108                         outcount = outcounts ? *outcounts++ : 0;
109                         outp = (outcount && outbufs) ? *outbufs++ : NULL;
110                 }
111                 if (!inp && !outp)
112                         break;
113                 count = SPI_FIFO_SIZE;
114                 if (incount)
115                         count = min(count, incount);
116                 if (outcount)
117                         count = min(count, outcount);
118
119                 /* now tx must be idle... */
120                 while (!(tx4938_spiptr->sr & TXx9_SPSR_SIDLE))
121                         ;
122
123                 tx4938_spiptr->cr0 =
124                         (tx4938_spiptr->cr0 & ~TXx9_SPCR0_RXIFL_MASK) |
125                         ((count - 1) << 12);
126                 if (cansleep) {
127                         /* enable rx intr */
128                         tx4938_spiptr->cr0 |= TXx9_SPCR0_RBSIE;
129                 }
130                 /* send */
131                 for (i = 0; i < count; i++)
132                         tx4938_spiptr->dr = inp ? *inp++ : 0;
133                 /* wait all rx data */
134                 if (cansleep) {
135                         wait_event(txx9_spi_wait,
136                                    tx4938_spiptr->sr & TXx9_SPSR_SRRDY);
137                 } else {
138                         while (!(tx4938_spiptr->sr & TXx9_SPSR_RBSI))
139                                 ;
140                 }
141                 /* receive */
142                 for (i = 0; i < count; i++) {
143                         data = tx4938_spiptr->dr;
144                         if (outp)
145                                 *outp++ = data;
146                 }
147                 if (incount)
148                         incount -= count;
149                 if (outcount)
150                         outcount -= count;
151         }
152
153         /* CS OFF */
154         udelay(desc->tcsh);
155         txx9_spi_cs_func(chipid, 0);
156         udelay(desc->tcsr);
157
158         spin_lock_irqsave(&txx9_spi_lock, flags);
159         /* enter config mode */
160         tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
161         spin_unlock_irqrestore(&txx9_spi_lock, flags);
162
163         return 0;
164 }