Statistics
| Branch: | Revision:

root / hw / milkymist-softusb.c @ a8170e5e

History | View | Annotate | Download (9 kB)

1
/*
2
 *  QEMU model of the Milkymist SoftUSB block.
3
 *
4
 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
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
    MemoryRegion regs_region;
53
    MemoryRegion pmem;
54
    MemoryRegion dmem;
55
    qemu_irq irq;
56

    
57
    /* device properties */
58
    uint32_t pmem_base;
59
    uint32_t pmem_size;
60
    uint32_t dmem_base;
61
    uint32_t dmem_size;
62

    
63
    /* device registers */
64
    uint32_t regs[R_MAX];
65

    
66
    /* mouse state */
67
    uint8_t mouse_hid_buffer[4];
68

    
69
    /* keyboard state */
70
    uint8_t kbd_hid_buffer[8];
71
};
72
typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
73

    
74
static uint64_t softusb_read(void *opaque, hwaddr addr,
75
                             unsigned size)
76
{
77
    MilkymistSoftUsbState *s = opaque;
78
    uint32_t r = 0;
79

    
80
    addr >>= 2;
81
    switch (addr) {
82
    case R_CTRL:
83
        r = s->regs[addr];
84
        break;
85

    
86
    default:
87
        error_report("milkymist_softusb: read access to unknown register 0x"
88
                TARGET_FMT_plx, addr << 2);
89
        break;
90
    }
91

    
92
    trace_milkymist_softusb_memory_read(addr << 2, r);
93

    
94
    return r;
95
}
96

    
97
static void
98
softusb_write(void *opaque, hwaddr addr, uint64_t value,
99
              unsigned size)
100
{
101
    MilkymistSoftUsbState *s = opaque;
102

    
103
    trace_milkymist_softusb_memory_write(addr, value);
104

    
105
    addr >>= 2;
106
    switch (addr) {
107
    case R_CTRL:
108
        s->regs[addr] = value;
109
        break;
110

    
111
    default:
112
        error_report("milkymist_softusb: write access to unknown register 0x"
113
                TARGET_FMT_plx, addr << 2);
114
        break;
115
    }
116
}
117

    
118
static const MemoryRegionOps softusb_mmio_ops = {
119
    .read = softusb_read,
120
    .write = softusb_write,
121
    .endianness = DEVICE_NATIVE_ENDIAN,
122
    .valid = {
123
        .min_access_size = 4,
124
        .max_access_size = 4,
125
    },
126
};
127

    
128
static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
129
        uint32_t offset, uint8_t *buf, uint32_t len)
130
{
131
    if (offset + len >= s->dmem_size) {
132
        error_report("milkymist_softusb: read dmem out of bounds "
133
                "at offset 0x%x, len %d", offset, len);
134
        return;
135
    }
136

    
137
    cpu_physical_memory_read(s->dmem_base + offset, buf, len);
138
}
139

    
140
static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
141
        uint32_t offset, uint8_t *buf, uint32_t len)
142
{
143
    if (offset + len >= s->dmem_size) {
144
        error_report("milkymist_softusb: write dmem out of bounds "
145
                "at offset 0x%x, len %d", offset, len);
146
        return;
147
    }
148

    
149
    cpu_physical_memory_write(s->dmem_base + offset, buf, len);
150
}
151

    
152
static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
153
        uint32_t offset, uint8_t *buf, uint32_t len)
154
{
155
    if (offset + len >= s->pmem_size) {
156
        error_report("milkymist_softusb: read pmem out of bounds "
157
                "at offset 0x%x, len %d", offset, len);
158
        return;
159
    }
160

    
161
    cpu_physical_memory_read(s->pmem_base + offset, buf, len);
162
}
163

    
164
static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
165
        uint32_t offset, uint8_t *buf, uint32_t len)
166
{
167
    if (offset + len >= s->pmem_size) {
168
        error_report("milkymist_softusb: write pmem out of bounds "
169
                "at offset 0x%x, len %d", offset, len);
170
        return;
171
    }
172

    
173
    cpu_physical_memory_write(s->pmem_base + offset, buf, len);
174
}
175

    
176
static void softusb_mouse_changed(MilkymistSoftUsbState *s)
177
{
178
    uint8_t m;
179

    
180
    softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
181
    trace_milkymist_softusb_mevt(m);
182
    softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
183
    m = (m + 1) & 0xf;
184
    softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
185

    
186
    trace_milkymist_softusb_pulse_irq();
187
    qemu_irq_pulse(s->irq);
188
}
189

    
190
static void softusb_kbd_changed(MilkymistSoftUsbState *s)
191
{
192
    uint8_t m;
193

    
194
    softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
195
    trace_milkymist_softusb_kevt(m);
196
    softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
197
    m = (m + 1) & 0x7;
198
    softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
199

    
200
    trace_milkymist_softusb_pulse_irq();
201
    qemu_irq_pulse(s->irq);
202
}
203

    
204
static void softusb_kbd_hid_datain(HIDState *hs)
205
{
206
    MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
207
    int len;
208

    
209
    /* if device is in reset, do nothing */
210
    if (s->regs[R_CTRL] & CTRL_RESET) {
211
        return;
212
    }
213

    
214
    len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer));
