2 * Nuvoton NPCM7xx MFT Module
4 * Copyright 2021 Google LLC
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "qemu/osdep.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"
30 #include "qemu/module.h"
31 #include "qemu/timer.h"
32 #include "qemu/units.h"
36 * Some of the registers can only accessed via 16-bit ops and some can only
37 * be accessed via 8-bit ops. However we mark all of them using REG16 to
38 * simplify implementation. npcm7xx_mft_check_mem_op checks the access length
39 * of memory operations.
41 REG16(NPCM7XX_MFT_CNT1
, 0x00);
42 REG16(NPCM7XX_MFT_CRA
, 0x02);
43 REG16(NPCM7XX_MFT_CRB
, 0x04);
44 REG16(NPCM7XX_MFT_CNT2
, 0x06);
45 REG16(NPCM7XX_MFT_PRSC
, 0x08);
46 REG16(NPCM7XX_MFT_CKC
, 0x0a);
47 REG16(NPCM7XX_MFT_MCTRL
, 0x0c);
48 REG16(NPCM7XX_MFT_ICTRL
, 0x0e);
49 REG16(NPCM7XX_MFT_ICLR
, 0x10);
50 REG16(NPCM7XX_MFT_IEN
, 0x12);
51 REG16(NPCM7XX_MFT_CPA
, 0x14);
52 REG16(NPCM7XX_MFT_CPB
, 0x16);
53 REG16(NPCM7XX_MFT_CPCFG
, 0x18);
54 REG16(NPCM7XX_MFT_INASEL
, 0x1a);
55 REG16(NPCM7XX_MFT_INBSEL
, 0x1c);
58 #define NPCM7XX_MFT_CKC_C2CSEL BIT(3)
59 #define NPCM7XX_MFT_CKC_C1CSEL BIT(0)
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)
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)
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)
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)
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)
95 #define NPCM7XX_MFT_INASEL_SELA BIT(0)
96 #define NPCM7XX_MFT_INBSEL_SELB BIT(0)
98 /* Max CNT values of the module. The CNT value is a countdown from it. */
99 #define NPCM7XX_MFT_MAX_CNT 0xFFFF
101 /* Each fan revolution should generated 2 pulses */
102 #define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2
104 typedef enum NPCM7xxMFTCaptureState
{
105 /* capture succeeded with a valid CNT value. */
106 NPCM7XX_CAPTURE_SUCCEED
,
107 /* capture stopped prematurely due to reaching CPCFG condition. */
108 NPCM7XX_CAPTURE_COMPARE_HIT
,
109 /* capture fails since it reaches underflow condition for CNT. */
110 NPCM7XX_CAPTURE_UNDERFLOW
,
111 } NPCM7xxMFTCaptureState
;
113 static void npcm7xx_mft_reset(NPCM7xxMFTState
*s
)
117 /* Only registers PRSC ~ INBSEL need to be reset. */
118 for (i
= R_NPCM7XX_MFT_PRSC
; i
<= R_NPCM7XX_MFT_INBSEL
; ++i
) {
123 static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState
*s
, uint8_t iclr
)
126 * Clear bits in ICTRL where corresponding bits in iclr is 1.
127 * Both iclr and ictrl are 8-bit regs. (See npcm7xx_mft_check_mem_op)
129 s
->regs
[R_NPCM7XX_MFT_ICTRL
] &= ~iclr
;
133 * If the CPCFG's condition should be triggered during count down from
134 * NPCM7XX_MFT_MAX_CNT to src if compared to tgt, return the count when
135 * the condition is triggered.
136 * Otherwise return -1.
137 * Since tgt is uint16_t it must always <= NPCM7XX_MFT_MAX_CNT.
139 static int npcm7xx_mft_compare(int32_t src
, uint16_t tgt
, uint8_t cpcfg
)
141 if (cpcfg
& NPCM7XX_MFT_CPCFG_HIEN
) {
142 return NPCM7XX_MFT_MAX_CNT
;
144 if ((cpcfg
& NPCM7XX_MFT_CPCFG_EQEN
) && (src
<= tgt
)) {
147 if ((cpcfg
& NPCM7XX_MFT_CPCFG_LOEN
) && (tgt
> 0) && (src
< tgt
)) {
154 /* Compute CNT according to corresponding fan's RPM. */
155 static 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
)
159 uint32_t rpm
= (uint64_t)max_rpm
* (uint64_t)duty
/ NPCM7XX_PWM_MAX_DUTY
;
162 NPCM7xxMFTCaptureState state
;
166 * If RPM = 0, capture won't happen. CNT will continue count down.
167 * So it's effective equivalent to have a cnt > NPCM7XX_MFT_MAX_CNT
169 count
= NPCM7XX_MFT_MAX_CNT
+ 1;
172 * RPM = revolution/min. The time for one revlution (in ns) is
173 * MINUTE_TO_NANOSECOND / RPM.
175 count
= clock_ns_to_ticks(clock
, (60 * NANOSECONDS_PER_SECOND
) /
176 (rpm
* NPCM7XX_MFT_PULSE_PER_REVOLUTION
));
179 if (count
> NPCM7XX_MFT_MAX_CNT
) {
182 /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */
183 count
= NPCM7XX_MFT_MAX_CNT
- count
;
185 stopped
= npcm7xx_mft_compare(count
, tgt
, cpcfg
);
189 state
= NPCM7XX_CAPTURE_UNDERFLOW
;
191 state
= NPCM7XX_CAPTURE_SUCCEED
;
195 state
= NPCM7XX_CAPTURE_COMPARE_HIT
;
201 trace_npcm7xx_mft_rpm(clock
->canonical_path
, clock_get_hz(clock
),
202 state
, count
, rpm
, duty
);
207 * Capture Fan RPM and update CNT and CR registers accordingly.
208 * Raise IRQ if certain contidions are met in IEN.
210 static void npcm7xx_mft_capture(NPCM7xxMFTState
*s
)
213 NPCM7xxMFTCaptureState state
;
218 * If not mode 5, the behavior is undefined. We just do nothing in this
221 if (!(s
->regs
[R_NPCM7XX_MFT_MCTRL
] & NPCM7XX_MFT_MCTRL_MODE5
)) {
225 /* Capture input A. */
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
],
235 &s
->regs
[R_NPCM7XX_MFT_CNT1
]);
237 case NPCM7XX_CAPTURE_SUCCEED
:
238 /* Interrupt on input capture on TAn transition - TAPND */
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
) {
246 case NPCM7XX_CAPTURE_COMPARE_HIT
:
247 /* Compare Hit - TEPND */
248 s
->regs
[R_NPCM7XX_MFT_ICTRL
] |= NPCM7XX_MFT_ICTRL_TEPND
;
249 if (s
->regs
[R_NPCM7XX_MFT_IEN
] & NPCM7XX_MFT_IEN_TEIEN
) {
254 case NPCM7XX_CAPTURE_UNDERFLOW
:
255 /* Underflow - TCPND */
256 s
->regs
[R_NPCM7XX_MFT_ICTRL
] |= NPCM7XX_MFT_ICTRL_TCPND
;
257 if (s
->regs
[R_NPCM7XX_MFT_IEN
] & NPCM7XX_MFT_IEN_TCIEN
) {
263 g_assert_not_reached();
267 /* Capture input B. */
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
],
277 &s
->regs
[R_NPCM7XX_MFT_CNT2
]);
279 case NPCM7XX_CAPTURE_SUCCEED
:
280 /* Interrupt on input capture on TBn transition - TBPND */
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
) {
288 case NPCM7XX_CAPTURE_COMPARE_HIT
:
289 /* Compare Hit - TFPND */
290 s
->regs
[R_NPCM7XX_MFT_ICTRL
] |= NPCM7XX_MFT_ICTRL_TFPND
;
291 if (s
->regs
[R_NPCM7XX_MFT_IEN
] & NPCM7XX_MFT_IEN_TFIEN
) {
296 case NPCM7XX_CAPTURE_UNDERFLOW
:
297 /* Underflow - TDPND */
298 s
->regs
[R_NPCM7XX_MFT_ICTRL
] |= NPCM7XX_MFT_ICTRL_TDPND
;
299 if (s
->regs
[R_NPCM7XX_MFT_IEN
] & NPCM7XX_MFT_IEN_TDIEN
) {
305 g_assert_not_reached();
309 trace_npcm7xx_mft_capture(DEVICE(s
)->canonical_path
, irq_level
);
310 qemu_set_irq(s
->irq
, irq_level
);
313 /* Update clock for counters. */
314 static void npcm7xx_mft_update_clock(void *opaque
, ClockEvent event
)
316 NPCM7xxMFTState
*s
= NPCM7XX_MFT(opaque
);
317 uint64_t prescaled_clock_period
;
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
);
326 if (s
->regs
[R_NPCM7XX_MFT_CKC
] & NPCM7XX_MFT_CKC_C1CSEL
) {
327 /* Clock is prescaled. */
328 clock_update(s
->clock_1
, prescaled_clock_period
);
331 clock_update(s
->clock_1
, 0);
334 if (s
->regs
[R_NPCM7XX_MFT_CKC
] & NPCM7XX_MFT_CKC_C2CSEL
) {
335 /* Clock is prescaled. */
336 clock_update(s
->clock_2
, prescaled_clock_period
);
339 clock_update(s
->clock_2
, 0);
342 npcm7xx_mft_capture(s
);
345 static uint64_t npcm7xx_mft_read(void *opaque
, hwaddr offset
, unsigned size
)
347 NPCM7xxMFTState
*s
= NPCM7XX_MFT(opaque
);
351 case A_NPCM7XX_MFT_ICLR
:
352 qemu_log_mask(LOG_GUEST_ERROR
,
353 "%s: register @ 0x%04" HWADDR_PRIx
" is write-only\n",
358 value
= s
->regs
[offset
/ 2];
361 trace_npcm7xx_mft_read(DEVICE(s
)->canonical_path
, offset
, value
);
365 static void npcm7xx_mft_write(void *opaque
, hwaddr offset
,
366 uint64_t v
, unsigned size
)
368 NPCM7xxMFTState
*s
= NPCM7XX_MFT(opaque
);
370 trace_npcm7xx_mft_write(DEVICE(s
)->canonical_path
, offset
, v
);
372 case A_NPCM7XX_MFT_ICLR
:
373 npcm7xx_mft_clear_interrupt(s
, v
);
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
);
383 s
->regs
[offset
/ 2] = v
;
384 npcm7xx_mft_capture(s
);
389 static bool npcm7xx_mft_check_mem_op(void *opaque
, hwaddr offset
,
390 unsigned size
, bool is_write
,
394 /* 16-bit registers. Must be accessed with 16-bit read/write.*/
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
:
403 /* 8-bit registers. Must be accessed with 8-bit read/write.*/
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
:
416 /* Invalid registers. */
421 static void npcm7xx_mft_get_max_rpm(Object
*obj
, Visitor
*v
, const char *name
,
422 void *opaque
, Error
**errp
)
424 visit_type_uint32(v
, name
, (uint32_t *)opaque
, errp
);
427 static void npcm7xx_mft_set_max_rpm(Object
*obj
, Visitor
*v
, const char *name
,
428 void *opaque
, Error
**errp
)
430 NPCM7xxMFTState
*s
= NPCM7XX_MFT(obj
);
431 uint32_t *max_rpm
= opaque
;
434 if (!visit_type_uint32(v
, name
, &value
, errp
)) {
439 npcm7xx_mft_capture(s
);
442 static void npcm7xx_mft_duty_handler(void *opaque
, int n
, int value
)
444 NPCM7xxMFTState
*s
= NPCM7XX_MFT(opaque
);
446 trace_npcm7xx_mft_set_duty(DEVICE(s
)->canonical_path
, n
, value
);
448 npcm7xx_mft_capture(s
);
451 static const struct MemoryRegionOps npcm7xx_mft_ops
= {
452 .read
= npcm7xx_mft_read
,
453 .write
= npcm7xx_mft_write
,
454 .endianness
= DEVICE_LITTLE_ENDIAN
,
456 .min_access_size
= 1,
457 .max_access_size
= 2,
459 .accepts
= npcm7xx_mft_check_mem_op
,
463 static void npcm7xx_mft_enter_reset(Object
*obj
, ResetType type
)
465 NPCM7xxMFTState
*s
= NPCM7XX_MFT(obj
);
467 npcm7xx_mft_reset(s
);
470 static void npcm7xx_mft_hold_reset(Object
*obj
)
472 NPCM7xxMFTState
*s
= NPCM7XX_MFT(obj
);
474 qemu_irq_lower(s
->irq
);
477 static void npcm7xx_mft_init(Object
*obj
)
479 NPCM7xxMFTState
*s
= NPCM7XX_MFT(obj
);
480 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
481 DeviceState
*dev
= DEVICE(obj
);
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
,
489 s
->clock_1
= qdev_init_clock_out(dev
, "clock1");
490 s
->clock_2
= qdev_init_clock_out(dev
, "clock2");
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
]);
498 qdev_init_gpio_in_named(dev
, npcm7xx_mft_duty_handler
, "duty",
499 NPCM7XX_MFT_FANIN_COUNT
);
502 static const VMStateDescription vmstate_npcm7xx_mft
= {
503 .name
= "npcm7xx-mft-module",
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(),
517 static void npcm7xx_mft_class_init(ObjectClass
*klass
, void *data
)
519 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
520 DeviceClass
*dc
= DEVICE_CLASS(klass
);
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
;
528 static 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
,
536 static void npcm7xx_mft_register_type(void)
538 type_register_static(&npcm7xx_mft_info
);
540 type_init(npcm7xx_mft_register_type
);