• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/src/linux/linux-2.6/arch/mips/tx4938/toshiba_rbtx4938/
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
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);
38
39static 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
48static struct irqaction txx9_spi_action = {
49	.handler	= txx9_spi_interrupt,
50	.name		= "spi",
51};
52
53void __init txx9_spi_irqinit(int irc_irq)
54{
55	setup_irq(irc_irq, &txx9_spi_action);
56}
57
58int 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}
165