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.1 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 "qemu/osdep.h"
  23#include "hw/irq.h"
  24#include "hw/sysbus.h"
  25#include "migration/vmstate.h"
  26#include "qemu/module.h"
  27#include "qom/object.h"
  28
  29#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
  30OBJECT_DECLARE_SIMPLE_TYPE(MPC8XXXGPIOState, MPC8XXX_GPIO)
  31
  32struct MPC8XXXGPIOState {
  33    SysBusDevice parent_obj;
  34
  35    MemoryRegion iomem;
  36    qemu_irq irq;
  37    qemu_irq out[32];
  38
  39    uint32_t dir;
  40    uint32_t odr;
  41    uint32_t dat;
  42    uint32_t ier;
  43    uint32_t imr;
  44    uint32_t icr;
  45};
  46
  47static const VMStateDescription vmstate_mpc8xxx_gpio = {
  48    .name = "mpc8xxx_gpio",
  49    .version_id = 1,
  50    .minimum_version_id = 1,
  51    .fields = (VMStateField[]) {
  52        VMSTATE_UINT32(dir, MPC8XXXGPIOState),
  53        VMSTATE_UINT32(odr, MPC8XXXGPIOState),
  54        VMSTATE_UINT32(dat, MPC8XXXGPIOState),
  55        VMSTATE_UINT32(ier, MPC8XXXGPIOState),
  56        VMSTATE_UINT32(imr, MPC8XXXGPIOState),
  57        VMSTATE_UINT32(icr, MPC8XXXGPIOState),
  58        VMSTATE_END_OF_LIST()
  59    }
  60};
  61
  62static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
  63{
  64    qemu_set_irq(s->irq, !!(s->ier & s->imr));
  65}
  66
  67static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
  68                                  unsigned size)
  69{
  70    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
  71
  72    if (size != 4) {
  73        /* All registers are 32bit */
  74        return 0;
  75    }
  76
  77    switch (offset) {
  78    case 0x0: /* Direction */
  79        return s->dir;
  80    case 0x4: /* Open Drain */
  81        return s->odr;
  82    case 0x8: /* Data */
  83        return s->dat;
  84    case 0xC: /* Interrupt Event */
  85        return s->ier;
  86    case 0x10: /* Interrupt Mask */
  87        return s->imr;
  88    case 0x14: /* Interrupt Control */
  89        return s->icr;
  90    default:
  91        return 0;
  92    }
  93}
  94
  95static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
  96{
  97    uint32_t old_data = s->dat;
  98    uint32_t diff = old_data ^ new_data;
  99    int i;
 100
 101    for (i = 0; i < 32; i++) {
 102        uint32_t mask = 0x80000000 >> i;
 103        if (!(diff & mask)) {
 104            continue;
 105        }
 106
 107        if (s->dir & mask) {
 108            /* Output */
 109            qemu_set_irq(s->out[i], (new_data & mask) != 0);
 110        }
 111    }
 112
 113    s->dat = new_data;
 114}
 115
 116static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
 117                        uint64_t value, unsigned size)
 118{
 119    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
 120
 121    if (size != 4) {
 122        /* All registers are 32bit */
 123        return;
 124    }
 125
 126    switch (offset) {
 127    case 0x0: /* Direction */
 128        s->dir = value;
 129        break;
 130    case 0x4: /* Open Drain */
 131        s->odr = value;
 132        break;
 133    case 0x8: /* Data */
 134        mpc8xxx_write_data(s, value);
 135        break;
 136    case 0xC: /* Interrupt Event */
 137        s->ier &= ~value;
 138        break;
 139    case 0x10: /* Interrupt Mask */
 140        s->imr = value;
 141        break;
 142    case 0x14: /* Interrupt Control */
 143        s->icr = value;
 144        break;
 145    }
 146
 147    mpc8xxx_gpio_update(s);
 148}
 149
 150static void mpc8xxx_gpio_reset(DeviceState *dev)
 151{
 152    MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
 153
 154    s->dir = 0;
 155    s->odr = 0;
 156    s->dat = 0;
 157    s->ier = 0;
 158    s->imr = 0;
 159    s->icr = 0;
 160}
 161
 162static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
 163{
 164    MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
 165    uint32_t mask;
 166
 167    mask = 0x80000000 >> irq;
 168    if ((s->dir & mask) == 0) {
 169        uint32_t old_value = s->dat & mask;
 170
 171        s->dat &= ~mask;
 172        if (level)
 173            s->dat |= mask;
 174
 175        if (!(s->icr & irq) || (old_value && !level)) {
 176            s->ier |= mask;
 177        }
 178
 179        mpc8xxx_gpio_update(s);
 180    }
 181}
 182
 183static const MemoryRegionOps mpc8xxx_gpio_ops = {
 184    .read = mpc8xxx_gpio_read,
 185    .write = mpc8xxx_gpio_write,
 186    .endianness = DEVICE_BIG_ENDIAN,
 187};
 188
 189static void mpc8xxx_gpio_initfn(Object *obj)
 190{
 191    DeviceState *dev = DEVICE(obj);
 192    MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj);
 193    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 194
 195    memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops,
 196                          s, "mpc8xxx_gpio", 0x1000);
 197    sysbus_init_mmio(sbd, &s->iomem);
 198    sysbus_init_irq(sbd, &s->irq);
 199    qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
 200    qdev_init_gpio_out(dev, s->out, 32);
 201}
 202
 203static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
 204{
 205    DeviceClass *dc = DEVICE_CLASS(klass);
 206
 207    dc->vmsd = &vmstate_mpc8xxx_gpio;
 208    dc->reset = mpc8xxx_gpio_reset;
 209}
 210
 211static const TypeInfo mpc8xxx_gpio_info = {
 212    .name          = TYPE_MPC8XXX_GPIO,
 213    .parent        = TYPE_SYS_BUS_DEVICE,
 214    .instance_size = sizeof(MPC8XXXGPIOState),
 215    .instance_init = mpc8xxx_gpio_initfn,
 216    .class_init    = mpc8xxx_gpio_class_init,
 217};
 218
 219static void mpc8xxx_gpio_register_types(void)
 220{
 221    type_register_static(&mpc8xxx_gpio_info);
 222}
 223
 224type_init(mpc8xxx_gpio_register_types)
 225