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, (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 /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */ 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 /* Underflow */ 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 * Capture Fan RPM and update CNT and CR registers accordingly. 208 * Raise IRQ if certain contidions are met in IEN. 209 */ 210 static 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 * If not mode 5, the behavior is undefined. We just do nothing in this 219 * case. 220 */ 221 if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) { 222 return; 223 } 224 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], 234 cpcfg, 235 &s->regs[R_NPCM7XX_MFT_CNT1]); 236 switch (state) { 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) { 242 irq_level = 1; 243 } 244 break; 245 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) { 250 irq_level = 1; 251 } 252 break; 253 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) { 258 irq_level = 1; 259 } 260 break; 261 262 default: 263 g_assert_not_reached(); 264 } 265 } 266 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], 276 cpcfg, 277 &s->regs[R_NPCM7XX_MFT_CNT2]); 278 switch (state) { 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) { 284 irq_level = 1; 285 } 286 break; 287 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) { 292 irq_level = 1; 293 } 294 break; 295 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) { 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 /* Update clock for counters. */ 314 static 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 /* Update clock 1 */ 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); 329 } else { 330 /* Clock stopped. */ 331 clock_update(s->clock_1, 0); 332 } 333 /* Update clock 2 */ 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); 337 } else { 338 /* Clock stopped. */ 339 clock_update(s->clock_2, 0); 340 } 341 342 npcm7xx_mft_capture(s); 343 } 344 345 static 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 365 static 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 389 static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset, 390 unsigned size, bool is_write, 391 MemTxAttrs attrs) 392 { 393 switch (offset) { 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: 401 return size == 2; 402 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: 413 return size == 1; 414 415 default: 416 /* Invalid registers. */ 417 return false; 418 } 419 } 420 421 static 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 427 static 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 442 static 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 451 static 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 463 static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) 464 { 465 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 466 467 npcm7xx_mft_reset(s); 468 } 469 470 static void npcm7xx_mft_hold_reset(Object *obj) 471 { 472 NPCM7xxMFTState *s = NPCM7XX_MFT(obj); 473 474 qemu_irq_lower(s->irq); 475 } 476 477 static 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 502 static const VMStateDescription vmstate_npcm7xx_mft = { 503 .name = "npcm7xx-mft-module", 504 .version_id = 0, 505 .minimum_version_id = 0, 506 .fields = (const 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 517 static 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 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, 534 }; 535 536 static void npcm7xx_mft_register_type(void) 537 { 538 type_register_static(&npcm7xx_mft_info); 539 } 540 type_init(npcm7xx_mft_register_type); 541