xref: /openbmc/qemu/hw/intc/omap_intc.c (revision d341d9f3)
1 /*
2  * TI OMAP interrupt controller emulation.
3  *
4  * Copyright (C) 2006-2008 Andrzej Zaborowski  <balrog@zabor.org>
5  * Copyright (C) 2007-2008 Nokia Corporation
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 or
10  * (at your option) version 3 of the License.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 #include "hw/hw.h"
21 #include "hw/arm/omap.h"
22 #include "hw/sysbus.h"
23 #include "qemu/error-report.h"
24 
25 /* Interrupt Handlers */
26 struct omap_intr_handler_bank_s {
27     uint32_t irqs;
28     uint32_t inputs;
29     uint32_t mask;
30     uint32_t fiq;
31     uint32_t sens_edge;
32     uint32_t swi;
33     unsigned char priority[32];
34 };
35 
36 #define TYPE_OMAP_INTC "common-omap-intc"
37 #define OMAP_INTC(obj) \
38     OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC)
39 
40 struct omap_intr_handler_s {
41     SysBusDevice parent_obj;
42 
43     qemu_irq *pins;
44     qemu_irq parent_intr[2];
45     MemoryRegion mmio;
46     void *iclk;
47     void *fclk;
48     unsigned char nbanks;
49     int level_only;
50     uint32_t size;
51 
52     uint8_t revision;
53 
54     /* state */
55     uint32_t new_agr[2];
56     int sir_intr[2];
57     int autoidle;
58     uint32_t mask;
59     struct omap_intr_handler_bank_s bank[3];
60 };
61 
62 static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
63 {
64     int i, j, sir_intr, p_intr, p;
65     uint32_t level;
66     sir_intr = 0;
67     p_intr = 255;
68 
69     /* Find the interrupt line with the highest dynamic priority.
70      * Note: 0 denotes the hightest priority.
71      * If all interrupts have the same priority, the default order is IRQ_N,
72      * IRQ_N-1,...,IRQ_0. */
73     for (j = 0; j < s->nbanks; ++j) {
74         level = s->bank[j].irqs & ~s->bank[j].mask &
75                 (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
76 
77         while (level != 0) {
78             i = ctz32(level);
79             p = s->bank[j].priority[i];
80             if (p <= p_intr) {
81                 p_intr = p;
82                 sir_intr = 32 * j + i;
83             }
84             level &= level - 1;
85         }
86     }
87     s->sir_intr[is_fiq] = sir_intr;
88 }
89 
90 static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq)
91 {
92     int i;
93     uint32_t has_intr = 0;
94 
95     for (i = 0; i < s->nbanks; ++i)
96         has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
97                 (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
98 
99     if (s->new_agr[is_fiq] & has_intr & s->mask) {
100         s->new_agr[is_fiq] = 0;
101         omap_inth_sir_update(s, is_fiq);
102         qemu_set_irq(s->parent_intr[is_fiq], 1);
103     }
104 }
105 
106 #define INT_FALLING_EDGE	0
107 #define INT_LOW_LEVEL		1
108 
109 static void omap_set_intr(void *opaque, int irq, int req)
110 {
111     struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
112     uint32_t rise;
113 
114     struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
115     int n = irq & 31;
116 
117     if (req) {
118         rise = ~bank->irqs & (1 << n);
119         if (~bank->sens_edge & (1 << n))
120             rise &= ~bank->inputs;
121 
122         bank->inputs |= (1 << n);
123         if (rise) {
124             bank->irqs |= rise;
125             omap_inth_update(ih, 0);
126             omap_inth_update(ih, 1);
127         }
128     } else {
129         rise = bank->sens_edge & bank->irqs & (1 << n);
130         bank->irqs &= ~rise;
131         bank->inputs &= ~(1 << n);
132     }
133 }
134 
135 /* Simplified version with no edge detection */
136 static void omap_set_intr_noedge(void *opaque, int irq, int req)
137 {
138     struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
139     uint32_t rise;
140 
141     struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
142     int n = irq & 31;
143 
144     if (req) {
145         rise = ~bank->inputs & (1 << n);
146         if (rise) {
147             bank->irqs |= bank->inputs |= rise;
148             omap_inth_update(ih, 0);
149             omap_inth_update(ih, 1);
150         }
151     } else
152         bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi;
153 }
154 
155 static uint64_t omap_inth_read(void *opaque, hwaddr addr,
156                                unsigned size)
157 {
158     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
159     int i, offset = addr;
160     int bank_no = offset >> 8;
161     int line_no;
162     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
163     offset &= 0xff;
164 
165     switch (offset) {
166     case 0x00:	/* ITR */
167         return bank->irqs;
168 
169     case 0x04:	/* MIR */
170         return bank->mask;
171 
172     case 0x10:	/* SIR_IRQ_CODE */
173     case 0x14:  /* SIR_FIQ_CODE */
174         if (bank_no != 0)
175             break;
176         line_no = s->sir_intr[(offset - 0x10) >> 2];
177         bank = &s->bank[line_no >> 5];
178         i = line_no & 31;
179         if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
180             bank->irqs &= ~(1 << i);
181         return line_no;
182 
183     case 0x18:	/* CONTROL_REG */
184         if (bank_no != 0)
185             break;
186         return 0;
187 
188     case 0x1c:	/* ILR0 */
189     case 0x20:	/* ILR1 */
190     case 0x24:	/* ILR2 */
191     case 0x28:	/* ILR3 */
192     case 0x2c:	/* ILR4 */
193     case 0x30:	/* ILR5 */
194     case 0x34:	/* ILR6 */
195     case 0x38:	/* ILR7 */
196     case 0x3c:	/* ILR8 */
197     case 0x40:	/* ILR9 */
198     case 0x44:	/* ILR10 */
199     case 0x48:	/* ILR11 */
200     case 0x4c:	/* ILR12 */
201     case 0x50:	/* ILR13 */
202     case 0x54:	/* ILR14 */
203     case 0x58:	/* ILR15 */
204     case 0x5c:	/* ILR16 */
205     case 0x60:	/* ILR17 */
206     case 0x64:	/* ILR18 */
207     case 0x68:	/* ILR19 */
208     case 0x6c:	/* ILR20 */
209     case 0x70:	/* ILR21 */
210     case 0x74:	/* ILR22 */
211     case 0x78:	/* ILR23 */
212     case 0x7c:	/* ILR24 */
213     case 0x80:	/* ILR25 */
214     case 0x84:	/* ILR26 */
215     case 0x88:	/* ILR27 */
216     case 0x8c:	/* ILR28 */
217     case 0x90:	/* ILR29 */
218     case 0x94:	/* ILR30 */
219     case 0x98:	/* ILR31 */
220         i = (offset - 0x1c) >> 2;
221         return (bank->priority[i] << 2) |
222                 (((bank->sens_edge >> i) & 1) << 1) |
223                 ((bank->fiq >> i) & 1);
224 
225     case 0x9c:	/* ISR */
226         return 0x00000000;
227 
228     }
229     OMAP_BAD_REG(addr);
230     return 0;
231 }
232 
233 static void omap_inth_write(void *opaque, hwaddr addr,
234                             uint64_t value, unsigned size)
235 {
236     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
237     int i, offset = addr;
238     int bank_no = offset >> 8;
239     struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
240     offset &= 0xff;
241 
242     switch (offset) {
243     case 0x00:	/* ITR */
244         /* Important: ignore the clearing if the IRQ is level-triggered and
245            the input bit is 1 */
246         bank->irqs &= value | (bank->inputs & bank->sens_edge);
247         return;
248 
249     case 0x04:	/* MIR */
250         bank->mask = value;
251         omap_inth_update(s, 0);
252         omap_inth_update(s, 1);
253         return;
254 
255     case 0x10:	/* SIR_IRQ_CODE */
256     case 0x14:	/* SIR_FIQ_CODE */
257         OMAP_RO_REG(addr);
258         break;
259 
260     case 0x18:	/* CONTROL_REG */
261         if (bank_no != 0)
262             break;
263         if (value & 2) {
264             qemu_set_irq(s->parent_intr[1], 0);
265             s->new_agr[1] = ~0;
266             omap_inth_update(s, 1);
267         }
268         if (value & 1) {
269             qemu_set_irq(s->parent_intr[0], 0);
270             s->new_agr[0] = ~0;
271             omap_inth_update(s, 0);
272         }
273         return;
274 
275     case 0x1c:	/* ILR0 */
276     case 0x20:	/* ILR1 */
277     case 0x24:	/* ILR2 */
278     case 0x28:	/* ILR3 */
279     case 0x2c:	/* ILR4 */
280     case 0x30:	/* ILR5 */
281     case 0x34:	/* ILR6 */
282     case 0x38:	/* ILR7 */
283     case 0x3c:	/* ILR8 */
284     case 0x40:	/* ILR9 */
285     case 0x44:	/* ILR10 */
286     case 0x48:	/* ILR11 */
287     case 0x4c:	/* ILR12 */
288     case 0x50:	/* ILR13 */
289     case 0x54:	/* ILR14 */
290     case 0x58:	/* ILR15 */
291     case 0x5c:	/* ILR16 */
292     case 0x60:	/* ILR17 */
293     case 0x64:	/* ILR18 */
294     case 0x68:	/* ILR19 */
295     case 0x6c:	/* ILR20 */
296     case 0x70:	/* ILR21 */
297     case 0x74:	/* ILR22 */
298     case 0x78:	/* ILR23 */
299     case 0x7c:	/* ILR24 */
300     case 0x80:	/* ILR25 */
301     case 0x84:	/* ILR26 */
302     case 0x88:	/* ILR27 */
303     case 0x8c:	/* ILR28 */
304     case 0x90:	/* ILR29 */
305     case 0x94:	/* ILR30 */
306     case 0x98:	/* ILR31 */
307         i = (offset - 0x1c) >> 2;
308         bank->priority[i] = (value >> 2) & 0x1f;
309         bank->sens_edge &= ~(1 << i);
310         bank->sens_edge |= ((value >> 1) & 1) << i;
311         bank->fiq &= ~(1 << i);
312         bank->fiq |= (value & 1) << i;
313         return;
314 
315     case 0x9c:	/* ISR */
316         for (i = 0; i < 32; i ++)
317             if (value & (1 << i)) {
318                 omap_set_intr(s, 32 * bank_no + i, 1);
319                 return;
320             }
321         return;
322     }
323     OMAP_BAD_REG(addr);
324 }
325 
326 static const MemoryRegionOps omap_inth_mem_ops = {
327     .read = omap_inth_read,
328     .write = omap_inth_write,
329     .endianness = DEVICE_NATIVE_ENDIAN,
330     .valid = {
331         .min_access_size = 4,
332         .max_access_size = 4,
333     },
334 };
335 
336 static void omap_inth_reset(DeviceState *dev)
337 {
338     struct omap_intr_handler_s *s = OMAP_INTC(dev);
339     int i;
340 
341     for (i = 0; i < s->nbanks; ++i){
342         s->bank[i].irqs = 0x00000000;
343         s->bank[i].mask = 0xffffffff;
344         s->bank[i].sens_edge = 0x00000000;
345         s->bank[i].fiq = 0x00000000;
346         s->bank[i].inputs = 0x00000000;
347         s->bank[i].swi = 0x00000000;
348         memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
349 
350         if (s->level_only)
351             s->bank[i].sens_edge = 0xffffffff;
352     }
353 
354     s->new_agr[0] = ~0;
355     s->new_agr[1] = ~0;
356     s->sir_intr[0] = 0;
357     s->sir_intr[1] = 0;
358     s->autoidle = 0;
359     s->mask = ~0;
360 
361     qemu_set_irq(s->parent_intr[0], 0);
362     qemu_set_irq(s->parent_intr[1], 0);
363 }
364 
365 static int omap_intc_init(SysBusDevice *sbd)
366 {
367     DeviceState *dev = DEVICE(sbd);
368     struct omap_intr_handler_s *s = OMAP_INTC(dev);
369 
370     if (!s->iclk) {
371         error_report("omap-intc: clk not connected");
372         return -1;
373     }
374     s->nbanks = 1;
375     sysbus_init_irq(sbd, &s->parent_intr[0]);
376     sysbus_init_irq(sbd, &s->parent_intr[1]);
377     qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32);
378     memory_region_init_io(&s->mmio, OBJECT(s), &omap_inth_mem_ops, s,
379                           "omap-intc", s->size);
380     sysbus_init_mmio(sbd, &s->mmio);
381     return 0;
382 }
383 
384 static Property omap_intc_properties[] = {
385     DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100),
386     DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk),
387     DEFINE_PROP_END_OF_LIST(),
388 };
389 
390 static void omap_intc_class_init(ObjectClass *klass, void *data)
391 {
392     DeviceClass *dc = DEVICE_CLASS(klass);
393     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
394 
395     k->init = omap_intc_init;
396     dc->reset = omap_inth_reset;
397     dc->props = omap_intc_properties;
398     /* Reason: pointer property "clk" */
399     dc->cannot_instantiate_with_device_add_yet = true;
400 }
401 
402 static const TypeInfo omap_intc_info = {
403     .name          = "omap-intc",
404     .parent        = TYPE_OMAP_INTC,
405     .class_init    = omap_intc_class_init,
406 };
407 
408 static uint64_t omap2_inth_read(void *opaque, hwaddr addr,
409                                 unsigned size)
410 {
411     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
412     int offset = addr;
413     int bank_no, line_no;
414     struct omap_intr_handler_bank_s *bank = NULL;
415 
416     if ((offset & 0xf80) == 0x80) {
417         bank_no = (offset & 0x60) >> 5;
418         if (bank_no < s->nbanks) {
419             offset &= ~0x60;
420             bank = &s->bank[bank_no];
421         } else {
422             OMAP_BAD_REG(addr);
423             return 0;
424         }
425     }
426 
427     switch (offset) {
428     case 0x00:	/* INTC_REVISION */
429         return s->revision;
430 
431     case 0x10:	/* INTC_SYSCONFIG */
432         return (s->autoidle >> 2) & 1;
433 
434     case 0x14:	/* INTC_SYSSTATUS */
435         return 1;						/* RESETDONE */
436 
437     case 0x40:	/* INTC_SIR_IRQ */
438         return s->sir_intr[0];
439 
440     case 0x44:	/* INTC_SIR_FIQ */
441         return s->sir_intr[1];
442 
443     case 0x48:	/* INTC_CONTROL */
444         return (!s->mask) << 2;					/* GLOBALMASK */
445 
446     case 0x4c:	/* INTC_PROTECTION */
447         return 0;
448 
449     case 0x50:	/* INTC_IDLE */
450         return s->autoidle & 3;
451 
452     /* Per-bank registers */
453     case 0x80:	/* INTC_ITR */
454         return bank->inputs;
455 
456     case 0x84:	/* INTC_MIR */
457         return bank->mask;
458 
459     case 0x88:	/* INTC_MIR_CLEAR */
460     case 0x8c:	/* INTC_MIR_SET */
461         return 0;
462 
463     case 0x90:	/* INTC_ISR_SET */
464         return bank->swi;
465 
466     case 0x94:	/* INTC_ISR_CLEAR */
467         return 0;
468 
469     case 0x98:	/* INTC_PENDING_IRQ */
470         return bank->irqs & ~bank->mask & ~bank->fiq;
471 
472     case 0x9c:	/* INTC_PENDING_FIQ */
473         return bank->irqs & ~bank->mask & bank->fiq;
474 
475     /* Per-line registers */
476     case 0x100 ... 0x300:	/* INTC_ILR */
477         bank_no = (offset - 0x100) >> 7;
478         if (bank_no > s->nbanks)
479             break;
480         bank = &s->bank[bank_no];
481         line_no = (offset & 0x7f) >> 2;
482         return (bank->priority[line_no] << 2) |
483                 ((bank->fiq >> line_no) & 1);
484     }
485     OMAP_BAD_REG(addr);
486     return 0;
487 }
488 
489 static void omap2_inth_write(void *opaque, hwaddr addr,
490                              uint64_t value, unsigned size)
491 {
492     struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
493     int offset = addr;
494     int bank_no, line_no;
495     struct omap_intr_handler_bank_s *bank = NULL;
496 
497     if ((offset & 0xf80) == 0x80) {
498         bank_no = (offset & 0x60) >> 5;
499         if (bank_no < s->nbanks) {
500             offset &= ~0x60;
501             bank = &s->bank[bank_no];
502         } else {
503             OMAP_BAD_REG(addr);
504             return;
505         }
506     }
507 
508     switch (offset) {
509     case 0x10:	/* INTC_SYSCONFIG */
510         s->autoidle &= 4;
511         s->autoidle |= (value & 1) << 2;
512         if (value & 2) {                                        /* SOFTRESET */
513             omap_inth_reset(DEVICE(s));
514         }
515         return;
516 
517     case 0x48:	/* INTC_CONTROL */
518         s->mask = (value & 4) ? 0 : ~0;				/* GLOBALMASK */
519         if (value & 2) {					/* NEWFIQAGR */
520             qemu_set_irq(s->parent_intr[1], 0);
521             s->new_agr[1] = ~0;
522             omap_inth_update(s, 1);
523         }
524         if (value & 1) {					/* NEWIRQAGR */
525             qemu_set_irq(s->parent_intr[0], 0);
526             s->new_agr[0] = ~0;
527             omap_inth_update(s, 0);
528         }
529         return;
530 
531     case 0x4c:	/* INTC_PROTECTION */
532         /* TODO: Make a bitmap (or sizeof(char)map) of access privileges
533          * for every register, see Chapter 3 and 4 for privileged mode.  */
534         if (value & 1)
535             fprintf(stderr, "%s: protection mode enable attempt\n",
536                             __FUNCTION__);
537         return;
538 
539     case 0x50:	/* INTC_IDLE */
540         s->autoidle &= ~3;
541         s->autoidle |= value & 3;
542         return;
543 
544     /* Per-bank registers */
545     case 0x84:	/* INTC_MIR */
546         bank->mask = value;
547         omap_inth_update(s, 0);
548         omap_inth_update(s, 1);
549         return;
550 
551     case 0x88:	/* INTC_MIR_CLEAR */
552         bank->mask &= ~value;
553         omap_inth_update(s, 0);
554         omap_inth_update(s, 1);
555         return;
556 
557     case 0x8c:	/* INTC_MIR_SET */
558         bank->mask |= value;
559         return;
560 
561     case 0x90:	/* INTC_ISR_SET */
562         bank->irqs |= bank->swi |= value;
563         omap_inth_update(s, 0);
564         omap_inth_update(s, 1);
565         return;
566 
567     case 0x94:	/* INTC_ISR_CLEAR */
568         bank->swi &= ~value;
569         bank->irqs = bank->swi & bank->inputs;
570         return;
571 
572     /* Per-line registers */
573     case 0x100 ... 0x300:	/* INTC_ILR */
574         bank_no = (offset - 0x100) >> 7;
575         if (bank_no > s->nbanks)
576             break;
577         bank = &s->bank[bank_no];
578         line_no = (offset & 0x7f) >> 2;
579         bank->priority[line_no] = (value >> 2) & 0x3f;
580         bank->fiq &= ~(1 << line_no);
581         bank->fiq |= (value & 1) << line_no;
582         return;
583 
584     case 0x00:	/* INTC_REVISION */
585     case 0x14:	/* INTC_SYSSTATUS */
586     case 0x40:	/* INTC_SIR_IRQ */
587     case 0x44:	/* INTC_SIR_FIQ */
588     case 0x80:	/* INTC_ITR */
589     case 0x98:	/* INTC_PENDING_IRQ */
590     case 0x9c:	/* INTC_PENDING_FIQ */
591         OMAP_RO_REG(addr);
592         return;
593     }
594     OMAP_BAD_REG(addr);
595 }
596 
597 static const MemoryRegionOps omap2_inth_mem_ops = {
598     .read = omap2_inth_read,
599     .write = omap2_inth_write,
600     .endianness = DEVICE_NATIVE_ENDIAN,
601     .valid = {
602         .min_access_size = 4,
603         .max_access_size = 4,
604     },
605 };
606 
607 static int omap2_intc_init(SysBusDevice *sbd)
608 {
609     DeviceState *dev = DEVICE(sbd);
610     struct omap_intr_handler_s *s = OMAP_INTC(dev);
611 
612     if (!s->iclk) {
613         error_report("omap2-intc: iclk not connected");
614         return -1;
615     }
616     if (!s->fclk) {
617         error_report("omap2-intc: fclk not connected");
618         return -1;
619     }
620     s->level_only = 1;
621     s->nbanks = 3;
622     sysbus_init_irq(sbd, &s->parent_intr[0]);
623     sysbus_init_irq(sbd, &s->parent_intr[1]);
624     qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32);
625     memory_region_init_io(&s->mmio, OBJECT(s), &omap2_inth_mem_ops, s,
626                           "omap2-intc", 0x1000);
627     sysbus_init_mmio(sbd, &s->mmio);
628     return 0;
629 }
630 
631 static Property omap2_intc_properties[] = {
632     DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s,
633     revision, 0x21),
634     DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk),
635     DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk),
636     DEFINE_PROP_END_OF_LIST(),
637 };
638 
639 static void omap2_intc_class_init(ObjectClass *klass, void *data)
640 {
641     DeviceClass *dc = DEVICE_CLASS(klass);
642     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
643 
644     k->init = omap2_intc_init;
645     dc->reset = omap_inth_reset;
646     dc->props = omap2_intc_properties;
647     /* Reason: pointer property "iclk", "fclk" */
648     dc->cannot_instantiate_with_device_add_yet = true;
649 }
650 
651 static const TypeInfo omap2_intc_info = {
652     .name          = "omap2-intc",
653     .parent        = TYPE_OMAP_INTC,
654     .class_init    = omap2_intc_class_init,
655 };
656 
657 static const TypeInfo omap_intc_type_info = {
658     .name          = TYPE_OMAP_INTC,
659     .parent        = TYPE_SYS_BUS_DEVICE,
660     .instance_size = sizeof(struct omap_intr_handler_s),
661     .abstract      = true,
662 };
663 
664 static void omap_intc_register_types(void)
665 {
666     type_register_static(&omap_intc_type_info);
667     type_register_static(&omap_intc_info);
668     type_register_static(&omap2_intc_info);
669 }
670 
671 type_init(omap_intc_register_types)
672