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