qemu/hw/gpio/xilinx_iomod_gpi.c
<<
>>
Prefs
   1/*
   2 * QEMU model of Xilinx I/O Module GPI
   3 *
   4 * Copyright (c) 2013 Xilinx Inc
   5 * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "hw/sysbus.h"
  28#include "hw/register.h"
  29#include "qemu/log.h"
  30
  31#include "hw/fdt_generic_util.h"
  32
  33#ifndef XILINX_IO_MODULE_GPI_ERR_DEBUG
  34#define XILINX_IO_MODULE_GPI_ERR_DEBUG 0
  35#endif
  36
  37#define TYPE_XILINX_IO_MODULE_GPI "xlnx.io_gpi"
  38
  39#define XILINX_IO_MODULE_GPI(obj) \
  40     OBJECT_CHECK(XilinxGPI, (obj), TYPE_XILINX_IO_MODULE_GPI)
  41
  42#define R_IOM_GPI                   (0x00 / 4)
  43#define R_MAX                       (1)
  44
  45typedef struct XilinxGPI {
  46    SysBusDevice parent_obj;
  47    MemoryRegion iomem;
  48    qemu_irq parent_irq;
  49    /* Interrupt Enable */
  50    uint32_t ien;
  51
  52    struct {
  53        bool use;
  54        bool interrupt;
  55        uint32_t size;
  56    } cfg;
  57    uint32_t regs[R_MAX];
  58    RegisterInfo regs_info[R_MAX];
  59    const char *prefix;
  60} XilinxGPI;
  61
  62static Property xlx_iom_properties[] = {
  63    DEFINE_PROP_BOOL("use-gpi", XilinxGPI, cfg.use, 0),
  64    DEFINE_PROP_BOOL("gpi-interrupt", XilinxGPI, cfg.interrupt, 0),
  65    DEFINE_PROP_UINT32("gpi-size", XilinxGPI, cfg.size, 0),
  66    DEFINE_PROP_END_OF_LIST(),
  67};
  68
  69static const MemoryRegionOps iom_gpi_ops = {
  70    .read = register_read_memory_le,
  71    .write = register_write_memory_le,
  72    .endianness = DEVICE_LITTLE_ENDIAN,
  73    .valid = {
  74        .min_access_size = 4,
  75        .max_access_size = 4,
  76    },
  77};
  78
  79static void irq_handler(void *opaque, int irq, int level)
  80{
  81    XilinxGPI *s = XILINX_IO_MODULE_GPI(opaque);
  82    uint32_t old = s->regs[R_IOM_GPI];
  83
  84    /* If enable is set for @irq pin, update @irq pin in GPI and
  85     * trigger interrupt if transition is 0->1 */
  86    if (s->ien & (1 << irq)) {
  87        s->regs[R_IOM_GPI] &= ~(1 << irq);
  88        s->regs[R_IOM_GPI] |= level << irq;
  89        /* On input pin transition 0->1 trigger interrupt. */
  90        if ((old != s->regs[R_IOM_GPI]) && level) {
  91            qemu_irq_pulse(s->parent_irq);
  92        }
  93    }
  94}
  95
  96
  97static void named_irq_handler(void *opaque, int n, int level)
  98{
  99    XilinxGPI *s  = XILINX_IO_MODULE_GPI(opaque);
 100    irq_handler(s, n, level);
 101}
 102
 103/* Called when someone writes into LOCAL GPIx_ENABLE */
 104static void ien_handler(void *opaque, int n, int level)
 105{
 106    XilinxGPI *s = XILINX_IO_MODULE_GPI(opaque);
 107
 108    s->ien = level;
 109    /* Clear all GPIs that got disabled */
 110    s->regs[R_IOM_GPI] &= s->ien;
 111}
 112
 113static const RegisterAccessInfo gpi_regs_info[] = {
 114    [R_IOM_GPI] = { .name = "GPI", .ro = ~0 },
 115};
 116
 117static void iom_gpi_reset(DeviceState *dev)
 118{
 119    XilinxGPI *s = XILINX_IO_MODULE_GPI(dev);
 120    unsigned int i;
 121
 122    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 123        register_reset(&s->regs_info[i]);
 124    }
 125    /* Disable all interrupts initially. */
 126    s->ien = 0;
 127}
 128
 129static void xlx_iom_realize(DeviceState *dev, Error **errp)
 130{
 131    XilinxGPI *s = XILINX_IO_MODULE_GPI(dev);
 132    unsigned int i;
 133    s->prefix = object_get_canonical_path(OBJECT(dev));
 134
 135    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 136        RegisterInfo *r = &s->regs_info[i];
 137
 138        *r = (RegisterInfo) {
 139            .data = (uint8_t *)&s->regs[i],
 140            .data_size = sizeof(uint32_t),
 141            .access = &gpi_regs_info[i],
 142            .debug = XILINX_IO_MODULE_GPI_ERR_DEBUG,
 143            .prefix = s->prefix,
 144            .opaque = s,
 145        };
 146        memory_region_init_io(&r->mem, OBJECT(dev), &iom_gpi_ops, r,
 147                              r->access->name, 4);
 148        memory_region_add_subregion(&s->iomem, i * 4, &r->mem);
 149    }
 150
 151    assert(s->cfg.size <= 32);
 152    /* FIXME: Leave the std ones for qtest. Add a qtest way to name
 153       the GPIO namespace.  */
 154    qdev_init_gpio_in(dev, irq_handler, s->cfg.size);
 155    qdev_init_gpio_in_named(dev, named_irq_handler, "GPI", 32);
 156    qdev_init_gpio_in_named(dev, ien_handler, "IEN", 32);
 157}
 158
 159static void xlx_iom_init(Object *obj)
 160{
 161    XilinxGPI *s = XILINX_IO_MODULE_GPI(obj);
 162    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 163
 164    memory_region_init_io(&s->iomem, obj, &iom_gpi_ops, s,
 165                          TYPE_XILINX_IO_MODULE_GPI,
 166                          R_MAX * 4);
 167    sysbus_init_mmio(sbd, &s->iomem);
 168    sysbus_init_irq(sbd, &s->parent_irq);
 169}
 170
 171static const VMStateDescription vmstate_xlx_iom = {
 172    .name = TYPE_XILINX_IO_MODULE_GPI,
 173    .version_id = 1,
 174    .minimum_version_id = 1,
 175    .minimum_version_id_old = 1,
 176    .fields = (VMStateField[]) {
 177        VMSTATE_END_OF_LIST(),
 178    }
 179};
 180
 181
 182static const FDTGenericGPIOSet gpio_sets [] = {
 183    {
 184      .names = &fdt_generic_gpio_name_set_gpio,
 185      .gpios = (FDTGenericGPIOConnection[]) {
 186        { .name = "GPI", .fdt_index = 0, .range = 32 },
 187        { },
 188      },
 189    },
 190    { },
 191};
 192
 193static const FDTGenericGPIOSet gpio_client_sets[] = {
 194    {
 195      .names = &fdt_generic_gpio_name_set_gpio,
 196      .gpios = (FDTGenericGPIOConnection[]) {
 197        { .name = "IEN", .fdt_index = 0 },
 198        { },
 199      },
 200    },
 201    { },
 202};
 203
 204static void xlx_iom_class_init(ObjectClass *klass, void *data)
 205{
 206    DeviceClass *dc = DEVICE_CLASS(klass);
 207    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);
 208
 209    dc->reset = iom_gpi_reset;
 210    dc->realize = xlx_iom_realize;
 211    dc->props = xlx_iom_properties;
 212    dc->vmsd = &vmstate_xlx_iom;
 213    fggc->controller_gpios = gpio_sets;
 214    fggc->client_gpios = gpio_client_sets;
 215}
 216
 217static const TypeInfo xlx_iom_info = {
 218    .name          = TYPE_XILINX_IO_MODULE_GPI,
 219    .parent        = TYPE_SYS_BUS_DEVICE,
 220    .instance_size = sizeof(XilinxGPI),
 221    .class_init    = xlx_iom_class_init,
 222    .instance_init = xlx_iom_init,
 223    .interfaces    = (InterfaceInfo[]) {
 224        { TYPE_FDT_GENERIC_GPIO },
 225        { }
 226    },
 227};
 228
 229static void xlx_iom_register_types(void)
 230{
 231    type_register_static(&xlx_iom_info);
 232}
 233
 234type_init(xlx_iom_register_types)
 235