]>
Commit | Line | Data |
---|---|---|
e5c6b25a | 1 | /* |
2 | * TI OMAP processors GPIO emulation. | |
3 | * | |
4 | * Copyright (C) 2006-2008 Andrzej Zaborowski <[email protected]> | |
5 | * Copyright (C) 2007-2009 Nokia Corporation | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 or | |
10 | * (at your option) version 3 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include "hw.h" | |
22 | #include "omap.h" | |
23 | /* General-Purpose I/O */ | |
24 | struct omap_gpio_s { | |
25 | qemu_irq irq; | |
26 | qemu_irq *in; | |
27 | qemu_irq handler[16]; | |
28 | ||
29 | uint16_t inputs; | |
30 | uint16_t outputs; | |
31 | uint16_t dir; | |
32 | uint16_t edge; | |
33 | uint16_t mask; | |
34 | uint16_t ints; | |
35 | uint16_t pins; | |
36 | }; | |
37 | ||
38 | static void omap_gpio_set(void *opaque, int line, int level) | |
39 | { | |
40 | struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; | |
41 | uint16_t prev = s->inputs; | |
42 | ||
43 | if (level) | |
44 | s->inputs |= 1 << line; | |
45 | else | |
46 | s->inputs &= ~(1 << line); | |
47 | ||
48 | if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & | |
49 | (1 << line) & s->dir & ~s->mask) { | |
50 | s->ints |= 1 << line; | |
51 | qemu_irq_raise(s->irq); | |
52 | } | |
53 | } | |
54 | ||
55 | static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) | |
56 | { | |
57 | struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; | |
58 | int offset = addr & OMAP_MPUI_REG_MASK; | |
59 | ||
60 | switch (offset) { | |
61 | case 0x00: /* DATA_INPUT */ | |
62 | return s->inputs & s->pins; | |
63 | ||
64 | case 0x04: /* DATA_OUTPUT */ | |
65 | return s->outputs; | |
66 | ||
67 | case 0x08: /* DIRECTION_CONTROL */ | |
68 | return s->dir; | |
69 | ||
70 | case 0x0c: /* INTERRUPT_CONTROL */ | |
71 | return s->edge; | |
72 | ||
73 | case 0x10: /* INTERRUPT_MASK */ | |
74 | return s->mask; | |
75 | ||
76 | case 0x14: /* INTERRUPT_STATUS */ | |
77 | return s->ints; | |
78 | ||
79 | case 0x18: /* PIN_CONTROL (not in OMAP310) */ | |
80 | OMAP_BAD_REG(addr); | |
81 | return s->pins; | |
82 | } | |
83 | ||
84 | OMAP_BAD_REG(addr); | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static void omap_gpio_write(void *opaque, target_phys_addr_t addr, | |
89 | uint32_t value) | |
90 | { | |
91 | struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; | |
92 | int offset = addr & OMAP_MPUI_REG_MASK; | |
93 | uint16_t diff; | |
94 | int ln; | |
95 | ||
96 | switch (offset) { | |
97 | case 0x00: /* DATA_INPUT */ | |
98 | OMAP_RO_REG(addr); | |
99 | return; | |
100 | ||
101 | case 0x04: /* DATA_OUTPUT */ | |
102 | diff = (s->outputs ^ value) & ~s->dir; | |
103 | s->outputs = value; | |
104 | while ((ln = ffs(diff))) { | |
105 | ln --; | |
106 | if (s->handler[ln]) | |
107 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
108 | diff &= ~(1 << ln); | |
109 | } | |
110 | break; | |
111 | ||
112 | case 0x08: /* DIRECTION_CONTROL */ | |
113 | diff = s->outputs & (s->dir ^ value); | |
114 | s->dir = value; | |
115 | ||
116 | value = s->outputs & ~s->dir; | |
117 | while ((ln = ffs(diff))) { | |
118 | ln --; | |
119 | if (s->handler[ln]) | |
120 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
121 | diff &= ~(1 << ln); | |
122 | } | |
123 | break; | |
124 | ||
125 | case 0x0c: /* INTERRUPT_CONTROL */ | |
126 | s->edge = value; | |
127 | break; | |
128 | ||
129 | case 0x10: /* INTERRUPT_MASK */ | |
130 | s->mask = value; | |
131 | break; | |
132 | ||
133 | case 0x14: /* INTERRUPT_STATUS */ | |
134 | s->ints &= ~value; | |
135 | if (!s->ints) | |
136 | qemu_irq_lower(s->irq); | |
137 | break; | |
138 | ||
139 | case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ | |
140 | OMAP_BAD_REG(addr); | |
141 | s->pins = value; | |
142 | break; | |
143 | ||
144 | default: | |
145 | OMAP_BAD_REG(addr); | |
146 | return; | |
147 | } | |
148 | } | |
149 | ||
150 | /* *Some* sources say the memory region is 32-bit. */ | |
151 | static CPUReadMemoryFunc * const omap_gpio_readfn[] = { | |
152 | omap_badwidth_read16, | |
153 | omap_gpio_read, | |
154 | omap_badwidth_read16, | |
155 | }; | |
156 | ||
157 | static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { | |
158 | omap_badwidth_write16, | |
159 | omap_gpio_write, | |
160 | omap_badwidth_write16, | |
161 | }; | |
162 | ||
163 | void omap_gpio_reset(struct omap_gpio_s *s) | |
164 | { | |
165 | s->inputs = 0; | |
166 | s->outputs = ~0; | |
167 | s->dir = ~0; | |
168 | s->edge = ~0; | |
169 | s->mask = ~0; | |
170 | s->ints = 0; | |
171 | s->pins = ~0; | |
172 | } | |
173 | ||
174 | struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, | |
175 | qemu_irq irq, omap_clk clk) | |
176 | { | |
177 | int iomemtype; | |
178 | struct omap_gpio_s *s = (struct omap_gpio_s *) | |
179 | qemu_mallocz(sizeof(struct omap_gpio_s)); | |
180 | ||
181 | s->irq = irq; | |
182 | s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); | |
183 | omap_gpio_reset(s); | |
184 | ||
185 | iomemtype = cpu_register_io_memory(omap_gpio_readfn, | |
2507c12a | 186 | omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); |
e5c6b25a | 187 | cpu_register_physical_memory(base, 0x1000, iomemtype); |
188 | ||
189 | return s; | |
190 | } | |
191 | ||
192 | qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) | |
193 | { | |
194 | return s->in; | |
195 | } | |
196 | ||
197 | void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) | |
198 | { | |
199 | if (line >= 16 || line < 0) | |
200 | hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); | |
201 | s->handler[line] = handler; | |
202 | } | |
d82310f7 | 203 | |
204 | /* General-Purpose Interface of OMAP2 */ | |
205 | struct omap2_gpio_s { | |
206 | qemu_irq irq[2]; | |
207 | qemu_irq wkup; | |
208 | qemu_irq *in; | |
209 | qemu_irq handler[32]; | |
210 | ||
211 | uint8_t config[2]; | |
212 | uint32_t inputs; | |
213 | uint32_t outputs; | |
214 | uint32_t dir; | |
215 | uint32_t level[2]; | |
216 | uint32_t edge[2]; | |
217 | uint32_t mask[2]; | |
218 | uint32_t wumask; | |
219 | uint32_t ints[2]; | |
220 | uint32_t debounce; | |
221 | uint8_t delay; | |
222 | }; | |
223 | ||
224 | static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, | |
225 | int line) | |
226 | { | |
227 | qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); | |
228 | } | |
229 | ||
230 | static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line) | |
231 | { | |
232 | if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ | |
233 | return; | |
234 | if (!(s->config[0] & (3 << 3))) /* Force Idle */ | |
235 | return; | |
236 | if (!(s->wumask & (1 << line))) | |
237 | return; | |
238 | ||
239 | qemu_irq_raise(s->wkup); | |
240 | } | |
241 | ||
242 | static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s, | |
243 | uint32_t diff) | |
244 | { | |
245 | int ln; | |
246 | ||
247 | s->outputs ^= diff; | |
248 | diff &= ~s->dir; | |
249 | while ((ln = ffs(diff))) { | |
250 | ln --; | |
251 | qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); | |
252 | diff &= ~(1 << ln); | |
253 | } | |
254 | } | |
255 | ||
256 | static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line) | |
257 | { | |
258 | s->ints[line] |= s->dir & | |
259 | ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); | |
260 | omap2_gpio_module_int_update(s, line); | |
261 | } | |
262 | ||
263 | static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) | |
264 | { | |
265 | s->ints[0] |= 1 << line; | |
266 | omap2_gpio_module_int_update(s, 0); | |
267 | s->ints[1] |= 1 << line; | |
268 | omap2_gpio_module_int_update(s, 1); | |
269 | omap2_gpio_module_wake(s, line); | |
270 | } | |
271 | ||
272 | static void omap2_gpio_module_set(void *opaque, int line, int level) | |
273 | { | |
274 | struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; | |
275 | ||
276 | if (level) { | |
277 | if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) | |
278 | omap2_gpio_module_int(s, line); | |
279 | s->inputs |= 1 << line; | |
280 | } else { | |
281 | if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) | |
282 | omap2_gpio_module_int(s, line); | |
283 | s->inputs &= ~(1 << line); | |
284 | } | |
285 | } | |
286 | ||
287 | static void omap2_gpio_module_reset(struct omap2_gpio_s *s) | |
288 | { | |
289 | s->config[0] = 0; | |
290 | s->config[1] = 2; | |
291 | s->ints[0] = 0; | |
292 | s->ints[1] = 0; | |
293 | s->mask[0] = 0; | |
294 | s->mask[1] = 0; | |
295 | s->wumask = 0; | |
296 | s->dir = ~0; | |
297 | s->level[0] = 0; | |
298 | s->level[1] = 0; | |
299 | s->edge[0] = 0; | |
300 | s->edge[1] = 0; | |
301 | s->debounce = 0; | |
302 | s->delay = 0; | |
303 | } | |
304 | ||
305 | static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr) | |
306 | { | |
307 | struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; | |
308 | ||
309 | switch (addr) { | |
310 | case 0x00: /* GPIO_REVISION */ | |
311 | return 0x18; | |
312 | ||
313 | case 0x10: /* GPIO_SYSCONFIG */ | |
314 | return s->config[0]; | |
315 | ||
316 | case 0x14: /* GPIO_SYSSTATUS */ | |
317 | return 0x01; | |
318 | ||
319 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
320 | return s->ints[0]; | |
321 | ||
322 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
323 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
324 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
325 | return s->mask[0]; | |
326 | ||
327 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
328 | case 0x80: /* GPIO_CLEARWKUENA */ | |
329 | case 0x84: /* GPIO_SETWKUENA */ | |
330 | return s->wumask; | |
331 | ||
332 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
333 | return s->ints[1]; | |
334 | ||
335 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
336 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
337 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
338 | return s->mask[1]; | |
339 | ||
340 | case 0x30: /* GPIO_CTRL */ | |
341 | return s->config[1]; | |
342 | ||
343 | case 0x34: /* GPIO_OE */ | |
344 | return s->dir; | |
345 | ||
346 | case 0x38: /* GPIO_DATAIN */ | |
347 | return s->inputs; | |
348 | ||
349 | case 0x3c: /* GPIO_DATAOUT */ | |
350 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
351 | case 0x94: /* GPIO_SETDATAOUT */ | |
352 | return s->outputs; | |
353 | ||
354 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
355 | return s->level[0]; | |
356 | ||
357 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
358 | return s->level[1]; | |
359 | ||
360 | case 0x48: /* GPIO_RISINGDETECT */ | |
361 | return s->edge[0]; | |
362 | ||
363 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
364 | return s->edge[1]; | |
365 | ||
366 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
367 | return s->debounce; | |
368 | ||
369 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
370 | return s->delay; | |
371 | } | |
372 | ||
373 | OMAP_BAD_REG(addr); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | static void omap2_gpio_module_write(void *opaque, target_phys_addr_t addr, | |
378 | uint32_t value) | |
379 | { | |
380 | struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; | |
381 | uint32_t diff; | |
382 | int ln; | |
383 | ||
384 | switch (addr) { | |
385 | case 0x00: /* GPIO_REVISION */ | |
386 | case 0x14: /* GPIO_SYSSTATUS */ | |
387 | case 0x38: /* GPIO_DATAIN */ | |
388 | OMAP_RO_REG(addr); | |
389 | break; | |
390 | ||
391 | case 0x10: /* GPIO_SYSCONFIG */ | |
392 | if (((value >> 3) & 3) == 3) | |
393 | fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__); | |
394 | if (value & 2) | |
395 | omap2_gpio_module_reset(s); | |
396 | s->config[0] = value & 0x1d; | |
397 | break; | |
398 | ||
399 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
400 | if (s->ints[0] & value) { | |
401 | s->ints[0] &= ~value; | |
402 | omap2_gpio_module_level_update(s, 0); | |
403 | } | |
404 | break; | |
405 | ||
406 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
407 | s->mask[0] = value; | |
408 | omap2_gpio_module_int_update(s, 0); | |
409 | break; | |
410 | ||
411 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
412 | s->wumask = value; | |
413 | break; | |
414 | ||
415 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
416 | if (s->ints[1] & value) { | |
417 | s->ints[1] &= ~value; | |
418 | omap2_gpio_module_level_update(s, 1); | |
419 | } | |
420 | break; | |
421 | ||
422 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
423 | s->mask[1] = value; | |
424 | omap2_gpio_module_int_update(s, 1); | |
425 | break; | |
426 | ||
427 | case 0x30: /* GPIO_CTRL */ | |
428 | s->config[1] = value & 7; | |
429 | break; | |
430 | ||
431 | case 0x34: /* GPIO_OE */ | |
432 | diff = s->outputs & (s->dir ^ value); | |
433 | s->dir = value; | |
434 | ||
435 | value = s->outputs & ~s->dir; | |
436 | while ((ln = ffs(diff))) { | |
437 | diff &= ~(1 <<-- ln); | |
438 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
439 | } | |
440 | ||
441 | omap2_gpio_module_level_update(s, 0); | |
442 | omap2_gpio_module_level_update(s, 1); | |
443 | break; | |
444 | ||
445 | case 0x3c: /* GPIO_DATAOUT */ | |
446 | omap2_gpio_module_out_update(s, s->outputs ^ value); | |
447 | break; | |
448 | ||
449 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
450 | s->level[0] = value; | |
451 | omap2_gpio_module_level_update(s, 0); | |
452 | omap2_gpio_module_level_update(s, 1); | |
453 | break; | |
454 | ||
455 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
456 | s->level[1] = value; | |
457 | omap2_gpio_module_level_update(s, 0); | |
458 | omap2_gpio_module_level_update(s, 1); | |
459 | break; | |
460 | ||
461 | case 0x48: /* GPIO_RISINGDETECT */ | |
462 | s->edge[0] = value; | |
463 | break; | |
464 | ||
465 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
466 | s->edge[1] = value; | |
467 | break; | |
468 | ||
469 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
470 | s->debounce = value; | |
471 | break; | |
472 | ||
473 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
474 | s->delay = value; | |
475 | break; | |
476 | ||
477 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
478 | s->mask[0] &= ~value; | |
479 | omap2_gpio_module_int_update(s, 0); | |
480 | break; | |
481 | ||
482 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
483 | s->mask[0] |= value; | |
484 | omap2_gpio_module_int_update(s, 0); | |
485 | break; | |
486 | ||
487 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
488 | s->mask[1] &= ~value; | |
489 | omap2_gpio_module_int_update(s, 1); | |
490 | break; | |
491 | ||
492 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
493 | s->mask[1] |= value; | |
494 | omap2_gpio_module_int_update(s, 1); | |
495 | break; | |
496 | ||
497 | case 0x80: /* GPIO_CLEARWKUENA */ | |
498 | s->wumask &= ~value; | |
499 | break; | |
500 | ||
501 | case 0x84: /* GPIO_SETWKUENA */ | |
502 | s->wumask |= value; | |
503 | break; | |
504 | ||
505 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
506 | omap2_gpio_module_out_update(s, s->outputs & value); | |
507 | break; | |
508 | ||
509 | case 0x94: /* GPIO_SETDATAOUT */ | |
510 | omap2_gpio_module_out_update(s, ~s->outputs & value); | |
511 | break; | |
512 | ||
513 | default: | |
514 | OMAP_BAD_REG(addr); | |
515 | return; | |
516 | } | |
517 | } | |
518 | ||
519 | static uint32_t omap2_gpio_module_readp(void *opaque, target_phys_addr_t addr) | |
520 | { | |
521 | return omap2_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3); | |
522 | } | |
523 | ||
524 | static void omap2_gpio_module_writep(void *opaque, target_phys_addr_t addr, | |
525 | uint32_t value) | |
526 | { | |
527 | uint32_t cur = 0; | |
528 | uint32_t mask = 0xffff; | |
529 | ||
530 | switch (addr & ~3) { | |
531 | case 0x00: /* GPIO_REVISION */ | |
532 | case 0x14: /* GPIO_SYSSTATUS */ | |
533 | case 0x38: /* GPIO_DATAIN */ | |
534 | OMAP_RO_REG(addr); | |
535 | break; | |
536 | ||
537 | case 0x10: /* GPIO_SYSCONFIG */ | |
538 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
539 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
540 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
541 | case 0x30: /* GPIO_CTRL */ | |
542 | case 0x34: /* GPIO_OE */ | |
543 | case 0x3c: /* GPIO_DATAOUT */ | |
544 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
545 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
546 | case 0x48: /* GPIO_RISINGDETECT */ | |
547 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
548 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
549 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
550 | cur = omap2_gpio_module_read(opaque, addr & ~3) & | |
551 | ~(mask << ((addr & 3) << 3)); | |
552 | ||
553 | /* Fall through. */ | |
554 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
555 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
556 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
557 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
558 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
559 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
560 | case 0x80: /* GPIO_CLEARWKUENA */ | |
561 | case 0x84: /* GPIO_SETWKUENA */ | |
562 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
563 | case 0x94: /* GPIO_SETDATAOUT */ | |
564 | value <<= (addr & 3) << 3; | |
565 | omap2_gpio_module_write(opaque, addr, cur | value); | |
566 | break; | |
567 | ||
568 | default: | |
569 | OMAP_BAD_REG(addr); | |
570 | return; | |
571 | } | |
572 | } | |
573 | ||
574 | static CPUReadMemoryFunc * const omap2_gpio_module_readfn[] = { | |
575 | omap2_gpio_module_readp, | |
576 | omap2_gpio_module_readp, | |
577 | omap2_gpio_module_read, | |
578 | }; | |
579 | ||
580 | static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = { | |
581 | omap2_gpio_module_writep, | |
582 | omap2_gpio_module_writep, | |
583 | omap2_gpio_module_write, | |
584 | }; | |
585 | ||
586 | static void omap2_gpio_module_init(struct omap2_gpio_s *s, | |
587 | struct omap_target_agent_s *ta, int region, | |
588 | qemu_irq mpu, qemu_irq dsp, qemu_irq wkup, | |
589 | omap_clk fclk, omap_clk iclk) | |
590 | { | |
591 | int iomemtype; | |
592 | ||
593 | s->irq[0] = mpu; | |
594 | s->irq[1] = dsp; | |
595 | s->wkup = wkup; | |
596 | s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32); | |
597 | ||
598 | iomemtype = l4_register_io_memory(omap2_gpio_module_readfn, | |
599 | omap2_gpio_module_writefn, s); | |
600 | omap_l4_attach(ta, region, iomemtype); | |
601 | } | |
602 | ||
603 | struct omap_gpif_s { | |
604 | struct omap2_gpio_s module[5]; | |
605 | int modules; | |
606 | ||
607 | int autoidle; | |
608 | int gpo; | |
609 | }; | |
610 | ||
611 | void omap_gpif_reset(struct omap_gpif_s *s) | |
612 | { | |
613 | int i; | |
614 | ||
615 | for (i = 0; i < s->modules; i ++) | |
616 | omap2_gpio_module_reset(s->module + i); | |
617 | ||
618 | s->autoidle = 0; | |
619 | s->gpo = 0; | |
620 | } | |
621 | ||
622 | static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) | |
623 | { | |
624 | struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; | |
625 | ||
626 | switch (addr) { | |
627 | case 0x00: /* IPGENERICOCPSPL_REVISION */ | |
628 | return 0x18; | |
629 | ||
630 | case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ | |
631 | return s->autoidle; | |
632 | ||
633 | case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ | |
634 | return 0x01; | |
635 | ||
636 | case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ | |
637 | return 0x00; | |
638 | ||
639 | case 0x40: /* IPGENERICOCPSPL_GPO */ | |
640 | return s->gpo; | |
641 | ||
642 | case 0x50: /* IPGENERICOCPSPL_GPI */ | |
643 | return 0x00; | |
644 | } | |
645 | ||
646 | OMAP_BAD_REG(addr); | |
647 | return 0; | |
648 | } | |
649 | ||
650 | static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, | |
651 | uint32_t value) | |
652 | { | |
653 | struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; | |
654 | ||
655 | switch (addr) { | |
656 | case 0x00: /* IPGENERICOCPSPL_REVISION */ | |
657 | case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ | |
658 | case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ | |
659 | case 0x50: /* IPGENERICOCPSPL_GPI */ | |
660 | OMAP_RO_REG(addr); | |
661 | break; | |
662 | ||
663 | case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ | |
664 | if (value & (1 << 1)) /* SOFTRESET */ | |
665 | omap_gpif_reset(s); | |
666 | s->autoidle = value & 1; | |
667 | break; | |
668 | ||
669 | case 0x40: /* IPGENERICOCPSPL_GPO */ | |
670 | s->gpo = value & 1; | |
671 | break; | |
672 | ||
673 | default: | |
674 | OMAP_BAD_REG(addr); | |
675 | return; | |
676 | } | |
677 | } | |
678 | ||
679 | static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = { | |
680 | omap_gpif_top_read, | |
681 | omap_gpif_top_read, | |
682 | omap_gpif_top_read, | |
683 | }; | |
684 | ||
685 | static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = { | |
686 | omap_gpif_top_write, | |
687 | omap_gpif_top_write, | |
688 | omap_gpif_top_write, | |
689 | }; | |
690 | ||
691 | struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, | |
692 | qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules) | |
693 | { | |
694 | int iomemtype, i; | |
695 | struct omap_gpif_s *s = (struct omap_gpif_s *) | |
696 | qemu_mallocz(sizeof(struct omap_gpif_s)); | |
697 | int region[4] = { 0, 2, 4, 5 }; | |
698 | ||
699 | s->modules = modules; | |
700 | for (i = 0; i < modules; i ++) | |
701 | omap2_gpio_module_init(s->module + i, ta, region[i], | |
702 | irq[i], NULL, NULL, fclk[i], iclk); | |
703 | ||
704 | omap_gpif_reset(s); | |
705 | ||
706 | iomemtype = l4_register_io_memory(omap_gpif_top_readfn, | |
707 | omap_gpif_top_writefn, s); | |
708 | omap_l4_attach(ta, 1, iomemtype); | |
709 | ||
710 | return s; | |
711 | } | |
712 | ||
713 | qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start) | |
714 | { | |
715 | if (start >= s->modules * 32 || start < 0) | |
716 | hw_error("%s: No GPIO line %i\n", __FUNCTION__, start); | |
717 | return s->module[start >> 5].in + (start & 31); | |
718 | } | |
719 | ||
720 | void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler) | |
721 | { | |
722 | if (line >= s->modules * 32 || line < 0) | |
723 | hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); | |
724 | s->module[line >> 5].handler[line & 31] = handler; | |
725 | } |