NAND read/write fix
[platform/kernel/u-boot.git] / common / soft_spi.c
1 /*
2  * (C) Copyright 2002
3  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
4  *
5  * Influenced by code from:
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  *
8  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26
27 #include <common.h>
28 #include <spi.h>
29
30 #if defined(CONFIG_SOFT_SPI)
31
32 #include <malloc.h>
33
34 /*-----------------------------------------------------------------------
35  * Definitions
36  */
37
38 #ifdef DEBUG_SPI
39 #define PRINTD(fmt,args...)     printf (fmt ,##args)
40 #else
41 #define PRINTD(fmt,args...)
42 #endif
43
44 struct soft_spi_slave {
45         struct spi_slave slave;
46         unsigned int mode;
47 };
48
49 static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)
50 {
51         return container_of(slave, struct soft_spi_slave, slave);
52 }
53
54 /*=====================================================================*/
55 /*                         Public Functions                            */
56 /*=====================================================================*/
57
58 /*-----------------------------------------------------------------------
59  * Initialization
60  */
61 void spi_init (void)
62 {
63 #ifdef  SPI_INIT
64         volatile immap_t *immr = (immap_t *)CFG_IMMR;
65
66         SPI_INIT;
67 #endif
68 }
69
70 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
71                 unsigned int max_hz, unsigned int mode)
72 {
73         struct soft_spi_slave *ss;
74
75         if (!spi_cs_is_valid(bus, cs))
76                 return NULL;
77
78         ss = malloc(sizeof(struct soft_spi_slave));
79         if (!ss)
80                 return NULL;
81
82         ss->slave.bus = bus;
83         ss->slave.cs = cs;
84         ss->mode = mode;
85
86         /* TODO: Use max_hz to limit the SCK rate */
87
88         return &ss->slave;
89 }
90
91 void spi_free_slave(struct spi_slave *slave)
92 {
93         struct soft_spi_slave *ss = to_soft_spi(slave);
94
95         free(ss);
96 }
97
98 int spi_claim_bus(struct spi_slave *slave)
99 {
100 #ifdef CFG_IMMR
101         volatile immap_t *immr = (immap_t *)CFG_IMMR;
102 #endif
103         struct soft_spi_slave *ss = to_soft_spi(slave);
104
105         /*
106          * Make sure the SPI clock is in idle state as defined for
107          * this slave.
108          */
109         if (ss->mode & SPI_CPOL)
110                 SPI_SCL(1);
111         else
112                 SPI_SCL(0);
113
114         return 0;
115 }
116
117 void spi_release_bus(struct spi_slave *slave)
118 {
119         /* Nothing to do */
120 }
121
122 /*-----------------------------------------------------------------------
123  * SPI transfer
124  *
125  * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
126  * "bitlen" bits in the SPI MISO port.  That's just the way SPI works.
127  *
128  * The source of the outgoing bits is the "dout" parameter and the
129  * destination of the input bits is the "din" parameter.  Note that "dout"
130  * and "din" can point to the same memory location, in which case the
131  * input data overwrites the output data (since both are buffered by
132  * temporary variables, this is OK).
133  */
134 int  spi_xfer(struct spi_slave *slave, unsigned int bitlen,
135                 const void *dout, void *din, unsigned long flags)
136 {
137 #ifdef CFG_IMMR
138         volatile immap_t *immr = (immap_t *)CFG_IMMR;
139 #endif
140         struct soft_spi_slave *ss = to_soft_spi(slave);
141         uchar           tmpdin  = 0;
142         uchar           tmpdout = 0;
143         const u8        *txd = dout;
144         u8              *rxd = din;
145         int             cpol = ss->mode & SPI_CPOL;
146         int             cpha = ss->mode & SPI_CPHA;
147         unsigned int    j;
148
149         PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
150                 slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen);
151
152         if (flags & SPI_XFER_BEGIN)
153                 spi_cs_activate(slave);
154
155         for(j = 0; j < bitlen; j++) {
156                 /*
157                  * Check if it is time to work on a new byte.
158                  */
159                 if((j % 8) == 0) {
160                         tmpdout = *txd++;
161                         if(j != 0) {
162                                 *rxd++ = tmpdin;
163                         }
164                         tmpdin  = 0;
165                 }
166
167                 if (!cpha)
168                         SPI_SCL(!cpol);
169                 SPI_SDA(tmpdout & 0x80);
170                 SPI_DELAY;
171                 if (cpha)
172                         SPI_SCL(!cpol);
173                 else
174                         SPI_SCL(cpol);
175                 tmpdin  <<= 1;
176                 tmpdin  |= SPI_READ;
177                 tmpdout <<= 1;
178                 SPI_DELAY;
179                 if (cpha)
180                         SPI_SCL(cpol);
181         }
182         /*
183          * If the number of bits isn't a multiple of 8, shift the last
184          * bits over to left-justify them.  Then store the last byte
185          * read in.
186          */
187         if((bitlen % 8) != 0)
188                 tmpdin <<= 8 - (bitlen % 8);
189         *rxd++ = tmpdin;
190
191         if (flags & SPI_XFER_END)
192                 spi_cs_deactivate(slave);
193
194         return(0);
195 }
196
197 #endif  /* CONFIG_SOFT_SPI */