blob: 80a4e4776c83d17314b54060e758d980d91975b8 [file] [log] [blame]
Dirk Behme53736ba2010-12-11 11:01:00 -05001/*
2 * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com>
3 *
4 * Driver for McSPI controller on OMAP3. Based on davinci_spi.c
5 * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
6 *
7 * Copyright (C) 2007 Atmel Corporation
8 *
9 * Parts taken from linux/drivers/spi/omap2_mcspi.c
10 * Copyright (C) 2005, 2006 Nokia Corporation
11 *
12 * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
13 *
14 * See file CREDITS for list of people who contributed to this
15 * project.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of
20 * the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 * MA 02111-1307 USA
31 *
32 */
33
34#include <common.h>
35#include <spi.h>
36#include <malloc.h>
37#include <asm/io.h>
38#include "omap3_spi.h"
39
40#define WORD_LEN 8
41#define SPI_WAIT_TIMEOUT 3000000;
42
43static void spi_reset(struct omap3_spi_slave *ds)
44{
45 unsigned int tmp;
46
47 writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig);
48 do {
49 tmp = readl(&ds->regs->sysstatus);
50 } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
51
52 writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
53 OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
54 OMAP3_MCSPI_SYSCONFIG_SMARTIDLE,
55 &ds->regs->sysconfig);
56
57 writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable);
58}
59
ajoycc1182b2012-11-17 21:10:15 +000060static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val)
61{
62 writel(val, &ds->regs->channel[ds->slave.cs].chconf);
63 /* Flash post writes to make immediate effect */
64 readl(&ds->regs->channel[ds->slave.cs].chconf);
65}
66
67static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable)
68{
69 writel(enable, &ds->regs->channel[ds->slave.cs].chctrl);
70 /* Flash post writes to make immediate effect */
71 readl(&ds->regs->channel[ds->slave.cs].chctrl);
72}
73
Dirk Behme53736ba2010-12-11 11:01:00 -050074void spi_init()
75{
76 /* do nothing */
77}
78
79struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
80 unsigned int max_hz, unsigned int mode)
81{
82 struct omap3_spi_slave *ds;
Simon Glassd3504fe2013-03-18 19:23:40 +000083 struct mcspi *regs;
Dirk Behme53736ba2010-12-11 11:01:00 -050084
85 /*
86 * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
87 * with different number of chip selects (CS, channels):
88 * McSPI1 has 4 CS (bus 0, cs 0 - 3)
89 * McSPI2 has 2 CS (bus 1, cs 0 - 1)
90 * McSPI3 has 2 CS (bus 2, cs 0 - 1)
91 * McSPI4 has 1 CS (bus 3, cs 0)
92 */
93
94 switch (bus) {
95 case 0:
Simon Glassd3504fe2013-03-18 19:23:40 +000096 regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
Dirk Behme53736ba2010-12-11 11:01:00 -050097 break;
Tom Rini4c0620b2012-08-08 14:29:51 -070098#ifdef OMAP3_MCSPI2_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -050099 case 1:
Simon Glassd3504fe2013-03-18 19:23:40 +0000100 regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
Dirk Behme53736ba2010-12-11 11:01:00 -0500101 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700102#endif
103#ifdef OMAP3_MCSPI3_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -0500104 case 2:
Simon Glassd3504fe2013-03-18 19:23:40 +0000105 regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
Dirk Behme53736ba2010-12-11 11:01:00 -0500106 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700107#endif
108#ifdef OMAP3_MCSPI4_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -0500109 case 3:
Simon Glassd3504fe2013-03-18 19:23:40 +0000110 regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
Dirk Behme53736ba2010-12-11 11:01:00 -0500111 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700112#endif
Dirk Behme53736ba2010-12-11 11:01:00 -0500113 default:
114 printf("SPI error: unsupported bus %i. \
115 Supported busses 0 - 3\n", bus);
116 return NULL;
117 }
Dirk Behme53736ba2010-12-11 11:01:00 -0500118
119 if (((bus == 0) && (cs > 3)) ||
120 ((bus == 1) && (cs > 1)) ||
121 ((bus == 2) && (cs > 1)) ||
122 ((bus == 3) && (cs > 0))) {
123 printf("SPI error: unsupported chip select %i \
124 on bus %i\n", cs, bus);
125 return NULL;
126 }
Dirk Behme53736ba2010-12-11 11:01:00 -0500127
128 if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
129 printf("SPI error: unsupported frequency %i Hz. \
130 Max frequency is 48 Mhz\n", max_hz);
131 return NULL;
132 }
Dirk Behme53736ba2010-12-11 11:01:00 -0500133
134 if (mode > SPI_MODE_3) {
135 printf("SPI error: unsupported SPI mode %i\n", mode);
136 return NULL;
137 }
Simon Glassd3504fe2013-03-18 19:23:40 +0000138
139 ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs);
140 if (!ds) {
141 printf("SPI error: malloc of SPI structure failed\n");
142 return NULL;
143 }
144
145 ds->regs = regs;
146 ds->freq = max_hz;
Dirk Behme53736ba2010-12-11 11:01:00 -0500147 ds->mode = mode;
148
149 return &ds->slave;
150}
151
152void spi_free_slave(struct spi_slave *slave)
153{
154 struct omap3_spi_slave *ds = to_omap3_spi(slave);
155
156 free(ds);
157}
158
159int spi_claim_bus(struct spi_slave *slave)
160{
161 struct omap3_spi_slave *ds = to_omap3_spi(slave);
162 unsigned int conf, div = 0;
163
164 /* McSPI global module configuration */
165
166 /*
167 * setup when switching from (reset default) slave mode
168 * to single-channel master mode
169 */
170 spi_reset(ds);
171 conf = readl(&ds->regs->modulctrl);
172 conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
173 conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
174 writel(conf, &ds->regs->modulctrl);
175
176 /* McSPI individual channel configuration */
177
178 /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
179 if (ds->freq) {
180 while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
181 > ds->freq)
182 div++;
183 } else
184 div = 0xC;
185
186 conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
187
188 /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
189 * REVISIT: this controller could support SPI_3WIRE mode.
190 */
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000191#ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED
Tom Rinia4a99ff2012-08-08 14:35:55 -0700192 /*
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000193 * Some boards have D0 wired as MOSI / D1 as MISO instead of
194 * The normal D0 as MISO / D1 as MOSI.
Tom Rinia4a99ff2012-08-08 14:35:55 -0700195 */
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000196 conf &= ~OMAP3_MCSPI_CHCONF_DPE0;
197 conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1;
Tom Rinia4a99ff2012-08-08 14:35:55 -0700198#else
Dirk Behme53736ba2010-12-11 11:01:00 -0500199 conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
200 conf |= OMAP3_MCSPI_CHCONF_DPE0;
Tom Rinia4a99ff2012-08-08 14:35:55 -0700201#endif
Dirk Behme53736ba2010-12-11 11:01:00 -0500202
203 /* wordlength */
204 conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
205 conf |= (WORD_LEN - 1) << 7;
206
207 /* set chipselect polarity; manage with FORCE */
208 if (!(ds->mode & SPI_CS_HIGH))
209 conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
210 else
211 conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
212
213 /* set clock divisor */
214 conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
215 conf |= div << 2;
216
217 /* set SPI mode 0..3 */
218 if (ds->mode & SPI_CPOL)
219 conf |= OMAP3_MCSPI_CHCONF_POL;
220 else
221 conf &= ~OMAP3_MCSPI_CHCONF_POL;
222 if (ds->mode & SPI_CPHA)
223 conf |= OMAP3_MCSPI_CHCONF_PHA;
224 else
225 conf &= ~OMAP3_MCSPI_CHCONF_PHA;
226
227 /* Transmit & receive mode */
228 conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
229
ajoycc1182b2012-11-17 21:10:15 +0000230 omap3_spi_write_chconf(ds,conf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500231
232 return 0;
233}
234
235void spi_release_bus(struct spi_slave *slave)
236{
237 struct omap3_spi_slave *ds = to_omap3_spi(slave);
238
239 /* Reset the SPI hardware */
240 spi_reset(ds);
241}
242
243int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
244 unsigned long flags)
245{
246 struct omap3_spi_slave *ds = to_omap3_spi(slave);
247 int i;
248 int timeout = SPI_WAIT_TIMEOUT;
249 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
250
ajoycc1182b2012-11-17 21:10:15 +0000251 /* Enable the channel */
252 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500253
254 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
255 chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
256 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000257 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500258
259 for (i = 0; i < len; i++) {
260 /* wait till TX register is empty (TXS == 1) */
261 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
262 OMAP3_MCSPI_CHSTAT_TXS)) {
263 if (--timeout <= 0) {
264 printf("SPI TXS timed out, status=0x%08x\n",
265 readl(&ds->regs->channel[ds->slave.cs].chstat));
266 return -1;
267 }
268 }
269 /* Write the data */
270 writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
271 }
272
ajoycc1182b2012-11-17 21:10:15 +0000273 /* wait to finish of transfer */
274 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
275 OMAP3_MCSPI_CHSTAT_EOT));
276
277 /* Disable the channel otherwise the next immediate RX will get affected */
278 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
279
Dirk Behme53736ba2010-12-11 11:01:00 -0500280 if (flags & SPI_XFER_END) {
Dirk Behme53736ba2010-12-11 11:01:00 -0500281
282 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000283 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500284 }
285 return 0;
286}
287
288int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
289 unsigned long flags)
290{
291 struct omap3_spi_slave *ds = to_omap3_spi(slave);
292 int i;
293 int timeout = SPI_WAIT_TIMEOUT;
294 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
295
ajoycc1182b2012-11-17 21:10:15 +0000296 /* Enable the channel */
297 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500298
299 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
300 chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
301 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000302 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500303
304 writel(0, &ds->regs->channel[ds->slave.cs].tx);
305
306 for (i = 0; i < len; i++) {
307 /* Wait till RX register contains data (RXS == 1) */
308 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
309 OMAP3_MCSPI_CHSTAT_RXS)) {
310 if (--timeout <= 0) {
311 printf("SPI RXS timed out, status=0x%08x\n",
312 readl(&ds->regs->channel[ds->slave.cs].chstat));
313 return -1;
314 }
315 }
ajoycc1182b2012-11-17 21:10:15 +0000316
317 /* Disable the channel to prevent furher receiving */
318 if(i == (len - 1))
319 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
320
Dirk Behme53736ba2010-12-11 11:01:00 -0500321 /* Read the data */
322 rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
323 }
324
325 if (flags & SPI_XFER_END) {
326 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000327 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500328 }
329
330 return 0;
331}
332
jacopo mondi08b5ab02011-03-02 05:13:22 +0000333/*McSPI Transmit Receive Mode*/
334int omap3_spi_txrx(struct spi_slave *slave,
335 unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags)
336{
337 struct omap3_spi_slave *ds = to_omap3_spi(slave);
338 int timeout = SPI_WAIT_TIMEOUT;
339 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
340 int irqstatus = readl(&ds->regs->irqstatus);
341 int i=0;
342
343 /*Enable SPI channel*/
ajoycc1182b2012-11-17 21:10:15 +0000344 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000345
346 /*set TRANSMIT-RECEIVE Mode*/
347 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
348 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000349 omap3_spi_write_chconf(ds,chconf);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000350
351 /*Shift in and out 1 byte at time*/
352 for (i=0; i < len; i++){
353 /* Write: wait for TX empty (TXS == 1)*/
354 irqstatus |= (1<< (4*(ds->slave.bus)));
355 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
356 OMAP3_MCSPI_CHSTAT_TXS)) {
357 if (--timeout <= 0) {
358 printf("SPI TXS timed out, status=0x%08x\n",
359 readl(&ds->regs->channel[ds->slave.cs].chstat));
360 return -1;
361 }
362 }
363 /* Write the data */
364 writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
365
366 /*Read: wait for RX containing data (RXS == 1)*/
367 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
368 OMAP3_MCSPI_CHSTAT_RXS)) {
369 if (--timeout <= 0) {
370 printf("SPI RXS timed out, status=0x%08x\n",
371 readl(&ds->regs->channel[ds->slave.cs].chstat));
372 return -1;
373 }
374 }
375 /* Read the data */
376 rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
377 }
ajoycc1182b2012-11-17 21:10:15 +0000378 /* Disable the channel */
379 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000380
381 /*if transfer must be terminated disable the channel*/
382 if (flags & SPI_XFER_END) {
383 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000384 omap3_spi_write_chconf(ds,chconf);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000385 }
386
387 return 0;
388}
389
Dirk Behme53736ba2010-12-11 11:01:00 -0500390int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
391 const void *dout, void *din, unsigned long flags)
392{
393 struct omap3_spi_slave *ds = to_omap3_spi(slave);
394 unsigned int len;
395 const u8 *txp = dout;
396 u8 *rxp = din;
397 int ret = -1;
398
399 if (bitlen % 8)
400 return -1;
401
402 len = bitlen / 8;
403
404 if (bitlen == 0) { /* only change CS */
405 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
406
407 if (flags & SPI_XFER_BEGIN) {
ajoycc1182b2012-11-17 21:10:15 +0000408 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500409 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000410 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500411 }
412 if (flags & SPI_XFER_END) {
413 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000414 omap3_spi_write_chconf(ds,chconf);
415 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
Dirk Behme53736ba2010-12-11 11:01:00 -0500416 }
417 ret = 0;
418 } else {
jacopo mondi08b5ab02011-03-02 05:13:22 +0000419 if (dout != NULL && din != NULL)
420 ret = omap3_spi_txrx(slave, len, txp, rxp, flags);
421 else if (dout != NULL)
Dirk Behme53736ba2010-12-11 11:01:00 -0500422 ret = omap3_spi_write(slave, len, txp, flags);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000423 else if (din != NULL)
Dirk Behme53736ba2010-12-11 11:01:00 -0500424 ret = omap3_spi_read(slave, len, rxp, flags);
425 }
426 return ret;
427}
428
429int spi_cs_is_valid(unsigned int bus, unsigned int cs)
430{
431 return 1;
432}
433
434void spi_cs_activate(struct spi_slave *slave)
435{
436}
437
438void spi_cs_deactivate(struct spi_slave *slave)
439{
440}