]>
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" | |
77831c20 JR |
23 | #include "sysbus.h" |
24 | ||
e5c6b25a | 25 | struct omap_gpio_s { |
26 | qemu_irq irq; | |
e5c6b25a | 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 | ||
77831c20 JR |
38 | struct omap_gpif_s { |
39 | SysBusDevice busdev; | |
40 | int mpu_model; | |
41 | void *clk; | |
42 | struct omap_gpio_s omap1; | |
43 | }; | |
44 | ||
45 | /* General-Purpose I/O of OMAP1 */ | |
e5c6b25a | 46 | static void omap_gpio_set(void *opaque, int line, int level) |
47 | { | |
77831c20 | 48 | struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1; |
e5c6b25a | 49 | uint16_t prev = s->inputs; |
50 | ||
51 | if (level) | |
52 | s->inputs |= 1 << line; | |
53 | else | |
54 | s->inputs &= ~(1 << line); | |
55 | ||
56 | if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & | |
57 | (1 << line) & s->dir & ~s->mask) { | |
58 | s->ints |= 1 << line; | |
59 | qemu_irq_raise(s->irq); | |
60 | } | |
61 | } | |
62 | ||
63 | static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) | |
64 | { | |
65 | struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; | |
66 | int offset = addr & OMAP_MPUI_REG_MASK; | |
67 | ||
68 | switch (offset) { | |
69 | case 0x00: /* DATA_INPUT */ | |
70 | return s->inputs & s->pins; | |
71 | ||
72 | case 0x04: /* DATA_OUTPUT */ | |
73 | return s->outputs; | |
74 | ||
75 | case 0x08: /* DIRECTION_CONTROL */ | |
76 | return s->dir; | |
77 | ||
78 | case 0x0c: /* INTERRUPT_CONTROL */ | |
79 | return s->edge; | |
80 | ||
81 | case 0x10: /* INTERRUPT_MASK */ | |
82 | return s->mask; | |
83 | ||
84 | case 0x14: /* INTERRUPT_STATUS */ | |
85 | return s->ints; | |
86 | ||
87 | case 0x18: /* PIN_CONTROL (not in OMAP310) */ | |
88 | OMAP_BAD_REG(addr); | |
89 | return s->pins; | |
90 | } | |
91 | ||
92 | OMAP_BAD_REG(addr); | |
93 | return 0; | |
94 | } | |
95 | ||
96 | static void omap_gpio_write(void *opaque, target_phys_addr_t addr, | |
97 | uint32_t value) | |
98 | { | |
99 | struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; | |
100 | int offset = addr & OMAP_MPUI_REG_MASK; | |
101 | uint16_t diff; | |
102 | int ln; | |
103 | ||
104 | switch (offset) { | |
105 | case 0x00: /* DATA_INPUT */ | |
106 | OMAP_RO_REG(addr); | |
107 | return; | |
108 | ||
109 | case 0x04: /* DATA_OUTPUT */ | |
110 | diff = (s->outputs ^ value) & ~s->dir; | |
111 | s->outputs = value; | |
112 | while ((ln = ffs(diff))) { | |
113 | ln --; | |
114 | if (s->handler[ln]) | |
115 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
116 | diff &= ~(1 << ln); | |
117 | } | |
118 | break; | |
119 | ||
120 | case 0x08: /* DIRECTION_CONTROL */ | |
121 | diff = s->outputs & (s->dir ^ value); | |
122 | s->dir = value; | |
123 | ||
124 | value = s->outputs & ~s->dir; | |
125 | while ((ln = ffs(diff))) { | |
126 | ln --; | |
127 | if (s->handler[ln]) | |
128 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
129 | diff &= ~(1 << ln); | |
130 | } | |
131 | break; | |
132 | ||
133 | case 0x0c: /* INTERRUPT_CONTROL */ | |
134 | s->edge = value; | |
135 | break; | |
136 | ||
137 | case 0x10: /* INTERRUPT_MASK */ | |
138 | s->mask = value; | |
139 | break; | |
140 | ||
141 | case 0x14: /* INTERRUPT_STATUS */ | |
142 | s->ints &= ~value; | |
143 | if (!s->ints) | |
144 | qemu_irq_lower(s->irq); | |
145 | break; | |
146 | ||
147 | case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ | |
148 | OMAP_BAD_REG(addr); | |
149 | s->pins = value; | |
150 | break; | |
151 | ||
152 | default: | |
153 | OMAP_BAD_REG(addr); | |
154 | return; | |
155 | } | |
156 | } | |
157 | ||
158 | /* *Some* sources say the memory region is 32-bit. */ | |
159 | static CPUReadMemoryFunc * const omap_gpio_readfn[] = { | |
160 | omap_badwidth_read16, | |
161 | omap_gpio_read, | |
162 | omap_badwidth_read16, | |
163 | }; | |
164 | ||
165 | static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { | |
166 | omap_badwidth_write16, | |
167 | omap_gpio_write, | |
168 | omap_badwidth_write16, | |
169 | }; | |
170 | ||
77831c20 | 171 | static void omap_gpio_reset(struct omap_gpio_s *s) |
e5c6b25a | 172 | { |
173 | s->inputs = 0; | |
174 | s->outputs = ~0; | |
175 | s->dir = ~0; | |
176 | s->edge = ~0; | |
177 | s->mask = ~0; | |
178 | s->ints = 0; | |
179 | s->pins = ~0; | |
180 | } | |
181 | ||
d82310f7 | 182 | struct omap2_gpio_s { |
183 | qemu_irq irq[2]; | |
184 | qemu_irq wkup; | |
77831c20 | 185 | qemu_irq *handler; |
d82310f7 | 186 | |
77831c20 | 187 | uint8_t revision; |
d82310f7 | 188 | uint8_t config[2]; |
189 | uint32_t inputs; | |
190 | uint32_t outputs; | |
191 | uint32_t dir; | |
192 | uint32_t level[2]; | |
193 | uint32_t edge[2]; | |
194 | uint32_t mask[2]; | |
195 | uint32_t wumask; | |
196 | uint32_t ints[2]; | |
197 | uint32_t debounce; | |
198 | uint8_t delay; | |
199 | }; | |
200 | ||
77831c20 JR |
201 | struct omap2_gpif_s { |
202 | SysBusDevice busdev; | |
203 | int mpu_model; | |
204 | void *iclk; | |
205 | void *fclk[6]; | |
206 | int modulecount; | |
207 | struct omap2_gpio_s *modules; | |
208 | qemu_irq *handler; | |
209 | int autoidle; | |
210 | int gpo; | |
211 | }; | |
212 | ||
213 | /* General-Purpose Interface of OMAP2/3 */ | |
d82310f7 | 214 | static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, |
77831c20 | 215 | int line) |
d82310f7 | 216 | { |
217 | qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); | |
218 | } | |
219 | ||
220 | static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line) | |
221 | { | |
222 | if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ | |
223 | return; | |
224 | if (!(s->config[0] & (3 << 3))) /* Force Idle */ | |
225 | return; | |
226 | if (!(s->wumask & (1 << line))) | |
227 | return; | |
228 | ||
229 | qemu_irq_raise(s->wkup); | |
230 | } | |
231 | ||
232 | static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s, | |
233 | uint32_t diff) | |
234 | { | |
235 | int ln; | |
236 | ||
237 | s->outputs ^= diff; | |
238 | diff &= ~s->dir; | |
239 | while ((ln = ffs(diff))) { | |
240 | ln --; | |
241 | qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); | |
242 | diff &= ~(1 << ln); | |
243 | } | |
244 | } | |
245 | ||
246 | static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line) | |
247 | { | |
248 | s->ints[line] |= s->dir & | |
249 | ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); | |
250 | omap2_gpio_module_int_update(s, line); | |
251 | } | |
252 | ||
253 | static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) | |
254 | { | |
255 | s->ints[0] |= 1 << line; | |
256 | omap2_gpio_module_int_update(s, 0); | |
257 | s->ints[1] |= 1 << line; | |
258 | omap2_gpio_module_int_update(s, 1); | |
259 | omap2_gpio_module_wake(s, line); | |
260 | } | |
261 | ||
77831c20 | 262 | static void omap2_gpio_set(void *opaque, int line, int level) |
d82310f7 | 263 | { |
77831c20 JR |
264 | struct omap2_gpif_s *p = opaque; |
265 | struct omap2_gpio_s *s = &p->modules[line >> 5]; | |
d82310f7 | 266 | |
77831c20 | 267 | line &= 31; |
d82310f7 | 268 | if (level) { |
269 | if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) | |
270 | omap2_gpio_module_int(s, line); | |
271 | s->inputs |= 1 << line; | |
272 | } else { | |
273 | if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) | |
274 | omap2_gpio_module_int(s, line); | |
275 | s->inputs &= ~(1 << line); | |
276 | } | |
277 | } | |
278 | ||
279 | static void omap2_gpio_module_reset(struct omap2_gpio_s *s) | |
280 | { | |
281 | s->config[0] = 0; | |
282 | s->config[1] = 2; | |
283 | s->ints[0] = 0; | |
284 | s->ints[1] = 0; | |
285 | s->mask[0] = 0; | |
286 | s->mask[1] = 0; | |
287 | s->wumask = 0; | |
288 | s->dir = ~0; | |
289 | s->level[0] = 0; | |
290 | s->level[1] = 0; | |
291 | s->edge[0] = 0; | |
292 | s->edge[1] = 0; | |
293 | s->debounce = 0; | |
294 | s->delay = 0; | |
295 | } | |
296 | ||
297 | static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr) | |
298 | { | |
299 | struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; | |
300 | ||
301 | switch (addr) { | |
302 | case 0x00: /* GPIO_REVISION */ | |
77831c20 | 303 | return s->revision; |
d82310f7 | 304 | |
305 | case 0x10: /* GPIO_SYSCONFIG */ | |
306 | return s->config[0]; | |
307 | ||
308 | case 0x14: /* GPIO_SYSSTATUS */ | |
309 | return 0x01; | |
310 | ||
311 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
312 | return s->ints[0]; | |
313 | ||
314 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
315 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
316 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
317 | return s->mask[0]; | |
318 | ||
319 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
320 | case 0x80: /* GPIO_CLEARWKUENA */ | |
321 | case 0x84: /* GPIO_SETWKUENA */ | |
322 | return s->wumask; | |
323 | ||
324 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
325 | return s->ints[1]; | |
326 | ||
327 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
328 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
329 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
330 | return s->mask[1]; | |
331 | ||
332 | case 0x30: /* GPIO_CTRL */ | |
333 | return s->config[1]; | |
334 | ||
335 | case 0x34: /* GPIO_OE */ | |
336 | return s->dir; | |
337 | ||
338 | case 0x38: /* GPIO_DATAIN */ | |
339 | return s->inputs; | |
340 | ||
341 | case 0x3c: /* GPIO_DATAOUT */ | |
342 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
343 | case 0x94: /* GPIO_SETDATAOUT */ | |
344 | return s->outputs; | |
345 | ||
346 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
347 | return s->level[0]; | |
348 | ||
349 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
350 | return s->level[1]; | |
351 | ||
352 | case 0x48: /* GPIO_RISINGDETECT */ | |
353 | return s->edge[0]; | |
354 | ||
355 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
356 | return s->edge[1]; | |
357 | ||
358 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
359 | return s->debounce; | |
360 | ||
361 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
362 | return s->delay; | |
363 | } | |
364 | ||
365 | OMAP_BAD_REG(addr); | |
366 | return 0; | |
367 | } | |
368 | ||
369 | static void omap2_gpio_module_write(void *opaque, target_phys_addr_t addr, | |
370 | uint32_t value) | |
371 | { | |
372 | struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; | |
373 | uint32_t diff; | |
374 | int ln; | |
375 | ||
376 | switch (addr) { | |
377 | case 0x00: /* GPIO_REVISION */ | |
378 | case 0x14: /* GPIO_SYSSTATUS */ | |
379 | case 0x38: /* GPIO_DATAIN */ | |
380 | OMAP_RO_REG(addr); | |
381 | break; | |
382 | ||
383 | case 0x10: /* GPIO_SYSCONFIG */ | |
384 | if (((value >> 3) & 3) == 3) | |
385 | fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__); | |
386 | if (value & 2) | |
387 | omap2_gpio_module_reset(s); | |
388 | s->config[0] = value & 0x1d; | |
389 | break; | |
390 | ||
391 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
392 | if (s->ints[0] & value) { | |
393 | s->ints[0] &= ~value; | |
394 | omap2_gpio_module_level_update(s, 0); | |
395 | } | |
396 | break; | |
397 | ||
398 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
399 | s->mask[0] = value; | |
400 | omap2_gpio_module_int_update(s, 0); | |
401 | break; | |
402 | ||
403 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
404 | s->wumask = value; | |
405 | break; | |
406 | ||
407 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
408 | if (s->ints[1] & value) { | |
409 | s->ints[1] &= ~value; | |
410 | omap2_gpio_module_level_update(s, 1); | |
411 | } | |
412 | break; | |
413 | ||
414 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
415 | s->mask[1] = value; | |
416 | omap2_gpio_module_int_update(s, 1); | |
417 | break; | |
418 | ||
419 | case 0x30: /* GPIO_CTRL */ | |
420 | s->config[1] = value & 7; | |
421 | break; | |
422 | ||
423 | case 0x34: /* GPIO_OE */ | |
424 | diff = s->outputs & (s->dir ^ value); | |
425 | s->dir = value; | |
426 | ||
427 | value = s->outputs & ~s->dir; | |
428 | while ((ln = ffs(diff))) { | |
429 | diff &= ~(1 <<-- ln); | |
430 | qemu_set_irq(s->handler[ln], (value >> ln) & 1); | |
431 | } | |
432 | ||
433 | omap2_gpio_module_level_update(s, 0); | |
434 | omap2_gpio_module_level_update(s, 1); | |
435 | break; | |
436 | ||
437 | case 0x3c: /* GPIO_DATAOUT */ | |
438 | omap2_gpio_module_out_update(s, s->outputs ^ value); | |
439 | break; | |
440 | ||
441 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
442 | s->level[0] = value; | |
443 | omap2_gpio_module_level_update(s, 0); | |
444 | omap2_gpio_module_level_update(s, 1); | |
445 | break; | |
446 | ||
447 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
448 | s->level[1] = value; | |
449 | omap2_gpio_module_level_update(s, 0); | |
450 | omap2_gpio_module_level_update(s, 1); | |
451 | break; | |
452 | ||
453 | case 0x48: /* GPIO_RISINGDETECT */ | |
454 | s->edge[0] = value; | |
455 | break; | |
456 | ||
457 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
458 | s->edge[1] = value; | |
459 | break; | |
460 | ||
461 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
462 | s->debounce = value; | |
463 | break; | |
464 | ||
465 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
466 | s->delay = value; | |
467 | break; | |
468 | ||
469 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
470 | s->mask[0] &= ~value; | |
471 | omap2_gpio_module_int_update(s, 0); | |
472 | break; | |
473 | ||
474 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
475 | s->mask[0] |= value; | |
476 | omap2_gpio_module_int_update(s, 0); | |
477 | break; | |
478 | ||
479 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
480 | s->mask[1] &= ~value; | |
481 | omap2_gpio_module_int_update(s, 1); | |
482 | break; | |
483 | ||
484 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
485 | s->mask[1] |= value; | |
486 | omap2_gpio_module_int_update(s, 1); | |
487 | break; | |
488 | ||
489 | case 0x80: /* GPIO_CLEARWKUENA */ | |
490 | s->wumask &= ~value; | |
491 | break; | |
492 | ||
493 | case 0x84: /* GPIO_SETWKUENA */ | |
494 | s->wumask |= value; | |
495 | break; | |
496 | ||
497 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
498 | omap2_gpio_module_out_update(s, s->outputs & value); | |
499 | break; | |
500 | ||
501 | case 0x94: /* GPIO_SETDATAOUT */ | |
502 | omap2_gpio_module_out_update(s, ~s->outputs & value); | |
503 | break; | |
504 | ||
505 | default: | |
506 | OMAP_BAD_REG(addr); | |
507 | return; | |
508 | } | |
509 | } | |
510 | ||
511 | static uint32_t omap2_gpio_module_readp(void *opaque, target_phys_addr_t addr) | |
512 | { | |
513 | return omap2_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3); | |
514 | } | |
515 | ||
516 | static void omap2_gpio_module_writep(void *opaque, target_phys_addr_t addr, | |
517 | uint32_t value) | |
518 | { | |
519 | uint32_t cur = 0; | |
520 | uint32_t mask = 0xffff; | |
521 | ||
522 | switch (addr & ~3) { | |
523 | case 0x00: /* GPIO_REVISION */ | |
524 | case 0x14: /* GPIO_SYSSTATUS */ | |
525 | case 0x38: /* GPIO_DATAIN */ | |
526 | OMAP_RO_REG(addr); | |
527 | break; | |
528 | ||
529 | case 0x10: /* GPIO_SYSCONFIG */ | |
530 | case 0x1c: /* GPIO_IRQENABLE1 */ | |
531 | case 0x20: /* GPIO_WAKEUPENABLE */ | |
532 | case 0x2c: /* GPIO_IRQENABLE2 */ | |
533 | case 0x30: /* GPIO_CTRL */ | |
534 | case 0x34: /* GPIO_OE */ | |
535 | case 0x3c: /* GPIO_DATAOUT */ | |
536 | case 0x40: /* GPIO_LEVELDETECT0 */ | |
537 | case 0x44: /* GPIO_LEVELDETECT1 */ | |
538 | case 0x48: /* GPIO_RISINGDETECT */ | |
539 | case 0x4c: /* GPIO_FALLINGDETECT */ | |
540 | case 0x50: /* GPIO_DEBOUNCENABLE */ | |
541 | case 0x54: /* GPIO_DEBOUNCINGTIME */ | |
542 | cur = omap2_gpio_module_read(opaque, addr & ~3) & | |
543 | ~(mask << ((addr & 3) << 3)); | |
544 | ||
545 | /* Fall through. */ | |
546 | case 0x18: /* GPIO_IRQSTATUS1 */ | |
547 | case 0x28: /* GPIO_IRQSTATUS2 */ | |
548 | case 0x60: /* GPIO_CLEARIRQENABLE1 */ | |
549 | case 0x64: /* GPIO_SETIRQENABLE1 */ | |
550 | case 0x70: /* GPIO_CLEARIRQENABLE2 */ | |
551 | case 0x74: /* GPIO_SETIREQNEABLE2 */ | |
552 | case 0x80: /* GPIO_CLEARWKUENA */ | |
553 | case 0x84: /* GPIO_SETWKUENA */ | |
554 | case 0x90: /* GPIO_CLEARDATAOUT */ | |
555 | case 0x94: /* GPIO_SETDATAOUT */ | |
556 | value <<= (addr & 3) << 3; | |
557 | omap2_gpio_module_write(opaque, addr, cur | value); | |
558 | break; | |
559 | ||
560 | default: | |
561 | OMAP_BAD_REG(addr); | |
562 | return; | |
563 | } | |
564 | } | |
565 | ||
566 | static CPUReadMemoryFunc * const omap2_gpio_module_readfn[] = { | |
567 | omap2_gpio_module_readp, | |
568 | omap2_gpio_module_readp, | |
569 | omap2_gpio_module_read, | |
570 | }; | |
571 | ||
572 | static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = { | |
573 | omap2_gpio_module_writep, | |
574 | omap2_gpio_module_writep, | |
575 | omap2_gpio_module_write, | |
576 | }; | |
577 | ||
77831c20 | 578 | static void omap_gpif_reset(DeviceState *dev) |
d82310f7 | 579 | { |
77831c20 JR |
580 | struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, |
581 | sysbus_from_qdev(dev)); | |
582 | omap_gpio_reset(&s->omap1); | |
d82310f7 | 583 | } |
584 | ||
77831c20 | 585 | static void omap2_gpif_reset(DeviceState *dev) |
d82310f7 | 586 | { |
587 | int i; | |
77831c20 JR |
588 | struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, |
589 | sysbus_from_qdev(dev)); | |
590 | for (i = 0; i < s->modulecount; i++) { | |
591 | omap2_gpio_module_reset(&s->modules[i]); | |
592 | } | |
d82310f7 | 593 | s->autoidle = 0; |
594 | s->gpo = 0; | |
595 | } | |
596 | ||
77831c20 | 597 | static uint32_t omap2_gpif_top_read(void *opaque, target_phys_addr_t addr) |
d82310f7 | 598 | { |
77831c20 | 599 | struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; |
d82310f7 | 600 | |
601 | switch (addr) { | |
602 | case 0x00: /* IPGENERICOCPSPL_REVISION */ | |
603 | return 0x18; | |
604 | ||
605 | case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ | |
606 | return s->autoidle; | |
607 | ||
608 | case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ | |
609 | return 0x01; | |
610 | ||
611 | case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ | |
612 | return 0x00; | |
613 | ||
614 | case 0x40: /* IPGENERICOCPSPL_GPO */ | |
615 | return s->gpo; | |
616 | ||
617 | case 0x50: /* IPGENERICOCPSPL_GPI */ | |
618 | return 0x00; | |
619 | } | |
620 | ||
621 | OMAP_BAD_REG(addr); | |
622 | return 0; | |
623 | } | |
624 | ||
77831c20 | 625 | static void omap2_gpif_top_write(void *opaque, target_phys_addr_t addr, |
d82310f7 | 626 | uint32_t value) |
627 | { | |
77831c20 | 628 | struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; |
d82310f7 | 629 | |
630 | switch (addr) { | |
631 | case 0x00: /* IPGENERICOCPSPL_REVISION */ | |
632 | case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ | |
633 | case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ | |
634 | case 0x50: /* IPGENERICOCPSPL_GPI */ | |
635 | OMAP_RO_REG(addr); | |
636 | break; | |
637 | ||
638 | case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ | |
639 | if (value & (1 << 1)) /* SOFTRESET */ | |
77831c20 | 640 | omap2_gpif_reset(&s->busdev.qdev); |
d82310f7 | 641 | s->autoidle = value & 1; |
642 | break; | |
643 | ||
644 | case 0x40: /* IPGENERICOCPSPL_GPO */ | |
645 | s->gpo = value & 1; | |
646 | break; | |
647 | ||
648 | default: | |
649 | OMAP_BAD_REG(addr); | |
650 | return; | |
651 | } | |
652 | } | |
653 | ||
77831c20 JR |
654 | static CPUReadMemoryFunc * const omap2_gpif_top_readfn[] = { |
655 | omap2_gpif_top_read, | |
656 | omap2_gpif_top_read, | |
657 | omap2_gpif_top_read, | |
d82310f7 | 658 | }; |
659 | ||
77831c20 JR |
660 | static CPUWriteMemoryFunc * const omap2_gpif_top_writefn[] = { |
661 | omap2_gpif_top_write, | |
662 | omap2_gpif_top_write, | |
663 | omap2_gpif_top_write, | |
d82310f7 | 664 | }; |
665 | ||
77831c20 | 666 | static int omap_gpio_init(SysBusDevice *dev) |
d82310f7 | 667 | { |
77831c20 JR |
668 | struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, dev); |
669 | if (!s->clk) { | |
670 | hw_error("omap-gpio: clk not connected\n"); | |
671 | } | |
672 | qdev_init_gpio_in(&dev->qdev, omap_gpio_set, 16); | |
673 | qdev_init_gpio_out(&dev->qdev, s->omap1.handler, 16); | |
674 | sysbus_init_irq(dev, &s->omap1.irq); | |
675 | sysbus_init_mmio(dev, 0x1000, | |
676 | cpu_register_io_memory(omap_gpio_readfn, | |
677 | omap_gpio_writefn, | |
678 | &s->omap1, | |
679 | DEVICE_NATIVE_ENDIAN)); | |
680 | return 0; | |
681 | } | |
d82310f7 | 682 | |
77831c20 JR |
683 | static int omap2_gpio_init(SysBusDevice *dev) |
684 | { | |
685 | int i; | |
686 | struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, dev); | |
687 | if (!s->iclk) { | |
688 | hw_error("omap2-gpio: iclk not connected\n"); | |
689 | } | |
690 | if (s->mpu_model < omap3430) { | |
691 | s->modulecount = (s->mpu_model < omap2430) ? 4 : 5; | |
692 | sysbus_init_mmio(dev, 0x1000, | |
693 | cpu_register_io_memory(omap2_gpif_top_readfn, | |
694 | omap2_gpif_top_writefn, s, | |
695 | DEVICE_NATIVE_ENDIAN)); | |
696 | } else { | |
697 | s->modulecount = 6; | |
698 | } | |
7267c094 AL |
699 | s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s)); |
700 | s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq)); | |
77831c20 JR |
701 | qdev_init_gpio_in(&dev->qdev, omap2_gpio_set, s->modulecount * 32); |
702 | qdev_init_gpio_out(&dev->qdev, s->handler, s->modulecount * 32); | |
703 | for (i = 0; i < s->modulecount; i++) { | |
704 | struct omap2_gpio_s *m = &s->modules[i]; | |
705 | if (!s->fclk[i]) { | |
706 | hw_error("omap2-gpio: fclk%d not connected\n", i); | |
707 | } | |
708 | m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; | |
709 | m->handler = &s->handler[i * 32]; | |
710 | sysbus_init_irq(dev, &m->irq[0]); /* mpu irq */ | |
711 | sysbus_init_irq(dev, &m->irq[1]); /* dsp irq */ | |
712 | sysbus_init_irq(dev, &m->wkup); | |
713 | sysbus_init_mmio(dev, 0x1000, | |
714 | cpu_register_io_memory(omap2_gpio_module_readfn, | |
715 | omap2_gpio_module_writefn, | |
716 | m, DEVICE_NATIVE_ENDIAN)); | |
717 | } | |
718 | return 0; | |
719 | } | |
d82310f7 | 720 | |
77831c20 JR |
721 | /* Using qdev pointer properties for the clocks is not ideal. |
722 | * qdev should support a generic means of defining a 'port' with | |
723 | * an arbitrary interface for connecting two devices. Then we | |
724 | * could reframe the omap clock API in terms of clock ports, | |
725 | * and get some type safety. For now the best qdev provides is | |
726 | * passing an arbitrary pointer. | |
727 | * (It's not possible to pass in the string which is the clock | |
728 | * name, because this device does not have the necessary information | |
729 | * (ie the struct omap_mpu_state_s*) to do the clockname to pointer | |
730 | * translation.) | |
731 | */ | |
d82310f7 | 732 | |
77831c20 JR |
733 | static SysBusDeviceInfo omap_gpio_info = { |
734 | .init = omap_gpio_init, | |
735 | .qdev.name = "omap-gpio", | |
736 | .qdev.size = sizeof(struct omap_gpif_s), | |
737 | .qdev.reset = omap_gpif_reset, | |
738 | .qdev.props = (Property[]) { | |
739 | DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0), | |
740 | DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk), | |
741 | DEFINE_PROP_END_OF_LIST() | |
742 | } | |
743 | }; | |
d82310f7 | 744 | |
77831c20 JR |
745 | static SysBusDeviceInfo omap2_gpio_info = { |
746 | .init = omap2_gpio_init, | |
747 | .qdev.name = "omap2-gpio", | |
748 | .qdev.size = sizeof(struct omap2_gpif_s), | |
749 | .qdev.reset = omap2_gpif_reset, | |
750 | .qdev.props = (Property[]) { | |
751 | DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), | |
752 | DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk), | |
753 | DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]), | |
754 | DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]), | |
755 | DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]), | |
756 | DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]), | |
757 | DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]), | |
758 | DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]), | |
759 | DEFINE_PROP_END_OF_LIST() | |
760 | } | |
761 | }; | |
d82310f7 | 762 | |
77831c20 | 763 | static void omap_gpio_register_device(void) |
d82310f7 | 764 | { |
77831c20 JR |
765 | sysbus_register_withprop(&omap_gpio_info); |
766 | sysbus_register_withprop(&omap2_gpio_info); | |
d82310f7 | 767 | } |
768 | ||
77831c20 | 769 | device_init(omap_gpio_register_device) |