]>
Commit | Line | Data |
---|---|---|
53736baa DB |
1 | /* |
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 | * | |
1a459660 | 14 | * SPDX-License-Identifier: GPL-2.0+ |
53736baa DB |
15 | */ |
16 | ||
17 | #include <common.h> | |
18 | #include <spi.h> | |
19 | #include <malloc.h> | |
20 | #include <asm/io.h> | |
53736baa | 21 | |
682c1723 JT |
22 | #if defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX) |
23 | #define OMAP3_MCSPI1_BASE 0x48030100 | |
24 | #define OMAP3_MCSPI2_BASE 0x481A0100 | |
25 | #else | |
26 | #define OMAP3_MCSPI1_BASE 0x48098000 | |
27 | #define OMAP3_MCSPI2_BASE 0x4809A000 | |
28 | #define OMAP3_MCSPI3_BASE 0x480B8000 | |
29 | #define OMAP3_MCSPI4_BASE 0x480BA000 | |
30 | #endif | |
31 | ||
32 | /* per-register bitmasks */ | |
33 | #define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) | |
34 | #define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2) | |
35 | #define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) | |
36 | #define OMAP3_MCSPI_SYSCONFIG_SOFTRESET BIT(1) | |
37 | ||
38 | #define OMAP3_MCSPI_SYSSTATUS_RESETDONE BIT(0) | |
39 | ||
40 | #define OMAP3_MCSPI_MODULCTRL_SINGLE BIT(0) | |
41 | #define OMAP3_MCSPI_MODULCTRL_MS BIT(2) | |
42 | #define OMAP3_MCSPI_MODULCTRL_STEST BIT(3) | |
43 | ||
44 | #define OMAP3_MCSPI_CHCONF_PHA BIT(0) | |
45 | #define OMAP3_MCSPI_CHCONF_POL BIT(1) | |
46 | #define OMAP3_MCSPI_CHCONF_CLKD_MASK GENMASK(5, 2) | |
47 | #define OMAP3_MCSPI_CHCONF_EPOL BIT(6) | |
48 | #define OMAP3_MCSPI_CHCONF_WL_MASK GENMASK(11, 7) | |
49 | #define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) | |
50 | #define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) | |
51 | #define OMAP3_MCSPI_CHCONF_TRM_MASK GENMASK(13, 12) | |
52 | #define OMAP3_MCSPI_CHCONF_DMAW BIT(14) | |
53 | #define OMAP3_MCSPI_CHCONF_DMAR BIT(15) | |
54 | #define OMAP3_MCSPI_CHCONF_DPE0 BIT(16) | |
55 | #define OMAP3_MCSPI_CHCONF_DPE1 BIT(17) | |
56 | #define OMAP3_MCSPI_CHCONF_IS BIT(18) | |
57 | #define OMAP3_MCSPI_CHCONF_TURBO BIT(19) | |
58 | #define OMAP3_MCSPI_CHCONF_FORCE BIT(20) | |
59 | ||
60 | #define OMAP3_MCSPI_CHSTAT_RXS BIT(0) | |
61 | #define OMAP3_MCSPI_CHSTAT_TXS BIT(1) | |
62 | #define OMAP3_MCSPI_CHSTAT_EOT BIT(2) | |
63 | ||
64 | #define OMAP3_MCSPI_CHCTRL_EN BIT(0) | |
65 | #define OMAP3_MCSPI_CHCTRL_DIS (0 << 0) | |
66 | ||
67 | #define OMAP3_MCSPI_WAKEUPENABLE_WKEN BIT(0) | |
68 | ||
69 | #define OMAP3_MCSPI_MAX_FREQ 48000000 | |
70 | #define SPI_WAIT_TIMEOUT 10 | |
71 | ||
72 | /* OMAP3 McSPI registers */ | |
73 | struct mcspi_channel { | |
74 | unsigned int chconf; /* 0x2C, 0x40, 0x54, 0x68 */ | |
75 | unsigned int chstat; /* 0x30, 0x44, 0x58, 0x6C */ | |
76 | unsigned int chctrl; /* 0x34, 0x48, 0x5C, 0x70 */ | |
77 | unsigned int tx; /* 0x38, 0x4C, 0x60, 0x74 */ | |
78 | unsigned int rx; /* 0x3C, 0x50, 0x64, 0x78 */ | |
79 | }; | |
80 | ||
81 | struct mcspi { | |
82 | unsigned char res1[0x10]; | |
83 | unsigned int sysconfig; /* 0x10 */ | |
84 | unsigned int sysstatus; /* 0x14 */ | |
85 | unsigned int irqstatus; /* 0x18 */ | |
86 | unsigned int irqenable; /* 0x1C */ | |
87 | unsigned int wakeupenable; /* 0x20 */ | |
88 | unsigned int syst; /* 0x24 */ | |
89 | unsigned int modulctrl; /* 0x28 */ | |
90 | struct mcspi_channel channel[4]; | |
91 | /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */ | |
92 | /* channel1: 0x40 - 0x50, bus 0 & 1 */ | |
93 | /* channel2: 0x54 - 0x64, bus 0 & 1 */ | |
94 | /* channel3: 0x68 - 0x78, bus 0 */ | |
95 | }; | |
96 | ||
97 | struct omap3_spi_slave { | |
98 | struct spi_slave slave; | |
99 | struct mcspi *regs; | |
100 | unsigned int freq; | |
101 | unsigned int mode; | |
102 | }; | |
103 | ||
104 | static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave) | |
105 | { | |
106 | return container_of(slave, struct omap3_spi_slave, slave); | |
107 | } | |
53736baa DB |
108 | |
109 | static void spi_reset(struct omap3_spi_slave *ds) | |
110 | { | |
111 | unsigned int tmp; | |
112 | ||
113 | writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig); | |
114 | do { | |
115 | tmp = readl(&ds->regs->sysstatus); | |
116 | } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); | |
117 | ||
118 | writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | | |
119 | OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | | |
120 | OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, | |
121 | &ds->regs->sysconfig); | |
122 | ||
123 | writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); | |
124 | } | |
125 | ||
cc1182be | 126 | static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val) |
127 | { | |
128 | writel(val, &ds->regs->channel[ds->slave.cs].chconf); | |
129 | /* Flash post writes to make immediate effect */ | |
130 | readl(&ds->regs->channel[ds->slave.cs].chconf); | |
131 | } | |
132 | ||
133 | static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable) | |
134 | { | |
135 | writel(enable, &ds->regs->channel[ds->slave.cs].chctrl); | |
93e14596 | 136 | /* Flash post writes to make immediate effect */ |
cc1182be | 137 | readl(&ds->regs->channel[ds->slave.cs].chctrl); |
138 | } | |
139 | ||
53736baa DB |
140 | void spi_init() |
141 | { | |
142 | /* do nothing */ | |
143 | } | |
144 | ||
145 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
146 | unsigned int max_hz, unsigned int mode) | |
147 | { | |
148 | struct omap3_spi_slave *ds; | |
d3504fee | 149 | struct mcspi *regs; |
53736baa DB |
150 | |
151 | /* | |
152 | * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) | |
153 | * with different number of chip selects (CS, channels): | |
154 | * McSPI1 has 4 CS (bus 0, cs 0 - 3) | |
155 | * McSPI2 has 2 CS (bus 1, cs 0 - 1) | |
156 | * McSPI3 has 2 CS (bus 2, cs 0 - 1) | |
157 | * McSPI4 has 1 CS (bus 3, cs 0) | |
158 | */ | |
159 | ||
160 | switch (bus) { | |
161 | case 0: | |
d3504fee | 162 | regs = (struct mcspi *)OMAP3_MCSPI1_BASE; |
53736baa | 163 | break; |
4c0620bf | 164 | #ifdef OMAP3_MCSPI2_BASE |
53736baa | 165 | case 1: |
d3504fee | 166 | regs = (struct mcspi *)OMAP3_MCSPI2_BASE; |
53736baa | 167 | break; |
4c0620bf | 168 | #endif |
3765b3e7 | 169 | #ifdef OMAP3_MCSPI3_BASE |
53736baa | 170 | case 2: |
d3504fee | 171 | regs = (struct mcspi *)OMAP3_MCSPI3_BASE; |
53736baa | 172 | break; |
4c0620bf TR |
173 | #endif |
174 | #ifdef OMAP3_MCSPI4_BASE | |
53736baa | 175 | case 3: |
d3504fee | 176 | regs = (struct mcspi *)OMAP3_MCSPI4_BASE; |
53736baa | 177 | break; |
4c0620bf | 178 | #endif |
53736baa DB |
179 | default: |
180 | printf("SPI error: unsupported bus %i. \ | |
181 | Supported busses 0 - 3\n", bus); | |
182 | return NULL; | |
183 | } | |
53736baa DB |
184 | |
185 | if (((bus == 0) && (cs > 3)) || | |
186 | ((bus == 1) && (cs > 1)) || | |
187 | ((bus == 2) && (cs > 1)) || | |
188 | ((bus == 3) && (cs > 0))) { | |
189 | printf("SPI error: unsupported chip select %i \ | |
190 | on bus %i\n", cs, bus); | |
191 | return NULL; | |
192 | } | |
53736baa DB |
193 | |
194 | if (max_hz > OMAP3_MCSPI_MAX_FREQ) { | |
195 | printf("SPI error: unsupported frequency %i Hz. \ | |
196 | Max frequency is 48 Mhz\n", max_hz); | |
197 | return NULL; | |
198 | } | |
53736baa DB |
199 | |
200 | if (mode > SPI_MODE_3) { | |
201 | printf("SPI error: unsupported SPI mode %i\n", mode); | |
202 | return NULL; | |
203 | } | |
d3504fee SG |
204 | |
205 | ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs); | |
206 | if (!ds) { | |
207 | printf("SPI error: malloc of SPI structure failed\n"); | |
208 | return NULL; | |
209 | } | |
210 | ||
211 | ds->regs = regs; | |
212 | ds->freq = max_hz; | |
53736baa DB |
213 | ds->mode = mode; |
214 | ||
215 | return &ds->slave; | |
216 | } | |
217 | ||
218 | void spi_free_slave(struct spi_slave *slave) | |
219 | { | |
220 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
221 | ||
222 | free(ds); | |
223 | } | |
224 | ||
225 | int spi_claim_bus(struct spi_slave *slave) | |
226 | { | |
227 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
228 | unsigned int conf, div = 0; | |
229 | ||
230 | /* McSPI global module configuration */ | |
231 | ||
232 | /* | |
233 | * setup when switching from (reset default) slave mode | |
234 | * to single-channel master mode | |
235 | */ | |
236 | spi_reset(ds); | |
237 | conf = readl(&ds->regs->modulctrl); | |
238 | conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); | |
239 | conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; | |
240 | writel(conf, &ds->regs->modulctrl); | |
241 | ||
242 | /* McSPI individual channel configuration */ | |
243 | ||
244 | /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ | |
245 | if (ds->freq) { | |
246 | while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) | |
247 | > ds->freq) | |
248 | div++; | |
249 | } else | |
250 | div = 0xC; | |
251 | ||
252 | conf = readl(&ds->regs->channel[ds->slave.cs].chconf); | |
253 | ||
254 | /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS | |
255 | * REVISIT: this controller could support SPI_3WIRE mode. | |
256 | */ | |
22cbeed4 | 257 | #ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED |
a4a99fff | 258 | /* |
22cbeed4 PK |
259 | * Some boards have D0 wired as MOSI / D1 as MISO instead of |
260 | * The normal D0 as MISO / D1 as MOSI. | |
a4a99fff | 261 | */ |
22cbeed4 PK |
262 | conf &= ~OMAP3_MCSPI_CHCONF_DPE0; |
263 | conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1; | |
a4a99fff | 264 | #else |
53736baa DB |
265 | conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); |
266 | conf |= OMAP3_MCSPI_CHCONF_DPE0; | |
a4a99fff | 267 | #endif |
53736baa DB |
268 | |
269 | /* wordlength */ | |
270 | conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; | |
5753d09b | 271 | conf |= (ds->slave.wordlen - 1) << 7; |
53736baa DB |
272 | |
273 | /* set chipselect polarity; manage with FORCE */ | |
274 | if (!(ds->mode & SPI_CS_HIGH)) | |
275 | conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ | |
276 | else | |
277 | conf &= ~OMAP3_MCSPI_CHCONF_EPOL; | |
278 | ||
279 | /* set clock divisor */ | |
280 | conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; | |
281 | conf |= div << 2; | |
282 | ||
283 | /* set SPI mode 0..3 */ | |
284 | if (ds->mode & SPI_CPOL) | |
285 | conf |= OMAP3_MCSPI_CHCONF_POL; | |
286 | else | |
287 | conf &= ~OMAP3_MCSPI_CHCONF_POL; | |
288 | if (ds->mode & SPI_CPHA) | |
289 | conf |= OMAP3_MCSPI_CHCONF_PHA; | |
290 | else | |
291 | conf &= ~OMAP3_MCSPI_CHCONF_PHA; | |
292 | ||
293 | /* Transmit & receive mode */ | |
294 | conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; | |
295 | ||
cc1182be | 296 | omap3_spi_write_chconf(ds,conf); |
53736baa DB |
297 | |
298 | return 0; | |
299 | } | |
300 | ||
301 | void spi_release_bus(struct spi_slave *slave) | |
302 | { | |
303 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
304 | ||
305 | /* Reset the SPI hardware */ | |
306 | spi_reset(ds); | |
307 | } | |
308 | ||
03661d85 JT |
309 | static int omap3_spi_write(struct spi_slave *slave, unsigned int len, |
310 | const void *txp, unsigned long flags) | |
53736baa DB |
311 | { |
312 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
313 | int i; | |
611c9ba2 | 314 | ulong start; |
53736baa DB |
315 | int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |
316 | ||
cc1182be | 317 | /* Enable the channel */ |
318 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); | |
53736baa | 319 | |
5753d09b NK |
320 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
321 | chconf |= (ds->slave.wordlen - 1) << 7; | |
53736baa DB |
322 | chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; |
323 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 324 | omap3_spi_write_chconf(ds,chconf); |
53736baa DB |
325 | |
326 | for (i = 0; i < len; i++) { | |
327 | /* wait till TX register is empty (TXS == 1) */ | |
611c9ba2 | 328 | start = get_timer(0); |
53736baa DB |
329 | while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |
330 | OMAP3_MCSPI_CHSTAT_TXS)) { | |
611c9ba2 | 331 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
53736baa DB |
332 | printf("SPI TXS timed out, status=0x%08x\n", |
333 | readl(&ds->regs->channel[ds->slave.cs].chstat)); | |
334 | return -1; | |
335 | } | |
336 | } | |
337 | /* Write the data */ | |
5753d09b NK |
338 | unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; |
339 | if (ds->slave.wordlen > 16) | |
340 | writel(((u32 *)txp)[i], tx); | |
341 | else if (ds->slave.wordlen > 8) | |
342 | writel(((u16 *)txp)[i], tx); | |
343 | else | |
344 | writel(((u8 *)txp)[i], tx); | |
53736baa DB |
345 | } |
346 | ||
93e14596 | 347 | /* wait to finish of transfer */ |
ce6889a9 VG |
348 | while ((readl(&ds->regs->channel[ds->slave.cs].chstat) & |
349 | (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) != | |
350 | (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)); | |
cc1182be | 351 | |
352 | /* Disable the channel otherwise the next immediate RX will get affected */ | |
353 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); | |
354 | ||
53736baa | 355 | if (flags & SPI_XFER_END) { |
53736baa DB |
356 | |
357 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 358 | omap3_spi_write_chconf(ds,chconf); |
53736baa DB |
359 | } |
360 | return 0; | |
361 | } | |
362 | ||
03661d85 JT |
363 | static int omap3_spi_read(struct spi_slave *slave, unsigned int len, |
364 | void *rxp, unsigned long flags) | |
53736baa DB |
365 | { |
366 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
367 | int i; | |
611c9ba2 | 368 | ulong start; |
53736baa DB |
369 | int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |
370 | ||
cc1182be | 371 | /* Enable the channel */ |
372 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); | |
53736baa | 373 | |
5753d09b NK |
374 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
375 | chconf |= (ds->slave.wordlen - 1) << 7; | |
53736baa DB |
376 | chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; |
377 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 378 | omap3_spi_write_chconf(ds,chconf); |
53736baa DB |
379 | |
380 | writel(0, &ds->regs->channel[ds->slave.cs].tx); | |
381 | ||
382 | for (i = 0; i < len; i++) { | |
611c9ba2 | 383 | start = get_timer(0); |
53736baa DB |
384 | /* Wait till RX register contains data (RXS == 1) */ |
385 | while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & | |
386 | OMAP3_MCSPI_CHSTAT_RXS)) { | |
611c9ba2 | 387 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
53736baa DB |
388 | printf("SPI RXS timed out, status=0x%08x\n", |
389 | readl(&ds->regs->channel[ds->slave.cs].chstat)); | |
390 | return -1; | |
391 | } | |
392 | } | |
cc1182be | 393 | |
394 | /* Disable the channel to prevent furher receiving */ | |
395 | if(i == (len - 1)) | |
396 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); | |
397 | ||
53736baa | 398 | /* Read the data */ |
5753d09b NK |
399 | unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; |
400 | if (ds->slave.wordlen > 16) | |
401 | ((u32 *)rxp)[i] = readl(rx); | |
402 | else if (ds->slave.wordlen > 8) | |
403 | ((u16 *)rxp)[i] = (u16)readl(rx); | |
404 | else | |
405 | ((u8 *)rxp)[i] = (u8)readl(rx); | |
53736baa DB |
406 | } |
407 | ||
408 | if (flags & SPI_XFER_END) { | |
409 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 410 | omap3_spi_write_chconf(ds,chconf); |
53736baa DB |
411 | } |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
08b5ab07 | 416 | /*McSPI Transmit Receive Mode*/ |
03661d85 JT |
417 | static int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, |
418 | const void *txp, void *rxp, unsigned long flags) | |
08b5ab07 | 419 | { |
420 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
611c9ba2 | 421 | ulong start; |
08b5ab07 | 422 | int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |
08b5ab07 | 423 | int i=0; |
424 | ||
425 | /*Enable SPI channel*/ | |
cc1182be | 426 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |
08b5ab07 | 427 | |
428 | /*set TRANSMIT-RECEIVE Mode*/ | |
5753d09b NK |
429 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
430 | chconf |= (ds->slave.wordlen - 1) << 7; | |
08b5ab07 | 431 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; |
cc1182be | 432 | omap3_spi_write_chconf(ds,chconf); |
08b5ab07 | 433 | |
434 | /*Shift in and out 1 byte at time*/ | |
435 | for (i=0; i < len; i++){ | |
436 | /* Write: wait for TX empty (TXS == 1)*/ | |
611c9ba2 | 437 | start = get_timer(0); |
08b5ab07 | 438 | while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |
439 | OMAP3_MCSPI_CHSTAT_TXS)) { | |
611c9ba2 | 440 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
08b5ab07 | 441 | printf("SPI TXS timed out, status=0x%08x\n", |
442 | readl(&ds->regs->channel[ds->slave.cs].chstat)); | |
443 | return -1; | |
444 | } | |
445 | } | |
446 | /* Write the data */ | |
5753d09b NK |
447 | unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; |
448 | if (ds->slave.wordlen > 16) | |
449 | writel(((u32 *)txp)[i], tx); | |
450 | else if (ds->slave.wordlen > 8) | |
451 | writel(((u16 *)txp)[i], tx); | |
452 | else | |
453 | writel(((u8 *)txp)[i], tx); | |
08b5ab07 | 454 | |
455 | /*Read: wait for RX containing data (RXS == 1)*/ | |
611c9ba2 | 456 | start = get_timer(0); |
08b5ab07 | 457 | while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |
458 | OMAP3_MCSPI_CHSTAT_RXS)) { | |
611c9ba2 | 459 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
08b5ab07 | 460 | printf("SPI RXS timed out, status=0x%08x\n", |
461 | readl(&ds->regs->channel[ds->slave.cs].chstat)); | |
462 | return -1; | |
463 | } | |
464 | } | |
465 | /* Read the data */ | |
5753d09b NK |
466 | unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; |
467 | if (ds->slave.wordlen > 16) | |
468 | ((u32 *)rxp)[i] = readl(rx); | |
469 | else if (ds->slave.wordlen > 8) | |
470 | ((u16 *)rxp)[i] = (u16)readl(rx); | |
471 | else | |
472 | ((u8 *)rxp)[i] = (u8)readl(rx); | |
08b5ab07 | 473 | } |
cc1182be | 474 | /* Disable the channel */ |
93e14596 | 475 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); |
08b5ab07 | 476 | |
477 | /*if transfer must be terminated disable the channel*/ | |
478 | if (flags & SPI_XFER_END) { | |
479 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 480 | omap3_spi_write_chconf(ds,chconf); |
08b5ab07 | 481 | } |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
53736baa DB |
486 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, |
487 | const void *dout, void *din, unsigned long flags) | |
488 | { | |
489 | struct omap3_spi_slave *ds = to_omap3_spi(slave); | |
490 | unsigned int len; | |
53736baa DB |
491 | int ret = -1; |
492 | ||
5753d09b NK |
493 | if (ds->slave.wordlen < 4 || ds->slave.wordlen > 32) { |
494 | printf("omap3_spi: invalid wordlen %d\n", ds->slave.wordlen); | |
495 | return -1; | |
496 | } | |
497 | ||
498 | if (bitlen % ds->slave.wordlen) | |
53736baa DB |
499 | return -1; |
500 | ||
5753d09b | 501 | len = bitlen / ds->slave.wordlen; |
53736baa DB |
502 | |
503 | if (bitlen == 0) { /* only change CS */ | |
504 | int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); | |
505 | ||
506 | if (flags & SPI_XFER_BEGIN) { | |
cc1182be | 507 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |
53736baa | 508 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; |
cc1182be | 509 | omap3_spi_write_chconf(ds,chconf); |
53736baa DB |
510 | } |
511 | if (flags & SPI_XFER_END) { | |
512 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
cc1182be | 513 | omap3_spi_write_chconf(ds,chconf); |
514 | omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); | |
53736baa DB |
515 | } |
516 | ret = 0; | |
517 | } else { | |
08b5ab07 | 518 | if (dout != NULL && din != NULL) |
5753d09b | 519 | ret = omap3_spi_txrx(slave, len, dout, din, flags); |
08b5ab07 | 520 | else if (dout != NULL) |
5753d09b | 521 | ret = omap3_spi_write(slave, len, dout, flags); |
08b5ab07 | 522 | else if (din != NULL) |
5753d09b | 523 | ret = omap3_spi_read(slave, len, din, flags); |
53736baa DB |
524 | } |
525 | return ret; | |
526 | } | |
527 | ||
528 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
529 | { | |
530 | return 1; | |
531 | } | |
532 | ||
533 | void spi_cs_activate(struct spi_slave *slave) | |
534 | { | |
535 | } | |
536 | ||
537 | void spi_cs_deactivate(struct spi_slave *slave) | |
538 | { | |
539 | } |