]> Git Repo - qemu.git/blob - hw/milkymist-softusb.c
Merge remote-tracking branch 'stefanha/trivial-patches' into staging
[qemu.git] / hw / milkymist-softusb.c
1 /*
2  *  QEMU model of the Milkymist SoftUSB block.
3  *
4  *  Copyright (c) 2010 Michael Walle <[email protected]>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  *
20  * Specification available at:
21  *   not available yet
22  */
23
24 #include "hw.h"
25 #include "sysbus.h"
26 #include "trace.h"
27 #include "console.h"
28 #include "hid.h"
29 #include "qemu-error.h"
30
31 enum {
32     R_CTRL = 0,
33     R_MAX
34 };
35
36 enum {
37     CTRL_RESET = (1<<0),
38 };
39
40 #define COMLOC_DEBUG_PRODUCE 0x1000
41 #define COMLOC_DEBUG_BASE    0x1001
42 #define COMLOC_MEVT_PRODUCE  0x1101
43 #define COMLOC_MEVT_BASE     0x1102
44 #define COMLOC_KEVT_PRODUCE  0x1142
45 #define COMLOC_KEVT_BASE     0x1143
46
47 struct MilkymistSoftUsbState {
48     SysBusDevice busdev;
49     HIDState hid_kbd;
50     HIDState hid_mouse;
51
52     qemu_irq irq;
53
54     /* device properties */
55     uint32_t pmem_base;
56     uint32_t pmem_size;
57     uint32_t dmem_base;
58     uint32_t dmem_size;
59
60     /* device registers */
61     uint32_t regs[R_MAX];
62
63     /* mouse state */
64     uint8_t mouse_hid_buffer[4];
65
66     /* keyboard state */
67     uint8_t kbd_hid_buffer[8];
68 };
69 typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
70
71 static uint32_t softusb_read(void *opaque, target_phys_addr_t addr)
72 {
73     MilkymistSoftUsbState *s = opaque;
74     uint32_t r = 0;
75
76     addr >>= 2;
77     switch (addr) {
78     case R_CTRL:
79         r = s->regs[addr];
80         break;
81
82     default:
83         error_report("milkymist_softusb: read access to unknown register 0x"
84                 TARGET_FMT_plx, addr << 2);
85         break;
86     }
87
88     trace_milkymist_softusb_memory_read(addr << 2, r);
89
90     return r;
91 }
92
93 static void
94 softusb_write(void *opaque, target_phys_addr_t addr, uint32_t value)
95 {
96     MilkymistSoftUsbState *s = opaque;
97
98     trace_milkymist_softusb_memory_write(addr, value);
99
100     addr >>= 2;
101     switch (addr) {
102     case R_CTRL:
103         s->regs[addr] = value;
104         break;
105
106     default:
107         error_report("milkymist_softusb: write access to unknown register 0x"
108                 TARGET_FMT_plx, addr << 2);
109         break;
110     }
111 }
112
113 static CPUReadMemoryFunc * const softusb_read_fn[] = {
114     NULL,
115     NULL,
116     &softusb_read,
117 };
118
119 static CPUWriteMemoryFunc * const softusb_write_fn[] = {
120     NULL,
121     NULL,
122     &softusb_write,
123 };
124
125 static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
126         uint32_t offset, uint8_t *buf, uint32_t len)
127 {
128     if (offset + len >= s->dmem_size) {
129         error_report("milkymist_softusb: read dmem out of bounds "
130                 "at offset 0x%x, len %d", offset, len);
131         return;
132     }
133
134     cpu_physical_memory_read(s->dmem_base + offset, buf, len);
135 }
136
137 static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
138         uint32_t offset, uint8_t *buf, uint32_t len)
139 {
140     if (offset + len >= s->dmem_size) {
141         error_report("milkymist_softusb: write dmem out of bounds "
142                 "at offset 0x%x, len %d", offset, len);
143         return;
144     }
145
146     cpu_physical_memory_write(s->dmem_base + offset, buf, len);
147 }
148
149 static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
150         uint32_t offset, uint8_t *buf, uint32_t len)
151 {
152     if (offset + len >= s->pmem_size) {
153         error_report("milkymist_softusb: read pmem out of bounds "
154                 "at offset 0x%x, len %d", offset, len);
155         return;
156     }
157
158     cpu_physical_memory_read(s->pmem_base + offset, buf, len);
159 }
160
161 static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
162         uint32_t offset, uint8_t *buf, uint32_t len)
163 {
164     if (offset + len >= s->pmem_size) {
165         error_report("milkymist_softusb: write pmem out of bounds "
166                 "at offset 0x%x, len %d", offset, len);
167         return;
168     }
169
170     cpu_physical_memory_write(s->pmem_base + offset, buf, len);
171 }
172
173 static void softusb_mouse_changed(MilkymistSoftUsbState *s)
174 {
175     uint8_t m;
176
177     softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
178     trace_milkymist_softusb_mevt(m);
179     softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
180     m = (m + 1) & 0xf;
181     softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
182
183     trace_milkymist_softusb_pulse_irq();
184     qemu_irq_pulse(s->irq);
185 }
186
187 static void softusb_kbd_changed(MilkymistSoftUsbState *s)
188 {
189     uint8_t m;
190
191     softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
192     trace_milkymist_softusb_kevt(m);
193     softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
194     m = (m + 1) & 0x7;
195     softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
196
197     trace_milkymist_softusb_pulse_irq();
198     qemu_irq_pulse(s->irq);
199 }
200
201 static void softusb_kbd_hid_datain(HIDState *hs)
202 {
203     MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
204     int len;
205
206     /* if device is in reset, do nothing */
207     if (s->regs[R_CTRL] & CTRL_RESET) {
208         return;
209     }
210
211     len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer));
212
213     if (len == 8) {
214         softusb_kbd_changed(s);
215     }
216 }
217
218 static void softusb_mouse_hid_datain(HIDState *hs)
219 {
220     MilkymistSoftUsbState *s =
221             container_of(hs, MilkymistSoftUsbState, hid_mouse);
222     int len;
223
224     /* if device is in reset, do nothing */
225     if (s->regs[R_CTRL] & CTRL_RESET) {
226         return;
227     }
228
229     len = hid_pointer_poll(hs, s->mouse_hid_buffer,
230             sizeof(s->mouse_hid_buffer));
231
232     if (len == 4) {
233         softusb_mouse_changed(s);
234     }
235 }
236
237 static void milkymist_softusb_reset(DeviceState *d)
238 {
239     MilkymistSoftUsbState *s =
240             container_of(d, MilkymistSoftUsbState, busdev.qdev);
241     int i;
242
243     for (i = 0; i < R_MAX; i++) {
244         s->regs[i] = 0;
245     }
246     memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
247     memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
248
249     hid_reset(&s->hid_kbd);
250     hid_reset(&s->hid_mouse);
251
252     /* defaults */
253     s->regs[R_CTRL] = CTRL_RESET;
254 }
255
256 static int milkymist_softusb_init(SysBusDevice *dev)
257 {
258     MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
259     int softusb_regs;
260     ram_addr_t pmem_ram;
261     ram_addr_t dmem_ram;
262
263     sysbus_init_irq(dev, &s->irq);
264
265     softusb_regs = cpu_register_io_memory(softusb_read_fn, softusb_write_fn, s,
266             DEVICE_NATIVE_ENDIAN);
267     sysbus_init_mmio(dev, R_MAX * 4, softusb_regs);
268
269     /* register pmem and dmem */
270     pmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.pmem", s->pmem_size);
271     cpu_register_physical_memory(s->pmem_base, s->pmem_size,
272             pmem_ram | IO_MEM_RAM);
273     dmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.dmem", s->dmem_size);
274     cpu_register_physical_memory(s->dmem_base, s->dmem_size,
275             dmem_ram | IO_MEM_RAM);
276
277     hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
278     hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
279
280     return 0;
281 }
282
283 static const VMStateDescription vmstate_milkymist_softusb = {
284     .name = "milkymist-softusb",
285     .version_id = 1,
286     .minimum_version_id = 1,
287     .minimum_version_id_old = 1,
288     .fields      = (VMStateField[]) {
289         VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
290         VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
291         VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
292         VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
293         VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
294         VMSTATE_END_OF_LIST()
295     }
296 };
297
298 static SysBusDeviceInfo milkymist_softusb_info = {
299     .init = milkymist_softusb_init,
300     .qdev.name  = "milkymist-softusb",
301     .qdev.size  = sizeof(MilkymistSoftUsbState),
302     .qdev.vmsd  = &vmstate_milkymist_softusb,
303     .qdev.reset = milkymist_softusb_reset,
304     .qdev.props = (Property[]) {
305         DEFINE_PROP_UINT32(
306                 "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000
307         ),
308         DEFINE_PROP_UINT32(
309                 "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000
310         ),
311         DEFINE_PROP_UINT32(
312                 "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000
313         ),
314         DEFINE_PROP_UINT32(
315                 "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000
316         ),
317         DEFINE_PROP_END_OF_LIST(),
318     }
319 };
320
321 static void milkymist_softusb_register(void)
322 {
323     sysbus_register_withprop(&milkymist_softusb_info);
324 }
325
326 device_init(milkymist_softusb_register)
This page took 0.04699 seconds and 4 git commands to generate.