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