1 /* 2 * Nuvoton NPCM7xx MFT Module 3 * 4 * Copyright 2021 Google LLC 5 * 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. 10 * 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 14 * for more details. 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 * 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. 40 */ 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); 56 57 /* Register Fields */ 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 /* Max CNT values of the module. The CNT value is a countdown from it. */ 99 #define NPCM7XX_MFT_MAX_CNT 0xFFFF 100 101 /* Each fan revolution should generated 2 pulses */ 102 #define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2 103 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; 112 113 static void npcm7xx_mft_reset(NPCM7xxMFTState *s) 114 { 115 int i; 116 117 /* Only registers PRSC ~ INBSEL need to be reset. */ 118 for (i = R_NPCM7XX_MFT_PRSC; i <= R_NPCM7XX_MFT_INBSEL; ++i) { 119 s->regs[i] = 0; 120 } 121 } 122 123 static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState *s, uint8_t iclr) 124 { 125 /* 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) 128 */ 129 s->regs[R_NPCM7XX_MFT_ICTRL] &= ~iclr; 130 } 131 132 /* 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. 138 */ 139 static 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 /* 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) 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 * 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 168 */ 169 count = NPCM7XX_MFT_MAX_CNT + 1; 170 } else { 171 /* 172 * RPM = revolution/min. The time for one revlution (in ns) is 173 * MINUTE_TO_NANOSECOND / RPM. 174 */ 175 count = clock_ns_to_ticks(clock, 176 (uint64_t)(60 * NANOSECONDS_PER_SECOND) / 177 ((uint64_t)rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION)); 178 } 179 180 if (count > NPCM7XX_MFT_MAX_CNT) { 181 count = -1; 182 } else { 183 /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */ 184 count = NPCM7XX_MFT_MAX_CNT - count; 185 } 186 stopped = npcm7xx_mft_compare(count, tgt, cpcfg); 187 if (stopped == -1) { 188 if (count == -1) { 189 /* Underflow */ 190 state = NPCM7XX_CAPTURE_UNDERFLOW; 191 } else { 192 state = NPCM7XX_CAPTURE_SUCCEED; 193 } 194 } else { 195 count = stopped; 196 state = NPCM7XX_CAPTURE_COMPARE_HIT; 197 } 198 199 if (count != -1) { 200 *cnt = count; 201 } 202 trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock), 203 state, count, rpm, duty); 204 return state; 205 } 206 207 /* 208 * Capture Fan RPM and update CNT and CR registers accordingly. 209 * Raise IRQ if certain contidions are met in IEN. 210 */ 211 static void npcm7xx_mft_capture(NPCM7xxMFTState *s) 212 { 213 int irq_level = 0; 214 NPCM7xxMFTCaptureState state; 215 int sel; 216 uint8_t cpcfg; 217 218 /* 219 * If not mode 5, the behavior is undefined. We just do nothing in this 220 * case. 221 */ 222 if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) { 223 return; 224 } 225 226 /* Capture input A. */ 227 if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN && 228 s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 229 sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA; 230 cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]); 231 state = npcm7xx_mft_compute_cnt(s->clock_1, 232 sel ? s->max_rpm[2] : s->max_rpm[0], 233 sel ? s->duty[2] : s->duty[0], 234 s->regs[R_NPCM7XX_MFT_CPA], 235 cpcfg, 236 &s->regs[R_NPCM7XX_MFT_CNT1]); 237 switch (state) { 238 case NPCM7XX_CAPTURE_SUCCEED: 239 /* Interrupt on input capture on TAn transition - TAPND */ 240 s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1]; 241 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND; 242 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) { 243 irq_level = 1; 244 } 245 break; 246 247 case NPCM7XX_CAPTURE_COMPARE_HIT: 248 /* Compare Hit - TEPND */ 249 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND; 250 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) { 251 irq_level = 1; 252 } 253 break; 254 255 case NPCM7XX_CAPTURE_UNDERFLOW: 256 /* Underflow - TCPND */ 257 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND; 258 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) { 259 irq_level = 1; 260 } 261 break; 262 263 default: 264 g_assert_not_reached(); 265 } 266 } 267 268 /* Capture input B. */ 269 if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN && 270 s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 271 sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB; 272 cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]); 273 state = npcm7xx_mft_compute_cnt(s->clock_2, 274 sel ? s->max_rpm[3] : s->max_rpm[1], 275 sel ? s->duty[3] : s->duty[1], 276 s->regs[R_NPCM7XX_MFT_CPB], 277 cpcfg, 278 &s->regs[R_NPCM7XX_MFT_CNT2]); 279 switch (state) { 280 case NPCM7XX_CAPTURE_SUCCEED: 281 /* Interrupt on input capture on TBn transition - TBPND */ 282 s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2]; 283 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND; 284 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) { 285 irq_level = 1; 286 } 287 break; 288 289 case NPCM7XX_CAPTURE_COMPARE_HIT: 290 /* Compare Hit - TFPND */ 291 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND; 292 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) { 293 irq_level = 1; 294 } 295 break; 296 297 case NPCM7XX_CAPTURE_UNDERFLOW: 298 /* Underflow - TDPND */ 299 s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND; 300 if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) { 301 irq_level = 1; 302 } 303 break; 304 305 default: 306 g_assert_not_reached(); 307 } 308 } 309 310 trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level); 311 qemu_set_irq(s->irq, irq_level); 312 } 313 314 /* Update clock for counters. */ 315 static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event) 316 { 317 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 318 uint64_t prescaled_clock_period; 319 320 prescaled_clock_period = clock_get(s->clock_in) * 321 (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL); 322 trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path, 323 s->regs[R_NPCM7XX_MFT_CKC], 324 clock_get(s->clock_in), 325 prescaled_clock_period); 326 /* Update clock 1 */ 327 if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) { 328 /* Clock is prescaled. */ 329 clock_update(s->clock_1, prescaled_clock_period); 330 } else { 331 /* Clock stopped. */ 332 clock_update(s->clock_1, 0); 333 } 334 /* Update clock 2 */ 335 if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) { 336 /* Clock is prescaled. */ 337 clock_update(s->clock_2, prescaled_clock_period); 338 } else { 339 /* Clock stopped. */ 340 clock_update(s->clock_2, 0); 341 } 342 343 npcm7xx_mft_capture(s); 344 } 345 346 static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size) 347 { 348 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 349 uint16_t value = 0; 350 351 switch (offset) { 352 case A_NPCM7XX_MFT_ICLR: 353 qemu_log_mask(LOG_GUEST_ERROR, 354 "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n", 355 __func__, offset); 356 break; 357 358 default: 359 value = s->regs[offset / 2]; 360 } 361 362 trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value); 363 return value; 364 } 365 366 static void npcm7xx_mft_write(void *opaque, hwaddr offset, 367 uint64_t v, unsigned size) 368 { 369 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 370 371 trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v); 372 switch (offset) { 373 case A_NPCM7XX_MFT_ICLR: 374 npcm7xx_mft_clear_interrupt(s, v); 375 break; 376 377 case A_NPCM7XX_MFT_CKC: 378 case A_NPCM7XX_MFT_PRSC: 379 s->regs[offset / 2] = v; 380 npcm7xx_mft_update_clock(s, ClockUpdate); 381 break; 382 383 default: 384 s->regs[offset / 2] = v; 385 npcm7xx_mft_capture(s); 386 break; 387 } 388 } 389 390 static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset, 391 unsigned size, bool is_write, 392 MemTxAttrs attrs) 393 { 394 switch (offset) { 395 /* 16-bit registers. Must be accessed with 16-bit read/write.*/ 396 case A_NPCM7XX_MFT_CNT1: 397 case A_NPCM7XX_MFT_CRA: 398 case A_NPCM7XX_MFT_CRB: 399 case A_NPCM7XX_MFT_CNT2: 400 case A_NPCM7XX_MFT_CPA: 401 case A_NPCM7XX_MFT_CPB: 402 return size == 2; 403 404 /* 8-bit registers. Must be accessed with 8-bit read/write.*/ 405 case A_NPCM7XX_MFT_PRSC: 406 case A_NPCM7XX_MFT_CKC: 407 case A_NPCM7XX_MFT_MCTRL: 408 case A_NPCM7XX_MFT_ICTRL: 409 case A_NPCM7XX_MFT_ICLR: 410 case A_NPCM7XX_MFT_IEN: 411 case A_NPCM7XX_MFT_CPCFG: 412 case A_NPCM7XX_MFT_INASEL: 413 case A_NPCM7XX_MFT_INBSEL: 414 return size == 1; 415 416 default: 417 /* Invalid registers. */ 418 return false; 419 } 420 } 421 422 static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name, 423 void *opaque, Error **errp) 424 { 425 visit_type_uint32(v, name, (uint32_t *)opaque, errp); 426 } 427 428 static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name, 429 void *opaque, Error **errp) 430 { 431 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 432 uint32_t *max_rpm = opaque; 433 uint32_t value; 434 435 if (!visit_type_uint32(v, name, &value, errp)) { 436 return; 437 } 438 439 *max_rpm = value; 440 npcm7xx_mft_capture(s); 441 } 442 443 static void npcm7xx_mft_duty_handler(void *opaque, int n, int value) 444 { 445 NPCM7xxMFTState *s = NPCM7XX_MFT(opaque); 446 447 trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value); 448 s->duty[n] = value; 449 npcm7xx_mft_capture(s); 450 } 451 452 static const struct MemoryRegionOps npcm7xx_mft_ops = { 453 .read = npcm7xx_mft_read, 454 .write = npcm7xx_mft_write, 455 .endianness = DEVICE_LITTLE_ENDIAN, 456 .valid = { 457 .min_access_size = 1, 458 .max_access_size = 2, 459 .unaligned = false, 460 .accepts = npcm7xx_mft_check_mem_op, 461 }, 462 }; 463 464 static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) 465 { 466 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 467 468 npcm7xx_mft_reset(s); 469 } 470 471 static void npcm7xx_mft_hold_reset(Object *obj, ResetType type) 472 { 473 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 474 475 qemu_irq_lower(s->irq); 476 } 477 478 static void npcm7xx_mft_init(Object *obj) 479 { 480 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 481 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 482 DeviceState *dev = DEVICE(obj); 483 484 memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s, 485 TYPE_NPCM7XX_MFT, 4 * KiB); 486 sysbus_init_mmio(sbd, &s->iomem); 487 sysbus_init_irq(sbd, &s->irq); 488 s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock, 489 s, ClockUpdate); 490 s->clock_1 = qdev_init_clock_out(dev, "clock1"); 491 s->clock_2 = qdev_init_clock_out(dev, "clock2"); 492 493 for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 494 object_property_add(obj, "max_rpm[*]", "uint32", 495 npcm7xx_mft_get_max_rpm, 496 npcm7xx_mft_set_max_rpm, 497 NULL, &s->max_rpm[i]); 498 } 499 qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty", 500 NPCM7XX_MFT_FANIN_COUNT); 501 } 502 503 static const VMStateDescription vmstate_npcm7xx_mft = { 504 .name = "npcm7xx-mft-module", 505 .version_id = 0, 506 .minimum_version_id = 0, 507 .fields = (const VMStateField[]) { 508 VMSTATE_CLOCK(clock_in, NPCM7xxMFTState), 509 VMSTATE_CLOCK(clock_1, NPCM7xxMFTState), 510 VMSTATE_CLOCK(clock_2, NPCM7xxMFTState), 511 VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS), 512 VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 513 VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT), 514 VMSTATE_END_OF_LIST(), 515 }, 516 }; 517 518 static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) 519 { 520 ResettableClass *rc = RESETTABLE_CLASS(klass); 521 DeviceClass *dc = DEVICE_CLASS(klass); 522 523 dc->desc = "NPCM7xx MFT Controller"; 524 dc->vmsd = &vmstate_npcm7xx_mft; 525 rc->phases.enter = npcm7xx_mft_enter_reset; 526 rc->phases.hold = npcm7xx_mft_hold_reset; 527 } 528 529 static const TypeInfo npcm7xx_mft_info = { 530 .name = TYPE_NPCM7XX_MFT, 531 .parent = TYPE_SYS_BUS_DEVICE, 532 .instance_size = sizeof(NPCM7xxMFTState), 533 .class_init = npcm7xx_mft_class_init, 534 .instance_init = npcm7xx_mft_init, 535 }; 536 537 static void npcm7xx_mft_register_type(void) 538 { 539 type_register_static(&npcm7xx_mft_info); 540 } 541 type_init(npcm7xx_mft_register_type); 542