Statistics
| Branch: | Revision:

root / hw / milkymist-softusb.c @ baaa86d9

History | View | Annotate | Download (8.6 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
    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)