1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include "qemu/osdep.h"
23#include "qemu/log.h"
24#include "hw/hw.h"
25#include "hw/arm/omap.h"
26
27
28struct omap_mcspi_s {
29 MemoryRegion iomem;
30 qemu_irq irq;
31 int chnum;
32
33 uint32_t sysconfig;
34 uint32_t systest;
35 uint32_t irqst;
36 uint32_t irqen;
37 uint32_t wken;
38 uint32_t control;
39
40 struct omap_mcspi_ch_s {
41 qemu_irq txdrq;
42 qemu_irq rxdrq;
43 uint32_t (*txrx)(void *opaque, uint32_t, int);
44 void *opaque;
45
46 uint32_t tx;
47 uint32_t rx;
48
49 uint32_t config;
50 uint32_t status;
51 uint32_t control;
52 } ch[4];
53};
54
55static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
56{
57 qemu_set_irq(s->irq, s->irqst & s->irqen);
58}
59
60static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
61{
62 qemu_set_irq(ch->txdrq,
63 (ch->control & 1) &&
64 (ch->config & (1 << 14)) &&
65 (ch->status & (1 << 1)) &&
66 ((ch->config >> 12) & 3) != 1);
67 qemu_set_irq(ch->rxdrq,
68 (ch->control & 1) &&
69 (ch->config & (1 << 15)) &&
70 (ch->status & (1 << 0)) &&
71 ((ch->config >> 12) & 3) != 2);
72}
73
74static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
75{
76 struct omap_mcspi_ch_s *ch = s->ch + chnum;
77
78 if (!(ch->control & 1))
79 return;
80 if ((ch->status & (1 << 0)) &&
81 ((ch->config >> 12) & 3) != 2 &&
82 !(ch->config & (1 << 19)))
83 goto intr_update;
84 if ((ch->status & (1 << 1)) &&
85 ((ch->config >> 12) & 3) != 1)
86 goto intr_update;
87
88 if (!(s->control & 1) ||
89 (ch->config & (1 << 20))) {
90 if (ch->txrx)
91 ch->rx = ch->txrx(ch->opaque, ch->tx,
92 1 + (0x1f & (ch->config >> 7)));
93 }
94
95 ch->tx = 0;
96 ch->status |= 1 << 2;
97 ch->status |= 1 << 1;
98 if (((ch->config >> 12) & 3) != 2)
99 ch->status |= 1 << 0;
100
101intr_update:
102 if ((ch->status & (1 << 0)) &&
103 ((ch->config >> 12) & 3) != 2 &&
104 !(ch->config & (1 << 19)))
105 s->irqst |= 1 << (2 + 4 * chnum);
106 if ((ch->status & (1 << 1)) &&
107 ((ch->config >> 12) & 3) != 1)
108 s->irqst |= 1 << (0 + 4 * chnum);
109 omap_mcspi_interrupt_update(s);
110 omap_mcspi_dmarequest_update(ch);
111}
112
113void omap_mcspi_reset(struct omap_mcspi_s *s)
114{
115 int ch;
116
117 s->sysconfig = 0;
118 s->systest = 0;
119 s->irqst = 0;
120 s->irqen = 0;
121 s->wken = 0;
122 s->control = 4;
123
124 for (ch = 0; ch < 4; ch ++) {
125 s->ch[ch].config = 0x060000;
126 s->ch[ch].status = 2;
127 s->ch[ch].control = 0;
128
129 omap_mcspi_dmarequest_update(s->ch + ch);
130 }
131
132 omap_mcspi_interrupt_update(s);
133}
134
135static uint64_t omap_mcspi_read(void *opaque, hwaddr addr,
136 unsigned size)
137{
138 struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
139 int ch = 0;
140 uint32_t ret;
141
142 if (size != 4) {
143 return omap_badwidth_read32(opaque, addr);
144 }
145
146 switch (addr) {
147 case 0x00:
148 return 0x91;
149
150 case 0x10:
151 return s->sysconfig;
152
153 case 0x14:
154 return 1;
155
156 case 0x18:
157 return s->irqst;
158
159 case 0x1c:
160 return s->irqen;
161
162 case 0x20:
163 return s->wken;
164
165 case 0x24:
166 return s->systest;
167
168 case 0x28:
169 return s->control;
170
171 case 0x68: ch ++;
172
173 case 0x54: ch ++;
174
175 case 0x40: ch ++;
176
177 case 0x2c:
178 return s->ch[ch].config;
179
180 case 0x6c: ch ++;
181
182 case 0x58: ch ++;
183
184 case 0x44: ch ++;
185
186 case 0x30:
187 return s->ch[ch].status;
188
189 case 0x70: ch ++;
190
191 case 0x5c: ch ++;
192
193 case 0x48: ch ++;
194
195 case 0x34:
196 return s->ch[ch].control;
197
198 case 0x74: ch ++;
199
200 case 0x60: ch ++;
201
202 case 0x4c: ch ++;
203
204 case 0x38:
205 return s->ch[ch].tx;
206
207 case 0x78: ch ++;
208
209 case 0x64: ch ++;
210
211 case 0x50: ch ++;
212
213 case 0x3c:
214 s->ch[ch].status &= ~(1 << 0);
215 ret = s->ch[ch].rx;
216 omap_mcspi_transfer_run(s, ch);
217 return ret;
218 }
219
220 OMAP_BAD_REG(addr);
221 return 0;
222}
223
224static void omap_mcspi_write(void *opaque, hwaddr addr,
225 uint64_t value, unsigned size)
226{
227 struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
228 int ch = 0;
229
230 if (size != 4) {
231 omap_badwidth_write32(opaque, addr, value);
232 return;
233 }
234
235 switch (addr) {
236 case 0x00:
237 case 0x14:
238 case 0x30:
239 case 0x3c:
240 case 0x44:
241 case 0x50:
242 case 0x58:
243 case 0x64:
244 case 0x6c:
245 case 0x78:
246 OMAP_RO_REG(addr);
247 return;
248
249 case 0x10:
250 if (value & (1 << 1))
251 omap_mcspi_reset(s);
252 s->sysconfig = value & 0x31d;
253 break;
254
255 case 0x18:
256 if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
257 s->irqst &= ~value;
258 omap_mcspi_interrupt_update(s);
259 }
260 break;
261
262 case 0x1c:
263 s->irqen = value & 0x1777f;
264 omap_mcspi_interrupt_update(s);
265 break;
266
267 case 0x20:
268 s->wken = value & 1;
269 break;
270
271 case 0x24:
272 if (s->control & (1 << 3))
273 if (value & (1 << 11)) {
274 s->irqst |= 0x1777f;
275 omap_mcspi_interrupt_update(s);
276 }
277 s->systest = value & 0xfff;
278 break;
279
280 case 0x28:
281 if (value & (1 << 3))
282 if (s->systest & (1 << 11)) {
283 s->irqst |= 0x1777f;
284 omap_mcspi_interrupt_update(s);
285 }
286 s->control = value & 0xf;
287 break;
288
289 case 0x68: ch ++;
290
291 case 0x54: ch ++;
292
293 case 0x40: ch ++;
294
295 case 0x2c:
296 if ((value ^ s->ch[ch].config) & (3 << 14))
297 omap_mcspi_dmarequest_update(s->ch + ch);
298 if (((value >> 12) & 3) == 3) {
299 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid TRM value (3)\n",
300 __func__);
301 }
302 if (((value >> 7) & 0x1f) < 3) {
303 qemu_log_mask(LOG_GUEST_ERROR,
304 "%s: invalid WL value (%" PRIx64 ")\n",
305 __func__, (value >> 7) & 0x1f);
306 }
307 s->ch[ch].config = value & 0x7fffff;
308 break;
309
310 case 0x70: ch ++;
311
312 case 0x5c: ch ++;
313
314 case 0x48: ch ++;
315
316 case 0x34:
317 if (value & ~s->ch[ch].control & 1) {
318 s->ch[ch].control |= 1;
319 omap_mcspi_transfer_run(s, ch);
320 } else
321 s->ch[ch].control = value & 1;
322 break;
323
324 case 0x74: ch ++;
325
326 case 0x60: ch ++;
327
328 case 0x4c: ch ++;
329
330 case 0x38:
331 s->ch[ch].tx = value;
332 s->ch[ch].status &= ~(1 << 1);
333 omap_mcspi_transfer_run(s, ch);
334 break;
335
336 default:
337 OMAP_BAD_REG(addr);
338 return;
339 }
340}
341
342static const MemoryRegionOps omap_mcspi_ops = {
343 .read = omap_mcspi_read,
344 .write = omap_mcspi_write,
345 .endianness = DEVICE_NATIVE_ENDIAN,
346};
347
348struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
349 qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
350{
351 struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1);
352 struct omap_mcspi_ch_s *ch = s->ch;
353
354 s->irq = irq;
355 s->chnum = chnum;
356 while (chnum --) {
357 ch->txdrq = *drq ++;
358 ch->rxdrq = *drq ++;
359 ch ++;
360 }
361 omap_mcspi_reset(s);
362
363 memory_region_init_io(&s->iomem, NULL, &omap_mcspi_ops, s, "omap.mcspi",
364 omap_l4_region_size(ta, 0));
365 omap_l4_attach(ta, 0, &s->iomem);
366
367 return s;
368}
369
370void omap_mcspi_attach(struct omap_mcspi_s *s,
371 uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
372 int chipselect)
373{
374 if (chipselect < 0 || chipselect >= s->chnum)
375 hw_error("%s: Bad chipselect %i\n", __func__, chipselect);
376
377 s->ch[chipselect].txrx = txrx;
378 s->ch[chipselect].opaque = opaque;
379}
380