qemu/hw/gpio/mpc8xxx.c
<<
>>
Prefs
   1/*
   2 *  GPIO Controller for a lot of Freescale SoCs
   3 *
   4 * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
   5 *
   6 * Author: Alexander Graf, <agraf@suse.de>
   7 *
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation; either
  11 * version 2 of the License, or (at your option) any later version.
  12 *
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU Lesser General Public
  19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  20 */
  21
  22#include "hw/sysbus.h"
  23
  24#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
  25#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO)
  26
  27typedef struct MPC8XXXGPIOState {
  28    SysBusDevice parent_obj;
  29
  30    MemoryRegion iomem;
  31    qemu_irq irq;
  32    qemu_irq out[32];
  33
  34    uint32_t dir;
  35    uint32_t odr;
  36    uint32_t dat;
  37    uint32_t ier;
  38    uint32_t imr;
  39    uint32_t icr;
  40} MPC8XXXGPIOState;
  41
  42static const VMStateDescription vmstate_mpc8xxx_gpio = {
  43    .name = "mpc8xxx_gpio",
  44    .version_id = 1,
  45    .minimum_version_id = 1,
  46    .fields = (VMStateField[]) {
  47        VMSTATE_UINT32(dir, MPC8XXXGPIOState),
  48        VMSTATE_UINT32(odr, MPC8XXXGPIOState),
  49        VMSTATE_UINT32(dat, MPC8XXXGPIOState),
  50        VMSTATE_UINT32(ier, MPC8XXXGPIOState),
  51        VMSTATE_UINT32(imr, MPC8XXXGPIOState),
  52        VMSTATE_UINT32(icr, MPC8XXXGPIOState),
  53        VMSTATE_END_OF_LIST()
  54    }
  55};
  56
  57static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
  58{
  59    qemu_set_irq(s->irq, !!(s->ier & s->imr));
  60}
  61
  62static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
  63                                  unsigned size)
  64{
  65    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
  66
  67    if (size != 4) {
  68        /* All registers are 32bit */
  69        return 0;
  70    }
  71
  72    switch (offset) {
  73    case 0x0: /* Direction */
  74        return s->dir;
  75    case 0x4: /* Open Drain */
  76        return s->odr;
  77    case 0x8: /* Data */
  78        return s->dat;
  79    case 0xC: /* Interrupt Event */
  80        return s->ier;
  81    case 0x10: /* Interrupt Mask */
  82        return s->imr;
  83    case 0x14: /* Interrupt Control */
  84        return s->icr;
  85    default:
  86        return 0;
  87    }
  88}
  89
  90static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
  91{
  92    uint32_t old_data = s->dat;
  93    uint32_t diff = old_data ^ new_data;
  94    int i;
  95
  96    for (i = 0; i < 32; i++) {
  97        uint32_t mask = 0x80000000 >> i;
  98        if (!(diff & mask)) {
  99            continue;
 100        }
 101
 102        if (s->dir & mask) {
 103            /* Output */
 104            qemu_set_irq(s->out[i], (new_data & mask) != 0);
 105        }
 106    }
 107
 108    s->dat = new_data;
 109}
 110
 111static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
 112                        uint64_t value, unsigned size)
 113{
 114    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
 115
 116    if (size != 4) {
 117        /* All registers are 32bit */
 118        return;
 119    }
 120
 121    switch (offset) {
 122    case 0x0: /* Direction */
 123        s->dir = value;
 124        break;
 125    case 0x4: /* Open Drain */
 126        s->odr = value;
 127        break;
 128    case 0x8: /* Data */
 129        mpc8xxx_write_data(s, value);
 130        break;
 131    case 0xC: /* Interrupt Event */
 132        s->ier &= ~value;
 133        break;
 134    case 0x10: /* Interrupt Mask */
 135        s->imr = value;
 136        break;
 137    case 0x14: /* Interrupt Control */
 138        s->icr = value;
 139        break;
 140    }
 141
 142    mpc8xxx_gpio_update(s);
 143}
 144
 145static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s)
 146{
 147    s->dir = 0;
 148    s->odr = 0;
 149    s->dat = 0;
 150    s->ier = 0;
 151    s->imr = 0;
 152    s->icr = 0;
 153}
 154
 155static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
 156{
 157    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
 158    uint32_t mask;
 159
 160    mask = 0x80000000 >> irq;
 161    if ((s->dir & mask) == 0) {
 162        uint32_t old_value = s->dat & mask;
 163
 164        s->dat &= ~mask;
 165        if (level)
 166            s->dat |= mask;
 167
 168        if (!(s->icr & irq) || (old_value && !level)) {
 169            s->ier |= mask;
 170        }
 171
 172        mpc8xxx_gpio_update(s);
 173    }
 174}
 175
 176static const MemoryRegionOps mpc8xxx_gpio_ops = {
 177    .read = mpc8xxx_gpio_read,
 178    .write = mpc8xxx_gpio_write,
 179    .endianness = DEVICE_BIG_ENDIAN,
 180};
 181
 182static int mpc8xxx_gpio_initfn(SysBusDevice *sbd)
 183{
 184    DeviceState *dev = DEVICE(sbd);
 185    MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
 186
 187    memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000);
 188    sysbus_init_mmio(sbd, &s->iomem);
 189    sysbus_init_irq(sbd, &s->irq);
 190    qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
 191    qdev_init_gpio_out(dev, s->out, 32);
 192    mpc8xxx_gpio_reset(s);
 193    return 0;
 194}
 195
 196static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
 197{
 198    DeviceClass *dc = DEVICE_CLASS(klass);
 199    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 200
 201    k->init = mpc8xxx_gpio_initfn;
 202    dc->vmsd = &vmstate_mpc8xxx_gpio;
 203}
 204
 205static const TypeInfo mpc8xxx_gpio_info = {
 206    .name          = TYPE_MPC8XXX_GPIO,
 207    .parent        = TYPE_SYS_BUS_DEVICE,
 208    .instance_size = sizeof(MPC8XXXGPIOState),
 209    .class_init    = mpc8xxx_gpio_class_init,
 210};
 211
 212static void mpc8xxx_gpio_register_types(void)
 213{
 214    type_register_static(&mpc8xxx_gpio_info);
 215}
 216
 217type_init(mpc8xxx_gpio_register_types)
 218