Commit ef3a8a3471ae46c58733102a48259226f7337287

Authored by Filip Navara
1 parent 266ff6da

AT91 Parallel I/O Controller

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 <filip.navara@gmail.com>
Showing 2 changed files with 341 additions and 1 deletions
Makefile.target
... ... @@ -431,7 +431,7 @@ obj-arm-y += framebuffer.o
431 431 obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
432 432 obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
433 433 obj-arm-y += syborg_virtio.o
434   -obj-arm-y += at91_aic.o at91_dbgu.o at91_pmc.o
  434 +obj-arm-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pmc.o
435 435 obj-arm-y += gpio_rotary.o gpio_keypad.o
436 436  
437 437 ifeq ($(TARGET_BASE_ARCH), arm)
... ...
hw/at91_pio.c 0 → 100644
  1 +/*
  2 + * AT91 Parallel I/O Controller
  3 + *
  4 + * Copyright (c) 2009 Filip Navara
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +/* TODO: Glitch-filter, multi-driver (ie. open drain) */
  26 +
  27 +#include "sysbus.h"
  28 +
  29 +/*
  30 + * Input/Output GPIO pins:
  31 + * 32x PIO device
  32 + * 32x peripheral A
  33 + * 32x peripheral B
  34 + */
  35 +
  36 +#define PIO_SIZE 0x200
  37 +#define PIO_PINS 32
  38 +
  39 +#define PIO_PER 0x00 /* PIO Enable Register */
  40 +#define PIO_PDR 0x04 /* PIO Disable Register */
  41 +#define PIO_PSR 0x08 /* PIO Status Register */
  42 +#define PIO_OER 0x10 /* Output Enable Register */
  43 +#define PIO_ODR 0x14 /* Output Disable Register */
  44 +#define PIO_OSR 0x18 /* Output Status Register */
  45 +#define PIO_IFER 0x20 /* Input Filter Enable Register */
  46 +#define PIO_IFDR 0x24 /* Input Filter Disable Register */
  47 +#define PIO_IFSR 0x28 /* Input Filter Status Register */
  48 +#define PIO_SODR 0x30 /* Set Output Data Register */
  49 +#define PIO_CODR 0x34 /* Clear Output Data Register */
  50 +#define PIO_ODSR 0x38 /* Output Data Status Register */
  51 +#define PIO_PDSR 0x3c /* Pin Data Status Register */
  52 +#define PIO_IER 0x40 /* Interrupt Enable Register */
  53 +#define PIO_IDR 0x44 /* Interrupt Disable Register */
  54 +#define PIO_IMR 0x48 /* Interrupt Mask Register */
  55 +#define PIO_ISR 0x4c /* Interrupt Status Register */
  56 +#define PIO_MDER 0x50 /* Multi-driver Enable Register */
  57 +#define PIO_MDDR 0x54 /* Multi-driver Disable Register */
  58 +#define PIO_MDSR 0x58 /* Multi-driver Status Register */
  59 +#define PIO_PPUDR 0x60 /* Pull-up Disable Register */
  60 +#define PIO_PPUER 0x64 /* Pull-up Enable Register */
  61 +#define PIO_PPUSR 0x68 /* Pull-up Status Register */
  62 +#define PIO_ASR 0x70 /* Select A Register */
  63 +#define PIO_BSR 0x74 /* Select B Register */
  64 +#define PIO_ABSR 0x78 /* AB Select Status Register */
  65 +#define PIO_OWER 0xa0 /* Output Write Enable Register */
  66 +#define PIO_OWDR 0xa4 /* Output Write Disable Register */
  67 +#define PIO_OWSR 0xa8 /* Output Write Status Register */
  68 +
  69 +typedef struct PIOState {
  70 + SysBusDevice busdev;
  71 + qemu_irq out[PIO_PINS * 3];
  72 + qemu_irq parent_irq;
  73 + uint32_t psr;
  74 + uint32_t osr;
  75 + uint32_t ifsr;
  76 + uint32_t odsr;
  77 + uint32_t pdsr;
  78 + uint32_t imr;
  79 + uint32_t isr;
  80 + uint32_t mdsr;
  81 + uint32_t ppusr;
  82 + uint32_t absr;
  83 + uint32_t owsr;
  84 + /* Mask of unknown state of PIO pins, needed for pull-up resistor
  85 + implementation */
  86 + uint32_t unknown_state;
  87 +} PIOState;
  88 +
  89 +static void at91_pio_set_pin(void *opaque, int pin, int level)
  90 +{
  91 + PIOState *s = opaque;
  92 + int mask = 1 << (pin % PIO_PINS);
  93 + int input_set = pin / PIO_PINS;
  94 + int output_set = !!(s->absr & mask);
  95 + uint32_t saved_pdsr;
  96 +
  97 + if (input_set == 0) {
  98 + /* PIO pin -> Peripheral / IO */
  99 +
  100 + /* Skip input if output mode is enabled for the pin */
  101 + if (s->osr & mask)
  102 + return;
  103 +
  104 + if (s->psr & mask) {
  105 + saved_pdsr = s->pdsr;
  106 + s->pdsr &= ~mask;
  107 + if (level == -1) {
  108 + s->unknown_state |= mask;
  109 + } else if (level) {
  110 + s->unknown_state &= ~mask;
  111 + s->pdsr |= mask;
  112 + }
  113 + if (saved_pdsr != s->pdsr) {
  114 + s->isr |= mask;
  115 + qemu_set_irq(s->parent_irq, !!(s->isr & s->imr));
  116 + }
  117 + } else {
  118 + qemu_set_irq(s->out[PIO_PINS + (output_set * PIO_PINS)], level);
  119 + }
  120 + } else {
  121 + /* Peripheral -> PIO pin */
  122 + if ((~s->psr & mask) && input_set == output_set) {
  123 + qemu_set_irq(s->out[pin & PIO_PINS], level);
  124 + }
  125 + }
  126 +}
  127 +
  128 +static uint32_t at91_pio_mem_read(void *opaque, target_phys_addr_t offset)
  129 +{
  130 + PIOState *s = opaque;
  131 + int isr;
  132 +
  133 + offset &= PIO_SIZE - 1;
  134 + switch (offset) {
  135 + case PIO_PSR:
  136 + return s->psr;
  137 + case PIO_OSR:
  138 + return s->osr;
  139 + case PIO_IFSR:
  140 + return s->ifsr;
  141 + case PIO_ODSR:
  142 + return s->odsr;
  143 + case PIO_PDSR:
  144 + return
  145 + (s->pdsr & ~s->unknown_state) |
  146 + (s->ppusr & s->unknown_state);
  147 + case PIO_IMR:
  148 + return s->imr;
  149 + case PIO_ISR:
  150 + isr = s->isr;
  151 + s->isr = 0;
  152 + qemu_set_irq(s->parent_irq, 0);
  153 + return isr;
  154 + case PIO_MDSR:
  155 + return s->mdsr;
  156 + case PIO_PPUSR:
  157 + return s->ppusr;
  158 + case PIO_ABSR:
  159 + return s->absr;
  160 + case PIO_OWSR:
  161 + return s->owsr;
  162 + default:
  163 + return 0;
  164 + }
  165 +}
  166 +
  167 +static void at91_pio_mem_write(void *opaque, target_phys_addr_t offset,
  168 + uint32_t value)
  169 +{
  170 + PIOState *s = opaque;
  171 + int i;
  172 +
  173 + offset &= PIO_SIZE - 1;
  174 + switch (offset) {
  175 + case PIO_PER:
  176 + s->psr |= value;
  177 + break;
  178 + case PIO_PDR:
  179 + s->psr &= ~value;
  180 + break;
  181 + case PIO_OER:
  182 + s->osr |= value;
  183 + break;
  184 + case PIO_ODR:
  185 + s->osr &= ~value;
  186 + break;
  187 + case PIO_IFER:
  188 + s->ifsr |= value;
  189 + break;
  190 + case PIO_IFDR:
  191 + s->ifsr &= ~value;
  192 + break;
  193 + case PIO_SODR:
  194 + s->odsr |= value;
  195 + for (i = 0; i < PIO_PINS; i++)
  196 + if (value & (1 << i) & s->osr)
  197 + qemu_set_irq(s->out[i], 1);
  198 + break;
  199 + case PIO_CODR:
  200 + s->odsr &= ~value;
  201 + for (i = 0; i < PIO_PINS; i++)
  202 + if (value & (1 << i) & s->osr)
  203 + qemu_set_irq(s->out[i], 0);
  204 + break;
  205 + case PIO_ODSR:
  206 + s->odsr = (s->odsr & ~s->owsr) | (value & s->owsr);
  207 + for (i = 0; i < PIO_PINS; i++)
  208 + if (s->owsr & (1 << i))
  209 + qemu_set_irq(s->out[i], !!(value & (1 << i)));
  210 + break;
  211 + case PIO_IER:
  212 + s->imr |= value;
  213 + break;
  214 + case PIO_IDR:
  215 + s->imr &= ~value;
  216 + break;
  217 + case PIO_MDER:
  218 + s->mdsr |= value;
  219 + break;
  220 + case PIO_MDDR:
  221 + s->mdsr &= ~value;
  222 + break;
  223 + case PIO_PPUER:
  224 + s->ppusr |= value;
  225 + break;
  226 + case PIO_PPUDR:
  227 + s->ppusr &= ~value;
  228 + break;
  229 + case PIO_ASR:
  230 + s->absr &= ~value;
  231 + break;
  232 + case PIO_BSR:
  233 + s->absr |= value;
  234 + break;
  235 + case PIO_OWER:
  236 + s->owsr |= value;
  237 + break;
  238 + case PIO_OWDR:
  239 + s->owsr &= ~value;
  240 + break;
  241 + default:
  242 + return;
  243 + }
  244 +}
  245 +
  246 +static CPUReadMemoryFunc *at91_pio_readfn[] = {
  247 + at91_pio_mem_read,
  248 + at91_pio_mem_read,
  249 + at91_pio_mem_read,
  250 +};
  251 +
  252 +static CPUWriteMemoryFunc *at91_pio_writefn[] = {
  253 + at91_pio_mem_write,
  254 + at91_pio_mem_write,
  255 + at91_pio_mem_write,
  256 +};
  257 +
  258 +static void at91_pio_save(QEMUFile *f, void *opaque)
  259 +{
  260 + PIOState *s = opaque;
  261 +
  262 + qemu_put_be32(f, s->psr);
  263 + qemu_put_be32(f, s->osr);
  264 + qemu_put_be32(f, s->ifsr);
  265 + qemu_put_be32(f, s->odsr);
  266 + qemu_put_be32(f, s->pdsr);
  267 + qemu_put_be32(f, s->imr);
  268 + qemu_put_be32(f, s->isr);
  269 + qemu_put_be32(f, s->mdsr);
  270 + qemu_put_be32(f, s->ppusr);
  271 + qemu_put_be32(f, s->absr);
  272 + qemu_put_be32(f, s->owsr);
  273 + qemu_put_be32(f, s->unknown_state);
  274 +}
  275 +
  276 +static int at91_pio_load(QEMUFile *f, void *opaque, int version_id)
  277 +{
  278 + PIOState *s = opaque;
  279 +
  280 + if (version_id != 1)
  281 + return -EINVAL;
  282 +
  283 + s->psr = qemu_get_be32(f);
  284 + s->osr = qemu_get_be32(f);
  285 + s->ifsr = qemu_get_be32(f);
  286 + s->odsr = qemu_get_be32(f);
  287 + s->pdsr = qemu_get_be32(f);
  288 + s->imr = qemu_get_be32(f);
  289 + s->isr = qemu_get_be32(f);
  290 + s->mdsr = qemu_get_be32(f);
  291 + s->ppusr = qemu_get_be32(f);
  292 + s->absr = qemu_get_be32(f);
  293 + s->owsr = qemu_get_be32(f);
  294 + s->unknown_state = qemu_get_be32(f);
  295 +
  296 + return 0;
  297 +}
  298 +
  299 +static void at91_pio_reset(void *opaque)
  300 +{
  301 + PIOState *s = opaque;
  302 +
  303 + s->psr = 0xffffffff;
  304 + s->osr = 0;
  305 + s->ifsr = 0;
  306 + s->odsr = 0;
  307 + s->pdsr = 0;
  308 + s->imr = 0;
  309 + s->isr = 0;
  310 + s->mdsr = 0;
  311 + s->ppusr = 0;
  312 + s->absr = 0;
  313 + s->owsr = 0;
  314 + s->unknown_state = 0xffffffff;
  315 +}
  316 +
  317 +static void at91_pio_init(SysBusDevice *dev)
  318 +{
  319 + PIOState *s = FROM_SYSBUS(typeof (*s), dev);
  320 + int pio_regs;
  321 +
  322 + sysbus_init_irq(dev, &s->parent_irq);
  323 + qdev_init_gpio_in(&dev->qdev, at91_pio_set_pin, PIO_PINS * 3);
  324 + qdev_init_gpio_out(&dev->qdev, s->out, PIO_PINS * 3);
  325 +
  326 + pio_regs = cpu_register_io_memory(at91_pio_readfn, at91_pio_writefn, s);
  327 + sysbus_init_mmio(dev, PIO_SIZE, pio_regs);
  328 +
  329 + at91_pio_reset(s);
  330 + qemu_register_reset(at91_pio_reset, s);
  331 +
  332 + register_savevm("at91_pio", -1, 1, at91_pio_save, at91_pio_load, s);
  333 +}
  334 +
  335 +static void at91_pio_register(void)
  336 +{
  337 + sysbus_register_dev("at91,pio", sizeof(PIOState), at91_pio_init);
  338 +}
  339 +
  340 +device_init(at91_pio_register)
... ...