149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * Arm PrimeCell PL050 Keyboard / Mouse Interface
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2006-2007 CodeSourcery.
549ab747fSPaolo Bonzini * Written by Paul Brook
649ab747fSPaolo Bonzini *
749ab747fSPaolo Bonzini * This code is licensed under the GPL.
849ab747fSPaolo Bonzini */
949ab747fSPaolo Bonzini
101d9d4b07SMark Cave-Ayland /*
111d9d4b07SMark Cave-Ayland * QEMU interface:
121d9d4b07SMark Cave-Ayland * + sysbus MMIO region 0: MemoryRegion defining the PL050 registers
131d9d4b07SMark Cave-Ayland * + Named GPIO input "ps2-input-irq": set to 1 if the downstream PS2 device
141d9d4b07SMark Cave-Ayland * has asserted its irq
151d9d4b07SMark Cave-Ayland * + sysbus IRQ 0: PL050 output irq
161d9d4b07SMark Cave-Ayland */
171d9d4b07SMark Cave-Ayland
188ef94f0bSPeter Maydell #include "qemu/osdep.h"
1949ab747fSPaolo Bonzini #include "hw/sysbus.h"
20d6454270SMarkus Armbruster #include "migration/vmstate.h"
2149ab747fSPaolo Bonzini #include "hw/input/ps2.h"
22bce0e9c1SMark Cave-Ayland #include "hw/input/pl050.h"
2364552b6bSMarkus Armbruster #include "hw/irq.h"
2403dd024fSPaolo Bonzini #include "qemu/log.h"
250b8fa32fSMarkus Armbruster #include "qemu/module.h"
26db1015e9SEduardo Habkost #include "qom/object.h"
2749ab747fSPaolo Bonzini
2849ab747fSPaolo Bonzini
2949ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl050 = {
3049ab747fSPaolo Bonzini .name = "pl050",
3149ab747fSPaolo Bonzini .version_id = 2,
3249ab747fSPaolo Bonzini .minimum_version_id = 2,
33*af0f07dfSRichard Henderson .fields = (const VMStateField[]) {
34e607f25aSAndreas Färber VMSTATE_UINT32(cr, PL050State),
35e607f25aSAndreas Färber VMSTATE_UINT32(clk, PL050State),
36e607f25aSAndreas Färber VMSTATE_UINT32(last, PL050State),
37e607f25aSAndreas Färber VMSTATE_INT32(pending, PL050State),
3849ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
3949ab747fSPaolo Bonzini }
4049ab747fSPaolo Bonzini };
4149ab747fSPaolo Bonzini
4249ab747fSPaolo Bonzini #define PL050_TXEMPTY (1 << 6)
4349ab747fSPaolo Bonzini #define PL050_TXBUSY (1 << 5)
4449ab747fSPaolo Bonzini #define PL050_RXFULL (1 << 4)
4549ab747fSPaolo Bonzini #define PL050_RXBUSY (1 << 3)
4649ab747fSPaolo Bonzini #define PL050_RXPARITY (1 << 2)
4749ab747fSPaolo Bonzini #define PL050_KMIC (1 << 1)
4849ab747fSPaolo Bonzini #define PL050_KMID (1 << 0)
4949ab747fSPaolo Bonzini
50600f7110SMark Cave-Ayland static const unsigned char pl050_id[] = {
51600f7110SMark Cave-Ayland 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
52600f7110SMark Cave-Ayland };
5349ab747fSPaolo Bonzini
pl050_update_irq(PL050State * s)54eca9e870SMark Cave-Ayland static void pl050_update_irq(PL050State *s)
55eca9e870SMark Cave-Ayland {
56eca9e870SMark Cave-Ayland int level = (s->pending && (s->cr & 0x10) != 0)
57eca9e870SMark Cave-Ayland || (s->cr & 0x08) != 0;
58eca9e870SMark Cave-Ayland
59eca9e870SMark Cave-Ayland qemu_set_irq(s->irq, level);
60eca9e870SMark Cave-Ayland }
61eca9e870SMark Cave-Ayland
pl050_set_irq(void * opaque,int n,int level)62423bcb23SMark Cave-Ayland static void pl050_set_irq(void *opaque, int n, int level)
6349ab747fSPaolo Bonzini {
64e607f25aSAndreas Färber PL050State *s = (PL050State *)opaque;
6549ab747fSPaolo Bonzini
6649ab747fSPaolo Bonzini s->pending = level;
67eca9e870SMark Cave-Ayland pl050_update_irq(s);
6849ab747fSPaolo Bonzini }
6949ab747fSPaolo Bonzini
pl050_read(void * opaque,hwaddr offset,unsigned size)7049ab747fSPaolo Bonzini static uint64_t pl050_read(void *opaque, hwaddr offset,
7149ab747fSPaolo Bonzini unsigned size)
7249ab747fSPaolo Bonzini {
73e607f25aSAndreas Färber PL050State *s = (PL050State *)opaque;
74600f7110SMark Cave-Ayland
75600f7110SMark Cave-Ayland if (offset >= 0xfe0 && offset < 0x1000) {
7649ab747fSPaolo Bonzini return pl050_id[(offset - 0xfe0) >> 2];
77600f7110SMark Cave-Ayland }
7849ab747fSPaolo Bonzini
7949ab747fSPaolo Bonzini switch (offset >> 2) {
8049ab747fSPaolo Bonzini case 0: /* KMICR */
8149ab747fSPaolo Bonzini return s->cr;
8249ab747fSPaolo Bonzini case 1: /* KMISTAT */
8349ab747fSPaolo Bonzini {
8449ab747fSPaolo Bonzini uint8_t val;
8549ab747fSPaolo Bonzini uint32_t stat;
8649ab747fSPaolo Bonzini
8749ab747fSPaolo Bonzini val = s->last;
8849ab747fSPaolo Bonzini val = val ^ (val >> 4);
8949ab747fSPaolo Bonzini val = val ^ (val >> 2);
9049ab747fSPaolo Bonzini val = (val ^ (val >> 1)) & 1;
9149ab747fSPaolo Bonzini
9249ab747fSPaolo Bonzini stat = PL050_TXEMPTY;
93600f7110SMark Cave-Ayland if (val) {
9449ab747fSPaolo Bonzini stat |= PL050_RXPARITY;
95600f7110SMark Cave-Ayland }
96600f7110SMark Cave-Ayland if (s->pending) {
9749ab747fSPaolo Bonzini stat |= PL050_RXFULL;
98600f7110SMark Cave-Ayland }
9949ab747fSPaolo Bonzini
10049ab747fSPaolo Bonzini return stat;
10149ab747fSPaolo Bonzini }
10249ab747fSPaolo Bonzini case 2: /* KMIDATA */
103600f7110SMark Cave-Ayland if (s->pending) {
10433e0958eSMark Cave-Ayland s->last = ps2_read_data(s->ps2dev);
105600f7110SMark Cave-Ayland }
10649ab747fSPaolo Bonzini return s->last;
10749ab747fSPaolo Bonzini case 3: /* KMICLKDIV */
10849ab747fSPaolo Bonzini return s->clk;
10949ab747fSPaolo Bonzini case 4: /* KMIIR */
11049ab747fSPaolo Bonzini return s->pending | 2;
11149ab747fSPaolo Bonzini default:
11249ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
11349ab747fSPaolo Bonzini "pl050_read: Bad offset %x\n", (int)offset);
11449ab747fSPaolo Bonzini return 0;
11549ab747fSPaolo Bonzini }
11649ab747fSPaolo Bonzini }
11749ab747fSPaolo Bonzini
pl050_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)11849ab747fSPaolo Bonzini static void pl050_write(void *opaque, hwaddr offset,
11949ab747fSPaolo Bonzini uint64_t value, unsigned size)
12049ab747fSPaolo Bonzini {
121e607f25aSAndreas Färber PL050State *s = (PL050State *)opaque;
122600f7110SMark Cave-Ayland
12349ab747fSPaolo Bonzini switch (offset >> 2) {
12449ab747fSPaolo Bonzini case 0: /* KMICR */
12549ab747fSPaolo Bonzini s->cr = value;
126eca9e870SMark Cave-Ayland pl050_update_irq(s);
12749ab747fSPaolo Bonzini /* ??? Need to implement the enable/disable bit. */
12849ab747fSPaolo Bonzini break;
12949ab747fSPaolo Bonzini case 2: /* KMIDATA */
13049ab747fSPaolo Bonzini /* ??? This should toggle the TX interrupt line. */
13149ab747fSPaolo Bonzini /* ??? This means kbd/mouse can block each other. */
13249ab747fSPaolo Bonzini if (s->is_mouse) {
13333e0958eSMark Cave-Ayland ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), value);
13449ab747fSPaolo Bonzini } else {
13533e0958eSMark Cave-Ayland ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), value);
13649ab747fSPaolo Bonzini }
13749ab747fSPaolo Bonzini break;
13849ab747fSPaolo Bonzini case 3: /* KMICLKDIV */
13949ab747fSPaolo Bonzini s->clk = value;
14049ab747fSPaolo Bonzini return;
14149ab747fSPaolo Bonzini default:
14249ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
14349ab747fSPaolo Bonzini "pl050_write: Bad offset %x\n", (int)offset);
14449ab747fSPaolo Bonzini }
14549ab747fSPaolo Bonzini }
14649ab747fSPaolo Bonzini static const MemoryRegionOps pl050_ops = {
14749ab747fSPaolo Bonzini .read = pl050_read,
14849ab747fSPaolo Bonzini .write = pl050_write,
14949ab747fSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
15049ab747fSPaolo Bonzini };
15149ab747fSPaolo Bonzini
pl050_realize(DeviceState * dev,Error ** errp)152988e501aSMao Zhongyi static void pl050_realize(DeviceState *dev, Error **errp)
15349ab747fSPaolo Bonzini {
1543e5dd364SAndreas Färber PL050State *s = PL050(dev);
15549ab747fSPaolo Bonzini
15633e0958eSMark Cave-Ayland qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ,
157423bcb23SMark Cave-Ayland qdev_get_gpio_in_named(dev, "ps2-input-irq", 0));
15849ab747fSPaolo Bonzini }
15949ab747fSPaolo Bonzini
pl050_kbd_realize(DeviceState * dev,Error ** errp)16087efd282SMark Cave-Ayland static void pl050_kbd_realize(DeviceState *dev, Error **errp)
16187efd282SMark Cave-Ayland {
16287efd282SMark Cave-Ayland PL050DeviceClass *pdc = PL050_GET_CLASS(dev);
1636a05d0b3SMark Cave-Ayland PL050KbdState *s = PL050_KBD_DEVICE(dev);
16487efd282SMark Cave-Ayland PL050State *ps = PL050(dev);
16587efd282SMark Cave-Ayland
1666a05d0b3SMark Cave-Ayland if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) {
1676a05d0b3SMark Cave-Ayland return;
1686a05d0b3SMark Cave-Ayland }
1696a05d0b3SMark Cave-Ayland
1706a05d0b3SMark Cave-Ayland ps->ps2dev = PS2_DEVICE(&s->kbd);
17187efd282SMark Cave-Ayland pdc->parent_realize(dev, errp);
17287efd282SMark Cave-Ayland }
17387efd282SMark Cave-Ayland
pl050_kbd_init(Object * obj)174b6c575d8SMark Cave-Ayland static void pl050_kbd_init(Object *obj)
17549ab747fSPaolo Bonzini {
1766a05d0b3SMark Cave-Ayland PL050KbdState *s = PL050_KBD_DEVICE(obj);
1776a05d0b3SMark Cave-Ayland PL050State *ps = PL050(obj);
1783e5dd364SAndreas Färber
1796a05d0b3SMark Cave-Ayland ps->is_mouse = false;
1806a05d0b3SMark Cave-Ayland object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE);
18149ab747fSPaolo Bonzini }
18249ab747fSPaolo Bonzini
pl050_mouse_realize(DeviceState * dev,Error ** errp)1835b0138b3SMark Cave-Ayland static void pl050_mouse_realize(DeviceState *dev, Error **errp)
1845b0138b3SMark Cave-Ayland {
1855b0138b3SMark Cave-Ayland PL050DeviceClass *pdc = PL050_GET_CLASS(dev);
1866f9f245bSMark Cave-Ayland PL050MouseState *s = PL050_MOUSE_DEVICE(dev);
1875b0138b3SMark Cave-Ayland PL050State *ps = PL050(dev);
1885b0138b3SMark Cave-Ayland
1896f9f245bSMark Cave-Ayland if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) {
1906f9f245bSMark Cave-Ayland return;
1916f9f245bSMark Cave-Ayland }
1926f9f245bSMark Cave-Ayland
1936f9f245bSMark Cave-Ayland ps->ps2dev = PS2_DEVICE(&s->mouse);
1945b0138b3SMark Cave-Ayland pdc->parent_realize(dev, errp);
1955b0138b3SMark Cave-Ayland }
1965b0138b3SMark Cave-Ayland
pl050_mouse_init(Object * obj)1973e5dd364SAndreas Färber static void pl050_mouse_init(Object *obj)
19849ab747fSPaolo Bonzini {
1996f9f245bSMark Cave-Ayland PL050MouseState *s = PL050_MOUSE_DEVICE(obj);
2006f9f245bSMark Cave-Ayland PL050State *ps = PL050(obj);
20149ab747fSPaolo Bonzini
2026f9f245bSMark Cave-Ayland ps->is_mouse = true;
2036f9f245bSMark Cave-Ayland object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE);
20449ab747fSPaolo Bonzini }
20549ab747fSPaolo Bonzini
pl050_kbd_class_init(ObjectClass * oc,void * data)20687efd282SMark Cave-Ayland static void pl050_kbd_class_init(ObjectClass *oc, void *data)
20787efd282SMark Cave-Ayland {
20887efd282SMark Cave-Ayland DeviceClass *dc = DEVICE_CLASS(oc);
20987efd282SMark Cave-Ayland PL050DeviceClass *pdc = PL050_CLASS(oc);
21087efd282SMark Cave-Ayland
21187efd282SMark Cave-Ayland device_class_set_parent_realize(dc, pl050_kbd_realize,
21287efd282SMark Cave-Ayland &pdc->parent_realize);
21387efd282SMark Cave-Ayland }
21487efd282SMark Cave-Ayland
21549ab747fSPaolo Bonzini static const TypeInfo pl050_kbd_info = {
2161d59315dSMark Cave-Ayland .name = TYPE_PL050_KBD_DEVICE,
2173e5dd364SAndreas Färber .parent = TYPE_PL050,
218b6c575d8SMark Cave-Ayland .instance_init = pl050_kbd_init,
2191d59315dSMark Cave-Ayland .instance_size = sizeof(PL050KbdState),
22087efd282SMark Cave-Ayland .class_init = pl050_kbd_class_init,
22149ab747fSPaolo Bonzini };
22249ab747fSPaolo Bonzini
pl050_mouse_class_init(ObjectClass * oc,void * data)2235b0138b3SMark Cave-Ayland static void pl050_mouse_class_init(ObjectClass *oc, void *data)
2245b0138b3SMark Cave-Ayland {
2255b0138b3SMark Cave-Ayland DeviceClass *dc = DEVICE_CLASS(oc);
2265b0138b3SMark Cave-Ayland PL050DeviceClass *pdc = PL050_CLASS(oc);
2275b0138b3SMark Cave-Ayland
2285b0138b3SMark Cave-Ayland device_class_set_parent_realize(dc, pl050_mouse_realize,
2295b0138b3SMark Cave-Ayland &pdc->parent_realize);
2305b0138b3SMark Cave-Ayland }
2315b0138b3SMark Cave-Ayland
23249ab747fSPaolo Bonzini static const TypeInfo pl050_mouse_info = {
2330a3c1e1bSMark Cave-Ayland .name = TYPE_PL050_MOUSE_DEVICE,
2343e5dd364SAndreas Färber .parent = TYPE_PL050,
2353e5dd364SAndreas Färber .instance_init = pl050_mouse_init,
2360a3c1e1bSMark Cave-Ayland .instance_size = sizeof(PL050MouseState),
2375b0138b3SMark Cave-Ayland .class_init = pl050_mouse_class_init,
2383e5dd364SAndreas Färber };
2393e5dd364SAndreas Färber
pl050_init(Object * obj)240423bcb23SMark Cave-Ayland static void pl050_init(Object *obj)
241423bcb23SMark Cave-Ayland {
2423d5e0995SMark Cave-Ayland PL050State *s = PL050(obj);
2433d5e0995SMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
2443d5e0995SMark Cave-Ayland
2453d5e0995SMark Cave-Ayland memory_region_init_io(&s->iomem, obj, &pl050_ops, s, "pl050", 0x1000);
2463d5e0995SMark Cave-Ayland sysbus_init_mmio(sbd, &s->iomem);
2473d5e0995SMark Cave-Ayland sysbus_init_irq(sbd, &s->irq);
2483d5e0995SMark Cave-Ayland
249423bcb23SMark Cave-Ayland qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1);
250423bcb23SMark Cave-Ayland }
251423bcb23SMark Cave-Ayland
pl050_class_init(ObjectClass * oc,void * data)2523e5dd364SAndreas Färber static void pl050_class_init(ObjectClass *oc, void *data)
2533e5dd364SAndreas Färber {
2543e5dd364SAndreas Färber DeviceClass *dc = DEVICE_CLASS(oc);
2553e5dd364SAndreas Färber
256988e501aSMao Zhongyi dc->realize = pl050_realize;
2573e5dd364SAndreas Färber dc->vmsd = &vmstate_pl050;
2583e5dd364SAndreas Färber }
2593e5dd364SAndreas Färber
2603e5dd364SAndreas Färber static const TypeInfo pl050_type_info = {
2613e5dd364SAndreas Färber .name = TYPE_PL050,
26249ab747fSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE,
263423bcb23SMark Cave-Ayland .instance_init = pl050_init,
264e607f25aSAndreas Färber .instance_size = sizeof(PL050State),
265475a4d46SMark Cave-Ayland .class_init = pl050_class_init,
266475a4d46SMark Cave-Ayland .class_size = sizeof(PL050DeviceClass),
2673e5dd364SAndreas Färber .abstract = true,
2683e5dd364SAndreas Färber .class_init = pl050_class_init,
26949ab747fSPaolo Bonzini };
27049ab747fSPaolo Bonzini
pl050_register_types(void)27149ab747fSPaolo Bonzini static void pl050_register_types(void)
27249ab747fSPaolo Bonzini {
2733e5dd364SAndreas Färber type_register_static(&pl050_type_info);
27449ab747fSPaolo Bonzini type_register_static(&pl050_kbd_info);
27549ab747fSPaolo Bonzini type_register_static(&pl050_mouse_info);
27649ab747fSPaolo Bonzini }
27749ab747fSPaolo Bonzini
27849ab747fSPaolo Bonzini type_init(pl050_register_types)
279