]>
Commit | Line | Data |
---|---|---|
53736baa | 1 | /* |
77b8d048 JT |
2 | * Copyright (C) 2016 Jagan Teki <jteki@openedev.com> |
3 | * Christophe Ricard <christophe.ricard@gmail.com> | |
4 | * | |
53736baa DB |
5 | * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> |
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 <ruslan.araslanov@vitecmm.com> | |
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); | |
459 | ||
460 | _omap3_spi_set_mode(priv); | |
461 | _omap3_spi_set_speed(priv); | |
462 | } | |
463 | ||
464 | #ifndef CONFIG_DM_SPI | |
465 | ||
41bccb81 | 466 | static inline struct omap3_spi_priv *to_omap3_spi(struct spi_slave *slave) |
77b8d048 | 467 | { |
41bccb81 | 468 | return container_of(slave, struct omap3_spi_priv, slave); |
77b8d048 JT |
469 | } |
470 | ||
471 | void spi_init(void) | |
53736baa | 472 | { |
77b8d048 | 473 | /* do nothing */ |
53736baa DB |
474 | } |
475 | ||
77b8d048 | 476 | void spi_free_slave(struct spi_slave *slave) |
53736baa | 477 | { |
41bccb81 | 478 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
77b8d048 | 479 | |
41bccb81 | 480 | free(priv); |
77b8d048 JT |
481 | } |
482 | ||
483 | int spi_claim_bus(struct spi_slave *slave) | |
484 | { | |
41bccb81 JT |
485 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
486 | ||
77b8d048 JT |
487 | _omap3_spi_claim_bus(priv); |
488 | _omap3_spi_set_wordlen(priv); | |
489 | _omap3_spi_set_mode(priv); | |
490 | _omap3_spi_set_speed(priv); | |
491 | ||
492 | return 0; | |
53736baa DB |
493 | } |
494 | ||
77b8d048 | 495 | void spi_release_bus(struct spi_slave *slave) |
53736baa | 496 | { |
41bccb81 JT |
497 | struct omap3_spi_priv *priv = to_omap3_spi(slave); |
498 | ||
77b8d048 JT |
499 | /* Reset the SPI hardware */ |
500 | spi_reset(priv->regs); | |
53736baa | 501 | } |
77b8d048 JT |
502 | |
503 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
504 | unsigned int max_hz, unsigned int mode) | |
505 | { | |
41bccb81 | 506 | struct omap3_spi_priv *priv; |
77b8d048 JT |
507 | struct mcspi *regs; |
508 | ||
509 | /* | |
510 | * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) | |
511 | * with different number of chip selects (CS, channels): | |
512 | * McSPI1 has 4 CS (bus 0, cs 0 - 3) | |
513 | * McSPI2 has 2 CS (bus 1, cs 0 - 1) | |
514 | * McSPI3 has 2 CS (bus 2, cs 0 - 1) | |
515 | * McSPI4 has 1 CS (bus 3, cs 0) | |
516 | */ | |
517 | ||
518 | switch (bus) { | |
519 | case 0: | |
520 | regs = (struct mcspi *)OMAP3_MCSPI1_BASE; | |
521 | break; | |
522 | #ifdef OMAP3_MCSPI2_BASE | |
523 | case 1: | |
524 | regs = (struct mcspi *)OMAP3_MCSPI2_BASE; | |
525 | break; | |
526 | #endif | |
527 | #ifdef OMAP3_MCSPI3_BASE | |
528 | case 2: | |
529 | regs = (struct mcspi *)OMAP3_MCSPI3_BASE; | |
530 | break; | |
531 | #endif | |
532 | #ifdef OMAP3_MCSPI4_BASE | |
533 | case 3: | |
534 | regs = (struct mcspi *)OMAP3_MCSPI4_BASE; | |
535 | break; | |
536 | #endif | |
537 | default: | |
538 | printf("SPI error: unsupported bus %i. Supported busses 0 - 3\n", bus); | |
539 | return NULL; | |
540 | } | |
541 | ||
542 | if (((bus == 0) && (cs > 3)) || | |
543 | ((bus == 1) && (cs > 1)) || | |
544 | ((bus == 2) && (cs > 1)) || | |
545 | ((bus == 3) && (cs > 0))) { | |
546 | printf("SPI error: unsupported chip select %i on bus %i\n", cs, bus); | |
547 | return NULL; | |
548 | } | |
549 | ||
550 | if (max_hz > OMAP3_MCSPI_MAX_FREQ) { | |
551 | printf("SPI error: unsupported frequency %i Hz. Max frequency is 48 Mhz\n", max_hz); | |
552 | return NULL; | |
553 | } | |
554 | ||
555 | if (mode > SPI_MODE_3) { | |
556 | printf("SPI error: unsupported SPI mode %i\n", mode); | |
557 | return NULL; | |
558 | } | |
559 | ||
41bccb81 JT |
560 | priv = spi_alloc_slave(struct omap3_spi_priv, bus, cs); |
561 | if (!priv) { | |
77b8d048 JT |
562 | printf("SPI error: malloc of SPI structure failed\n"); |
563 | return NULL; | |
564 | } | |
565 | ||
77b8d048 JT |
566 | priv->regs = regs; |
567 | priv->cs = cs; | |
568 | priv->freq = max_hz; | |
569 | priv->mode = mode; | |
41bccb81 | 570 | priv->wordlen = priv->slave.wordlen; |
77b8d048 JT |
571 | #ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED |
572 | priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; | |
573 | #endif | |
574 | ||
41bccb81 | 575 | return &priv->slave; |
77b8d048 JT |
576 | } |
577 | ||
578 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | |
579 | const void *dout, void *din, unsigned long flags) | |
41bccb81 JT |
580 | { |
581 | struct omap3_spi_priv *priv = to_omap3_spi(slave); | |
582 | ||
583 | return _spi_xfer(priv, bitlen, dout, din, flags); | |
584 | } | |
77b8d048 JT |
585 | |
586 | #else | |
587 | ||
588 | static int omap3_spi_claim_bus(struct udevice *dev) | |
589 | { | |
590 | struct udevice *bus = dev->parent; | |
591 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
592 | struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | |
593 | ||
594 | priv->cs = slave_plat->cs; | |
595 | priv->mode = slave_plat->mode; | |
596 | priv->freq = slave_plat->max_hz; | |
597 | _omap3_spi_claim_bus(priv); | |
598 | ||
599 | return 0; | |
600 | } | |
601 | ||
602 | static int omap3_spi_release_bus(struct udevice *dev) | |
603 | { | |
604 | struct udevice *bus = dev->parent; | |
605 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
606 | ||
607 | /* Reset the SPI hardware */ | |
608 | spi_reset(priv->regs); | |
609 | ||
610 | return 0; | |
611 | } | |
612 | ||
613 | static int omap3_spi_set_wordlen(struct udevice *dev, unsigned int wordlen) | |
614 | { | |
615 | struct udevice *bus = dev->parent; | |
616 | struct omap3_spi_priv *priv = dev_get_priv(bus); | |
617 | struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | |
618 | ||
619 | priv->cs = slave_plat->cs; | |
620 | priv->wordlen = wordlen; | |
621 | _omap3_spi_set_wordlen(priv); | |
622 | ||
623 | return 0; | |
624 | } | |
625 | ||
626 | static int omap3_spi_probe(struct udevice *dev) | |
627 | { | |
628 | struct omap3_spi_priv *priv = dev_get_priv(dev); | |
629 | const void *blob = gd->fdt_blob; | |
630 | int node = dev->of_offset; | |
631 | ||
5f89a15e MH |
632 | struct omap2_mcspi_platform_config* data = |
633 | (struct omap2_mcspi_platform_config*)dev_get_driver_data(dev); | |
634 | ||
635 | priv->regs = (struct mcspi *)(dev_get_addr(dev) + data->regs_offset); | |
77b8d048 JT |
636 | priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in", |
637 | MCSPI_PINDIR_D0_IN_D1_OUT); | |
638 | priv->wordlen = SPI_DEFAULT_WORDLEN; | |
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 | ||
651 | static int omap3_spi_set_speed(struct udevice *bus, unsigned int speed) | |
652 | { | |
653 | return 0; | |
654 | } | |
655 | ||
656 | static int omap3_spi_set_mode(struct udevice *bus, uint mode) | |
657 | { | |
658 | return 0; | |
659 | } | |
660 | ||
661 | static const struct dm_spi_ops omap3_spi_ops = { | |
662 | .claim_bus = omap3_spi_claim_bus, | |
663 | .release_bus = omap3_spi_release_bus, | |
664 | .set_wordlen = omap3_spi_set_wordlen, | |
665 | .xfer = omap3_spi_xfer, | |
666 | .set_speed = omap3_spi_set_speed, | |
667 | .set_mode = omap3_spi_set_mode, | |
668 | /* | |
669 | * cs_info is not needed, since we require all chip selects to be | |
670 | * in the device tree explicitly | |
671 | */ | |
672 | }; | |
673 | ||
5f89a15e MH |
674 | static struct omap2_mcspi_platform_config omap2_pdata = { |
675 | .regs_offset = 0, | |
676 | }; | |
677 | ||
678 | static struct omap2_mcspi_platform_config omap4_pdata = { | |
679 | .regs_offset = OMAP4_MCSPI_REG_OFFSET, | |
680 | }; | |
681 | ||
77b8d048 | 682 | static const struct udevice_id omap3_spi_ids[] = { |
5f89a15e MH |
683 | { .compatible = "ti,omap2-mcspi", .data = (ulong)&omap2_pdata }, |
684 | { .compatible = "ti,omap4-mcspi", .data = (ulong)&omap4_pdata }, | |
77b8d048 JT |
685 | { } |
686 | }; | |
687 | ||
688 | U_BOOT_DRIVER(omap3_spi) = { | |
689 | .name = "omap3_spi", | |
690 | .id = UCLASS_SPI, | |
691 | .of_match = omap3_spi_ids, | |
692 | .probe = omap3_spi_probe, | |
693 | .ops = &omap3_spi_ops, | |
694 | .priv_auto_alloc_size = sizeof(struct omap3_spi_priv), | |
695 | .probe = omap3_spi_probe, | |
696 | }; | |
697 | #endif |