qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hw/misc: Add support for ADC controller in Xilinx Zynq 7000

Add support for the Xilinx XADC core used in Zynq 7000.

References:
- Zynq-7000 All Programmable SoC Technical Reference Manual
- 7 Series FPGAs and Zynq-7000 All Programmable SoC XADC
Dual 12-Bit 1 MSPS Analog-to-Digital Converter

Tested with Linux using QEMU machine xilinx-zynq-a9 with devicetree
files zynq-zc702.dtb and zynq-zc706.dtb, and kernel configuration
multi_v7_defconfig.

Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
[ PC changes:
* Changed macro names to match TRM where possible
* Made programmers model macro scheme consistent
* Dropped XADC_ZYNQ_ prefix on local macros
* Fix ALM field width
* Update threshold-comparison interrupts in _update_ints()
* factored out DFIFO pushes into helper. Renamed to "push/pop"
* Changed xadc_reg to 10 bits and added OOB check.
* Reduced scope of MCTL reset to just stop channel coms.
* Added dummy read data to write commands
* Changed _ to - seperators in string names and filenames
* Dropped ------------ in header comment
* Catchall'ed _update_ints() in _write handler.
* Minor whitespace changes.
* Use ZYNQ_XADC_FIFO_DEPTH instead of ARRAY_SIZE()
]
Signed-off-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
Tested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

authored by

Guenter Roeck and committed by
Peter Maydell
74fcbd22 f3bcfc56

+355
+6
hw/arm/xilinx_zynq.c
··· 24 24 #include "hw/block/flash.h" 25 25 #include "sysemu/block-backend.h" 26 26 #include "hw/loader.h" 27 + #include "hw/misc/zynq-xadc.h" 27 28 #include "hw/ssi.h" 28 29 #include "qemu/error-report.h" 29 30 ··· 263 264 qdev_init_nofail(dev); 264 265 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000); 265 266 sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]); 267 + 268 + dev = qdev_create(NULL, TYPE_ZYNQ_XADC); 269 + qdev_init_nofail(dev); 270 + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100); 271 + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]); 266 272 267 273 dev = qdev_create(NULL, "pl330"); 268 274 qdev_prop_set_uint8(dev, "num_chnls", 8);
+1
hw/misc/Makefile.objs
··· 36 36 obj-$(CONFIG_OMAP) += omap_tap.o 37 37 obj-$(CONFIG_SLAVIO) += slavio_misc.o 38 38 obj-$(CONFIG_ZYNQ) += zynq_slcr.o 39 + obj-$(CONFIG_ZYNQ) += zynq-xadc.o 39 40 obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o 40 41 41 42 obj-$(CONFIG_PVPANIC) += pvpanic.o
+302
hw/misc/zynq-xadc.c
··· 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 + 21 + enum { 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 + 73 + static 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 + 89 + static 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 + 108 + static 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 + 124 + static 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 + 133 + static 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 + 151 + static 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 + 186 + static 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 + 248 + static const MemoryRegionOps xadc_ops = { 249 + .read = zynq_xadc_read, 250 + .write = zynq_xadc_write, 251 + .endianness = DEVICE_NATIVE_ENDIAN, 252 + }; 253 + 254 + static 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 + 265 + static 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 + 281 + static 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 + 289 + static 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 + 297 + static void zynq_xadc_register_types(void) 298 + { 299 + type_register_static(&zynq_xadc_info); 300 + } 301 + 302 + type_init(zynq_xadc_register_types)
+46
include/hw/misc/zynq-xadc.h
··· 1 + /* 2 + * Device model for Zynq ADC controller 3 + * 4 + * Copyright (c) 2015 Guenter Roeck <linux@roeck-us.net> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + * 11 + * You should have received a copy of the GNU General Public License along 12 + * with this program; if not, see <http://www.gnu.org/licenses/>. 13 + */ 14 + 15 + #ifndef ZYNQ_XADC_H 16 + #define ZYNQ_XADC_H 17 + 18 + #include "hw/sysbus.h" 19 + 20 + #define ZYNQ_XADC_MMIO_SIZE 0x0020 21 + #define ZYNQ_XADC_NUM_IO_REGS (ZYNQ_XADC_MMIO_SIZE / 4) 22 + #define ZYNQ_XADC_NUM_ADC_REGS 128 23 + #define ZYNQ_XADC_FIFO_DEPTH 15 24 + 25 + #define TYPE_ZYNQ_XADC "xlnx,zynq-xadc" 26 + #define ZYNQ_XADC(obj) \ 27 + OBJECT_CHECK(ZynqXADCState, (obj), TYPE_ZYNQ_XADC) 28 + 29 + typedef struct ZynqXADCState { 30 + /*< private >*/ 31 + SysBusDevice parent_obj; 32 + 33 + /*< public >*/ 34 + MemoryRegion iomem; 35 + 36 + uint32_t regs[ZYNQ_XADC_NUM_IO_REGS]; 37 + uint16_t xadc_regs[ZYNQ_XADC_NUM_ADC_REGS]; 38 + uint16_t xadc_read_reg_previous; 39 + uint16_t xadc_dfifo[ZYNQ_XADC_FIFO_DEPTH]; 40 + uint16_t xadc_dfifo_entries; 41 + 42 + struct IRQState *qemu_irq; 43 + 44 + } ZynqXADCState; 45 + 46 + #endif /* ZYNQ_XADC_H */