[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 06/14] AT91 Parallel I/O Controller
From: |
Filip Navara |
Subject: |
[Qemu-devel] [PATCH 06/14] AT91 Parallel I/O Controller |
Date: |
Wed, 15 Jul 09 16:52:21 Central Europe Standard Time |
This patch implements the GPIO controller used in the AT91 microcontrollers.
PIO controller is implemented as a device on the system bus with 3 sets of 32
input/output pins and one IRQ pin. The first set of the 32 I/O pins is used for
connections to external devices and the other two are used for pass-through
connections of external pins to internal peripherals.
Internal pull-up resistors are implemented by simulating the input value of 1
for every input pins where the value of the pin is unknown (a device is not
connected) or where explicit -1 constant was sent by the external device
emulation (a device is connected, but the wire is not) on the GPIO pin.
Unimplemented features are correct emulation of open-drain mode and
glitch-filter.
Signed-off-by: Filip Navara <address@hidden>
---
Makefile.target | 2 +-
hw/at91_pio.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 323 insertions(+), 1 deletions(-)
create mode 100644 hw/at91_pio.c
diff --git a/Makefile.target b/Makefile.target
index 5501371..5b1f35a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -666,7 +666,7 @@ obj-y += framebuffer.o
obj-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-y += syborg_virtio.o
-obj-y += at91_aic.o at91_dbgu.o at91_pmc.o
+obj-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pmc.o
obj-y += gpio_rotary.o gpio_keypad.o
CPPFLAGS += -DHAS_AUDIO
endif
diff --git a/hw/at91_pio.c b/hw/at91_pio.c
new file mode 100644
index 0000000..158f661
--- /dev/null
+++ b/hw/at91_pio.c
@@ -0,0 +1,322 @@
+/*
+ * AT91 Parallel I/O Controller
+ *
+ * Copyright (c) 2009 Filip Navara
+ *
+ * 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.
+ */
+
+/* TODO: Glitch-filter, multi-driver (ie. open drain) */
+
+#include "sysbus.h"
+
+/*
+ * Input/Output GPIO pins:
+ * 32x PIO device
+ * 32x peripheral A
+ * 32x peripheral B
+ */
+
+#define PIO_SIZE 0x200
+#define PIO_PINS 32
+
+#define PIO_PER 0x00 /* PIO Enable Register */
+#define PIO_PDR 0x04 /* PIO Disable Register */
+#define PIO_PSR 0x08 /* PIO Status Register */
+#define PIO_OER 0x10 /* Output Enable Register */
+#define PIO_ODR 0x14 /* Output Disable Register */
+#define PIO_OSR 0x18 /* Output Status Register */
+#define PIO_IFER 0x20 /* Input Filter Enable Register */
+#define PIO_IFDR 0x24 /* Input Filter Disable Register */
+#define PIO_IFSR 0x28 /* Input Filter Status Register */
+#define PIO_SODR 0x30 /* Set Output Data Register */
+#define PIO_CODR 0x34 /* Clear Output Data Register */
+#define PIO_ODSR 0x38 /* Output Data Status Register */
+#define PIO_PDSR 0x3c /* Pin Data Status Register */
+#define PIO_IER 0x40 /* Interrupt Enable Register */
+#define PIO_IDR 0x44 /* Interrupt Disable Register */
+#define PIO_IMR 0x48 /* Interrupt Mask Register */
+#define PIO_ISR 0x4c /* Interrupt Status Register */
+#define PIO_MDER 0x50 /* Multi-driver Enable Register */
+#define PIO_MDDR 0x54 /* Multi-driver Disable Register */
+#define PIO_MDSR 0x58 /* Multi-driver Status Register */
+#define PIO_PPUDR 0x60 /* Pull-up Disable Register */
+#define PIO_PPUER 0x64 /* Pull-up Enable Register */
+#define PIO_PPUSR 0x68 /* Pull-up Status Register */
+#define PIO_ASR 0x70 /* Select A Register */
+#define PIO_BSR 0x74 /* Select B Register */
+#define PIO_ABSR 0x78 /* AB Select Status Register */
+#define PIO_OWER 0xa0 /* Output Write Enable Register */
+#define PIO_OWDR 0xa4 /* Output Write Disable Register */
+#define PIO_OWSR 0xa8 /* Output Write Status Register */
+
+typedef struct PIOState {
+ SysBusDevice busdev;
+ qemu_irq out[PIO_PINS * 3];
+ qemu_irq parent_irq;
+ uint32_t psr;
+ uint32_t osr;
+ uint32_t ifsr;
+ uint32_t odsr;
+ uint32_t pdsr;
+ uint32_t imr;
+ uint32_t isr;
+ uint32_t mdsr;
+ uint32_t ppusr;
+ uint32_t absr;
+ uint32_t owsr;
+ /* Mask of unknown state of PIO pins, needed for pull-up resistor
+ implementation */
+ uint32_t unknown_state;
+} PIOState;
+
+static void at91_pio_set_pin(void *opaque, int pin, int level)
+{
+ PIOState *s = opaque;
+ int mask = 1 << (pin % PIO_PINS);
+ int input_set = pin / PIO_PINS;
+ int output_set = !!(s->absr & mask);
+ uint32_t saved_pdsr;
+
+ if (input_set == 0) {
+ /* PIO pin -> Peripheral / IO */
+
+ /* Skip input if output mode is enabled for the pin */
+ if (s->osr & mask)
+ return;
+
+ if (s->psr & mask) {
+ saved_pdsr = s->pdsr;
+ s->pdsr &= ~mask;
+ if (level == -1) {
+ s->unknown_state |= mask;
+ } else if (level) {
+ s->unknown_state &= ~mask;
+ s->pdsr |= mask;
+ }
+ if (saved_pdsr != s->pdsr) {
+ s->isr |= mask;
+ qemu_set_irq(s->parent_irq, !!(s->isr & s->imr));
+ }
+ } else {
+ qemu_set_irq(s->out[PIO_PINS + (output_set * PIO_PINS)], level);
+ }
+ } else {
+ /* Peripheral -> PIO pin */
+ if ((~s->psr & mask) && input_set == output_set) {
+ qemu_set_irq(s->out[pin & PIO_PINS], level);
+ }
+ }
+}
+
+static uint32_t at91_pio_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ PIOState *s = opaque;
+ int isr;
+
+ offset &= PIO_SIZE - 1;
+ switch (offset) {
+ case PIO_PSR:
+ return s->psr;
+ case PIO_OSR:
+ return s->osr;
+ case PIO_IFSR:
+ return s->ifsr;
+ case PIO_ODSR:
+ return s->odsr;
+ case PIO_PDSR:
+ return
+ (s->pdsr & ~s->unknown_state) |
+ (s->ppusr & s->unknown_state);
+ case PIO_IMR:
+ return s->imr;
+ case PIO_ISR:
+ isr = s->isr;
+ s->isr = 0;
+ qemu_set_irq(s->parent_irq, 0);
+ return isr;
+ case PIO_MDSR:
+ return s->mdsr;
+ case PIO_PPUSR:
+ return s->ppusr;
+ case PIO_ABSR:
+ return s->absr;
+ case PIO_OWSR:
+ return s->owsr;
+ default:
+ return 0;
+ }
+}
+
+static void at91_pio_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ PIOState *s = opaque;
+ int i;
+
+ offset &= PIO_SIZE - 1;
+ switch (offset) {
+ case PIO_PER:
+ s->psr |= value;
+ break;
+ case PIO_PDR:
+ s->psr &= ~value;
+ break;
+ case PIO_OER:
+ s->osr |= value;
+ break;
+ case PIO_ODR:
+ s->osr &= ~value;
+ break;
+ case PIO_IFER:
+ s->ifsr |= value;
+ break;
+ case PIO_IFDR:
+ s->ifsr &= ~value;
+ break;
+ case PIO_SODR:
+ s->odsr |= value;
+ for (i = 0; i < PIO_PINS; i++)
+ if (value & (1 << i) & s->osr)
+ qemu_set_irq(s->out[i], 1);
+ break;
+ case PIO_CODR:
+ s->odsr &= ~value;
+ for (i = 0; i < PIO_PINS; i++)
+ if (value & (1 << i) & s->osr)
+ qemu_set_irq(s->out[i], 0);
+ break;
+ case PIO_ODSR:
+ s->odsr = (s->odsr & ~s->owsr) | (value & s->owsr);
+ for (i = 0; i < PIO_PINS; i++)
+ if (s->owsr & (1 << i))
+ qemu_set_irq(s->out[i], !!(value & (1 << i)));
+ break;
+ case PIO_IER:
+ s->imr |= value;
+ break;
+ case PIO_IDR:
+ s->imr &= ~value;
+ break;
+ case PIO_MDER:
+ s->mdsr |= value;
+ break;
+ case PIO_MDDR:
+ s->mdsr &= ~value;
+ break;
+ case PIO_PPUER:
+ s->ppusr |= value;
+ break;
+ case PIO_PPUDR:
+ s->ppusr &= ~value;
+ break;
+ case PIO_ASR:
+ s->absr &= ~value;
+ break;
+ case PIO_BSR:
+ s->absr |= value;
+ break;
+ case PIO_OWER:
+ s->owsr |= value;
+ break;
+ case PIO_OWDR:
+ s->owsr &= ~value;
+ break;
+ default:
+ return;
+ }
+}
+
+static CPUReadMemoryFunc *at91_pio_readfn[] = {
+ at91_pio_mem_read,
+ at91_pio_mem_read,
+ at91_pio_mem_read,
+};
+
+static CPUWriteMemoryFunc *at91_pio_writefn[] = {
+ at91_pio_mem_write,
+ at91_pio_mem_write,
+ at91_pio_mem_write,
+};
+
+static void at91_pio_save(QEMUFile *f, void *opaque)
+{
+ PIOState *s = opaque;
+
+ qemu_put_be32(f, s->psr);
+ qemu_put_be32(f, s->osr);
+ qemu_put_be32(f, s->ifsr);
+ qemu_put_be32(f, s->odsr);
+ qemu_put_be32(f, s->pdsr);
+ qemu_put_be32(f, s->imr);
+ qemu_put_be32(f, s->isr);
+ qemu_put_be32(f, s->mdsr);
+ qemu_put_be32(f, s->ppusr);
+ qemu_put_be32(f, s->absr);
+ qemu_put_be32(f, s->owsr);
+ qemu_put_be32(f, s->unknown_state);
+}
+
+static int at91_pio_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PIOState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->psr = qemu_get_be32(f);
+ s->osr = qemu_get_be32(f);
+ s->ifsr = qemu_get_be32(f);
+ s->odsr = qemu_get_be32(f);
+ s->pdsr = qemu_get_be32(f);
+ s->imr = qemu_get_be32(f);
+ s->isr = qemu_get_be32(f);
+ s->mdsr = qemu_get_be32(f);
+ s->ppusr = qemu_get_be32(f);
+ s->absr = qemu_get_be32(f);
+ s->owsr = qemu_get_be32(f);
+ s->unknown_state = qemu_get_be32(f);
+
+ return 0;
+}
+
+static void at91_pio_init(SysBusDevice *dev)
+{
+ PIOState *s = FROM_SYSBUS(typeof (*s), dev);
+ int pio_regs;
+
+ s->psr = 0xffffffff;
+ s->unknown_state = 0xffffffff;
+
+ sysbus_init_irq(dev, &s->parent_irq);
+ qdev_init_gpio_in(&dev->qdev, at91_pio_set_pin, PIO_PINS * 3);
+ qdev_init_gpio_out(&dev->qdev, s->out, PIO_PINS * 3);
+
+ pio_regs = cpu_register_io_memory(at91_pio_readfn, at91_pio_writefn, s);
+ sysbus_init_mmio(dev, PIO_SIZE, pio_regs);
+
+ register_savevm("at91_pio", -1, 1, at91_pio_save, at91_pio_load, s);
+}
+
+static void at91_pio_register(void)
+{
+ sysbus_register_dev("at91,pio", sizeof(PIOState), at91_pio_init);
+}
+
+device_init(at91_pio_register)
--
1.6.3.msysgit.0
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH 06/14] AT91 Parallel I/O Controller,
Filip Navara <=