]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
53736baa | 2 | /* |
77b8d048 JT |
3 | * Copyright (C) 2016 Jagan Teki <[email protected]> |
4 | * Christophe Ricard <[email protected]> | |
5 | * | |
53736baa DB |
6 | * Copyright (C) 2010 Dirk Behme <[email protected]> |
7 | * | |
8 | * Driver for McSPI controller on OMAP3. Based on davinci_spi.c | |
9 | * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ | |
10 | * | |
11 | * Copyright (C) 2007 Atmel Corporation | |
12 | * | |
13 | * Parts taken from linux/drivers/spi/omap2_mcspi.c | |
14 | * Copyright (C) 2005, 2006 Nokia Corporation | |
15 | * | |
16 | * Modified by Ruslan Araslanov <[email protected]> | |
53736baa DB |
17 | */ |
18 | ||
19 | #include <common.h> | |
77b8d048 | 20 | #include <dm.h> |
53736baa DB |
21 | #include <spi.h> |
22 | #include <malloc.h> | |
23 | #include <asm/io.h> | |
53736baa | 24 | |
77b8d048 JT |
25 | DECLARE_GLOBAL_DATA_PTR; |
26 | ||
682c1723 JT |
27 | #if defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX) |
28 | #define OMAP3_MCSPI1_BASE 0x48030100 | |
29 | #define OMAP3_MCSPI2_BASE 0x481A0100 | |
30 | #else | |
31 | #define OMAP3_MCSPI1_BASE 0x48098000 | |
32 | #define OMAP3_MCSPI2_BASE 0x4809A000 | |
33 | #define OMAP3_MCSPI3_BASE 0x480B8000 | |
34 | #define OMAP3_MCSPI4_BASE 0x480BA000 | |
35 | #endif | |
36 | ||
5f89a15e MH |
37 | #define OMAP4_MCSPI_REG_OFFSET 0x100 |
38 | ||
39 | struct omap2_mcspi_platform_config { | |
40 | unsigned int regs_offset; | |
41 | }; | |
42 | ||
682c1723 JT |
43 | /* per-register bitmasks */ |
44 | #define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) | |
45 | #define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2) | |
46 | #define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) | |
47 | #define OMAP3_MCSPI_SYSCONFIG_SOFTRESET BIT(1) | |
48 | ||
49 | #define OMAP3_MCSPI_SYSSTATUS_RESETDONE BIT(0) | |
50 | ||
51 | #define OMAP3_MCSPI_MODULCTRL_SINGLE BIT(0) | |
52 | #define OMAP3_MCSPI_MODULCTRL_MS BIT(2) | |
53 | #define OMAP3_MCSPI_MODULCTRL_STEST BIT(3) | |
54 | ||
55 | #define OMAP3_MCSPI_CHCONF_PHA BIT(0) | |
56 | #define OMAP3_MCSPI_CHCONF_POL BIT(1) | |
57 | #define OMAP3_MCSPI_CHCONF_CLKD_MASK GENMASK(5, 2) | |
58 | #define OMAP3_MCSPI_CHCONF_EPOL BIT(6) | |
59 | #define OMAP3_MCSPI_CHCONF_WL_MASK GENMASK(11, 7) | |
60 | #define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) | |
61 | #define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) | |
62 | #define OMAP3_MCSPI_CHCONF_TRM_MASK GENMASK(13, 12) | |
63 | #define OMAP3_MCSPI_CHCONF_DMAW BIT(14) | |
64 | #define OMAP3_MCSPI_CHCONF_DMAR BIT(15) | |
65 | #define OMAP3_MCSPI_CHCONF_DPE0 BIT(16) | |
66 | #define OMAP3_MCSPI_CHCONF_DPE1 BIT(17) | |
67 | #define OMAP3_MCSPI_CHCONF_IS BIT(18) | |
68 | #define OMAP3_MCSPI_CHCONF_TURBO BIT(19) | |
69 | #define OMAP3_MCSPI_CHCONF_FORCE BIT(20) | |
70 | ||
71 | #define OMAP3_MCSPI_CHSTAT_RXS BIT(0) | |
72 | #define OMAP3_MCSPI_CHSTAT_TXS BIT(1) | |
73 | #define OMAP3_MCSPI_CHSTAT_EOT BIT(2) | |
74 | ||
75 | #define OMAP3_MCSPI_CHCTRL_EN BIT(0) | |
76 | #define OMAP3_MCSPI_CHCTRL_DIS (0 << 0) | |
77 | ||
78 | #define OMAP3_MCSPI_WAKEUPENABLE_WKEN BIT(0) | |
77b8d048 JT |
79 | #define MCSPI_PINDIR_D0_IN_D1_OUT 0 |
80 | #define MCSPI_PINDIR_D0_OUT_D1_IN 1 | |
682c1723 JT |
81 | |
82 | #define OMAP3_MCSPI_MAX_FREQ 48000000 | |
83 | #define SPI_WAIT_TIMEOUT 10 | |
84 | ||
85 | /* OMAP3 McSPI registers */ | |
86 | struct mcspi_channel { | |
87 | unsigned int chconf; /* 0x2C, 0x40, 0x54, 0x68 */ | |
88 | unsigned int chstat; /* 0x30, 0x44, 0x58, 0x6C */ | |
89 | unsigned int chctrl; /* 0x34, 0x48, 0x5C, 0x70 */ | |
90 | unsigned int tx; /* 0x38, 0x4C, 0x60, 0x74 */ | |
91 | unsigned int rx; /* 0x3C, 0x50, 0x64, 0x78 */ | |
92 | }; | |
93 | ||
94 | struct mcspi { | |
95 | unsigned char res1[0x10]; | |
96 | unsigned int sysconfig; /* 0x10 */ | |
97 | unsigned int sysstatus; /* 0x14 */ | |
98 | unsigned int irqstatus; /* 0x18 */ | |
99 | unsigned int irqenable; /* 0x1C */ | |
100 | unsigned int wakeupenable; /* 0x20 */ | |
101 | unsigned int syst; /* 0x24 */ | |
102 | unsigned int modulctrl; /* 0x28 */ | |
103 | struct mcspi_channel channel[4]; | |
104 | /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */ | |
105 | /* channel1: 0x40 - 0x50, bus 0 & 1 */ | |
106 | /* channel2: 0x54 - 0x64, bus 0 & 1 */ | |
107 | /* channel3: 0x68 - 0x78, bus 0 */ | |
108 | }; | |
109 | ||
77b8d048 | 110 | struct omap3_spi_priv { |
41bccb81 JT |
111 | #ifndef CONFIG_DM_SPI |
112 | struct spi_slave slave; | |
113 | #endif | |
682c1723 | 114 | struct mcspi *regs; |
77b8d048 | 115 | unsigned int cs; |
682c1723 JT |
116 | unsigned int freq; |
117 | unsigned int mode; | |
77b8d048 JT |
118 | unsigned int wordlen; |
119 | unsigned int pin_dir:1; | |
682c1723 JT |
120 | }; |
121 | ||
77b8d048 | 122 | static void omap3_spi_write_chconf(struct omap3_spi_priv *priv, int val) |
682c1723 | 123 | { |
77b8d048 | 124 | writel(val, &priv->regs->channel[priv->cs].chconf); |
cc1182be | 125 | /* Flash post writes to make immediate effect */ |
77b8d048 | 126 | readl(&priv->regs->channel[priv->cs].chconf); |
cc1182be | 127 | } |
128 | ||
77b8d048 | 129 | static void omap3_spi_set_enable(struct omap3_spi_priv *priv, int enable) |
cc1182be | 130 | { |
77b8d048 | 131 | writel(enable, &priv->regs->channel[priv->cs].chctrl); |
93e14596 | 132 | /* Flash post writes to make immediate effect */ |
77b8d048 | 133 | readl(&priv->regs->channel[priv->cs].chctrl); |
53736baa DB |
134 | } |
135 | ||
77b8d048 | 136 | static int omap3_spi_write(struct omap3_spi_priv *priv, unsigned int len, |
03661d85 | 137 | const void *txp, unsigned long flags) |
53736baa | 138 | { |
611c9ba2 | 139 | ulong start; |
77b8d048 JT |
140 | int i, chconf; |
141 | ||
142 | chconf = readl(&priv->regs->channel[priv->cs].chconf); | |
53736baa | 143 | |
cc1182be | 144 | /* Enable the channel */ |
77b8d048 | 145 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); |
53736baa | 146 | |
5753d09b | 147 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
77b8d048 | 148 | chconf |= (priv->wordlen - 1) << 7; |
53736baa DB |
149 | chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; |
150 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 | 151 | omap3_spi_write_chconf(priv, chconf); |
53736baa DB |
152 | |
153 | for (i = 0; i < len; i++) { | |
154 | /* wait till TX register is empty (TXS == 1) */ | |
611c9ba2 | 155 | start = get_timer(0); |
77b8d048 | 156 | while (!(readl(&priv->regs->channel[priv->cs].chstat) & |
53736baa | 157 | OMAP3_MCSPI_CHSTAT_TXS)) { |
611c9ba2 | 158 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
53736baa | 159 | printf("SPI TXS timed out, status=0x%08x\n", |
77b8d048 | 160 | readl(&priv->regs->channel[priv->cs].chstat)); |
53736baa DB |
161 | return -1; |
162 | } | |
163 | } | |
164 | /* Write the data */ | |
77b8d048 JT |
165 | unsigned int *tx = &priv->regs->channel[priv->cs].tx; |
166 | if (priv->wordlen > 16) | |
5753d09b | 167 | writel(((u32 *)txp)[i], tx); |
77b8d048 | 168 | else if (priv->wordlen > 8) |
5753d09b NK |
169 | writel(((u16 *)txp)[i], tx); |
170 | else | |
171 | writel(((u8 *)txp)[i], tx); | |
53736baa DB |
172 | } |
173 | ||
93e14596 | 174 | /* wait to finish of transfer */ |
77b8d048 JT |
175 | while ((readl(&priv->regs->channel[priv->cs].chstat) & |
176 | (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) != | |
177 | (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) | |
178 | ; | |
cc1182be | 179 | |
180 | /* Disable the channel otherwise the next immediate RX will get affected */ | |
77b8d048 | 181 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); |
cc1182be | 182 | |
53736baa | 183 | if (flags & SPI_XFER_END) { |
53736baa DB |
184 | |
185 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 | 186 | omap3_spi_write_chconf(priv, chconf); |
53736baa DB |
187 | } |
188 | return 0; | |
189 | } | |
190 | ||
77b8d048 | 191 | static int omap3_spi_read(struct omap3_spi_priv *priv, unsigned int len, |
03661d85 | 192 | void *rxp, unsigned long flags) |
53736baa | 193 | { |
77b8d048 | 194 | int i, chconf; |
611c9ba2 | 195 | ulong start; |
77b8d048 JT |
196 | |
197 | chconf = readl(&priv->regs->channel[priv->cs].chconf); | |
53736baa | 198 | |
cc1182be | 199 | /* Enable the channel */ |
77b8d048 | 200 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); |
53736baa | 201 | |
5753d09b | 202 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
77b8d048 | 203 | chconf |= (priv->wordlen - 1) << 7; |
53736baa DB |
204 | chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; |
205 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 | 206 | omap3_spi_write_chconf(priv, chconf); |
53736baa | 207 | |
77b8d048 | 208 | writel(0, &priv->regs->channel[priv->cs].tx); |
53736baa DB |
209 | |
210 | for (i = 0; i < len; i++) { | |
611c9ba2 | 211 | start = get_timer(0); |
53736baa | 212 | /* Wait till RX register contains data (RXS == 1) */ |
77b8d048 | 213 | while (!(readl(&priv->regs->channel[priv->cs].chstat) & |
53736baa | 214 | OMAP3_MCSPI_CHSTAT_RXS)) { |
611c9ba2 | 215 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
53736baa | 216 | printf("SPI RXS timed out, status=0x%08x\n", |
77b8d048 | 217 | readl(&priv->regs->channel[priv->cs].chstat)); |
53736baa DB |
218 | return -1; |
219 | } | |
220 | } | |
cc1182be | 221 | |
222 | /* Disable the channel to prevent furher receiving */ | |
77b8d048 JT |
223 | if (i == (len - 1)) |
224 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | |
cc1182be | 225 | |
53736baa | 226 | /* Read the data */ |
77b8d048 JT |
227 | unsigned int *rx = &priv->regs->channel[priv->cs].rx; |
228 | if (priv->wordlen > 16) | |
5753d09b | 229 | ((u32 *)rxp)[i] = readl(rx); |
77b8d048 | 230 | else if (priv->wordlen > 8) |
5753d09b NK |
231 | ((u16 *)rxp)[i] = (u16)readl(rx); |
232 | else | |
233 | ((u8 *)rxp)[i] = (u8)readl(rx); | |
53736baa DB |
234 | } |
235 | ||
236 | if (flags & SPI_XFER_END) { | |
237 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 | 238 | omap3_spi_write_chconf(priv, chconf); |
53736baa DB |
239 | } |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
08b5ab07 | 244 | /*McSPI Transmit Receive Mode*/ |
77b8d048 | 245 | static int omap3_spi_txrx(struct omap3_spi_priv *priv, unsigned int len, |
03661d85 | 246 | const void *txp, void *rxp, unsigned long flags) |
08b5ab07 | 247 | { |
611c9ba2 | 248 | ulong start; |
77b8d048 JT |
249 | int chconf, i = 0; |
250 | ||
251 | chconf = readl(&priv->regs->channel[priv->cs].chconf); | |
08b5ab07 | 252 | |
253 | /*Enable SPI channel*/ | |
77b8d048 | 254 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); |
08b5ab07 | 255 | |
256 | /*set TRANSMIT-RECEIVE Mode*/ | |
5753d09b | 257 | chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |
77b8d048 | 258 | chconf |= (priv->wordlen - 1) << 7; |
08b5ab07 | 259 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; |
77b8d048 | 260 | omap3_spi_write_chconf(priv, chconf); |
08b5ab07 | 261 | |
262 | /*Shift in and out 1 byte at time*/ | |
263 | for (i=0; i < len; i++){ | |
264 | /* Write: wait for TX empty (TXS == 1)*/ | |
611c9ba2 | 265 | start = get_timer(0); |
77b8d048 | 266 | while (!(readl(&priv->regs->channel[priv->cs].chstat) & |
08b5ab07 | 267 | OMAP3_MCSPI_CHSTAT_TXS)) { |
611c9ba2 | 268 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
08b5ab07 | 269 | printf("SPI TXS timed out, status=0x%08x\n", |
77b8d048 | 270 | readl(&priv->regs->channel[priv->cs].chstat)); |
08b5ab07 | 271 | return -1; |
272 | } | |
273 | } | |
274 | /* Write the data */ | |
77b8d048 JT |
275 | unsigned int *tx = &priv->regs->channel[priv->cs].tx; |
276 | if (priv->wordlen > 16) | |
5753d09b | 277 | writel(((u32 *)txp)[i], tx); |
77b8d048 | 278 | else if (priv->wordlen > 8) |
5753d09b NK |
279 | writel(((u16 *)txp)[i], tx); |
280 | else | |
281 | writel(((u8 *)txp)[i], tx); | |
08b5ab07 | 282 | |
283 | /*Read: wait for RX containing data (RXS == 1)*/ | |
611c9ba2 | 284 | start = get_timer(0); |
77b8d048 | 285 | while (!(readl(&priv->regs->channel[priv->cs].chstat) & |
08b5ab07 | 286 | OMAP3_MCSPI_CHSTAT_RXS)) { |
611c9ba2 | 287 | if (get_timer(start) > SPI_WAIT_TIMEOUT) { |
08b5ab07 | 288 | printf("SPI RXS timed out, status=0x%08x\n", |
77b8d048 | 289 | readl(&priv->regs->channel[priv->cs].chstat)); |
08b5ab07 | 290 | return -1; |
291 | } | |
292 | } | |
293 | /* Read the data */ | |
77b8d048 JT |
294 | unsigned int *rx = &priv->regs->channel[priv->cs].rx; |
295 | if (priv->wordlen > 16) | |
5753d09b | 296 | ((u32 *)rxp)[i] = readl(rx); |
77b8d048 | 297 | else if (priv->wordlen > 8) |
5753d09b NK |
298 | ((u16 *)rxp)[i] = (u16)readl(rx); |
299 | else | |
300 | ((u8 *)rxp)[i] = (u8)readl(rx); | |
08b5ab07 | 301 | } |
cc1182be | 302 | /* Disable the channel */ |
77b8d048 | 303 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); |
08b5ab07 | 304 | |
305 | /*if transfer must be terminated disable the channel*/ | |
306 | if (flags & SPI_XFER_END) { | |
307 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 | 308 | omap3_spi_write_chconf(priv, chconf); |
08b5ab07 | 309 | } |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
77b8d048 JT |
314 | static int _spi_xfer(struct omap3_spi_priv *priv, unsigned int bitlen, |
315 | const void *dout, void *din, unsigned long flags) | |
53736baa | 316 | { |
53736baa | 317 | unsigned int len; |
53736baa DB |
318 | int ret = -1; |
319 | ||
77b8d048 JT |
320 | if (priv->wordlen < 4 || priv->wordlen > 32) { |
321 | printf("omap3_spi: invalid wordlen %d\n", priv->wordlen); | |
5753d09b NK |
322 | return -1; |
323 | } | |
324 | ||
77b8d048 | 325 | if (bitlen % priv->wordlen) |
53736baa DB |
326 | return -1; |
327 | ||
77b8d048 | 328 | len = bitlen / priv->wordlen; |
53736baa DB |
329 | |
330 | if (bitlen == 0) { /* only change CS */ | |
77b8d048 | 331 | int chconf = readl(&priv->regs->channel[priv->cs].chconf); |
53736baa DB |
332 | |
333 | if (flags & SPI_XFER_BEGIN) { | |
77b8d048 | 334 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); |
53736baa | 335 | chconf |= OMAP3_MCSPI_CHCONF_FORCE; |
77b8d048 | 336 | omap3_spi_write_chconf(priv, chconf); |
53736baa DB |
337 | } |
338 | if (flags & SPI_XFER_END) { | |
339 | chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | |
77b8d048 JT |
340 | omap3_spi_write_chconf(priv, chconf); |
341 | omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | |
53736baa DB |
342 | } |
343 | ret = 0; | |
344 | } else { | |
08b5ab07 | 345 | if (dout != NULL && din != NULL) |
77b8d048 | 346 | ret = omap3_spi_txrx(priv, len, dout, din, flags); |
08b5ab07 | 347 | else if (dout != NULL) |
77b8d048 | 348 | ret = omap3_spi_write(priv, len, dout, flags); |
08b5ab07 | 349 | else if (din != NULL) |
77b8d048 | 350 | ret = omap3_spi_read(priv, len, din, flags); |
53736baa DB |
351 | } |
352 | return ret; | |
353 | } | |
354 | ||
77b8d048 JT |
355 | static void _omap3_spi_set_speed(struct omap3_spi_priv *priv) |
356 | { | |
357 | uint32_t confr, div = 0; | |
358 | ||
359 | confr = readl(&priv->regs->channel[priv->cs].chconf); | |
360 | ||
361 | /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ | |
362 | if (priv->freq) { | |
363 | while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) | |
364 | > priv->freq) | |
365 | div++; | |
366 | } else { | |
367 | div = 0xC; | |
368 | } | |
369 | ||
370 | /* set clock divisor */ | |
371 | confr &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; | |
372 | confr |= div << 2; | |
373 | ||
374 | omap3_spi_write_chconf(priv, confr); | |
375 | } | |
376 | ||
377 | static void _omap3_spi_set_mode(struct omap3_spi_priv *priv) | |
378 | { | |
379 | uint32_t confr; | |
380 | ||
381 | confr = readl(&priv->regs->channel[priv->cs].chconf); | |
382 | ||
383 | /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS | |
384 | * REVISIT: this controller could support SPI_3WIRE mode. | |
385 | */ | |
386 | if (priv->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { | |
387 | confr &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); | |
388 | confr |= OMAP3_MCSPI_CHCONF_DPE0; | |
389 | } else { | |
390 | confr &= ~OMAP3_MCSPI_CHCONF_DPE0; | |
391 | confr |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1; | |
392 | } | |
393 | ||
394 | /* set SPI mode 0..3 */ | |
395 | confr &= ~(OMAP3_MCSPI_CHCONF_POL | OMAP3_MCSPI_CHCONF_PHA); | |
396 | if (priv->mode & SPI_CPHA) | |
397 | confr |= OMAP3_MCSPI_CHCONF_PHA; | |
398 | if (priv->mode & SPI_CPOL) | |
399 | confr |= OMAP3_MCSPI_CHCONF_POL; | |
400 | ||
401 | /* set chipselect polarity; manage with FORCE */ | |
402 | if (!(priv->mode & SPI_CS_HIGH)) | |
403 | confr |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ | |
404 | else | |
405 | confr &= ~OMAP3_MCSPI_CHCONF_EPOL; | |
406 | ||
407 | /* Transmit & receive mode */ | |
408 | confr &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; | |
409 | ||
410 | omap3_spi_write_chconf(priv, confr); | |
411 | } | |
412 | ||
413 | static void _omap3_spi_set_wordlen(struct omap3_spi_priv *priv) | |
414 | { | |
415 | unsigned int confr; | |
416 | ||
417 | /* McSPI individual channel configuration */ | |
418 | confr = readl(&priv->regs->channel[priv->wordlen].chconf); | |
419 | ||
420 | /* wordlength */ | |
421 | confr &= ~OMAP3_MCSPI_CHCONF_WL_MASK; | |
422 | confr |= (priv->wordlen - 1) << 7; | |
423 | ||
424 | omap3_spi_write_chconf(priv, confr); | |
425 | } | |
426 | ||
427 | static void spi_reset(struct mcspi *regs) | |
428 | { | |
429 | unsigned int tmp; | |
430 | ||
431 | writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, ®s->sysconfig); | |
432 | do { | |
433 | tmp = readl(®s->sysstatus); | |
434 | } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); | |
435 | ||
436 | writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | | |
437 | OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | | |
438 | OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, ®s->sysconfig); | |
439 | ||
440 | writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, ®s->wakeupenable); | |
441 | } | |
442 | ||
443 | static void _omap3_spi_claim_bus(struct omap3_spi_priv *priv) | |
444 | { | |
445 | unsigned int conf; | |
77b8d048 JT |
446 | /* |
447 | * setup when switching from (reset default) slave mode | |
448 | * to single-channel master mode | |
449 | */ | |
450 | conf = readl(&priv->regs->modulctrl); | |
451 | conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); | |
452 | conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; | |
453 | ||
454 | writel(conf, &priv->regs->modulctrl); | |
77b8d048 JT |
455 | } |
456 | ||
457 | #ifndef CONFIG_DM_SPI | |
458 | ||
41bccb81 | 459 | static inline struct omap3_spi_priv *to_omap3_spi(struct spi_slave *slave) |
77b8d048 | 460 | { |
41bccb81 | 461 | return container_of(slave, struct omap3_spi_priv, slave); |
77b8d048 JT |
462 | } |
463 | ||
464 | void spi_init(void) | |
53736baa | 465 | { |
77b8d048 | 466 | /* do nothing */ |
53736baa DB |
467 | } |
468 | ||
77b8d048 | 469 | void spi_free_slave(struct spi_slave *slave) |
53736baa | 470 | { |
41bccb81 | 471 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
77b8d048 | 472 | |
41bccb81 | 473 | free(priv); |
77b8d048 JT |
474 | } |
475 | ||
476 | int spi_claim_bus(struct spi_slave *slave) | |
477 | { | |
41bccb81 JT |
478 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
479 | ||
c0eaffa0 HS |
480 | spi_reset(priv->regs); |
481 | ||
77b8d048 JT |
482 | _omap3_spi_claim_bus(priv); |
483 | _omap3_spi_set_wordlen(priv); | |
484 | _omap3_spi_set_mode(priv); | |
485 | _omap3_spi_set_speed(priv); | |
486 | ||
487 | return 0; | |
53736baa DB |
488 | } |
489 | ||
77b8d048 | 490 | void spi_release_bus(struct spi_slave *slave) |
53736baa | 491 | { |
41bccb81 JT |
492 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
493 | ||
c0eaffa0 | 494 | writel(OMAP3_MCSPI_MODULCTRL_MS, &priv->regs->modulctrl); |
53736baa | 495 | } |
77b8d048 JT |
496 | |
497 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
498 | unsigned int max_hz, unsigned int mode) | |
499 | { | |
41bccb81 | 500 | struct omap3_spi_priv *priv; |
77b8d048 JT |
501 | struct mcspi *regs; |
502 | ||
503 | /* | |
504 | * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) | |
505 | * with different number of chip selects (CS, channels): | |
506 | * McSPI1 has 4 CS (bus 0, cs 0 - 3) | |
507 | * McSPI2 has 2 CS (bus 1, cs 0 - 1) | |
508 | * McSPI3 has 2 CS (bus 2, cs 0 - 1) | |
509 | * McSPI4 has 1 CS (bus 3, cs 0) | |
510 | */ | |
511 | ||
512 | switch (bus) { | |
513 | case 0: | |
514 | regs = (struct mcspi *)OMAP3_MCSPI1_BASE; | |
515 | break; | |
516 | #ifdef OMAP3_MCSPI2_BASE | |
517 | case 1: | |
518 | regs = (struct mcspi *)OMAP3_MCSPI2_BASE; | |
519 | break; | |
520 | #endif | |
521 | #ifdef OMAP3_MCSPI3_BASE | |
522 | case 2: | |
523 | regs = (struct mcspi *)OMAP3_MCSPI3_BASE; | |
524 | break; | |
525 | #endif | |
526 | #ifdef OMAP3_MCSPI4_BASE | |
527 | case 3: | |
528 | regs = (struct mcspi *)OMAP3_MCSPI4_BASE; | |
529 | break; | |
530 | #endif | |
531 | default: | |
532 | printf("SPI error: unsupported bus %i. Supported busses 0 - 3\n", bus); | |
533 | return NULL; | |
534 | } | |
535 | ||
536 | if (((bus == 0) && (cs > 3)) || | |
537 | ((bus == 1) && (cs > 1)) || | |
538 | ((bus == 2) && (cs > 1)) || | |
539 | ((bus == 3) && (cs > 0))) { | |
540 | printf("SPI error: unsupported chip select %i on bus %i\n", cs, bus); | |
541 | return NULL; | |
542 | } | |
543 | ||
544 | if (max_hz > OMAP3_MCSPI_MAX_FREQ) { | |
042de609 HS |
545 | printf("SPI error: unsupported frequency %i Hz. Max frequency is 48 MHz\n", |
546 | max_hz); | |
77b8d048 JT |
547 | return NULL; |
548 | } | |
549 | ||
550 | if (mode > SPI_MODE_3) { | |
551 | printf("SPI error: unsupported SPI mode %i\n", mode); | |
552 | return NULL; | |
553 | } | |
554 | ||
41bccb81 JT |
555 | priv = spi_alloc_slave(struct omap3_spi_priv, bus, cs); |
556 | if (!priv) { | |
77b8d048 JT |
557 | printf("SPI error: malloc of SPI structure failed\n"); |
558 | return NULL; | |
559 | } | |
560 | ||
77b8d048 JT |
561 | priv->regs = regs; |
562 | priv->cs = cs; | |
563 | priv->freq = max_hz; | |
564 | priv->mode = mode; | |
41bccb81 | 565 | priv->wordlen = priv->slave.wordlen; |
25eaa288 TR |
566 | #if 0 |
567 | /* Please migrate to DM_SPI support for this feature. */ | |
77b8d048 JT |
568 | priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; |
569 | #endif | |
570 | ||
41bccb81 | 571 | return &priv->slave; |
77b8d048 JT |
572 | } |
573 | ||
574 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | |
575 | const void *dout, void *din, unsigned long flags) | |
41bccb81 JT |
576 | { |
577 | struct omap3_spi_priv *priv = to_omap3_spi(slave); | |
578 | ||
579 | return _spi_xfer(priv, bitlen, dout, din, flags); | |
580 | } | |
77b8d048 JT |
581 | |
582 | #else | |
583 | ||
584 | static int omap3_spi_claim_bus(struct udevice *dev) | |
585 | { | |
586 | struct udevice *bus = dev->parent; | |
587 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
588 | struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | |
589 | ||
590 | priv->cs = slave_plat->cs; | |
b1d2b529 HS |
591 | priv->freq = slave_plat->max_hz; |
592 | ||
77b8d048 JT |
593 | _omap3_spi_claim_bus(priv); |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
598 | static int omap3_spi_release_bus(struct udevice *dev) | |
599 | { | |
600 | struct udevice *bus = dev->parent; | |
601 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
602 | ||
c0eaffa0 | 603 | writel(OMAP3_MCSPI_MODULCTRL_MS, &priv->regs->modulctrl); |
77b8d048 JT |
604 | |
605 | return 0; | |
606 | } | |
607 | ||
608 | static int omap3_spi_set_wordlen(struct udevice *dev, unsigned int wordlen) | |
609 | { | |
610 | struct udevice *bus = dev->parent; | |
611 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
612 | struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | |
613 | ||
614 | priv->cs = slave_plat->cs; | |
615 | priv->wordlen = wordlen; | |
616 | _omap3_spi_set_wordlen(priv); | |
617 | ||
618 | return 0; | |
619 | } | |
620 | ||
621 | static int omap3_spi_probe(struct udevice *dev) | |
622 | { | |
623 | struct omap3_spi_priv *priv = dev_get_priv(dev); | |
624 | const void *blob = gd->fdt_blob; | |
e160f7d4 | 625 | int node = dev_of_offset(dev); |
77b8d048 | 626 | |
5f89a15e MH |
627 | struct omap2_mcspi_platform_config* data = |
628 | (struct omap2_mcspi_platform_config*)dev_get_driver_data(dev); | |
629 | ||
a821c4af | 630 | priv->regs = (struct mcspi *)(devfdt_get_addr(dev) + data->regs_offset); |
63018a3e SS |
631 | if (fdtdec_get_bool(blob, node, "ti,pindir-d0-out-d1-in")) |
632 | priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; | |
633 | else | |
634 | priv->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; | |
77b8d048 | 635 | priv->wordlen = SPI_DEFAULT_WORDLEN; |
c0eaffa0 HS |
636 | |
637 | spi_reset(priv->regs); | |
638 | ||
77b8d048 JT |
639 | return 0; |
640 | } | |
641 | ||
642 | static int omap3_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
643 | const void *dout, void *din, unsigned long flags) | |
644 | { | |
645 | struct udevice *bus = dev->parent; | |
646 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
647 | ||
648 | return _spi_xfer(priv, bitlen, dout, din, flags); | |
649 | } | |
650 | ||
b2b41d27 | 651 | static int omap3_spi_set_speed(struct udevice *dev, unsigned int speed) |
77b8d048 | 652 | { |
84807922 | 653 | |
9cddf70e HS |
654 | struct omap3_spi_priv *priv = dev_get_priv(dev); |
655 | ||
656 | priv->freq = speed; | |
84807922 JT |
657 | _omap3_spi_set_speed(priv); |
658 | ||
77b8d048 JT |
659 | return 0; |
660 | } | |
661 | ||
b2b41d27 | 662 | static int omap3_spi_set_mode(struct udevice *dev, uint mode) |
77b8d048 | 663 | { |
9cddf70e HS |
664 | struct omap3_spi_priv *priv = dev_get_priv(dev); |
665 | ||
666 | priv->mode = mode; | |
84807922 | 667 | |
84807922 JT |
668 | _omap3_spi_set_mode(priv); |
669 | ||
77b8d048 JT |
670 | return 0; |
671 | } | |
672 | ||
673 | static const struct dm_spi_ops omap3_spi_ops = { | |
674 | .claim_bus = omap3_spi_claim_bus, | |
675 | .release_bus = omap3_spi_release_bus, | |
676 | .set_wordlen = omap3_spi_set_wordlen, | |
677 | .xfer = omap3_spi_xfer, | |
678 | .set_speed = omap3_spi_set_speed, | |
679 | .set_mode = omap3_spi_set_mode, | |
680 | /* | |
681 | * cs_info is not needed, since we require all chip selects to be | |
682 | * in the device tree explicitly | |
683 | */ | |
684 | }; | |
685 | ||
5f89a15e MH |
686 | static struct omap2_mcspi_platform_config omap2_pdata = { |
687 | .regs_offset = 0, | |
688 | }; | |
689 | ||
690 | static struct omap2_mcspi_platform_config omap4_pdata = { | |
691 | .regs_offset = OMAP4_MCSPI_REG_OFFSET, | |
692 | }; | |
693 | ||
77b8d048 | 694 | static const struct udevice_id omap3_spi_ids[] = { |
5f89a15e MH |
695 | { .compatible = "ti,omap2-mcspi", .data = (ulong)&omap2_pdata }, |
696 | { .compatible = "ti,omap4-mcspi", .data = (ulong)&omap4_pdata }, | |
77b8d048 JT |
697 | { } |
698 | }; | |
699 | ||
700 | U_BOOT_DRIVER(omap3_spi) = { | |
701 | .name = "omap3_spi", | |
702 | .id = UCLASS_SPI, | |
703 | .of_match = omap3_spi_ids, | |
704 | .probe = omap3_spi_probe, | |
705 | .ops = &omap3_spi_ops, | |
706 | .priv_auto_alloc_size = sizeof(struct omap3_spi_priv), | |
77b8d048 JT |
707 | }; |
708 | #endif |