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