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