blob: b926e6a75c29b8ace85a6fea448eacf92dc4bd48 [file] [log] [blame]
Ralf Baechle23fbee92005-07-25 22:45:45 +00001/*
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
23static int (*txx9_spi_cs_func)(int chipid, int on);
24static DEFINE_SPINLOCK(txx9_spi_lock);
25
26extern unsigned int txx9_gbus_clock;
27
28#define SPI_FIFO_SIZE 4
29
30void __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
37static DECLARE_WAIT_QUEUE_HEAD(txx9_spi_wait);
Ralf Baechle937a8012006-10-07 19:44:33 +010038
39static void txx9_spi_interrupt(int irq, void *dev_id)
Ralf Baechle23fbee92005-07-25 22:45:45 +000040{
41 /* disable rx intr */
42 tx4938_spiptr->cr0 &= ~TXx9_SPCR0_RBSIE;
43 wake_up(&txx9_spi_wait);
44}
45static struct irqaction txx9_spi_action = {
46 txx9_spi_interrupt, 0, 0, "spi", NULL, NULL,
47};
48
49void __init txx9_spi_irqinit(int irc_irq)
50{
51 setup_irq(irc_irq, &txx9_spi_action);
52}
53
54int 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}