215

    
216
    if (len == 8) {
217
        softusb_kbd_changed(s);
218
    }
219
}
220

    
221
static void softusb_mouse_hid_datain(HIDState *hs)
222
{
223
    MilkymistSoftUsbState *s =
224
            container_of(hs, MilkymistSoftUsbState, hid_mouse);
225
    int len;
226

    
227
    /* if device is in reset, do nothing */
228
    if (s->regs[R_CTRL] & CTRL_RESET) {
229
        return;
230
    }
231

    
232
    len = hid_pointer_poll(hs, s->mouse_hid_buffer,
233
            sizeof(s->mouse_hid_buffer));
234

    
235
    if (len == 4) {
236
        softusb_mouse_changed(s);
237
    }
238
}
239

    
240
static void milkymist_softusb_reset(DeviceState *d)
241
{
242
    MilkymistSoftUsbState *s =
243
            container_of(d, MilkymistSoftUsbState, busdev.qdev);
244
    int i;
245

    
246
    for (i = 0; i < R_MAX; i++) {
247
        s->regs[i] = 0;
248
    }
249
    memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
250
    memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
251

    
252
    hid_reset(&s->hid_kbd);
253
    hid_reset(&s->hid_mouse);
254

    
255
    /* defaults */
256
    s->regs[R_CTRL] = CTRL_RESET;
257
}
258

    
259
static int milkymist_softusb_init(SysBusDevice *dev)
260
{
261
    MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
262

    
263
    sysbus_init_irq(dev, &s->irq);
264

    
265
    memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s,
266
                          "milkymist-softusb", R_MAX * 4);
267
    sysbus_init_mmio(dev, &s->regs_region);
268

    
269
    /* register pmem and dmem */
270
    memory_region_init_ram(&s->pmem, "milkymist-softusb.pmem",
271
                           s->pmem_size);
272
    vmstate_register_ram_global(&s->pmem);
273
    sysbus_add_memory(dev, s->pmem_base, &s->pmem);
274
    memory_region_init_ram(&s->dmem, "milkymist-softusb.dmem",
275
                           s->dmem_size);
276
    vmstate_register_ram_global(&s->dmem);
277
    sysbus_add_memory(dev, s->dmem_base, &s->dmem);
278

    
279
    hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
280
    hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
281

    
282
    return 0;
283
}
284

    
285
static const VMStateDescription vmstate_milkymist_softusb = {
286
    .name = "milkymist-softusb",
287
    .version_id = 1,
288
    .minimum_version_id = 1,
289
    .minimum_version_id_old = 1,
290
    .fields      = (VMStateField[]) {
291
        VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
292
        VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
293
        VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
294
        VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
295
        VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
296
        VMSTATE_END_OF_LIST()
297
    }
298
};
299

    
300
static Property milkymist_softusb_properties[] = {
301
    DEFINE_PROP_UINT32("pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000),
302
    DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000),
303
    DEFINE_PROP_UINT32("dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000),
304
    DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000),
305
    DEFINE_PROP_END_OF_LIST(),
306
};
307

    
308
static void milkymist_softusb_class_init(ObjectClass *klass, void *data)
309
{
310
    DeviceClass *dc = DEVICE_CLASS(klass);
311
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
312

    
313
    k->init = milkymist_softusb_init;
314
    dc->reset = milkymist_softusb_reset;
315
    dc->vmsd = &vmstate_milkymist_softusb;
316
    dc->props = milkymist_softusb_properties;
317
}
318

    
319
static TypeInfo milkymist_softusb_info = {
320
    .name          = "milkymist-softusb",
321
    .parent        = TYPE_SYS_BUS_DEVICE,
322
    .instance_size = sizeof(MilkymistSoftUsbState),
323
    .class_init    = milkymist_softusb_class_init,
324
};
325

    
326
static void milkymist_softusb_register_types(void)
327
{
328
    type_register_static(&milkymist_softusb_info);
329
}
330

    
331
type_init(milkymist_softusb_register_types)