1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include "qemu/osdep.h"
22#include "qemu/log.h"
23#include "hw/i2c/i2c.h"
24
25#ifndef SI57X_DEBUG
26#define SI57X_DEBUG 0
27#endif
28
29#define DPRINT(fmt, args...) \
30 do { \
31 if (SI57X_DEBUG) { \
32 qemu_log("%s: "fmt, __func__, ## args); \
33 } \
34 } while (0)
35
36#define HS_DIV_OFFSET 5
37#define HS_DIV_MASK 0xE0
38
39#define N1_DIV_MSB_OFFSET 6
40#define N1_DIV_MSB_MASK 0x3F
41
42#define N1_DIV_LSB_OFFSET 0
43#define N1_DIV_LSB_MASK 0xC0
44
45#define CTRL_REG0 135
46#define CTRL_REG1 137
47
48#define REG0 0
49#define REG1 1
50#define REG2 2
51#define REG3 3
52#define REG4 4
53#define REG5 5
54#define REG6 6
55#define REG7 7
56
57
58#define CTRL_REG0_REL 6
59 #define CTRL_REG0_RST_REG 7
60 #define CTRL_REG0_NEWFREQ 6
61 #define CTRL_REG0_FREZ_M 5
62 #define CTRL_REG0_FREZ_VCDC 4
63 #define CTRL_REG0_RECALL 0
64#define CTRL_REG1_REL 7
65 #define CTRL_REG1_FREZ_DCO 4
66
67#define M(x) (1 << x)
68
69#define TYPE_SI57X "si57x"
70#define SI57X(obj) \
71 OBJECT_CHECK(Si57xState, (obj), TYPE_SI57X)
72
73typedef struct Si57xState {
74
75 I2CSlave parent_obj;
76
77
78 uint64_t rfreq;
79
80 uint16_t hs_div;
81 uint16_t n1;
82
83
84
85 uint16_t temp_stab;
86 uint8_t regs[8];
87 uint8_t state;
88 uint8_t ptr;
89} Si57xState;
90
91enum states {
92 IDEAL,
93 ADDRESSING,
94 ADDRESSING_DONE,
95 WRITING,
96 READING,
97};
98
99enum temp_stability {
100 TEMP_STAB_7PPM = 7,
101 TEMP_STAB_20PPM = 20,
102 TEMP_STAB_50PPM = 50,
103};
104
105static bool rfreq_is_updating(Si57xState *s)
106{
107 uint8_t addr = s->ptr;
108
109
110 if (addr > REG0) {
111 if (addr == REG1) {
112
113 return (s->regs[addr] & 0x3F) ? true : false;
114 }
115 return true;
116 }
117 return false;
118}
119
120
121
122
123static void si57x_freez_filter(Si57xState *s, int data)
124{
125 if (rfreq_is_updating(s)) {
126
127 if ((s->regs[CTRL_REG0_REL] & M(CTRL_REG0_FREZ_M)) ||
128 (s->regs[CTRL_REG1_REL] & M(CTRL_REG1_FREZ_DCO))) {
129 DPRINT("Update RFREQ 0x%x\n", data);
130 } else {
131 qemu_log_mask(LOG_GUEST_ERROR, "Update RFREQ without asserting"
132 " FREEZ_M/FREEZ_DCO\n");
133 }
134 } else {
135
136 if (!(s->regs[CTRL_REG1_REL] & M(CTRL_REG1_FREZ_DCO))) {
137 qemu_log_mask(LOG_GUEST_ERROR, "Updateing HS_DIV/N1 without"
138 " FREEZ_DCO assert\n");
139 }
140 }
141}
142
143static void si57x_reset(DeviceState *dev)
144{
145 Si57xState *s = SI57X(dev);
146
147
148
149
150
151 s->regs[REG0] = 0;
152
153
154 s->regs[REG1] = 0x3 << N1_DIV_LSB_OFFSET;
155 s->regs[REG0] |= 0x1 << N1_DIV_MSB_OFFSET;
156
157
158 s->regs[REG5] = 0xB8;
159 s->regs[REG4] = 0x1E;
160 s->regs[REG3] = 0x01;
161 s->regs[REG2] = 0xBC;
162 s->regs[REG1] |= 0x2;
163
164 s->regs[CTRL_REG0_REL] &= ~M(CTRL_REG0_RST_REG);
165
166
167
168}
169
170static void si57x_ctrl0_pw(Si57xState *s)
171{
172 if (s->regs[CTRL_REG0_REL] & M(CTRL_REG0_RST_REG)) {
173 si57x_reset(DEVICE(s));
174 }
175 s->regs[CTRL_REG0_REL] &= ~M(CTRL_REG0_NEWFREQ);
176 s->regs[CTRL_REG1_REL] &= ~M(CTRL_REG1_FREZ_DCO);
177}
178
179
180
181
182
183
184static void si57x_set_addr(Si57xState *s, uint8_t addr)
185{
186 if (addr > 18) {
187 switch (addr) {
188 case CTRL_REG0:
189 s->ptr = 6;
190 break;
191 case CTRL_REG1:
192 s->ptr = 7;
193 break;
194 }
195 DPRINT("Setting ptr to %d\n", s->ptr);
196 return;
197 }
198
199 switch (s->temp_stab) {
200 case TEMP_STAB_50PPM:
201 case TEMP_STAB_20PPM:
202 s->ptr = addr - 7;
203 break;
204 case TEMP_STAB_7PPM:
205 s->ptr = addr - 13;
206 break;
207 }
208 DPRINT("Setting ptr to %d\n", s->ptr);
209}
210
211
212static int si57x_tx(I2CSlave *s, uint8_t data)
213{
214 Si57xState *slave = SI57X(s);
215 uint8_t addr;
216
217 if (slave->state == ADDRESSING) {
218 DPRINT("addr: 0x%x\n", data);
219 si57x_set_addr(slave, data);
220 slave->state = ADDRESSING_DONE;
221 } else {
222 DPRINT("data: 0x%x\n", data);
223 slave->state = WRITING;
224 addr = slave->ptr;
225 if (addr < 6) {
226 si57x_freez_filter(slave, data);
227 slave->regs[addr] = data;
228 slave->ptr++;
229 } else {
230 switch (addr) {
231 case CTRL_REG0_REL:
232 slave->regs[addr] = data;
233 si57x_ctrl0_pw(slave);
234 break;
235 case CTRL_REG1_REL:
236 slave->regs[addr] = data;
237 break;
238 }
239 }
240 }
241
242 return 0;
243}
244
245
246static int si57x_rx(I2CSlave *s)
247{
248 Si57xState *slave = SI57X(s);
249
250 DPRINT("data: 0x%x\n", slave->regs[slave->ptr]);
251
252 return slave->regs[slave->ptr];
253}
254
255static void si57x_event(I2CSlave *i2c, enum i2c_event event)
256{
257 Si57xState *s = SI57X(i2c);
258
259 switch (event) {
260 case I2C_START_SEND:
261 s->state = ADDRESSING;
262 break;
263 case I2C_START_RECV:
264 s->state = READING;
265 break;
266 case I2C_FINISH:
267 case I2C_NACK:
268 s->state = IDEAL;
269 break;
270 }
271}
272
273static int si57x_init(I2CSlave *i2c)
274{
275
276 return 0;
277}
278
279static Property si57x_properties[] = {
280 DEFINE_PROP_UINT16("temperature-stability", Si57xState, temp_stab,
281 TEMP_STAB_50PPM),
282 DEFINE_PROP_END_OF_LIST(),
283};
284
285static void si57x_class_init(ObjectClass *klass, void *data)
286{
287 DeviceClass *dc = DEVICE_CLASS(klass);
288 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
289
290 k->init = si57x_init;
291 k->event = si57x_event;
292 k->recv = si57x_rx;
293 k->send = si57x_tx;
294 dc->props = si57x_properties;
295 dc->reset = si57x_reset;
296}
297
298static const TypeInfo si57x_info = {
299 .name = TYPE_SI57X,
300 .parent = TYPE_I2C_SLAVE,
301 .instance_size = sizeof(Si57xState),
302 .class_init = si57x_class_init,
303};
304
305static void si57x_register_type(void)
306{
307 type_register_static(&si57x_info);
308}
309
310type_init(si57x_register_type)
311