]>
Commit | Line | Data |
---|---|---|
fafc2454 ML |
1 | /* |
2 | * Driver of Andes SPI Controller | |
3 | * | |
4 | * (C) Copyright 2011 Andes Technology | |
5 | * Macpaul Lin <macpaul@andestech.com> | |
6 | * | |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
fafc2454 ML |
8 | */ |
9 | ||
10 | #include <common.h> | |
11 | #include <malloc.h> | |
12 | #include <spi.h> | |
13 | ||
14 | #include <asm/io.h> | |
15 | #include "andes_spi.h" | |
16 | ||
17 | void spi_init(void) | |
18 | { | |
19 | /* do nothing */ | |
20 | } | |
21 | ||
22 | static void andes_spi_spit_en(struct andes_spi_slave *ds) | |
23 | { | |
24 | unsigned int dcr = readl(&ds->regs->dcr); | |
25 | ||
26 | debug("%s: dcr: %x, write value: %x\n", | |
27 | __func__, dcr, (dcr | ANDES_SPI_DCR_SPIT)); | |
28 | ||
29 | writel((dcr | ANDES_SPI_DCR_SPIT), &ds->regs->dcr); | |
30 | } | |
31 | ||
32 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
33 | unsigned int max_hz, unsigned int mode) | |
34 | { | |
35 | struct andes_spi_slave *ds; | |
36 | ||
37 | if (!spi_cs_is_valid(bus, cs)) | |
38 | return NULL; | |
39 | ||
d3504fee | 40 | ds = spi_alloc_slave(struct andes_spi_slave, bus, cs); |
fafc2454 ML |
41 | if (!ds) |
42 | return NULL; | |
43 | ||
fafc2454 ML |
44 | ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE; |
45 | ||
46 | /* | |
47 | * The hardware of andes_spi will set its frequency according | |
48 | * to APB/AHB bus clock. Hence the hardware doesn't allow changing of | |
49 | * requency and so the user requested speed is always ignored. | |
50 | */ | |
51 | ds->freq = max_hz; | |
52 | ||
53 | return &ds->slave; | |
54 | } | |
55 | ||
56 | void spi_free_slave(struct spi_slave *slave) | |
57 | { | |
58 | struct andes_spi_slave *ds = to_andes_spi(slave); | |
59 | ||
60 | free(ds); | |
61 | } | |
62 | ||
63 | int spi_claim_bus(struct spi_slave *slave) | |
64 | { | |
65 | struct andes_spi_slave *ds = to_andes_spi(slave); | |
66 | unsigned int apb; | |
67 | unsigned int baud; | |
68 | ||
69 | /* Enable the SPI hardware */ | |
70 | writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); | |
71 | udelay(1000); | |
72 | ||
73 | /* setup format */ | |
74 | baud = ((CONFIG_SYS_CLK_FREQ / CONFIG_SYS_SPI_CLK / 2) - 1) & 0xFF; | |
75 | ||
76 | /* | |
77 | * SPI_CLK = AHB bus clock / ((BAUD + 1)*2) | |
78 | * BAUD = AHB bus clock / SPI_CLK / 2) - 1 | |
79 | */ | |
80 | apb = (readl(&ds->regs->apb) & 0xffffff00) | baud; | |
81 | writel(apb, &ds->regs->apb); | |
82 | ||
83 | /* no interrupts */ | |
84 | writel(0, &ds->regs->ie); | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | void spi_release_bus(struct spi_slave *slave) | |
90 | { | |
91 | struct andes_spi_slave *ds = to_andes_spi(slave); | |
92 | ||
93 | /* Disable the SPI hardware */ | |
94 | writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr); | |
95 | } | |
96 | ||
97 | static int andes_spi_read(struct spi_slave *slave, unsigned int len, | |
98 | u8 *rxp, unsigned long flags) | |
99 | { | |
100 | struct andes_spi_slave *ds = to_andes_spi(slave); | |
101 | unsigned int i, left; | |
102 | unsigned int data; | |
103 | ||
104 | debug("%s: slave: %x, len: %d, rxp: %x, flags: %d\n", | |
105 | __func__, slave, len, rxp, flags); | |
106 | ||
107 | debug("%s: data: ", __func__); | |
108 | while (len > 0) { | |
109 | left = min(len, 4); | |
110 | data = readl(&ds->regs->data); | |
111 | ||
112 | debug(" "); | |
113 | for (i = 0; i < left; i++) { | |
114 | debug("%02x ", data & 0xff); | |
115 | *rxp++ = data; | |
116 | data >>= 8; | |
117 | len--; | |
118 | } | |
119 | } | |
120 | debug("\n"); | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static int andes_spi_write(struct spi_slave *slave, unsigned int wlen, | |
126 | unsigned int rlen, const u8 *txp, unsigned long flags) | |
127 | { | |
128 | struct andes_spi_slave *ds = to_andes_spi(slave); | |
129 | unsigned int data; | |
130 | unsigned int i, left; | |
131 | unsigned int spit_enabled = 0; | |
132 | ||
133 | debug("%s: slave: %x, wlen: %d, rlen: %d, txp: %x, flags: %x\n", | |
134 | __func__, slave, wlen, rlen, txp, flags); | |
135 | ||
136 | /* The value of wlen and rlen wrote to register must minus 1 */ | |
137 | if (rlen == 0) /* write only */ | |
138 | writel(ANDES_SPI_DCR_MODE_WO | ANDES_SPI_DCR_WCNT(wlen-1) | | |
139 | ANDES_SPI_DCR_RCNT(0), &ds->regs->dcr); | |
140 | else /* write then read */ | |
141 | writel(ANDES_SPI_DCR_MODE_WR | ANDES_SPI_DCR_WCNT(wlen-1) | | |
142 | ANDES_SPI_DCR_RCNT(rlen-1), &ds->regs->dcr); | |
143 | ||
144 | /* wait till SPIBSY is cleared */ | |
145 | while (readl(&ds->regs->st) & ANDES_SPI_ST_SPIBSY) | |
146 | ; | |
147 | ||
148 | /* data write process */ | |
149 | debug("%s: txp: ", __func__); | |
150 | while (wlen > 0) { | |
151 | /* clear the data */ | |
152 | data = 0; | |
153 | ||
154 | /* data are usually be read 32bits once a time */ | |
155 | left = min(wlen, 4); | |
156 | ||
157 | for (i = 0; i < left; i++) { | |
158 | debug("%x ", *txp); | |
159 | data |= *txp++ << (i * 8); | |
160 | wlen--; | |
161 | } | |
162 | debug("\n"); | |
163 | ||
164 | debug("data: %08x\n", data); | |
165 | debug("streg before write: %08x\n", readl(&ds->regs->st)); | |
166 | /* wait till TXFULL is deasserted */ | |
167 | while (readl(&ds->regs->st) & ANDES_SPI_ST_TXFEL) | |
168 | ; | |
169 | writel(data, &ds->regs->data); | |
170 | debug("streg after write: %08x\n", readl(&ds->regs->st)); | |
171 | ||
172 | ||
173 | if (spit_enabled == 0) { | |
174 | /* enable SPIT bit - trigger the tx and rx progress */ | |
175 | andes_spi_spit_en(ds); | |
176 | spit_enabled = 1; | |
177 | } | |
178 | ||
179 | } | |
180 | debug("\n"); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | /* | |
186 | * spi_xfer: | |
187 | * Since andes_spi doesn't support independent command transaction, | |
188 | * that is, write and than read must be operated in continuous | |
189 | * execution, there is no need to set dcr and trigger spit again in | |
190 | * RX process. | |
191 | */ | |
192 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | |
193 | const void *dout, void *din, unsigned long flags) | |
194 | { | |
195 | unsigned int len; | |
196 | static int op_nextime; | |
197 | static u8 tmp_cmd[5]; | |
198 | static int tmp_wlen; | |
199 | unsigned int i; | |
200 | ||
201 | if (bitlen == 0) | |
202 | /* Finish any previously submitted transfers */ | |
203 | goto out; | |
204 | ||
205 | if (bitlen % 8) { | |
206 | /* Errors always terminate an ongoing transfer */ | |
207 | flags |= SPI_XFER_END; | |
208 | goto out; | |
209 | } | |
210 | ||
211 | len = bitlen / 8; | |
212 | ||
213 | debug("%s: slave: %08x, bitlen: %d, dout: " | |
214 | "%08x, din: %08x, flags: %d, len: %d\n", | |
215 | __func__, slave, bitlen, dout, din, flags, len); | |
216 | ||
217 | /* | |
218 | * Important: | |
219 | * andes_spi's hardware doesn't support 2 data channel. The read | |
220 | * and write cmd/data share the same register (data register). | |
221 | * | |
222 | * If a command has write and read transaction, you cannot do write | |
223 | * this time and then do read on next time. | |
224 | * | |
225 | * A command writes first with a read response must indicating | |
226 | * the read length in write operation. Hence the write action must | |
227 | * be stored temporary and wait until the next read action has been | |
228 | * arrived. Then we flush the write and read action out together. | |
229 | */ | |
230 | if (!dout) { | |
231 | if (op_nextime == 1) { | |
232 | /* flags should be SPI_XFER_END, value is 2 */ | |
233 | op_nextime = 0; | |
234 | andes_spi_write(slave, tmp_wlen, len, tmp_cmd, flags); | |
235 | } | |
236 | return andes_spi_read(slave, len, din, flags); | |
237 | } else if (!din) { | |
238 | if (flags == SPI_XFER_BEGIN) { | |
239 | /* store the write command and do operation next time */ | |
240 | op_nextime = 1; | |
241 | memset(tmp_cmd, 0, sizeof(tmp_cmd)); | |
242 | memcpy(tmp_cmd, dout, len); | |
243 | ||
244 | debug("%s: tmp_cmd: ", __func__); | |
245 | for (i = 0; i < len; i++) | |
246 | debug("%x ", *(tmp_cmd + i)); | |
247 | debug("\n"); | |
248 | ||
249 | tmp_wlen = len; | |
250 | } else { | |
251 | /* | |
252 | * flags should be (SPI_XFER_BEGIN | SPI_XFER_END), | |
253 | * the value is 3. | |
254 | */ | |
255 | if (op_nextime == 1) { | |
256 | /* flags should be SPI_XFER_END, value is 2 */ | |
257 | op_nextime = 0; | |
258 | /* flags 3 implies write only */ | |
259 | andes_spi_write(slave, tmp_wlen, 0, tmp_cmd, 3); | |
260 | } | |
261 | ||
262 | debug("flags: %x\n", flags); | |
263 | return andes_spi_write(slave, len, 0, dout, flags); | |
264 | } | |
265 | } | |
266 | ||
267 | out: | |
268 | return 0; | |
269 | } | |
270 | ||
271 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
272 | { | |
273 | return bus == 0 && cs == 0; | |
274 | } | |
275 | ||
276 | void spi_cs_activate(struct spi_slave *slave) | |
277 | { | |
278 | /* do nothing */ | |
279 | } | |
280 | ||
281 | void spi_cs_deactivate(struct spi_slave *slave) | |
282 | { | |
283 | /* do nothing */ | |
284 | } |