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