The LPC32XX is a simple MMIO touch screen device with a Linux device driver. The device is suitable for small machines which require mouse input but have no suitable bus(SPI, I2C).
Add the LPC32XX device to the default OpenRisc machine. Signed-off-by: Valentin Manea <valentin.ma...@gmail.com> --- default-configs/or32-softmmu.mak | 1 + hw/input/Makefile.objs | 1 + hw/input/lpc32xx.c | 273 +++++++++++++++++++++++++++++++++++++++ hw/openrisc/openrisc_asim.c | 5 + 4 files changed, 280 insertions(+) create mode 100644 hw/input/lpc32xx.c diff --git a/default-configs/or32-softmmu.mak b/default-configs/or32-softmmu.mak index 9c8dad8..e4d63a0 100644 --- a/default-configs/or32-softmmu.mak +++ b/default-configs/or32-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_IDE_MMIO=y CONFIG_FRAMEBUFFER=y CONFIG_OPENCORESFB=y CONFIG_OPENCORESKBD=y +CONFIG_LPC32XX=y diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index 1179055..4e732b5 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -12,3 +12,4 @@ obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o obj-$(CONFIG_TSC210X) += tsc210x.o obj-$(CONFIG_OPENCORESKBD) += ockbd.o +obj-$(CONFIG_LPC32XX) += lpc32xx.o diff --git a/hw/input/lpc32xx.c b/hw/input/lpc32xx.c new file mode 100644 index 0000000..e51e189 --- /dev/null +++ b/hw/input/lpc32xx.c @@ -0,0 +1,273 @@ +/* + * OpenCores framebuffer device + * + * Copyright (c) 2014 Valentin Manea + * Based on work by Sebastian Macke for jor1k http://s-macke.github.io/jor1k/ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "ui/console.h" +#include "ui/input.h" +#include "qemu/timer.h" + +/* + * Touchscreen controller register offsets + */ +#define LPC32XX_TSC_STAT 0x00 +#define LPC32XX_TSC_SEL 0x04 +#define LPC32XX_TSC_CON 0x08 +#define LPC32XX_TSC_FIFO 0x0C +#define LPC32XX_TSC_DTR 0x10 +#define LPC32XX_TSC_RTR 0x14 +#define LPC32XX_TSC_UTR 0x18 +#define LPC32XX_TSC_TTR 0x1C +#define LPC32XX_TSC_DXP 0x20 +#define LPC32XX_TSC_MIN_X 0x24 +#define LPC32XX_TSC_MAX_X 0x28 +#define LPC32XX_TSC_MIN_Y 0x2C +#define LPC32XX_TSC_MAX_Y 0x30 +#define LPC32XX_TSC_AUX_UTR 0x34 +#define LPC32XX_TSC_AUX_MIN 0x38 +#define LPC32XX_TSC_AUX_MAX 0x3C + +#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) +#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) +#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) + +#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) +#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) + +#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) + +#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF +#define LPC32XX_TSC_FIFO_X_VAL(x) (((LPC32XX_TSC_ADCDAT_VALUE_MASK - x) & \ + LPC32XX_TSC_ADCDAT_VALUE_MASK) << 16) +#define LPC32XX_TSC_FIFO_Y_VAL(y) ((LPC32XX_TSC_ADCDAT_VALUE_MASK - y) & \ + LPC32XX_TSC_ADCDAT_VALUE_MASK) + + +#define LPC32XX_TSC_MIN_XY_VAL 0x0 +#define LPC32XX_TSC_MAX_XY_VAL 0x3FF + + +#define TYPE_LPC32XX "lpc32xx" +#define LPC32XX(obj) OBJECT_CHECK(LPC32XXState, (obj), TYPE_LPC32XX) + + +#ifdef DEBUG +#define DPRINTF(fmt, ...) \ + do { printf("lpc32xx: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif + + +typedef struct LPC32XXState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + uint32_t control; + uint32_t status; + bool pressed; + uint32_t move_count; + uint32_t fifo; + int32_t fifo_size; +} LPC32XXState; + +static const VMStateDescription vmstate_lpc32xx = { + .name = "lpc32xx", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, LPC32XXState), + VMSTATE_UINT32(status, LPC32XXState), + VMSTATE_UINT32(move_count, LPC32XXState), + VMSTATE_UINT32(fifo, LPC32XXState), + VMSTATE_INT32(fifo_size, LPC32XXState), + VMSTATE_END_OF_LIST() + } +}; + + +static int lpc32xx_enabled(LPC32XXState *s) +{ + return s->control & LPC32XX_TSC_ADCCON_AUTO_EN; +} + +static uint64_t lpc32xx_read(void *opaque, hwaddr offset, + unsigned size) +{ + LPC32XXState *s = (LPC32XXState *)opaque; + DPRINTF("read at 0x%08X\n", (unsigned int)offset); + switch (offset) { + case LPC32XX_TSC_CON: + return s->control; + case LPC32XX_TSC_STAT: + qemu_irq_lower(s->irq); + return s->status; + case LPC32XX_TSC_FIFO: + if (s->fifo_size <= 0) { + s->status |= LPC32XX_TSC_STAT_FIFO_EMPTY; + } else { + s->fifo_size--; + } + return s->fifo; + } + return 0; +} + +static void lpc32xx_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + LPC32XXState *s = (LPC32XXState *)opaque; + + DPRINTF("write at 0x%08X 0x%08X\n", (uint32_t)offset, (uint32_t)val); + switch (offset) { + case LPC32XX_TSC_CON: + s->control = val; + break; + break; + case LPC32XX_TSC_SEL: + case LPC32XX_TSC_MIN_X: + case LPC32XX_TSC_MAX_X: + case LPC32XX_TSC_MIN_Y: + case LPC32XX_TSC_MAX_Y: + case LPC32XX_TSC_AUX_UTR: + case LPC32XX_TSC_AUX_MIN: + case LPC32XX_TSC_AUX_MAX: + case LPC32XX_TSC_RTR: + case LPC32XX_TSC_DTR: + case LPC32XX_TSC_TTR: + case LPC32XX_TSC_DXP: + case LPC32XX_TSC_UTR: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "lpc32xx: Bad offset %x\n", (int)offset); + } +} + +static void lpc32xx_touchscreen_event(void *opaque, + int x, int y, int z, int buttons_state) +{ + LPC32XXState *s = (LPC32XXState *)opaque; + /* Current driver has LPC32XX_TSC_MAX_XY_VAL hardcoded */ + x = qemu_input_scale_axis(x, INPUT_EVENT_ABS_SIZE, LPC32XX_TSC_MAX_XY_VAL); + y = qemu_input_scale_axis(y, INPUT_EVENT_ABS_SIZE, LPC32XX_TSC_MAX_XY_VAL); + + DPRINTF("event %d %d %d\n", x, y, buttons_state); + if (!lpc32xx_enabled(s)) { + return; + } + + if (!buttons_state) { + /* Finger up */ + if (s->pressed) { + s->status &= ~LPC32XX_TSC_STAT_FIFO_EMPTY; + /* just a button up event */ + s->fifo_size = 0; + s->fifo = LPC32XX_TSC_FIFO_TS_P_LEVEL; + s->pressed = false; + qemu_irq_raise(s->irq); + return; + } + /* Just mouse move */ + else { + return; + } + } + + /* Move */ + if (buttons_state && s->pressed) { + s->move_count++; + /* handle mouse move only every fourth time */ + if (s->move_count & 3) { + return; + } + } + + s->status &= ~LPC32XX_TSC_STAT_FIFO_EMPTY; + s->fifo_size = 4; + s->fifo = LPC32XX_TSC_FIFO_X_VAL(x); + s->fifo |= LPC32XX_TSC_FIFO_Y_VAL(y); + s->pressed = true; + qemu_irq_raise(s->irq); +} + +static const MemoryRegionOps lpc32xx_ops = { + .read = lpc32xx_read, + .write = lpc32xx_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int lpc32xx_initfn(SysBusDevice *sbd) +{ + DeviceState *dev = DEVICE(sbd); + LPC32XXState *s = LPC32XX(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &lpc32xx_ops, + s, "lpc32xx", 0x100); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->irq); + qemu_add_mouse_event_handler(lpc32xx_touchscreen_event, s, 1, + "QEMU LPC32XX-driven Touchscreen"); + + return 0; +} + +static void lpc32xx_init(Object *obj) +{ + LPC32XXState *s = LPC32XX(obj); + s->control = 0x0; + s->status = LPC32XX_TSC_STAT_FIFO_EMPTY; + s->pressed = false; + s->move_count = 0; + s->fifo = 0; + s->fifo_size = 0; +} + +static void lpc32xx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = lpc32xx_initfn; + dc->vmsd = &vmstate_lpc32xx; +} + +static const TypeInfo lpc32xx_info = { + .name = TYPE_LPC32XX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LPC32XXState), + .instance_init = lpc32xx_init, + .class_init = lpc32xx_class_init, +}; + +static void lpc32xx_register_types(void) +{ + type_register_static(&lpc32xx_info); +} + +type_init(lpc32xx_register_types) diff --git a/hw/openrisc/openrisc_asim.c b/hw/openrisc/openrisc_asim.c index 05b59e8..2199a63 100644 --- a/hw/openrisc/openrisc_asim.c +++ b/hw/openrisc/openrisc_asim.c @@ -47,6 +47,7 @@ enum { OR_OPENETH, OR_FRAMEBUFFER, OR_KEYBOARD, + OR_TOUCHSCREEN, }; static hwaddr mem_map[] = { @@ -55,6 +56,7 @@ static hwaddr mem_map[] = { [OR_OPENETH] = 0x92000000, [OR_FRAMEBUFFER] = 0x91000000, [OR_KEYBOARD] = 0x94000000, + [OR_TOUCHSCREEN] = 0x93000000, }; @@ -193,6 +195,9 @@ static void openrisc_sim_init(MachineState *machine) /* OpenCores keyboard */ sysbus_create_simple("ockb", mem_map[OR_KEYBOARD], cpu->env.irq[5]); + /* LPC32XX Touch Screen */ + sysbus_create_simple("lpc32xx", mem_map[OR_TOUCHSCREEN], cpu->env.irq[9]); + cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu); } -- 1.9.1