1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "hw/hw.h"
25#include "hw/sysbus.h"
26#include "trace.h"
27#include "qemu/error-report.h"
28
29enum {
30 R_SYSTEM = 0,
31 R_BYPASS,
32 R_TIMING,
33 R_IODELAY,
34 R_MAX
35};
36
37enum {
38 IODELAY_DQSDELAY_RDY = (1<<5),
39 IODELAY_PLL1_LOCKED = (1<<6),
40 IODELAY_PLL2_LOCKED = (1<<7),
41};
42
43#define TYPE_MILKYMIST_HPDMC "milkymist-hpdmc"
44#define MILKYMIST_HPDMC(obj) \
45 OBJECT_CHECK(MilkymistHpdmcState, (obj), TYPE_MILKYMIST_HPDMC)
46
47struct MilkymistHpdmcState {
48 SysBusDevice parent_obj;
49
50 MemoryRegion regs_region;
51
52 uint32_t regs[R_MAX];
53};
54typedef struct MilkymistHpdmcState MilkymistHpdmcState;
55
56static uint64_t hpdmc_read(void *opaque, hwaddr addr,
57 unsigned size)
58{
59 MilkymistHpdmcState *s = opaque;
60 uint32_t r = 0;
61
62 addr >>= 2;
63 switch (addr) {
64 case R_SYSTEM:
65 case R_BYPASS:
66 case R_TIMING:
67 case R_IODELAY:
68 r = s->regs[addr];
69 break;
70
71 default:
72 error_report("milkymist_hpdmc: read access to unknown register 0x"
73 TARGET_FMT_plx, addr << 2);
74 break;
75 }
76
77 trace_milkymist_hpdmc_memory_read(addr << 2, r);
78
79 return r;
80}
81
82static void hpdmc_write(void *opaque, hwaddr addr, uint64_t value,
83 unsigned size)
84{
85 MilkymistHpdmcState *s = opaque;
86
87 trace_milkymist_hpdmc_memory_write(addr, value);
88
89 addr >>= 2;
90 switch (addr) {
91 case R_SYSTEM:
92 case R_BYPASS:
93 case R_TIMING:
94 s->regs[addr] = value;
95 break;
96 case R_IODELAY:
97
98 break;
99
100 default:
101 error_report("milkymist_hpdmc: write access to unknown register 0x"
102 TARGET_FMT_plx, addr << 2);
103 break;
104 }
105}
106
107static const MemoryRegionOps hpdmc_mmio_ops = {
108 .read = hpdmc_read,
109 .write = hpdmc_write,
110 .valid = {
111 .min_access_size = 4,
112 .max_access_size = 4,
113 },
114 .endianness = DEVICE_NATIVE_ENDIAN,
115};
116
117static void milkymist_hpdmc_reset(DeviceState *d)
118{
119 MilkymistHpdmcState *s = MILKYMIST_HPDMC(d);
120 int i;
121
122 for (i = 0; i < R_MAX; i++) {
123 s->regs[i] = 0;
124 }
125
126
127 s->regs[R_IODELAY] = IODELAY_DQSDELAY_RDY | IODELAY_PLL1_LOCKED
128 | IODELAY_PLL2_LOCKED;
129}
130
131static int milkymist_hpdmc_init(SysBusDevice *dev)
132{
133 MilkymistHpdmcState *s = MILKYMIST_HPDMC(dev);
134
135 memory_region_init_io(&s->regs_region, OBJECT(dev), &hpdmc_mmio_ops, s,
136 "milkymist-hpdmc", R_MAX * 4);
137 sysbus_init_mmio(dev, &s->regs_region);
138
139 return 0;
140}
141
142static const VMStateDescription vmstate_milkymist_hpdmc = {
143 .name = "milkymist-hpdmc",
144 .version_id = 1,
145 .minimum_version_id = 1,
146 .fields = (VMStateField[]) {
147 VMSTATE_UINT32_ARRAY(regs, MilkymistHpdmcState, R_MAX),
148 VMSTATE_END_OF_LIST()
149 }
150};
151
152static void milkymist_hpdmc_class_init(ObjectClass *klass, void *data)
153{
154 DeviceClass *dc = DEVICE_CLASS(klass);
155 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
156
157 k->init = milkymist_hpdmc_init;
158 dc->reset = milkymist_hpdmc_reset;
159 dc->vmsd = &vmstate_milkymist_hpdmc;
160}
161
162static const TypeInfo milkymist_hpdmc_info = {
163 .name = TYPE_MILKYMIST_HPDMC,
164 .parent = TYPE_SYS_BUS_DEVICE,
165 .instance_size = sizeof(MilkymistHpdmcState),
166 .class_init = milkymist_hpdmc_class_init,
167};
168
169static void milkymist_hpdmc_register_types(void)
170{
171 type_register_static(&milkymist_hpdmc_info);
172}
173
174type_init(milkymist_hpdmc_register_types)
175