blob: 344d5b8a7e2dc5d9006793e00d40343dec18be44 [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;
83
84 ds = malloc(sizeof(struct omap3_spi_slave));
85 if (!ds) {
86 printf("SPI error: malloc of SPI structure failed\n");
87 return NULL;
88 }
89
90 /*
91 * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
92 * with different number of chip selects (CS, channels):
93 * McSPI1 has 4 CS (bus 0, cs 0 - 3)
94 * McSPI2 has 2 CS (bus 1, cs 0 - 1)
95 * McSPI3 has 2 CS (bus 2, cs 0 - 1)
96 * McSPI4 has 1 CS (bus 3, cs 0)
97 */
98
99 switch (bus) {
100 case 0:
101 ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
102 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700103#ifdef OMAP3_MCSPI2_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -0500104 case 1:
105 ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
106 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700107#endif
108#ifdef OMAP3_MCSPI3_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -0500109 case 2:
110 ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
111 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700112#endif
113#ifdef OMAP3_MCSPI4_BASE
Dirk Behme53736ba2010-12-11 11:01:00 -0500114 case 3:
115 ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
116 break;
Tom Rini4c0620b2012-08-08 14:29:51 -0700117#endif
Dirk Behme53736ba2010-12-11 11:01:00 -0500118 default:
119 printf("SPI error: unsupported bus %i. \
120 Supported busses 0 - 3\n", bus);
121 return NULL;
122 }
123 ds->slave.bus = bus;
124
125 if (((bus == 0) && (cs > 3)) ||
126 ((bus == 1) && (cs > 1)) ||
127 ((bus == 2) && (cs > 1)) ||
128 ((bus == 3) && (cs > 0))) {
129 printf("SPI error: unsupported chip select %i \
130 on bus %i\n", cs, bus);
131 return NULL;
132 }
133 ds->slave.cs = cs;
134
135 if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
136 printf("SPI error: unsupported frequency %i Hz. \
137 Max frequency is 48 Mhz\n", max_hz);
138 return NULL;
139 }
140 ds->freq = max_hz;
141
142 if (mode > SPI_MODE_3) {
143 printf("SPI error: unsupported SPI mode %i\n", mode);
144 return NULL;
145 }
146 ds->mode = mode;
147
148 return &ds->slave;
149}
150
151void spi_free_slave(struct spi_slave *slave)
152{
153 struct omap3_spi_slave *ds = to_omap3_spi(slave);
154
155 free(ds);
156}
157
158int spi_claim_bus(struct spi_slave *slave)
159{
160 struct omap3_spi_slave *ds = to_omap3_spi(slave);
161 unsigned int conf, div = 0;
162
163 /* McSPI global module configuration */
164
165 /*
166 * setup when switching from (reset default) slave mode
167 * to single-channel master mode
168 */
169 spi_reset(ds);
170 conf = readl(&ds->regs->modulctrl);
171 conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
172 conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
173 writel(conf, &ds->regs->modulctrl);
174
175 /* McSPI individual channel configuration */
176
177 /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
178 if (ds->freq) {
179 while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
180 > ds->freq)
181 div++;
182 } else
183 div = 0xC;
184
185 conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
186
187 /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
188 * REVISIT: this controller could support SPI_3WIRE mode.
189 */
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000190#ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED
Tom Rinia4a99ff2012-08-08 14:35:55 -0700191 /*
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000192 * Some boards have D0 wired as MOSI / D1 as MISO instead of
193 * The normal D0 as MISO / D1 as MOSI.
Tom Rinia4a99ff2012-08-08 14:35:55 -0700194 */
Peter Korsgaard22cbeed2012-10-17 09:20:46 +0000195 conf &= ~OMAP3_MCSPI_CHCONF_DPE0;
196 conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1;
Tom Rinia4a99ff2012-08-08 14:35:55 -0700197#else
Dirk Behme53736ba2010-12-11 11:01:00 -0500198 conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
199 conf |= OMAP3_MCSPI_CHCONF_DPE0;
Tom Rinia4a99ff2012-08-08 14:35:55 -0700200#endif
Dirk Behme53736ba2010-12-11 11:01:00 -0500201
202 /* wordlength */
203 conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
204 conf |= (WORD_LEN - 1) << 7;
205
206 /* set chipselect polarity; manage with FORCE */
207 if (!(ds->mode & SPI_CS_HIGH))
208 conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
209 else
210 conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
211
212 /* set clock divisor */
213 conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
214 conf |= div << 2;
215
216 /* set SPI mode 0..3 */
217 if (ds->mode & SPI_CPOL)
218 conf |= OMAP3_MCSPI_CHCONF_POL;
219 else
220 conf &= ~OMAP3_MCSPI_CHCONF_POL;
221 if (ds->mode & SPI_CPHA)
222 conf |= OMAP3_MCSPI_CHCONF_PHA;
223 else
224 conf &= ~OMAP3_MCSPI_CHCONF_PHA;
225
226 /* Transmit & receive mode */
227 conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
228
ajoycc1182b2012-11-17 21:10:15 +0000229 omap3_spi_write_chconf(ds,conf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500230
231 return 0;
232}
233
234void spi_release_bus(struct spi_slave *slave)
235{
236 struct omap3_spi_slave *ds = to_omap3_spi(slave);
237
238 /* Reset the SPI hardware */
239 spi_reset(ds);
240}
241
242int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
243 unsigned long flags)
244{
245 struct omap3_spi_slave *ds = to_omap3_spi(slave);
246 int i;
247 int timeout = SPI_WAIT_TIMEOUT;
248 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
249
ajoycc1182b2012-11-17 21:10:15 +0000250 /* Enable the channel */
251 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500252
253 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
254 chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
255 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000256 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500257
258 for (i = 0; i < len; i++) {
259 /* wait till TX register is empty (TXS == 1) */
260 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
261 OMAP3_MCSPI_CHSTAT_TXS)) {
262 if (--timeout <= 0) {
263 printf("SPI TXS timed out, status=0x%08x\n",
264 readl(&ds->regs->channel[ds->slave.cs].chstat));
265 return -1;
266 }
267 }
268 /* Write the data */
269 writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
270 }
271
ajoycc1182b2012-11-17 21:10:15 +0000272 /* wait to finish of transfer */
273 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
274 OMAP3_MCSPI_CHSTAT_EOT));
275
276 /* Disable the channel otherwise the next immediate RX will get affected */
277 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
278
Dirk Behme53736ba2010-12-11 11:01:00 -0500279 if (flags & SPI_XFER_END) {
Dirk Behme53736ba2010-12-11 11:01:00 -0500280
281 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000282 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500283 }
284 return 0;
285}
286
287int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
288 unsigned long flags)
289{
290 struct omap3_spi_slave *ds = to_omap3_spi(slave);
291 int i;
292 int timeout = SPI_WAIT_TIMEOUT;
293 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
294
ajoycc1182b2012-11-17 21:10:15 +0000295 /* Enable the channel */
296 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500297
298 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
299 chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
300 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000301 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500302
303 writel(0, &ds->regs->channel[ds->slave.cs].tx);
304
305 for (i = 0; i < len; i++) {
306 /* Wait till RX register contains data (RXS == 1) */
307 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
308 OMAP3_MCSPI_CHSTAT_RXS)) {
309 if (--timeout <= 0) {
310 printf("SPI RXS timed out, status=0x%08x\n",
311 readl(&ds->regs->channel[ds->slave.cs].chstat));
312 return -1;
313 }
314 }
ajoycc1182b2012-11-17 21:10:15 +0000315
316 /* Disable the channel to prevent furher receiving */
317 if(i == (len - 1))
318 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
319
Dirk Behme53736ba2010-12-11 11:01:00 -0500320 /* Read the data */
321 rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
322 }
323
324 if (flags & SPI_XFER_END) {
325 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000326 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500327 }
328
329 return 0;
330}
331
jacopo mondi08b5ab02011-03-02 05:13:22 +0000332/*McSPI Transmit Receive Mode*/
333int omap3_spi_txrx(struct spi_slave *slave,
334 unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags)
335{
336 struct omap3_spi_slave *ds = to_omap3_spi(slave);
337 int timeout = SPI_WAIT_TIMEOUT;
338 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
339 int irqstatus = readl(&ds->regs->irqstatus);
340 int i=0;
341
342 /*Enable SPI channel*/
ajoycc1182b2012-11-17 21:10:15 +0000343 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000344
345 /*set TRANSMIT-RECEIVE Mode*/
346 chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
347 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000348 omap3_spi_write_chconf(ds,chconf);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000349
350 /*Shift in and out 1 byte at time*/
351 for (i=0; i < len; i++){
352 /* Write: wait for TX empty (TXS == 1)*/
353 irqstatus |= (1<< (4*(ds->slave.bus)));
354 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
355 OMAP3_MCSPI_CHSTAT_TXS)) {
356 if (--timeout <= 0) {
357 printf("SPI TXS timed out, status=0x%08x\n",
358 readl(&ds->regs->channel[ds->slave.cs].chstat));
359 return -1;
360 }
361 }
362 /* Write the data */
363 writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
364
365 /*Read: wait for RX containing data (RXS == 1)*/
366 while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
367 OMAP3_MCSPI_CHSTAT_RXS)) {
368 if (--timeout <= 0) {
369 printf("SPI RXS timed out, status=0x%08x\n",
370 readl(&ds->regs->channel[ds->slave.cs].chstat));
371 return -1;
372 }
373 }
374 /* Read the data */
375 rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
376 }
ajoycc1182b2012-11-17 21:10:15 +0000377 /* Disable the channel */
378 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000379
380 /*if transfer must be terminated disable the channel*/
381 if (flags & SPI_XFER_END) {
382 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000383 omap3_spi_write_chconf(ds,chconf);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000384 }
385
386 return 0;
387}
388
Dirk Behme53736ba2010-12-11 11:01:00 -0500389int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
390 const void *dout, void *din, unsigned long flags)
391{
392 struct omap3_spi_slave *ds = to_omap3_spi(slave);
393 unsigned int len;
394 const u8 *txp = dout;
395 u8 *rxp = din;
396 int ret = -1;
397
398 if (bitlen % 8)
399 return -1;
400
401 len = bitlen / 8;
402
403 if (bitlen == 0) { /* only change CS */
404 int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
405
406 if (flags & SPI_XFER_BEGIN) {
ajoycc1182b2012-11-17 21:10:15 +0000407 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
Dirk Behme53736ba2010-12-11 11:01:00 -0500408 chconf |= OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000409 omap3_spi_write_chconf(ds,chconf);
Dirk Behme53736ba2010-12-11 11:01:00 -0500410 }
411 if (flags & SPI_XFER_END) {
412 chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
ajoycc1182b2012-11-17 21:10:15 +0000413 omap3_spi_write_chconf(ds,chconf);
414 omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
Dirk Behme53736ba2010-12-11 11:01:00 -0500415 }
416 ret = 0;
417 } else {
jacopo mondi08b5ab02011-03-02 05:13:22 +0000418 if (dout != NULL && din != NULL)
419 ret = omap3_spi_txrx(slave, len, txp, rxp, flags);
420 else if (dout != NULL)
Dirk Behme53736ba2010-12-11 11:01:00 -0500421 ret = omap3_spi_write(slave, len, txp, flags);
jacopo mondi08b5ab02011-03-02 05:13:22 +0000422 else if (din != NULL)
Dirk Behme53736ba2010-12-11 11:01:00 -0500423 ret = omap3_spi_read(slave, len, rxp, flags);
424 }
425 return ret;
426}
427
428int spi_cs_is_valid(unsigned int bus, unsigned int cs)
429{
430 return 1;
431}
432
433void spi_cs_activate(struct spi_slave *slave)
434{
435}
436
437void spi_cs_deactivate(struct spi_slave *slave)
438{
439}