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