1246f530cSCorey Minyard /*
2246f530cSCorey Minyard * ADC registers for Xilinx Zynq Platform
3246f530cSCorey Minyard *
4246f530cSCorey Minyard * Copyright (c) 2015 Guenter Roeck
5246f530cSCorey Minyard * Based on hw/misc/zynq_slcr.c, written by Michal Simek
6246f530cSCorey Minyard *
7246f530cSCorey Minyard * This program is free software; you can redistribute it and/or
8246f530cSCorey Minyard * modify it under the terms of the GNU General Public License
9246f530cSCorey Minyard * as published by the Free Software Foundation; either version
10246f530cSCorey Minyard * 2 of the License, or (at your option) any later version.
11246f530cSCorey Minyard *
12246f530cSCorey Minyard * You should have received a copy of the GNU General Public License along
13246f530cSCorey Minyard * with this program; if not, see <http://www.gnu.org/licenses/>.
14246f530cSCorey Minyard */
15246f530cSCorey Minyard
16246f530cSCorey Minyard #include "qemu/osdep.h"
17246f530cSCorey Minyard #include "hw/irq.h"
18246f530cSCorey Minyard #include "hw/adc/zynq-xadc.h"
19246f530cSCorey Minyard #include "migration/vmstate.h"
20246f530cSCorey Minyard #include "qemu/timer.h"
21246f530cSCorey Minyard #include "qemu/log.h"
22246f530cSCorey Minyard #include "qemu/module.h"
23246f530cSCorey Minyard
24246f530cSCorey Minyard enum {
25246f530cSCorey Minyard CFG = 0x000 / 4,
26246f530cSCorey Minyard INT_STS,
27246f530cSCorey Minyard INT_MASK,
28246f530cSCorey Minyard MSTS,
29246f530cSCorey Minyard CMDFIFO,
30246f530cSCorey Minyard RDFIFO,
31246f530cSCorey Minyard MCTL,
32246f530cSCorey Minyard };
33246f530cSCorey Minyard
34246f530cSCorey Minyard #define CFG_ENABLE BIT(31)
35246f530cSCorey Minyard #define CFG_CFIFOTH_SHIFT 20
36246f530cSCorey Minyard #define CFG_CFIFOTH_LENGTH 4
37246f530cSCorey Minyard #define CFG_DFIFOTH_SHIFT 16
38246f530cSCorey Minyard #define CFG_DFIFOTH_LENGTH 4
39246f530cSCorey Minyard #define CFG_WEDGE BIT(13)
40246f530cSCorey Minyard #define CFG_REDGE BIT(12)
41246f530cSCorey Minyard #define CFG_TCKRATE_SHIFT 8
42246f530cSCorey Minyard #define CFG_TCKRATE_LENGTH 2
43246f530cSCorey Minyard
44246f530cSCorey Minyard #define CFG_TCKRATE_DIV(x) (0x1 << (x - 1))
45246f530cSCorey Minyard
46246f530cSCorey Minyard #define CFG_IGAP_SHIFT 0
47246f530cSCorey Minyard #define CFG_IGAP_LENGTH 5
48246f530cSCorey Minyard
49246f530cSCorey Minyard #define INT_CFIFO_LTH BIT(9)
50246f530cSCorey Minyard #define INT_DFIFO_GTH BIT(8)
51246f530cSCorey Minyard #define INT_OT BIT(7)
52246f530cSCorey Minyard #define INT_ALM_SHIFT 0
53246f530cSCorey Minyard #define INT_ALM_LENGTH 7
54246f530cSCorey Minyard #define INT_ALM_MASK (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT)
55246f530cSCorey Minyard
56246f530cSCorey Minyard #define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK)
57246f530cSCorey Minyard
58246f530cSCorey Minyard #define MSTS_CFIFO_LVL_SHIFT 16
59246f530cSCorey Minyard #define MSTS_CFIFO_LVL_LENGTH 4
60246f530cSCorey Minyard #define MSTS_DFIFO_LVL_SHIFT 12
61246f530cSCorey Minyard #define MSTS_DFIFO_LVL_LENGTH 4
62246f530cSCorey Minyard #define MSTS_CFIFOF BIT(11)
63246f530cSCorey Minyard #define MSTS_CFIFOE BIT(10)
64246f530cSCorey Minyard #define MSTS_DFIFOF BIT(9)
65246f530cSCorey Minyard #define MSTS_DFIFOE BIT(8)
66246f530cSCorey Minyard #define MSTS_OT BIT(7)
67246f530cSCorey Minyard #define MSTS_ALM_SHIFT 0
68246f530cSCorey Minyard #define MSTS_ALM_LENGTH 7
69246f530cSCorey Minyard
70246f530cSCorey Minyard #define MCTL_RESET BIT(4)
71246f530cSCorey Minyard
72246f530cSCorey Minyard #define CMD_NOP 0x00
73246f530cSCorey Minyard #define CMD_READ 0x01
74246f530cSCorey Minyard #define CMD_WRITE 0x02
75246f530cSCorey Minyard
zynq_xadc_update_ints(ZynqXADCState * s)76246f530cSCorey Minyard static void zynq_xadc_update_ints(ZynqXADCState *s)
77246f530cSCorey Minyard {
78246f530cSCorey Minyard
79246f530cSCorey Minyard /* We are fast, commands are actioned instantly so the CFIFO is always
80246f530cSCorey Minyard * empty (and below threshold).
81246f530cSCorey Minyard */
82246f530cSCorey Minyard s->regs[INT_STS] |= INT_CFIFO_LTH;
83246f530cSCorey Minyard
84246f530cSCorey Minyard if (s->xadc_dfifo_entries >
85246f530cSCorey Minyard extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) {
86246f530cSCorey Minyard s->regs[INT_STS] |= INT_DFIFO_GTH;
87246f530cSCorey Minyard }
88246f530cSCorey Minyard
896e76d35fSPhilippe Mathieu-Daudé qemu_set_irq(s->irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK]));
90246f530cSCorey Minyard }
91246f530cSCorey Minyard
zynq_xadc_reset(DeviceState * d)92246f530cSCorey Minyard static void zynq_xadc_reset(DeviceState *d)
93246f530cSCorey Minyard {
94246f530cSCorey Minyard ZynqXADCState *s = ZYNQ_XADC(d);
95246f530cSCorey Minyard
96246f530cSCorey Minyard s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT |
97246f530cSCorey Minyard CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE;
98246f530cSCorey Minyard s->regs[INT_STS] = INT_CFIFO_LTH;
99246f530cSCorey Minyard s->regs[INT_MASK] = 0xffffffff;
100246f530cSCorey Minyard s->regs[CMDFIFO] = 0;
101246f530cSCorey Minyard s->regs[RDFIFO] = 0;
102246f530cSCorey Minyard s->regs[MCTL] = MCTL_RESET;
103246f530cSCorey Minyard
104246f530cSCorey Minyard memset(s->xadc_regs, 0, sizeof(s->xadc_regs));
105246f530cSCorey Minyard memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo));
106246f530cSCorey Minyard s->xadc_dfifo_entries = 0;
107246f530cSCorey Minyard
108246f530cSCorey Minyard zynq_xadc_update_ints(s);
109246f530cSCorey Minyard }
110246f530cSCorey Minyard
xadc_pop_dfifo(ZynqXADCState * s)111246f530cSCorey Minyard static uint16_t xadc_pop_dfifo(ZynqXADCState *s)
112246f530cSCorey Minyard {
113246f530cSCorey Minyard uint16_t rv = s->xadc_dfifo[0];
114246f530cSCorey Minyard int i;
115246f530cSCorey Minyard
116246f530cSCorey Minyard if (s->xadc_dfifo_entries > 0) {
117246f530cSCorey Minyard s->xadc_dfifo_entries--;
118246f530cSCorey Minyard }
119246f530cSCorey Minyard for (i = 0; i < s->xadc_dfifo_entries; i++) {
120246f530cSCorey Minyard s->xadc_dfifo[i] = s->xadc_dfifo[i + 1];
121246f530cSCorey Minyard }
122246f530cSCorey Minyard s->xadc_dfifo[s->xadc_dfifo_entries] = 0;
123246f530cSCorey Minyard zynq_xadc_update_ints(s);
124246f530cSCorey Minyard return rv;
125246f530cSCorey Minyard }
126246f530cSCorey Minyard
xadc_push_dfifo(ZynqXADCState * s,uint16_t regval)127246f530cSCorey Minyard static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval)
128246f530cSCorey Minyard {
129246f530cSCorey Minyard if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) {
130246f530cSCorey Minyard s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous;
131246f530cSCorey Minyard }
132246f530cSCorey Minyard s->xadc_read_reg_previous = regval;
133246f530cSCorey Minyard zynq_xadc_update_ints(s);
134246f530cSCorey Minyard }
135246f530cSCorey Minyard
zynq_xadc_check_offset(hwaddr offset,bool rnw)136246f530cSCorey Minyard static bool zynq_xadc_check_offset(hwaddr offset, bool rnw)
137246f530cSCorey Minyard {
138246f530cSCorey Minyard switch (offset) {
139246f530cSCorey Minyard case CFG:
140246f530cSCorey Minyard case INT_MASK:
141246f530cSCorey Minyard case INT_STS:
142246f530cSCorey Minyard case MCTL:
143246f530cSCorey Minyard return true;
144246f530cSCorey Minyard case RDFIFO:
145246f530cSCorey Minyard case MSTS:
146246f530cSCorey Minyard return rnw; /* read only */
147246f530cSCorey Minyard case CMDFIFO:
148246f530cSCorey Minyard return !rnw; /* write only */
149246f530cSCorey Minyard default:
150246f530cSCorey Minyard return false;
151246f530cSCorey Minyard }
152246f530cSCorey Minyard }
153246f530cSCorey Minyard
zynq_xadc_read(void * opaque,hwaddr offset,unsigned size)154246f530cSCorey Minyard static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size)
155246f530cSCorey Minyard {
156246f530cSCorey Minyard ZynqXADCState *s = opaque;
157246f530cSCorey Minyard int reg = offset / 4;
158246f530cSCorey Minyard uint32_t rv = 0;
159246f530cSCorey Minyard
160246f530cSCorey Minyard if (!zynq_xadc_check_offset(reg, true)) {
161246f530cSCorey Minyard qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to "
162246f530cSCorey Minyard "addr %" HWADDR_PRIx "\n", offset);
163246f530cSCorey Minyard return 0;
164246f530cSCorey Minyard }
165246f530cSCorey Minyard
166246f530cSCorey Minyard switch (reg) {
167246f530cSCorey Minyard case CFG:
168246f530cSCorey Minyard case INT_MASK:
169246f530cSCorey Minyard case INT_STS:
170246f530cSCorey Minyard case MCTL:
171246f530cSCorey Minyard rv = s->regs[reg];
172246f530cSCorey Minyard break;
173246f530cSCorey Minyard case MSTS:
174246f530cSCorey Minyard rv = MSTS_CFIFOE;
175246f530cSCorey Minyard rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT;
176246f530cSCorey Minyard if (!s->xadc_dfifo_entries) {
177246f530cSCorey Minyard rv |= MSTS_DFIFOE;
178246f530cSCorey Minyard } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) {
179246f530cSCorey Minyard rv |= MSTS_DFIFOF;
180246f530cSCorey Minyard }
181246f530cSCorey Minyard break;
182246f530cSCorey Minyard case RDFIFO:
183246f530cSCorey Minyard rv = xadc_pop_dfifo(s);
184246f530cSCorey Minyard break;
185246f530cSCorey Minyard }
186246f530cSCorey Minyard return rv;
187246f530cSCorey Minyard }
188246f530cSCorey Minyard
zynq_xadc_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)189246f530cSCorey Minyard static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
190246f530cSCorey Minyard unsigned size)
191246f530cSCorey Minyard {
192246f530cSCorey Minyard ZynqXADCState *s = (ZynqXADCState *)opaque;
193246f530cSCorey Minyard int reg = offset / 4;
194246f530cSCorey Minyard int xadc_reg;
195246f530cSCorey Minyard int xadc_cmd;
196246f530cSCorey Minyard int xadc_data;
197246f530cSCorey Minyard
198246f530cSCorey Minyard if (!zynq_xadc_check_offset(reg, false)) {
199246f530cSCorey Minyard qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access "
200246f530cSCorey Minyard "to addr %" HWADDR_PRIx "\n", offset);
201246f530cSCorey Minyard return;
202246f530cSCorey Minyard }
203246f530cSCorey Minyard
204246f530cSCorey Minyard switch (reg) {
205246f530cSCorey Minyard case CFG:
206246f530cSCorey Minyard s->regs[CFG] = val;
207246f530cSCorey Minyard break;
208246f530cSCorey Minyard case INT_STS:
209246f530cSCorey Minyard s->regs[INT_STS] &= ~val;
210246f530cSCorey Minyard break;
211246f530cSCorey Minyard case INT_MASK:
212246f530cSCorey Minyard s->regs[INT_MASK] = val & INT_ALL;
213246f530cSCorey Minyard break;
214246f530cSCorey Minyard case CMDFIFO:
215246f530cSCorey Minyard xadc_cmd = extract32(val, 26, 4);
216246f530cSCorey Minyard xadc_reg = extract32(val, 16, 10);
217246f530cSCorey Minyard xadc_data = extract32(val, 0, 16);
218246f530cSCorey Minyard
219246f530cSCorey Minyard if (s->regs[MCTL] & MCTL_RESET) {
220246f530cSCorey Minyard qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command "
221246f530cSCorey Minyard "while comm channel held in reset: %" PRIx32 "\n",
222246f530cSCorey Minyard (uint32_t) val);
223246f530cSCorey Minyard break;
224246f530cSCorey Minyard }
225246f530cSCorey Minyard
226246f530cSCorey Minyard if (xadc_reg >= ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
227246f530cSCorey Minyard qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
228246f530cSCorey Minyard "reg 0x%x\n", xadc_reg);
229246f530cSCorey Minyard break;
230246f530cSCorey Minyard }
231246f530cSCorey Minyard
232246f530cSCorey Minyard switch (xadc_cmd) {
233246f530cSCorey Minyard case CMD_READ:
234246f530cSCorey Minyard xadc_push_dfifo(s, s->xadc_regs[xadc_reg]);
235246f530cSCorey Minyard break;
236246f530cSCorey Minyard case CMD_WRITE:
237246f530cSCorey Minyard s->xadc_regs[xadc_reg] = xadc_data;
238246f530cSCorey Minyard /* fallthrough */
239246f530cSCorey Minyard case CMD_NOP:
240246f530cSCorey Minyard xadc_push_dfifo(s, 0);
241246f530cSCorey Minyard break;
242246f530cSCorey Minyard }
243246f530cSCorey Minyard break;
244246f530cSCorey Minyard case MCTL:
245246f530cSCorey Minyard s->regs[MCTL] = val & 0x00fffeff;
246246f530cSCorey Minyard break;
247246f530cSCorey Minyard }
248246f530cSCorey Minyard zynq_xadc_update_ints(s);
249246f530cSCorey Minyard }
250246f530cSCorey Minyard
251246f530cSCorey Minyard static const MemoryRegionOps xadc_ops = {
252246f530cSCorey Minyard .read = zynq_xadc_read,
253246f530cSCorey Minyard .write = zynq_xadc_write,
254246f530cSCorey Minyard .endianness = DEVICE_NATIVE_ENDIAN,
255246f530cSCorey Minyard };
256246f530cSCorey Minyard
zynq_xadc_init(Object * obj)257246f530cSCorey Minyard static void zynq_xadc_init(Object *obj)
258246f530cSCorey Minyard {
259246f530cSCorey Minyard SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
260246f530cSCorey Minyard ZynqXADCState *s = ZYNQ_XADC(obj);
261246f530cSCorey Minyard
262246f530cSCorey Minyard memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc",
263246f530cSCorey Minyard ZYNQ_XADC_MMIO_SIZE);
264246f530cSCorey Minyard sysbus_init_mmio(sbd, &s->iomem);
2656e76d35fSPhilippe Mathieu-Daudé sysbus_init_irq(sbd, &s->irq);
266246f530cSCorey Minyard }
267246f530cSCorey Minyard
268246f530cSCorey Minyard static const VMStateDescription vmstate_zynq_xadc = {
269246f530cSCorey Minyard .name = "zynq-xadc",
270246f530cSCorey Minyard .version_id = 1,
271246f530cSCorey Minyard .minimum_version_id = 1,
27299367627SRichard Henderson .fields = (const VMStateField[]) {
273246f530cSCorey Minyard VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS),
274246f530cSCorey Minyard VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState,
275246f530cSCorey Minyard ZYNQ_XADC_NUM_ADC_REGS),
276246f530cSCorey Minyard VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState,
277246f530cSCorey Minyard ZYNQ_XADC_FIFO_DEPTH),
278246f530cSCorey Minyard VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState),
279246f530cSCorey Minyard VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState),
280246f530cSCorey Minyard VMSTATE_END_OF_LIST()
281246f530cSCorey Minyard }
282246f530cSCorey Minyard };
283246f530cSCorey Minyard
zynq_xadc_class_init(ObjectClass * klass,void * data)284246f530cSCorey Minyard static void zynq_xadc_class_init(ObjectClass *klass, void *data)
285246f530cSCorey Minyard {
286246f530cSCorey Minyard DeviceClass *dc = DEVICE_CLASS(klass);
287246f530cSCorey Minyard
288246f530cSCorey Minyard dc->vmsd = &vmstate_zynq_xadc;
289*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, zynq_xadc_reset);
290246f530cSCorey Minyard }
291246f530cSCorey Minyard
292246f530cSCorey Minyard static const TypeInfo zynq_xadc_info = {
293246f530cSCorey Minyard .class_init = zynq_xadc_class_init,
294246f530cSCorey Minyard .name = TYPE_ZYNQ_XADC,
295246f530cSCorey Minyard .parent = TYPE_SYS_BUS_DEVICE,
296246f530cSCorey Minyard .instance_size = sizeof(ZynqXADCState),
297246f530cSCorey Minyard .instance_init = zynq_xadc_init,
298246f530cSCorey Minyard };
299246f530cSCorey Minyard
zynq_xadc_register_types(void)300246f530cSCorey Minyard static void zynq_xadc_register_types(void)
301246f530cSCorey Minyard {
302246f530cSCorey Minyard type_register_static(&zynq_xadc_info);
303246f530cSCorey Minyard }
304246f530cSCorey Minyard
305246f530cSCorey Minyard type_init(zynq_xadc_register_types)
306