]>
Commit | Line | Data |
---|---|---|
400f5778 MF |
1 | /* |
2 | * Driver for Blackfin On-Chip SPI device | |
3 | * | |
6f070e18 | 4 | * Copyright (c) 2005-2010 Analog Devices Inc. |
400f5778 | 5 | * |
e7b1e452 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
400f5778 MF |
7 | */ |
8 | ||
9 | /*#define DEBUG*/ | |
10 | ||
11 | #include <common.h> | |
24b852a7 | 12 | #include <console.h> |
400f5778 MF |
13 | #include <malloc.h> |
14 | #include <spi.h> | |
15 | ||
16 | #include <asm/blackfin.h> | |
d6a320d5 | 17 | #include <asm/clock.h> |
37a4b75d | 18 | #include <asm/gpio.h> |
f3732edf | 19 | #include <asm/portmux.h> |
400f5778 MF |
20 | #include <asm/mach-common/bits/spi.h> |
21 | ||
22 | struct bfin_spi_slave { | |
23 | struct spi_slave slave; | |
24 | void *mmr_base; | |
25 | u16 ctl, baud, flg; | |
26 | }; | |
27 | ||
28 | #define MAKE_SPI_FUNC(mmr, off) \ | |
29 | static inline void write_##mmr(struct bfin_spi_slave *bss, u16 val) { bfin_write16(bss->mmr_base + off, val); } \ | |
30 | static inline u16 read_##mmr(struct bfin_spi_slave *bss) { return bfin_read16(bss->mmr_base + off); } | |
31 | MAKE_SPI_FUNC(SPI_CTL, 0x00) | |
32 | MAKE_SPI_FUNC(SPI_FLG, 0x04) | |
33 | MAKE_SPI_FUNC(SPI_STAT, 0x08) | |
34 | MAKE_SPI_FUNC(SPI_TDBR, 0x0c) | |
35 | MAKE_SPI_FUNC(SPI_RDBR, 0x10) | |
36 | MAKE_SPI_FUNC(SPI_BAUD, 0x14) | |
37 | ||
38 | #define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) | |
39 | ||
37a4b75d MF |
40 | #define gpio_cs(cs) ((cs) - MAX_CTRL_CS) |
41 | #ifdef CONFIG_BFIN_SPI_GPIO_CS | |
42 | # define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) | |
43 | #else | |
44 | # define is_gpio_cs(cs) 0 | |
45 | #endif | |
46 | ||
400f5778 MF |
47 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) |
48 | { | |
37a4b75d MF |
49 | if (is_gpio_cs(cs)) |
50 | return gpio_is_valid(gpio_cs(cs)); | |
51 | else | |
52 | return (cs >= 1 && cs <= MAX_CTRL_CS); | |
400f5778 MF |
53 | } |
54 | ||
400f5778 MF |
55 | void spi_cs_activate(struct spi_slave *slave) |
56 | { | |
57 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
37a4b75d MF |
58 | |
59 | if (is_gpio_cs(slave->cs)) { | |
60 | unsigned int cs = gpio_cs(slave->cs); | |
61 | gpio_set_value(cs, bss->flg); | |
62 | debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); | |
63 | } else { | |
64 | write_SPI_FLG(bss, | |
65 | (read_SPI_FLG(bss) & | |
66 | ~((!bss->flg << 8) << slave->cs)) | | |
67 | (1 << slave->cs)); | |
68 | debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); | |
69 | } | |
70 | ||
d04371a1 | 71 | SSYNC(); |
400f5778 MF |
72 | } |
73 | ||
400f5778 MF |
74 | void spi_cs_deactivate(struct spi_slave *slave) |
75 | { | |
76 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
d04371a1 | 77 | |
37a4b75d MF |
78 | if (is_gpio_cs(slave->cs)) { |
79 | unsigned int cs = gpio_cs(slave->cs); | |
80 | gpio_set_value(cs, !bss->flg); | |
81 | debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs)); | |
82 | } else { | |
83 | u16 flg; | |
84 | ||
85 | /* make sure we force the cs to deassert rather than let the | |
86 | * pin float back up. otherwise, exact timings may not be | |
87 | * met some of the time leading to random behavior (ugh). | |
88 | */ | |
89 | flg = read_SPI_FLG(bss) | ((!bss->flg << 8) << slave->cs); | |
90 | write_SPI_FLG(bss, flg); | |
91 | SSYNC(); | |
92 | debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); | |
93 | ||
94 | flg &= ~(1 << slave->cs); | |
95 | write_SPI_FLG(bss, flg); | |
96 | debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss)); | |
97 | } | |
98 | ||
d04371a1 | 99 | SSYNC(); |
400f5778 MF |
100 | } |
101 | ||
102 | void spi_init() | |
103 | { | |
104 | } | |
105 | ||
f3732edf MF |
106 | #ifdef SPI_CTL |
107 | # define SPI0_CTL SPI_CTL | |
108 | #endif | |
109 | ||
110 | #define SPI_PINS(n) \ | |
111 | [n] = { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } | |
112 | static unsigned short pins[][5] = { | |
113 | #ifdef SPI0_CTL | |
114 | SPI_PINS(0), | |
115 | #endif | |
116 | #ifdef SPI1_CTL | |
117 | SPI_PINS(1), | |
118 | #endif | |
119 | #ifdef SPI2_CTL | |
120 | SPI_PINS(2), | |
121 | #endif | |
122 | }; | |
123 | ||
124 | #define SPI_CS_PINS(n) \ | |
125 | [n] = { \ | |
126 | P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ | |
127 | P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ | |
128 | P_SPI##n##_SSEL7, \ | |
129 | } | |
130 | static const unsigned short cs_pins[][7] = { | |
131 | #ifdef SPI0_CTL | |
132 | SPI_CS_PINS(0), | |
133 | #endif | |
134 | #ifdef SPI1_CTL | |
135 | SPI_CS_PINS(1), | |
136 | #endif | |
137 | #ifdef SPI2_CTL | |
138 | SPI_CS_PINS(2), | |
139 | #endif | |
140 | }; | |
141 | ||
6c3eb43a TC |
142 | void spi_set_speed(struct spi_slave *slave, uint hz) |
143 | { | |
144 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
d6a320d5 | 145 | ulong clk; |
6c3eb43a TC |
146 | u32 baud; |
147 | ||
d6a320d5 | 148 | clk = get_spi_clk(); |
6c3eb43a | 149 | /* baud should be rounded up */ |
d6a320d5 | 150 | baud = DIV_ROUND_UP(clk, 2 * hz); |
6c3eb43a TC |
151 | if (baud < 2) |
152 | baud = 2; | |
153 | else if (baud > (u16)-1) | |
154 | baud = -1; | |
155 | bss->baud = baud; | |
156 | } | |
157 | ||
400f5778 MF |
158 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, |
159 | unsigned int max_hz, unsigned int mode) | |
160 | { | |
161 | struct bfin_spi_slave *bss; | |
162 | u32 mmr_base; | |
400f5778 MF |
163 | |
164 | if (!spi_cs_is_valid(bus, cs)) | |
165 | return NULL; | |
166 | ||
167 | switch (bus) { | |
f3732edf | 168 | #ifdef SPI0_CTL |
727cbe14 AL |
169 | case 0: |
170 | mmr_base = SPI0_CTL; break; | |
f3732edf | 171 | #endif |
400f5778 | 172 | #ifdef SPI1_CTL |
727cbe14 AL |
173 | case 1: |
174 | mmr_base = SPI1_CTL; break; | |
400f5778 MF |
175 | #endif |
176 | #ifdef SPI2_CTL | |
727cbe14 AL |
177 | case 2: |
178 | mmr_base = SPI2_CTL; break; | |
400f5778 | 179 | #endif |
727cbe14 AL |
180 | default: |
181 | debug("%s: invalid bus %u\n", __func__, bus); | |
182 | return NULL; | |
400f5778 MF |
183 | } |
184 | ||
d3504fee | 185 | bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs); |
400f5778 MF |
186 | if (!bss) |
187 | return NULL; | |
188 | ||
400f5778 MF |
189 | bss->mmr_base = (void *)mmr_base; |
190 | bss->ctl = SPE | MSTR | TDBR_CORE; | |
191 | if (mode & SPI_CPHA) bss->ctl |= CPHA; | |
192 | if (mode & SPI_CPOL) bss->ctl |= CPOL; | |
193 | if (mode & SPI_LSB_FIRST) bss->ctl |= LSBF; | |
400f5778 | 194 | bss->flg = mode & SPI_CS_HIGH ? 1 : 0; |
6c3eb43a | 195 | spi_set_speed(&bss->slave, max_hz); |
400f5778 MF |
196 | |
197 | debug("%s: bus:%i cs:%i mmr:%x ctl:%x baud:%i flg:%i\n", __func__, | |
e2f01450 | 198 | bus, cs, mmr_base, bss->ctl, bss->baud, bss->flg); |
400f5778 MF |
199 | |
200 | return &bss->slave; | |
201 | } | |
202 | ||
203 | void spi_free_slave(struct spi_slave *slave) | |
204 | { | |
205 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
206 | free(bss); | |
207 | } | |
208 | ||
400f5778 MF |
209 | int spi_claim_bus(struct spi_slave *slave) |
210 | { | |
211 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
212 | ||
213 | debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); | |
214 | ||
37a4b75d MF |
215 | if (is_gpio_cs(slave->cs)) { |
216 | unsigned int cs = gpio_cs(slave->cs); | |
217 | gpio_request(cs, "bfin-spi"); | |
218 | gpio_direction_output(cs, !bss->flg); | |
219 | pins[slave->bus][0] = P_DONTCARE; | |
220 | } else | |
221 | pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; | |
f3732edf MF |
222 | peripheral_request_list(pins[slave->bus], "bfin-spi"); |
223 | ||
400f5778 MF |
224 | write_SPI_CTL(bss, bss->ctl); |
225 | write_SPI_BAUD(bss, bss->baud); | |
226 | SSYNC(); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | void spi_release_bus(struct spi_slave *slave) | |
232 | { | |
233 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
f3732edf | 234 | |
400f5778 | 235 | debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); |
f3732edf MF |
236 | |
237 | peripheral_free_list(pins[slave->bus]); | |
37a4b75d MF |
238 | if (is_gpio_cs(slave->cs)) |
239 | gpio_free(gpio_cs(slave->cs)); | |
f3732edf | 240 | |
400f5778 MF |
241 | write_SPI_CTL(bss, 0); |
242 | SSYNC(); | |
243 | } | |
244 | ||
17ebd5bf CC |
245 | #ifndef CONFIG_BFIN_SPI_IDLE_VAL |
246 | # define CONFIG_BFIN_SPI_IDLE_VAL 0xff | |
247 | #endif | |
248 | ||
6f070e18 MF |
249 | static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, |
250 | uint bytes) | |
251 | { | |
d4d4f903 SJ |
252 | /* discard invalid data and clear RXS */ |
253 | read_SPI_RDBR(bss); | |
6f070e18 MF |
254 | /* todo: take advantage of hardware fifos */ |
255 | while (bytes--) { | |
256 | u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); | |
257 | debug("%s: tx:%x ", __func__, value); | |
258 | write_SPI_TDBR(bss, value); | |
259 | SSYNC(); | |
260 | while ((read_SPI_STAT(bss) & TXS)) | |
261 | if (ctrlc()) | |
262 | return -1; | |
263 | while (!(read_SPI_STAT(bss) & SPIF)) | |
264 | if (ctrlc()) | |
265 | return -1; | |
266 | while (!(read_SPI_STAT(bss) & RXS)) | |
267 | if (ctrlc()) | |
268 | return -1; | |
269 | value = read_SPI_RDBR(bss); | |
270 | if (rx) | |
271 | *rx++ = value; | |
272 | debug("rx:%x\n", value); | |
273 | } | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
400f5778 MF |
278 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, |
279 | void *din, unsigned long flags) | |
280 | { | |
281 | struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | |
282 | const u8 *tx = dout; | |
283 | u8 *rx = din; | |
284 | uint bytes = bitlen / 8; | |
285 | int ret = 0; | |
286 | ||
287 | debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, | |
288 | slave->bus, slave->cs, bitlen, bytes, flags); | |
289 | ||
290 | if (bitlen == 0) | |
291 | goto done; | |
292 | ||
293 | /* we can only do 8 bit transfers */ | |
294 | if (bitlen % 8) { | |
295 | flags |= SPI_XFER_END; | |
296 | goto done; | |
297 | } | |
298 | ||
299 | if (flags & SPI_XFER_BEGIN) | |
300 | spi_cs_activate(slave); | |
301 | ||
e76276df | 302 | ret = spi_pio_xfer(bss, tx, rx, bytes); |
400f5778 MF |
303 | |
304 | done: | |
305 | if (flags & SPI_XFER_END) | |
306 | spi_cs_deactivate(slave); | |
307 | ||
308 | return ret; | |
309 | } |