1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include "qemu/osdep.h"
18#include "hw/irq.h"
19#include "hw/qdev-clock.h"
20#include "hw/qdev-properties.h"
21#include "hw/misc/npcm7xx_mft.h"
22#include "hw/misc/npcm7xx_pwm.h"
23#include "hw/registerfields.h"
24#include "migration/vmstate.h"
25#include "qapi/error.h"
26#include "qapi/visitor.h"
27#include "qemu/bitops.h"
28#include "qemu/error-report.h"
29#include "qemu/log.h"
30#include "qemu/module.h"
31#include "qemu/timer.h"
32#include "qemu/units.h"
33#include "trace.h"
34
35
36
37
38
39
40
41REG16(NPCM7XX_MFT_CNT1, 0x00);
42REG16(NPCM7XX_MFT_CRA, 0x02);
43REG16(NPCM7XX_MFT_CRB, 0x04);
44REG16(NPCM7XX_MFT_CNT2, 0x06);
45REG16(NPCM7XX_MFT_PRSC, 0x08);
46REG16(NPCM7XX_MFT_CKC, 0x0a);
47REG16(NPCM7XX_MFT_MCTRL, 0x0c);
48REG16(NPCM7XX_MFT_ICTRL, 0x0e);
49REG16(NPCM7XX_MFT_ICLR, 0x10);
50REG16(NPCM7XX_MFT_IEN, 0x12);
51REG16(NPCM7XX_MFT_CPA, 0x14);
52REG16(NPCM7XX_MFT_CPB, 0x16);
53REG16(NPCM7XX_MFT_CPCFG, 0x18);
54REG16(NPCM7XX_MFT_INASEL, 0x1a);
55REG16(NPCM7XX_MFT_INBSEL, 0x1c);
56
57
58#define NPCM7XX_MFT_CKC_C2CSEL BIT(3)
59#define NPCM7XX_MFT_CKC_C1CSEL BIT(0)
60
61#define NPCM7XX_MFT_MCTRL_TBEN BIT(6)
62#define NPCM7XX_MFT_MCTRL_TAEN BIT(5)
63#define NPCM7XX_MFT_MCTRL_TBEDG BIT(4)
64#define NPCM7XX_MFT_MCTRL_TAEDG BIT(3)
65#define NPCM7XX_MFT_MCTRL_MODE5 BIT(2)
66
67#define NPCM7XX_MFT_ICTRL_TFPND BIT(5)
68#define NPCM7XX_MFT_ICTRL_TEPND BIT(4)
69#define NPCM7XX_MFT_ICTRL_TDPND BIT(3)
70#define NPCM7XX_MFT_ICTRL_TCPND BIT(2)
71#define NPCM7XX_MFT_ICTRL_TBPND BIT(1)
72#define NPCM7XX_MFT_ICTRL_TAPND BIT(0)
73
74#define NPCM7XX_MFT_ICLR_TFCLR BIT(5)
75#define NPCM7XX_MFT_ICLR_TECLR BIT(4)
76#define NPCM7XX_MFT_ICLR_TDCLR BIT(3)
77#define NPCM7XX_MFT_ICLR_TCCLR BIT(2)
78#define NPCM7XX_MFT_ICLR_TBCLR BIT(1)
79#define NPCM7XX_MFT_ICLR_TACLR BIT(0)
80
81#define NPCM7XX_MFT_IEN_TFIEN BIT(5)
82#define NPCM7XX_MFT_IEN_TEIEN BIT(4)
83#define NPCM7XX_MFT_IEN_TDIEN BIT(3)
84#define NPCM7XX_MFT_IEN_TCIEN BIT(2)
85#define NPCM7XX_MFT_IEN_TBIEN BIT(1)
86#define NPCM7XX_MFT_IEN_TAIEN BIT(0)
87
88#define NPCM7XX_MFT_CPCFG_GET_B(rv) extract8((rv), 4, 4)
89#define NPCM7XX_MFT_CPCFG_GET_A(rv) extract8((rv), 0, 4)
90#define NPCM7XX_MFT_CPCFG_HIEN BIT(3)
91#define NPCM7XX_MFT_CPCFG_EQEN BIT(2)
92#define NPCM7XX_MFT_CPCFG_LOEN BIT(1)
93#define NPCM7XX_MFT_CPCFG_CPSEL BIT(0)
94
95#define NPCM7XX_MFT_INASEL_SELA BIT(0)
96#define NPCM7XX_MFT_INBSEL_SELB BIT(0)
97
98
99#define NPCM7XX_MFT_MAX_CNT 0xFFFF
100
101
102#define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2
103
104typedef enum NPCM7xxMFTCaptureState {
105
106 NPCM7XX_CAPTURE_SUCCEED,
107
108 NPCM7XX_CAPTURE_COMPARE_HIT,
109
110 NPCM7XX_CAPTURE_UNDERFLOW,
111} NPCM7xxMFTCaptureState;
112
113static void npcm7xx_mft_reset(NPCM7xxMFTState *s)
114{
115 int i;
116
117
118 for (i = R_NPCM7XX_MFT_PRSC; i <= R_NPCM7XX_MFT_INBSEL; ++i) {
119 s->regs[i] = 0;
120 }
121}
122
123static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState *s, uint8_t iclr)
124{
125
126
127
128
129 s->regs[R_NPCM7XX_MFT_ICTRL] &= ~iclr;
130}
131
132
133
134
135
136
137
138
139static int npcm7xx_mft_compare(int32_t src, uint16_t tgt, uint8_t cpcfg)
140{
141 if (cpcfg & NPCM7XX_MFT_CPCFG_HIEN) {
142 return NPCM7XX_MFT_MAX_CNT;
143 }
144 if ((cpcfg & NPCM7XX_MFT_CPCFG_EQEN) && (src <= tgt)) {
145 return tgt;
146 }
147 if ((cpcfg & NPCM7XX_MFT_CPCFG_LOEN) && (tgt > 0) && (src < tgt)) {
148 return tgt - 1;
149 }
150
151 return -1;
152}
153
154
155static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt(
156 Clock *clock, uint32_t max_rpm, uint32_t duty, uint16_t tgt,
157 uint8_t cpcfg, uint16_t *cnt)
158{
159 uint32_t rpm = (uint64_t)max_rpm * (uint64_t)duty / NPCM7XX_PWM_MAX_DUTY;
160 int32_t count;
161 int stopped;
162 NPCM7xxMFTCaptureState state;
163
164 if (rpm == 0) {
165
166
167
168
169 count = NPCM7XX_MFT_MAX_CNT + 1;
170 } else {
171
172
173
174
175 count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) /
176 (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION));
177 }
178
179 if (count > NPCM7XX_MFT_MAX_CNT) {
180 count = -1;
181 } else {
182
183 count = NPCM7XX_MFT_MAX_CNT - count;
184 }
185 stopped = npcm7xx_mft_compare(count, tgt, cpcfg);
186 if (stopped == -1) {
187 if (count == -1) {
188
189 state = NPCM7XX_CAPTURE_UNDERFLOW;
190 } else {
191 state = NPCM7XX_CAPTURE_SUCCEED;
192 }
193 } else {
194 count = stopped;
195 state = NPCM7XX_CAPTURE_COMPARE_HIT;
196 }
197
198 if (count != -1) {
199 *cnt = count;
200 }
201 trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock),
202 state, count, rpm, duty);
203 return state;
204}
205
206
207
208
209
210static void npcm7xx_mft_capture(NPCM7xxMFTState *s)
211{
212 int irq_level = 0;
213 NPCM7xxMFTCaptureState state;
214 int sel;
215 uint8_t cpcfg;
216
217
218
219
220
221 if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) {
222 return;
223 }
224
225
226 if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN &&
227 s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
228 sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA;
229 cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]);
230 state = npcm7xx_mft_compute_cnt(s->clock_1,
231 sel ? s->max_rpm[2] : s->max_rpm[0],
232 sel ? s->duty[2] : s->duty[0],
233 s->regs[R_NPCM7XX_MFT_CPA],
234 cpcfg,
235 &s->regs[R_NPCM7XX_MFT_CNT1]);
236 switch (state) {
237 case NPCM7XX_CAPTURE_SUCCEED:
238
239 s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1];
240 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND;
241 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) {
242 irq_level = 1;
243 }
244 break;
245
246 case NPCM7XX_CAPTURE_COMPARE_HIT:
247
248 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND;
249 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) {
250 irq_level = 1;
251 }
252 break;
253
254 case NPCM7XX_CAPTURE_UNDERFLOW:
255
256 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND;
257 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) {
258 irq_level = 1;
259 }
260 break;
261
262 default:
263 g_assert_not_reached();
264 }
265 }
266
267
268 if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN &&
269 s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
270 sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB;
271 cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]);
272 state = npcm7xx_mft_compute_cnt(s->clock_2,
273 sel ? s->max_rpm[3] : s->max_rpm[1],
274 sel ? s->duty[3] : s->duty[1],
275 s->regs[R_NPCM7XX_MFT_CPB],
276 cpcfg,
277 &s->regs[R_NPCM7XX_MFT_CNT2]);
278 switch (state) {
279 case NPCM7XX_CAPTURE_SUCCEED:
280
281 s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2];
282 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND;
283 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) {
284 irq_level = 1;
285 }
286 break;
287
288 case NPCM7XX_CAPTURE_COMPARE_HIT:
289
290 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND;
291 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) {
292 irq_level = 1;
293 }
294 break;
295
296 case NPCM7XX_CAPTURE_UNDERFLOW:
297
298 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND;
299 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) {
300 irq_level = 1;
301 }
302 break;
303
304 default:
305 g_assert_not_reached();
306 }
307 }
308
309 trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level);
310 qemu_set_irq(s->irq, irq_level);
311}
312
313
314static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event)
315{
316 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
317 uint64_t prescaled_clock_period;
318
319 prescaled_clock_period = clock_get(s->clock_in) *
320 (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL);
321 trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path,
322 s->regs[R_NPCM7XX_MFT_CKC],
323 clock_get(s->clock_in),
324 prescaled_clock_period);
325
326 if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
327
328 clock_update(s->clock_1, prescaled_clock_period);
329 } else {
330
331 clock_update(s->clock_1, 0);
332 }
333
334 if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
335
336 clock_update(s->clock_2, prescaled_clock_period);
337 } else {
338
339 clock_update(s->clock_2, 0);
340 }
341
342 npcm7xx_mft_capture(s);
343}
344
345static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size)
346{
347 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
348 uint16_t value = 0;
349
350 switch (offset) {
351 case A_NPCM7XX_MFT_ICLR:
352 qemu_log_mask(LOG_GUEST_ERROR,
353 "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
354 __func__, offset);
355 break;
356
357 default:
358 value = s->regs[offset / 2];
359 }
360
361 trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value);
362 return value;
363}
364
365static void npcm7xx_mft_write(void *opaque, hwaddr offset,
366 uint64_t v, unsigned size)
367{
368 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
369
370 trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v);
371 switch (offset) {
372 case A_NPCM7XX_MFT_ICLR:
373 npcm7xx_mft_clear_interrupt(s, v);
374 break;
375
376 case A_NPCM7XX_MFT_CKC:
377 case A_NPCM7XX_MFT_PRSC:
378 s->regs[offset / 2] = v;
379 npcm7xx_mft_update_clock(s, ClockUpdate);
380 break;
381
382 default:
383 s->regs[offset / 2] = v;
384 npcm7xx_mft_capture(s);
385 break;
386 }
387}
388
389static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset,
390 unsigned size, bool is_write,
391 MemTxAttrs attrs)
392{
393 switch (offset) {
394
395 case A_NPCM7XX_MFT_CNT1:
396 case A_NPCM7XX_MFT_CRA:
397 case A_NPCM7XX_MFT_CRB:
398 case A_NPCM7XX_MFT_CNT2:
399 case A_NPCM7XX_MFT_CPA:
400 case A_NPCM7XX_MFT_CPB:
401 return size == 2;
402
403
404 case A_NPCM7XX_MFT_PRSC:
405 case A_NPCM7XX_MFT_CKC:
406 case A_NPCM7XX_MFT_MCTRL:
407 case A_NPCM7XX_MFT_ICTRL:
408 case A_NPCM7XX_MFT_ICLR:
409 case A_NPCM7XX_MFT_IEN:
410 case A_NPCM7XX_MFT_CPCFG:
411 case A_NPCM7XX_MFT_INASEL:
412 case A_NPCM7XX_MFT_INBSEL:
413 return size == 1;
414
415 default:
416
417 return false;
418 }
419}
420
421static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name,
422 void *opaque, Error **errp)
423{
424 visit_type_uint32(v, name, (uint32_t *)opaque, errp);
425}
426
427static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name,
428 void *opaque, Error **errp)
429{
430 NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
431 uint32_t *max_rpm = opaque;
432 uint32_t value;
433
434 if (!visit_type_uint32(v, name, &value, errp)) {
435 return;
436 }
437
438 *max_rpm = value;
439 npcm7xx_mft_capture(s);
440}
441
442static void npcm7xx_mft_duty_handler(void *opaque, int n, int value)
443{
444 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
445
446 trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value);
447 s->duty[n] = value;
448 npcm7xx_mft_capture(s);
449}
450
451static const struct MemoryRegionOps npcm7xx_mft_ops = {
452 .read = npcm7xx_mft_read,
453 .write = npcm7xx_mft_write,
454 .endianness = DEVICE_LITTLE_ENDIAN,
455 .valid = {
456 .min_access_size = 1,
457 .max_access_size = 2,
458 .unaligned = false,
459 .accepts = npcm7xx_mft_check_mem_op,
460 },
461};
462
463static void npcm7xx_mft_enter_reset(Object *obj, ResetType type)
464{
465 NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
466
467 npcm7xx_mft_reset(s);
468}
469
470static void npcm7xx_mft_hold_reset(Object *obj)
471{
472 NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
473
474 qemu_irq_lower(s->irq);
475}
476
477static void npcm7xx_mft_init(Object *obj)
478{
479 NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
480 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
481 DeviceState *dev = DEVICE(obj);
482
483 memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s,
484 TYPE_NPCM7XX_MFT, 4 * KiB);
485 sysbus_init_mmio(sbd, &s->iomem);
486 sysbus_init_irq(sbd, &s->irq);
487 s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock,
488 s, ClockUpdate);
489 s->clock_1 = qdev_init_clock_out(dev, "clock1");
490 s->clock_2 = qdev_init_clock_out(dev, "clock2");
491
492 for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
493 object_property_add(obj, "max_rpm[*]", "uint32",
494 npcm7xx_mft_get_max_rpm,
495 npcm7xx_mft_set_max_rpm,
496 NULL, &s->max_rpm[i]);
497 }
498 qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty",
499 NPCM7XX_MFT_FANIN_COUNT);
500}
501
502static const VMStateDescription vmstate_npcm7xx_mft = {
503 .name = "npcm7xx-mft-module",
504 .version_id = 0,
505 .minimum_version_id = 0,
506 .fields = (VMStateField[]) {
507 VMSTATE_CLOCK(clock_in, NPCM7xxMFTState),
508 VMSTATE_CLOCK(clock_1, NPCM7xxMFTState),
509 VMSTATE_CLOCK(clock_2, NPCM7xxMFTState),
510 VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS),
511 VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
512 VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
513 VMSTATE_END_OF_LIST(),
514 },
515};
516
517static void npcm7xx_mft_class_init(ObjectClass *klass, void *data)
518{
519 ResettableClass *rc = RESETTABLE_CLASS(klass);
520 DeviceClass *dc = DEVICE_CLASS(klass);
521
522 dc->desc = "NPCM7xx MFT Controller";
523 dc->vmsd = &vmstate_npcm7xx_mft;
524 rc->phases.enter = npcm7xx_mft_enter_reset;
525 rc->phases.hold = npcm7xx_mft_hold_reset;
526}
527
528static const TypeInfo npcm7xx_mft_info = {
529 .name = TYPE_NPCM7XX_MFT,
530 .parent = TYPE_SYS_BUS_DEVICE,
531 .instance_size = sizeof(NPCM7xxMFTState),
532 .class_init = npcm7xx_mft_class_init,
533 .instance_init = npcm7xx_mft_init,
534};
535
536static void npcm7xx_mft_register_type(void)
537{
538 type_register_static(&npcm7xx_mft_info);
539}
540type_init(npcm7xx_mft_register_type);
541