xref: /openbmc/qemu/hw/adc/zynq-xadc.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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