]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - cpu/arm926ejs/mx23/spi.c
applied patches from Freescale and Ka-Ro
[karo-tx-uboot.git] / cpu / arm926ejs / mx23 / spi.c
1 /*
2  * Copyright (C) 2008 Embedded Alley Solutions Inc.
3  *
4  * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
5  *
6  * Freescale MX23 SSP/SPI driver
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <asm/arch/spi.h>
24
25 #define SPI_NUM_BUSES   2
26 #define SPI_NUM_SLAVES  3
27
28 /* Initalized in spi_init() depending on SSP port configuration */
29 static unsigned long ssp_bases[SPI_NUM_BUSES];
30
31 /* Set in spi_set_cfg() depending on which SSP port is being used */
32 static unsigned long ssp_base = SSP1_BASE;
33
34 /*
35  * Init SSP port: SSP1 (@bus = 0) or SSP2 (@bus == 1)
36  */
37 static void ssp_spi_init(unsigned int bus)
38 {
39         u32 spi_div;
40         u32 val = 0;
41
42         if (bus >= SPI_NUM_BUSES) {
43                 printf("SPI bus %d doesn't exist\n", bus);
44                 return;
45         }
46
47         ssp_base = ssp_bases[bus];
48
49         /* Reset block */
50
51         /* Clear SFTRST */
52         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_SFTRST);
53         while (REG_RD(ssp_base + SSP_CTRL0) & CTRL0_SFTRST)
54                 ;
55
56         /* Clear CLKGATE */
57         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_CLKGATE);
58
59         /* Set SFTRST and wait until CLKGATE is set */
60         REG_SET(ssp_base + SSP_CTRL0, CTRL0_SFTRST);
61         while (!(REG_RD(ssp_base + SSP_CTRL0) & CTRL0_CLKGATE))
62                 ;
63
64         /* Clear SFTRST and CLKGATE */
65         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_SFTRST);
66         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_CLKGATE);
67
68         /*
69         * Set CLK to desired value
70         */
71
72         spi_div = ((CONFIG_SSP_CLK>>1) + CONFIG_SPI_CLK - 1) / CONFIG_SPI_CLK;
73         val = (2 << TIMING_CLOCK_DIVIDE) | ((spi_div - 1) << TIMING_CLOCK_RATE);
74         REG_WR(ssp_base + SSP_TIMING, val);
75
76         /* Set transfer parameters */
77
78         /* Set SSP SPI Master mode and word length to 8 bit */
79         REG_WR(ssp_base + SSP_CTRL1, WORD_LENGTH8 | SSP_MODE_SPI);
80
81         /* Set BUS_WIDTH to 1 bit and XFER_COUNT to 1 byte */
82         REG_WR(ssp_base + SSP_CTRL0,
83                BUS_WIDTH_SPI1 | (0x1 << CTRL0_XFER_COUNT));
84
85         /*
86         * Set BLOCK_SIZE and BLOCK_COUNT to 0, so that XFER_COUNT
87         * reflects number of bytes to send. Disalbe other bits as
88         * well
89         */
90         REG_WR(ssp_base + SSP_CMD0, 0x0);
91 }
92
93 /*
94  * Init SSP ports, must be called first and only once
95  */
96 void spi_init(void)
97 {
98 #ifdef CONFIG_SPI_SSP1
99         ssp_bases[0] = SSP1_BASE;
100         ssp_spi_init(0);
101 #endif
102
103 #ifdef CONFIG_SPI_SSP2
104         ssp_bases[1] = SSP2_BASE;
105         ssp_spi_init(1);
106 #endif
107 }
108
109 void spi_set_cfg(unsigned int bus, unsigned int cs, unsigned long mode)
110 {
111         u32 clr_mask = 0;
112         u32 set_mask = 0;
113
114         if (bus >= SPI_NUM_BUSES || cs >= SPI_NUM_SLAVES) {
115                 printf("SPI device %d:%d doesn't exist", bus, cs);
116                 return;
117         }
118
119         if (ssp_bases[bus] == 0) {
120                 printf("SSP port %d isn't in SPI mode\n", bus + 1);
121                 return;
122         }
123
124         /* Set SSP port to use */
125         ssp_base = ssp_bases[bus];
126
127         /* Set phase and polarity: HW_SSP_CTRL1 */
128         if (mode & SPI_PHASE)
129                 set_mask |= CTRL1_PHASE;
130         else
131                 clr_mask |= CTRL1_PHASE;
132
133         if (mode & SPI_POLARITY)
134                 set_mask |= CTRL1_POLARITY;
135         else
136                 clr_mask |= CTRL1_POLARITY;
137
138         REG_SET(ssp_base + SSP_CTRL1, set_mask);
139         REG_CLR(ssp_base + SSP_CTRL1, clr_mask);
140
141         /* Set SSn number: HW_SSP_CTRL0 */
142         REG_CLR(ssp_base + SSP_CTRL0, SPI_CS_CLR_MASK);
143
144         switch (cs) {
145         case 0:
146                 set_mask = SPI_CS0;
147                 break;
148         case 1:
149                 set_mask = SPI_CS1;
150                 break;
151         case 2:
152                 set_mask = SPI_CS2;
153                 break;
154         }
155
156         REG_SET(ssp_base + SSP_CTRL0, set_mask);
157 }
158
159 /* Read single data byte */
160 static unsigned char spi_read(void)
161 {
162         unsigned char b = 0;
163
164         /* Set XFER_LENGTH to 1 */
165         REG_CLR(ssp_base + SSP_CTRL0, 0xffff);
166         REG_SET(ssp_base + SSP_CTRL0, 1);
167
168         /* Enable READ mode */
169         REG_SET(ssp_base + SSP_CTRL0, CTRL0_READ);
170
171         /* Set RUN bit */
172         REG_SET(ssp_base + SSP_CTRL0, CTRL0_RUN);
173
174
175         /* Set transfer */
176         REG_SET(ssp_base + SSP_CTRL0, CTRL0_DATA_XFER);
177
178         while (REG_RD(ssp_base + SSP_STATUS) & STATUS_FIFO_EMPTY)
179                 ;
180
181         /* Read data byte */
182         b = REG_RD(ssp_base + SSP_DATA) & 0xff;
183
184         /* Wait until RUN bit is cleared */
185         while (REG_RD(ssp_base + SSP_CTRL0) & CTRL0_RUN)
186                         ;
187
188         return b;
189 }
190
191 /* Write single data byte */
192 static void spi_write(unsigned char b)
193 {
194         /* Set XFER_LENGTH to 1 */
195         REG_CLR(ssp_base + SSP_CTRL0, 0xffff);
196         REG_SET(ssp_base + SSP_CTRL0, 1);
197
198         /* Enable WRITE mode */
199         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_READ);
200
201         /* Set RUN bit */
202         REG_SET(ssp_base + SSP_CTRL0, CTRL0_RUN);
203
204         /* Write data byte */
205         REG_WR(ssp_base + SSP_DATA, b);
206
207         /* Set transfer */
208         REG_SET(ssp_base + SSP_CTRL0, CTRL0_DATA_XFER);
209
210         /* Wait until RUN bit is cleared */
211         while (REG_RD(ssp_base + SSP_CTRL0) & CTRL0_RUN)
212                 ;
213 }
214
215 static void spi_lock_cs(void)
216 {
217         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_IGNORE_CRC);
218         REG_SET(ssp_base + SSP_CTRL0, CTRL0_LOCK_CS);
219 }
220
221 static void spi_unlock_cs(void)
222 {
223         REG_CLR(ssp_base + SSP_CTRL0, CTRL0_LOCK_CS);
224         REG_SET(ssp_base + SSP_CTRL0, CTRL0_IGNORE_CRC);
225 }
226
227 void spi_txrx(const char *dout, unsigned int tx_len, char *din,
228                unsigned int rx_len, unsigned long flags)
229 {
230         int i;
231
232         if (tx_len == 0 && rx_len == 0)
233                 return;
234
235         if (flags & SPI_START)
236                 spi_lock_cs();
237
238         for (i = 0; i < tx_len; i++) {
239
240                 /* Check if it is last data byte to transfer */
241                 if (flags & SPI_STOP && rx_len == 0 && i == tx_len - 1)
242                         spi_unlock_cs();
243
244                 spi_write(dout[i]);
245         }
246
247         for (i = 0; i < rx_len; i++) {
248
249                 /* Check if it is last data byte to transfer */
250                 if (flags & SPI_STOP && i == rx_len - 1)
251                         spi_unlock_cs();
252
253                 din[i] = spi_read();
254         }
255 }