qemu/hw/misc/zynq-xadc.c
<<
>>
Prefs
   1/*
   2 * ADC registers for Xilinx Zynq Platform
   3 *
   4 * Copyright (c) 2015 Guenter Roeck
   5 * Based on hw/misc/zynq_slcr.c, written by Michal Simek
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * as published by the Free Software Foundation; either version
  10 * 2 of the License, or (at your option) any later version.
  11 *
  12 * You should have received a copy of the GNU General Public License along
  13 * with this program; if not, see <http://www.gnu.org/licenses/>.
  14 */
  15
  16#include "hw/hw.h"
  17#include "hw/misc/zynq-xadc.h"
  18#include "qemu/timer.h"
  19#include "sysemu/sysemu.h"
  20
  21enum {
  22    CFG                = 0x000 / 4,
  23    INT_STS,
  24    INT_MASK,
  25    MSTS,
  26    CMDFIFO,
  27    RDFIFO,
  28    MCTL,
  29};
  30
  31#define CFG_ENABLE              BIT(31)
  32#define CFG_CFIFOTH_SHIFT       20
  33#define CFG_CFIFOTH_LENGTH      4
  34#define CFG_DFIFOTH_SHIFT       16
  35#define CFG_DFIFOTH_LENGTH      4
  36#define CFG_WEDGE               BIT(13)
  37#define CFG_REDGE               BIT(12)
  38#define CFG_TCKRATE_SHIFT       8
  39#define CFG_TCKRATE_LENGTH      2
  40
  41#define CFG_TCKRATE_DIV(x)      (0x1 << (x - 1))
  42
  43#define CFG_IGAP_SHIFT          0
  44#define CFG_IGAP_LENGTH         5
  45
  46#define INT_CFIFO_LTH           BIT(9)
  47#define INT_DFIFO_GTH           BIT(8)
  48#define INT_OT                  BIT(7)
  49#define INT_ALM_SHIFT           0
  50#define INT_ALM_LENGTH          7
  51#define INT_ALM_MASK            (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT)
  52
  53#define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK)
  54
  55#define MSTS_CFIFO_LVL_SHIFT    16
  56#define MSTS_CFIFO_LVL_LENGTH   4
  57#define MSTS_DFIFO_LVL_SHIFT    12
  58#define MSTS_DFIFO_LVL_LENGTH   4
  59#define MSTS_CFIFOF             BIT(11)
  60#define MSTS_CFIFOE             BIT(10)
  61#define MSTS_DFIFOF             BIT(9)
  62#define MSTS_DFIFOE             BIT(8)
  63#define MSTS_OT                 BIT(7)
  64#define MSTS_ALM_SHIFT          0
  65#define MSTS_ALM_LENGTH         7
  66
  67#define MCTL_RESET              BIT(4)
  68
  69#define CMD_NOP                 0x00
  70#define CMD_READ                0x01
  71#define CMD_WRITE               0x02
  72
  73static void zynq_xadc_update_ints(ZynqXADCState *s)
  74{
  75
  76    /* We are fast, commands are actioned instantly so the CFIFO is always
  77     * empty (and below threshold).
  78     */
  79    s->regs[INT_STS] |= INT_CFIFO_LTH;
  80
  81    if (s->xadc_dfifo_entries >
  82        extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) {
  83        s->regs[INT_STS] |= INT_DFIFO_GTH;
  84    }
  85
  86    qemu_set_irq(s->qemu_irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK]));
  87}
  88
  89static void zynq_xadc_reset(DeviceState *d)
  90{
  91    ZynqXADCState *s = ZYNQ_XADC(d);
  92
  93    s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT |
  94                   CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE;
  95    s->regs[INT_STS] = INT_CFIFO_LTH;
  96    s->regs[INT_MASK] = 0xffffffff;
  97    s->regs[CMDFIFO] = 0;
  98    s->regs[RDFIFO] = 0;
  99    s->regs[MCTL] = MCTL_RESET;
 100
 101    memset(s->xadc_regs, 0, sizeof(s->xadc_regs));
 102    memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo));
 103    s->xadc_dfifo_entries = 0;
 104
 105    zynq_xadc_update_ints(s);
 106}
 107
 108static uint16_t xadc_pop_dfifo(ZynqXADCState *s)
 109{
 110    uint16_t rv = s->xadc_dfifo[0];
 111    int i;
 112
 113    if (s->xadc_dfifo_entries > 0) {
 114        s->xadc_dfifo_entries--;
 115    }
 116    for (i = 0; i < s->xadc_dfifo_entries; i++) {
 117        s->xadc_dfifo[i] = s->xadc_dfifo[i + 1];
 118    }
 119    s->xadc_dfifo[s->xadc_dfifo_entries] = 0;
 120    zynq_xadc_update_ints(s);
 121    return rv;
 122}
 123
 124static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval)
 125{
 126    if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) {
 127        s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous;
 128    }
 129    s->xadc_read_reg_previous = regval;
 130    zynq_xadc_update_ints(s);
 131}
 132
 133static bool zynq_xadc_check_offset(hwaddr offset, bool rnw)
 134{
 135    switch (offset) {
 136    case CFG:
 137    case INT_MASK:
 138    case INT_STS:
 139    case MCTL:
 140        return true;
 141    case RDFIFO:
 142    case MSTS:
 143        return rnw;     /* read only */
 144    case CMDFIFO:
 145        return !rnw;    /* write only */
 146    default:
 147        return false;
 148    }
 149}
 150
 151static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size)
 152{
 153    ZynqXADCState *s = opaque;
 154    int reg = offset / 4;
 155    uint32_t rv = 0;
 156
 157    if (!zynq_xadc_check_offset(reg, true)) {
 158        qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to "
 159                      "addr %" HWADDR_PRIx "\n", offset);
 160        return 0;
 161    }
 162
 163    switch (reg) {
 164    case CFG:
 165    case INT_MASK:
 166    case INT_STS:
 167    case MCTL:
 168        rv = s->regs[reg];
 169        break;
 170    case MSTS:
 171        rv = MSTS_CFIFOE;
 172        rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT;
 173        if (!s->xadc_dfifo_entries) {
 174            rv |= MSTS_DFIFOE;
 175        } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) {
 176            rv |= MSTS_DFIFOF;
 177        }
 178        break;
 179    case RDFIFO:
 180        rv = xadc_pop_dfifo(s);
 181        break;
 182    }
 183    return rv;
 184}
 185
 186static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
 187                            unsigned size)
 188{
 189    ZynqXADCState *s = (ZynqXADCState *)opaque;
 190    int reg = offset / 4;
 191    int xadc_reg;
 192    int xadc_cmd;
 193    int xadc_data;
 194
 195    if (!zynq_xadc_check_offset(reg, false)) {
 196        qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access "
 197                      "to addr %" HWADDR_PRIx "\n", offset);
 198        return;
 199    }
 200
 201    switch (reg) {
 202    case CFG:
 203        s->regs[CFG] = val;
 204        break;
 205    case INT_STS:
 206        s->regs[INT_STS] &= ~val;
 207        break;
 208    case INT_MASK:
 209        s->regs[INT_MASK] = val & INT_ALL;
 210        break;
 211    case CMDFIFO:
 212        xadc_cmd  = extract32(val, 26,  4);
 213        xadc_reg  = extract32(val, 16, 10);
 214        xadc_data = extract32(val,  0, 16);
 215
 216        if (s->regs[MCTL] & MCTL_RESET) {
 217            qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command "
 218                          "while comm channel held in reset: %" PRIx32 "\n",
 219                          (uint32_t) val);
 220            break;
 221        }
 222
 223        if (xadc_reg > ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
 224            qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
 225                          "reg 0x%x\n", xadc_reg);
 226            break;
 227        }
 228
 229        switch (xadc_cmd) {
 230        case CMD_READ:
 231            xadc_push_dfifo(s, s->xadc_regs[xadc_reg]);
 232            break;
 233        case CMD_WRITE:
 234            s->xadc_regs[xadc_reg] = xadc_data;
 235            /* fallthrough */
 236        case CMD_NOP:
 237            xadc_push_dfifo(s, 0);
 238            break;
 239        }
 240        break;
 241    case MCTL:
 242        s->regs[MCTL] = val & 0x00fffeff;
 243        break;
 244    }
 245    zynq_xadc_update_ints(s);
 246}
 247
 248static const MemoryRegionOps xadc_ops = {
 249    .read = zynq_xadc_read,
 250    .write = zynq_xadc_write,
 251    .endianness = DEVICE_NATIVE_ENDIAN,
 252};
 253
 254static void zynq_xadc_init(Object *obj)
 255{
 256    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 257    ZynqXADCState *s = ZYNQ_XADC(obj);
 258
 259    memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc",
 260                          ZYNQ_XADC_MMIO_SIZE);
 261    sysbus_init_mmio(sbd, &s->iomem);
 262    sysbus_init_irq(sbd, &s->qemu_irq);
 263}
 264
 265static const VMStateDescription vmstate_zynq_xadc = {
 266    .name = "zynq-xadc",
 267    .version_id = 1,
 268    .minimum_version_id = 1,
 269    .fields = (VMStateField[]) {
 270        VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS),
 271        VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState,
 272                             ZYNQ_XADC_NUM_ADC_REGS),
 273        VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState,
 274                             ZYNQ_XADC_FIFO_DEPTH),
 275        VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState),
 276        VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState),
 277        VMSTATE_END_OF_LIST()
 278    }
 279};
 280
 281static void zynq_xadc_class_init(ObjectClass *klass, void *data)
 282{
 283    DeviceClass *dc = DEVICE_CLASS(klass);
 284
 285    dc->vmsd = &vmstate_zynq_xadc;
 286    dc->reset = zynq_xadc_reset;
 287}
 288
 289static const TypeInfo zynq_xadc_info = {
 290    .class_init = zynq_xadc_class_init,
 291    .name  = TYPE_ZYNQ_XADC,
 292    .parent = TYPE_SYS_BUS_DEVICE,
 293    .instance_size  = sizeof(ZynqXADCState),
 294    .instance_init = zynq_xadc_init,
 295};
 296
 297static void zynq_xadc_register_types(void)
 298{
 299    type_register_static(&zynq_xadc_info);
 300}
 301
 302type_init(zynq_xadc_register_types)
 303