1 /* 2 * Arm PrimeCell PL050 Keyboard / Mouse Interface 3 * 4 * Copyright (c) 2006-2007 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licensed under the GPL. 8 */ 9 10 /* 11 * QEMU interface: 12 * + sysbus MMIO region 0: MemoryRegion defining the PL050 registers 13 * + Named GPIO input "ps2-input-irq": set to 1 if the downstream PS2 device 14 * has asserted its irq 15 * + sysbus IRQ 0: PL050 output irq 16 */ 17 18 #include "qemu/osdep.h" 19 #include "hw/sysbus.h" 20 #include "migration/vmstate.h" 21 #include "hw/input/ps2.h" 22 #include "hw/input/pl050.h" 23 #include "hw/irq.h" 24 #include "qemu/log.h" 25 #include "qemu/module.h" 26 #include "qom/object.h" 27 28 29 static const VMStateDescription vmstate_pl050 = { 30 .name = "pl050", 31 .version_id = 2, 32 .minimum_version_id = 2, 33 .fields = (const VMStateField[]) { 34 VMSTATE_UINT32(cr, PL050State), 35 VMSTATE_UINT32(clk, PL050State), 36 VMSTATE_UINT32(last, PL050State), 37 VMSTATE_INT32(pending, PL050State), 38 VMSTATE_END_OF_LIST() 39 } 40 }; 41 42 #define PL050_TXEMPTY (1 << 6) 43 #define PL050_TXBUSY (1 << 5) 44 #define PL050_RXFULL (1 << 4) 45 #define PL050_RXBUSY (1 << 3) 46 #define PL050_RXPARITY (1 << 2) 47 #define PL050_KMIC (1 << 1) 48 #define PL050_KMID (1 << 0) 49 50 static const unsigned char pl050_id[] = { 51 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 52 }; 53 54 static void pl050_update_irq(PL050State *s) 55 { 56 int level = (s->pending && (s->cr & 0x10) != 0) 57 || (s->cr & 0x08) != 0; 58 59 qemu_set_irq(s->irq, level); 60 } 61 62 static void pl050_set_irq(void *opaque, int n, int level) 63 { 64 PL050State *s = (PL050State *)opaque; 65 66 s->pending = level; 67 pl050_update_irq(s); 68 } 69 70 static uint64_t pl050_read(void *opaque, hwaddr offset, 71 unsigned size) 72 { 73 PL050State *s = (PL050State *)opaque; 74 75 if (offset >= 0xfe0 && offset < 0x1000) { 76 return pl050_id[(offset - 0xfe0) >> 2]; 77 } 78 79 switch (offset >> 2) { 80 case 0: /* KMICR */ 81 return s->cr; 82 case 1: /* KMISTAT */ 83 { 84 uint8_t val; 85 uint32_t stat; 86 87 val = s->last; 88 val = val ^ (val >> 4); 89 val = val ^ (val >> 2); 90 val = (val ^ (val >> 1)) & 1; 91 92 stat = PL050_TXEMPTY; 93 if (val) { 94 stat |= PL050_RXPARITY; 95 } 96 if (s->pending) { 97 stat |= PL050_RXFULL; 98 } 99 100 return stat; 101 } 102 case 2: /* KMIDATA */ 103 if (s->pending) { 104 s->last = ps2_read_data(s->ps2dev); 105 } 106 return s->last; 107 case 3: /* KMICLKDIV */ 108 return s->clk; 109 case 4: /* KMIIR */ 110 return s->pending | 2; 111 default: 112 qemu_log_mask(LOG_GUEST_ERROR, 113 "pl050_read: Bad offset %x\n", (int)offset); 114 return 0; 115 } 116 } 117 118 static void pl050_write(void *opaque, hwaddr offset, 119 uint64_t value, unsigned size) 120 { 121 PL050State *s = (PL050State *)opaque; 122 123 switch (offset >> 2) { 124 case 0: /* KMICR */ 125 s->cr = value; 126 pl050_update_irq(s); 127 /* ??? Need to implement the enable/disable bit. */ 128 break; 129 case 2: /* KMIDATA */ 130 /* ??? This should toggle the TX interrupt line. */ 131 /* ??? This means kbd/mouse can block each other. */ 132 if (s->is_mouse) { 133 ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), value); 134 } else { 135 ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), value); 136 } 137 break; 138 case 3: /* KMICLKDIV */ 139 s->clk = value; 140 return; 141 default: 142 qemu_log_mask(LOG_GUEST_ERROR, 143 "pl050_write: Bad offset %x\n", (int)offset); 144 } 145 } 146 static const MemoryRegionOps pl050_ops = { 147 .read = pl050_read, 148 .write = pl050_write, 149 .endianness = DEVICE_NATIVE_ENDIAN, 150 }; 151 152 static void pl050_realize(DeviceState *dev, Error **errp) 153 { 154 PL050State *s = PL050(dev); 155 156 qdev_connect_gpio_out(DEVICE(s->ps2dev), PS2_DEVICE_IRQ, 157 qdev_get_gpio_in_named(dev, "ps2-input-irq", 0)); 158 } 159 160 static void pl050_kbd_realize(DeviceState *dev, Error **errp) 161 { 162 PL050DeviceClass *pdc = PL050_GET_CLASS(dev); 163 PL050KbdState *s = PL050_KBD_DEVICE(dev); 164 PL050State *ps = PL050(dev); 165 166 if (!sysbus_realize(SYS_BUS_DEVICE(&s->kbd), errp)) { 167 return; 168 } 169 170 ps->ps2dev = PS2_DEVICE(&s->kbd); 171 pdc->parent_realize(dev, errp); 172 } 173 174 static void pl050_kbd_init(Object *obj) 175 { 176 PL050KbdState *s = PL050_KBD_DEVICE(obj); 177 PL050State *ps = PL050(obj); 178 179 ps->is_mouse = false; 180 object_initialize_child(obj, "kbd", &s->kbd, TYPE_PS2_KBD_DEVICE); 181 } 182 183 static void pl050_mouse_realize(DeviceState *dev, Error **errp) 184 { 185 PL050DeviceClass *pdc = PL050_GET_CLASS(dev); 186 PL050MouseState *s = PL050_MOUSE_DEVICE(dev); 187 PL050State *ps = PL050(dev); 188 189 if (!sysbus_realize(SYS_BUS_DEVICE(&s->mouse), errp)) { 190 return; 191 } 192 193 ps->ps2dev = PS2_DEVICE(&s->mouse); 194 pdc->parent_realize(dev, errp); 195 } 196 197 static void pl050_mouse_init(Object *obj) 198 { 199 PL050MouseState *s = PL050_MOUSE_DEVICE(obj); 200 PL050State *ps = PL050(obj); 201 202 ps->is_mouse = true; 203 object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); 204 } 205 206 static void pl050_kbd_class_init(ObjectClass *oc, void *data) 207 { 208 DeviceClass *dc = DEVICE_CLASS(oc); 209 PL050DeviceClass *pdc = PL050_CLASS(oc); 210 211 device_class_set_parent_realize(dc, pl050_kbd_realize, 212 &pdc->parent_realize); 213 } 214 215 static const TypeInfo pl050_kbd_info = { 216 .name = TYPE_PL050_KBD_DEVICE, 217 .parent = TYPE_PL050, 218 .instance_init = pl050_kbd_init, 219 .instance_size = sizeof(PL050KbdState), 220 .class_init = pl050_kbd_class_init, 221 }; 222 223 static void pl050_mouse_class_init(ObjectClass *oc, void *data) 224 { 225 DeviceClass *dc = DEVICE_CLASS(oc); 226 PL050DeviceClass *pdc = PL050_CLASS(oc); 227 228 device_class_set_parent_realize(dc, pl050_mouse_realize, 229 &pdc->parent_realize); 230 } 231 232 static const TypeInfo pl050_mouse_info = { 233 .name = TYPE_PL050_MOUSE_DEVICE, 234 .parent = TYPE_PL050, 235 .instance_init = pl050_mouse_init, 236 .instance_size = sizeof(PL050MouseState), 237 .class_init = pl050_mouse_class_init, 238 }; 239 240 static void pl050_init(Object *obj) 241 { 242 PL050State *s = PL050(obj); 243 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 244 245 memory_region_init_io(&s->iomem, obj, &pl050_ops, s, "pl050", 0x1000); 246 sysbus_init_mmio(sbd, &s->iomem); 247 sysbus_init_irq(sbd, &s->irq); 248 249 qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1); 250 } 251 252 static void pl050_class_init(ObjectClass *oc, void *data) 253 { 254 DeviceClass *dc = DEVICE_CLASS(oc); 255 256 dc->realize = pl050_realize; 257 dc->vmsd = &vmstate_pl050; 258 } 259 260 static const TypeInfo pl050_type_info = { 261 .name = TYPE_PL050, 262 .parent = TYPE_SYS_BUS_DEVICE, 263 .instance_init = pl050_init, 264 .instance_size = sizeof(PL050State), 265 .class_init = pl050_class_init, 266 .class_size = sizeof(PL050DeviceClass), 267 .abstract = true, 268 .class_init = pl050_class_init, 269 }; 270 271 static void pl050_register_types(void) 272 { 273 type_register_static(&pl050_type_info); 274 type_register_static(&pl050_kbd_info); 275 type_register_static(&pl050_mouse_info); 276 } 277 278 type_init(pl050_register_types) 279