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