]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Arm PrimeCell PL050 Keyboard / Mouse Interface | |
3 | * | |
4 | * Copyright (c) 2006 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
7 | * This code is licenced under the GPL. | |
8 | */ | |
9 | ||
10 | #include "vl.h" | |
11 | ||
12 | typedef struct { | |
13 | void *dev; | |
14 | uint32_t base; | |
15 | uint32_t cr; | |
16 | uint32_t clk; | |
17 | uint32_t last; | |
18 | void *pic; | |
19 | int pending; | |
20 | int irq; | |
21 | int is_mouse; | |
22 | } pl050_state; | |
23 | ||
24 | static const unsigned char pl050_id[] = | |
25 | { 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | |
26 | ||
27 | static void pl050_update(void *opaque, int level) | |
28 | { | |
29 | pl050_state *s = (pl050_state *)opaque; | |
30 | int raise; | |
31 | ||
32 | s->pending = level; | |
33 | raise = (s->pending && (s->cr & 0x10) != 0) | |
34 | || (s->cr & 0x08) != 0; | |
35 | pic_set_irq_new(s->pic, s->irq, raise); | |
36 | } | |
37 | ||
38 | static uint32_t pl050_read(void *opaque, target_phys_addr_t offset) | |
39 | { | |
40 | pl050_state *s = (pl050_state *)opaque; | |
41 | offset -= s->base; | |
42 | if (offset >= 0xfe0 && offset < 0x1000) | |
43 | return pl050_id[(offset - 0xfe0) >> 2]; | |
44 | ||
45 | switch (offset >> 2) { | |
46 | case 0: /* KMICR */ | |
47 | return s->cr; | |
48 | case 1: /* KMISTAT */ | |
49 | /* KMIC and KMID bits not implemented. */ | |
50 | if (s->pending) { | |
51 | return 0x10; | |
52 | } else { | |
53 | return 0; | |
54 | } | |
55 | case 2: /* KMIDATA */ | |
56 | if (s->pending) | |
57 | s->last = ps2_read_data(s->dev); | |
58 | return s->last; | |
59 | case 3: /* KMICLKDIV */ | |
60 | return s->clk; | |
61 | case 4: /* KMIIR */ | |
62 | return s->pending | 2; | |
63 | default: | |
64 | cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", offset); | |
65 | return 0; | |
66 | } | |
67 | } | |
68 | ||
69 | static void pl050_write(void *opaque, target_phys_addr_t offset, | |
70 | uint32_t value) | |
71 | { | |
72 | pl050_state *s = (pl050_state *)opaque; | |
73 | offset -= s->base; | |
74 | switch (offset >> 2) { | |
75 | case 0: /* KMICR */ | |
76 | s->cr = value; | |
77 | pl050_update(s, s->pending); | |
78 | /* ??? Need to implement the enable/disable bit. */ | |
79 | break; | |
80 | case 2: /* KMIDATA */ | |
81 | /* ??? This should toggle the TX interrupt line. */ | |
82 | /* ??? This means kbd/mouse can block each other. */ | |
83 | if (s->is_mouse) { | |
84 | ps2_write_mouse(s->dev, value); | |
85 | } else { | |
86 | ps2_write_keyboard(s->dev, value); | |
87 | } | |
88 | break; | |
89 | case 3: /* KMICLKDIV */ | |
90 | s->clk = value; | |
91 | return; | |
92 | default: | |
93 | cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", offset); | |
94 | } | |
95 | } | |
96 | static CPUReadMemoryFunc *pl050_readfn[] = { | |
97 | pl050_read, | |
98 | pl050_read, | |
99 | pl050_read | |
100 | }; | |
101 | ||
102 | static CPUWriteMemoryFunc *pl050_writefn[] = { | |
103 | pl050_write, | |
104 | pl050_write, | |
105 | pl050_write | |
106 | }; | |
107 | ||
108 | void pl050_init(uint32_t base, void *pic, int irq, int is_mouse) | |
109 | { | |
110 | int iomemtype; | |
111 | pl050_state *s; | |
112 | ||
113 | s = (pl050_state *)qemu_mallocz(sizeof(pl050_state)); | |
114 | iomemtype = cpu_register_io_memory(0, pl050_readfn, | |
115 | pl050_writefn, s); | |
116 | cpu_register_physical_memory(base, 0x00000fff, iomemtype); | |
117 | s->base = base; | |
118 | s->pic = pic; | |
119 | s->irq = irq; | |
120 | s->is_mouse = is_mouse; | |
121 | if (is_mouse) | |
122 | s->dev = ps2_mouse_init(pl050_update, s); | |
123 | else | |
124 | s->dev = ps2_kbd_init(pl050_update, s); | |
125 | /* ??? Save/restore. */ | |
126 | } | |
127 |