From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MR5qO-0006ZO-Ik for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:28 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MR5qK-0006Tz-IT for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:28 -0400 Received: from [199.232.76.173] (port=53475 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MR5qK-0006Tb-B8 for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:24 -0400 Received: from [82.113.48.144] (port=4816 helo=FilipNavara-PC) by monty-python.gnu.org with smtp (Exim 4.60) (envelope-from ) id 1MR5qJ-0000bC-EV for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:24 -0400 Date: Wed, 15 Jul 09 16:52:21 Central Europe Standard Time From: Filip Navara Sender: Filip Navara MIME-Version: 1.0 Content-Type: text/plain; Message-Id: Subject: [Qemu-devel] [PATCH 06/14] AT91 Parallel I/O Controller List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org 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 --- 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