1/*  This file is part of the program GDB, the GNU debugger.
2
3    Copyright (C) 1998, 2007 Free Software Foundation, Inc.
4    Contributed by Cygnus Solutions.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 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,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19    */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24#include "sim-hw.h"
25
26/* DEVICE
27
28
29   mn103int - mn103002 interrupt controller
30
31
32   DESCRIPTION
33
34
35   Implements the mn103002 interrupt controller described in the
36   mn103002 user guide.
37
38
39   PROPERTIES
40
41
42   reg = <icr-adr> <icr-siz> <iagr-adr> <iadr-siz> <extmd-adr> <extmd-siz>
43
44   Specify the address of the ICR (total of 30 registers), IAGR and
45   EXTMD registers (within the parent bus).
46
47   The reg property value `0x34000100 0x7C 0x34000200 0x8 0x3400280
48   0x8' locates the interrupt controller at the addresses specified in
49   the mn103002 interrupt controller user guide.
50
51
52   PORTS
53
54
55   nmi (output)
56
57   Non-maskable interrupt output port.  An event on this output ports
58   indicates a NMI request from the interrupt controller.  The value
59   attached to the event should be ignored.
60
61
62   level (output)
63
64   Maskable interrupt level output port.  An event on this output port
65   indicates a maskable interrupt request at the specified level.  The
66   event value defines the level being requested.
67
68   The interrupt controller will generate an event on this port
69   whenever there is a change to the internal state of the interrupt
70   controller.
71
72
73   ack (input)
74
75   Signal from processor indicating that a maskable interrupt has been
76   accepted and the interrupt controller should latch the IAGR with
77   value of the current highest priority interrupting group.
78
79   The event value is the interrupt level being accepted by the
80   processor.  It should be consistent with the most recent LEVEL sent
81   to the processor from the interrupt controller.
82
83
84   int[0..100] (input)
85
86   Level or edge triggered interrupt input port.  Each of the 30
87   groups (0..30) can have up to 4 (0..3) interrupt inputs.  The
88   interpretation of a port event/value is determined by the
89   configuration of the corresponding interrupt group.
90
91   For convenience, numerous aliases to these interrupt inputs are
92   provided.
93
94
95   BUGS
96
97
98   For edge triggered interrupts, the interrupt controller does not
99   differentiate between POSITIVE (rising) and NEGATIVE (falling)
100   edges.  Instead any input port event is considered to be an
101   interrupt trigger.
102
103   For level sensitive interrupts, the interrupt controller ignores
104   active HIGH/LOW settings and instead always interprets a nonzero
105   port value as an interrupt assertion and a zero port value as a
106   negation.
107
108   */
109
110
111/* The interrupt groups - numbered according to mn103002 convention */
112
113enum mn103int_trigger {
114  ACTIVE_LOW,
115  ACTIVE_HIGH,
116  POSITIVE_EDGE,
117  NEGATIVE_EDGE,
118};
119
120enum mn103int_type {
121  NMI_GROUP,
122  LEVEL_GROUP,
123};
124
125struct mn103int_group {
126  int gid;
127  int level;
128  unsigned enable;
129  unsigned request;
130  unsigned input;
131  enum mn103int_trigger trigger;
132  enum mn103int_type type;
133};
134
135enum {
136  FIRST_NMI_GROUP = 0,
137  LAST_NMI_GROUP = 1,
138  FIRST_LEVEL_GROUP = 2,
139  LAST_LEVEL_GROUP = 30,
140  NR_GROUPS,
141};
142
143enum {
144  LOWEST_LEVEL = 7,
145};
146
147/* The interrupt controller register address blocks */
148
149struct mn103int_block {
150  unsigned_word base;
151  unsigned_word bound;
152};
153
154enum { ICR_BLOCK, IAGR_BLOCK, EXTMD_BLOCK, NR_BLOCKS };
155
156
157struct mn103int {
158  struct mn103int_block block[NR_BLOCKS];
159  struct mn103int_group group[NR_GROUPS];
160  unsigned interrupt_accepted_group;
161};
162
163
164
165/* output port ID's */
166
167enum {
168  NMI_PORT,
169  LEVEL_PORT,
170};
171
172
173/* input port ID's */
174
175enum {
176  G0_PORT = 0,
177  G1_PORT = 4,
178  G2_PORT = 8,
179  G3_PORT = 12,
180  G4_PORT = 16,
181  G5_PORT = 20,
182  G6_PORT = 24,
183  G7_PORT = 28,
184  G8_PORT = 32,
185  G9_PORT = 36,
186  G10_PORT = 40,
187  G11_PORT = 44,
188  G12_PORT = 48,
189  G13_PORT = 52,
190  G14_PORT = 56,
191  G15_PORT = 60,
192  G16_PORT = 64,
193  G17_PORT = 68,
194  G18_PORT = 72,
195  G19_PORT = 76,
196  G20_PORT = 80,
197  G21_PORT = 84,
198  G22_PORT = 88,
199  G23_PORT = 92,
200  IRQ0_PORT = G23_PORT,
201  G24_PORT = 96,
202  G25_PORT = 100,
203  G26_PORT = 104,
204  G27_PORT = 108,
205  IRQ4_PORT = G27_PORT,
206  G28_PORT = 112,
207  G29_PORT = 116,
208  G30_PORT = 120,
209  NR_G_PORTS = 124,
210  ACK_PORT,
211};
212
213static const struct hw_port_descriptor mn103int_ports[] = {
214
215  /* interrupt outputs */
216
217  { "nmi", NMI_PORT, 0, output_port, },
218  { "level", LEVEL_PORT, 0, output_port, },
219
220  /* interrupt ack (latch) input from cpu */
221
222  { "ack", ACK_PORT, 0, input_port, },
223
224  /* interrupt inputs (as names) */
225
226  { "nmirq", G0_PORT + 0, 0, input_port, },
227  { "watchdog", G0_PORT + 1, 0, input_port, },
228  { "syserr", G0_PORT + 2, 0, input_port, },
229
230  { "timer-0-underflow", G2_PORT, 0, input_port, },
231  { "timer-1-underflow", G3_PORT, 0, input_port, },
232  { "timer-2-underflow", G4_PORT, 0, input_port, },
233  { "timer-3-underflow", G5_PORT, 0, input_port, },
234  { "timer-4-underflow", G6_PORT, 0, input_port, },
235  { "timer-5-underflow", G7_PORT, 0, input_port, },
236  { "timer-6-underflow", G8_PORT, 0, input_port, },
237
238  { "timer-6-compare-a", G9_PORT, 0, input_port, },
239  { "timer-6-compare-b", G10_PORT, 0, input_port, },
240
241  { "dma-0-end", G12_PORT, 0, input_port, },
242  { "dma-1-end", G13_PORT, 0, input_port, },
243  { "dma-2-end", G14_PORT, 0, input_port, },
244  { "dma-3-end", G15_PORT, 0, input_port, },
245
246  { "serial-0-receive",  G16_PORT, 0, input_port, },
247  { "serial-0-transmit", G17_PORT, 0, input_port, },
248
249  { "serial-1-receive",  G18_PORT, 0, input_port, },
250  { "serial-1-transmit", G19_PORT, 0, input_port, },
251
252  { "serial-2-receive",  G20_PORT, 0, input_port, },
253  { "serial-2-transmit", G21_PORT, 0, input_port, },
254
255  { "irq-0", G23_PORT, 0, input_port, },
256  { "irq-1", G24_PORT, 0, input_port, },
257  { "irq-2", G25_PORT, 0, input_port, },
258  { "irq-3", G26_PORT, 0, input_port, },
259  { "irq-4", G27_PORT, 0, input_port, },
260  { "irq-5", G28_PORT, 0, input_port, },
261  { "irq-6", G29_PORT, 0, input_port, },
262  { "irq-7", G30_PORT, 0, input_port, },
263
264  /* interrupt inputs (as generic numbers) */
265
266  { "int", 0, NR_G_PORTS, input_port, },
267
268  { NULL, },
269};
270
271
272/* Macros for extracting/restoring the various register bits */
273
274#define EXTRACT_ID(X) (LSEXTRACTED8 ((X), 3, 0))
275#define INSERT_ID(X) (LSINSERTED8 ((X), 3, 0))
276
277#define EXTRACT_IR(X) (LSEXTRACTED8 ((X), 7, 4))
278#define INSERT_IR(X) (LSINSERTED8 ((X), 7, 4))
279
280#define EXTRACT_IE(X) (LSEXTRACTED8 ((X), 3, 0))
281#define INSERT_IE(X) (LSINSERTED8 ((X), 3, 0))
282
283#define EXTRACT_LV(X) (LSEXTRACTED8 ((X), 6, 4))
284#define INSERT_LV(X) (LSINSERTED8 ((X), 6, 4))
285
286
287
288/* Finish off the partially created hw device.  Attach our local
289   callbacks.  Wire up our port names etc */
290
291static hw_io_read_buffer_method mn103int_io_read_buffer;
292static hw_io_write_buffer_method mn103int_io_write_buffer;
293static hw_port_event_method mn103int_port_event;
294static hw_ioctl_method mn103int_ioctl;
295
296
297
298static void
299attach_mn103int_regs (struct hw *me,
300		      struct mn103int *controller)
301{
302  int i;
303  if (hw_find_property (me, "reg") == NULL)
304    hw_abort (me, "Missing \"reg\" property");
305  for (i = 0; i < NR_BLOCKS; i++)
306    {
307      unsigned_word attach_address;
308      int attach_space;
309      unsigned attach_size;
310      reg_property_spec reg;
311      if (!hw_find_reg_array_property (me, "reg", i, &reg))
312	hw_abort (me, "\"reg\" property must contain three addr/size entries");
313      hw_unit_address_to_attach_address (hw_parent (me),
314					 &reg.address,
315					 &attach_space,
316					 &attach_address,
317					 me);
318      controller->block[i].base = attach_address;
319      hw_unit_size_to_attach_size (hw_parent (me),
320				   &reg.size,
321				   &attach_size, me);
322      controller->block[i].bound = attach_address + (attach_size - 1);
323      hw_attach_address (hw_parent (me),
324			 0,
325			 attach_space, attach_address, attach_size,
326			 me);
327    }
328}
329
330static void
331mn103int_finish (struct hw *me)
332{
333  int gid;
334  struct mn103int *controller;
335
336  controller = HW_ZALLOC (me, struct mn103int);
337  set_hw_data (me, controller);
338  set_hw_io_read_buffer (me, mn103int_io_read_buffer);
339  set_hw_io_write_buffer (me, mn103int_io_write_buffer);
340  set_hw_ports (me, mn103int_ports);
341  set_hw_port_event (me, mn103int_port_event);
342  me->to_ioctl = mn103int_ioctl;
343
344  /* Attach ourself to our parent bus */
345  attach_mn103int_regs (me, controller);
346
347  /* Initialize all the groups according to their default configuration */
348  for (gid = 0; gid < NR_GROUPS; gid++)
349    {
350      struct mn103int_group *group = &controller->group[gid];
351      group->trigger = NEGATIVE_EDGE;
352      group->gid = gid;
353      if (FIRST_NMI_GROUP <= gid && gid <= LAST_NMI_GROUP)
354	{
355	  group->enable = 0xf;
356	  group->type = NMI_GROUP;
357	}
358      else if (FIRST_LEVEL_GROUP <= gid && gid <= LAST_LEVEL_GROUP)
359	{
360	  group->enable = 0x0;
361	  group->type = LEVEL_GROUP;
362	}
363      else
364	hw_abort (me, "internal error - unknown group id");
365    }
366}
367
368
369
370/* Perform the nasty work of figuring out which of the interrupt
371   groups should have its interrupt delivered. */
372
373static int
374find_highest_interrupt_group (struct hw *me,
375			      struct mn103int *controller)
376{
377  int gid;
378  int selected;
379
380  /* FIRST_NMI_GROUP (group zero) is used as a special default value
381     when searching for an interrupt group.*/
382  selected = FIRST_NMI_GROUP;
383  controller->group[FIRST_NMI_GROUP].level = 7;
384
385  for (gid = FIRST_LEVEL_GROUP; gid <= LAST_LEVEL_GROUP; gid++)
386    {
387      struct mn103int_group *group = &controller->group[gid];
388      if ((group->request & group->enable) != 0)
389	{
390	  /* Remember, lower level, higher priority.  */
391	  if (group->level < controller->group[selected].level)
392	    {
393	      selected = gid;
394	    }
395	}
396    }
397  return selected;
398}
399
400
401/* Notify the processor of an interrupt level update */
402
403static void
404push_interrupt_level (struct hw *me,
405		      struct mn103int *controller)
406{
407  int selected = find_highest_interrupt_group (me, controller);
408  int level = controller->group[selected].level;
409  HW_TRACE ((me, "port-out - selected=%d level=%d", selected, level));
410  hw_port_event (me, LEVEL_PORT, level);
411}
412
413
414/* An event arrives on an interrupt port */
415
416static void
417mn103int_port_event (struct hw *me,
418		     int my_port,
419		     struct hw *source,
420		     int source_port,
421		     int level)
422{
423  struct mn103int *controller = hw_data (me);
424
425  switch (my_port)
426    {
427
428    case ACK_PORT:
429      {
430	int selected = find_highest_interrupt_group (me, controller);
431	if (controller->group[selected].level != level)
432	  hw_abort (me, "botched level synchronisation");
433	controller->interrupt_accepted_group = selected;
434	HW_TRACE ((me, "port-event port=ack level=%d - selected=%d",
435		   level, selected));
436	break;
437      }
438
439    default:
440      {
441	int gid;
442	int iid;
443	struct mn103int_group *group;
444	unsigned interrupt;
445	if (my_port > NR_G_PORTS)
446	  hw_abort (me, "Event on unknown port %d", my_port);
447
448	/* map the port onto an interrupt group */
449	gid = (my_port % NR_G_PORTS) / 4;
450	group = &controller->group[gid];
451	iid = (my_port % 4);
452	interrupt = 1 << iid;
453
454	/* update our cached input */
455	if (level)
456	  group->input |= interrupt;
457	else
458	  group->input &= ~interrupt;
459
460	/* update the request bits */
461	switch (group->trigger)
462	  {
463	  case ACTIVE_LOW:
464	  case ACTIVE_HIGH:
465	    if (level)
466	      group->request |= interrupt;
467	    break;
468	  case NEGATIVE_EDGE:
469	  case POSITIVE_EDGE:
470	    group->request |= interrupt;
471	  }
472
473	/* force a corresponding output */
474	switch (group->type)
475	  {
476
477	  case NMI_GROUP:
478	    {
479	      /* for NMI's the event is the trigger */
480	      HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - NMI",
481			 my_port, gid, iid));
482	      if ((group->request & group->enable) != 0)
483		{
484		  HW_TRACE ((me, "port-out NMI"));
485		  hw_port_event (me, NMI_PORT, 1);
486		}
487	      break;
488	    }
489
490	  case LEVEL_GROUP:
491	    {
492	      /* if an interrupt is now pending */
493	      HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - INT",
494			 my_port, gid, iid));
495	      push_interrupt_level (me, controller);
496	      break;
497	    }
498	  }
499	break;
500      }
501
502    }
503}
504
505/* Read/write to to an ICR (group control register) */
506
507static struct mn103int_group *
508decode_group (struct hw *me,
509	      struct mn103int *controller,
510	      unsigned_word base,
511	      unsigned_word *offset)
512{
513  int gid = (base / 4) % NR_GROUPS;
514  *offset = (base % 4);
515  return &controller->group[gid];
516}
517
518static unsigned8
519read_icr (struct hw *me,
520	  struct mn103int *controller,
521	  unsigned_word base)
522{
523  unsigned_word offset;
524  struct mn103int_group *group = decode_group (me, controller, base, &offset);
525  unsigned8 val = 0;
526  switch (group->type)
527    {
528
529    case NMI_GROUP:
530      switch (offset)
531	{
532	case 0:
533	  val = INSERT_ID (group->request);
534	  HW_TRACE ((me, "read-icr group=%d:0 nmi 0x%02x",
535		     group->gid, val));
536	  break;
537	default:
538	  break;
539	}
540      break;
541
542    case LEVEL_GROUP:
543      switch (offset)
544	{
545	case 0:
546	  val = (INSERT_IR (group->request)
547		 | INSERT_ID (group->request & group->enable));
548	  HW_TRACE ((me, "read-icr group=%d:0 level 0x%02x",
549		     group->gid, val));
550	  break;
551	case 1:
552	  val = (INSERT_LV (group->level)
553		 | INSERT_IE (group->enable));
554	  HW_TRACE ((me, "read-icr level-%d:1 level 0x%02x",
555		     group->gid, val));
556	  break;
557	}
558      break;
559
560    default:
561      break;
562
563    }
564
565  return val;
566}
567
568static void
569write_icr (struct hw *me,
570	   struct mn103int *controller,
571	   unsigned_word base,
572	   unsigned8 val)
573{
574  unsigned_word offset;
575  struct mn103int_group *group = decode_group (me, controller, base, &offset);
576  switch (group->type)
577    {
578
579    case NMI_GROUP:
580      switch (offset)
581	{
582	case 0:
583	  HW_TRACE ((me, "write-icr group=%d:0 nmi 0x%02x",
584		     group->gid, val));
585	  group->request &= ~EXTRACT_ID (val);
586	  break;
587	  /* Special backdoor access to SYSEF flag from CPU.  See
588             interp.c:program_interrupt(). */
589	case 3:
590	  HW_TRACE ((me, "write-icr-special group=%d:0 nmi 0x%02x",
591		     group->gid, val));
592	  group->request |= EXTRACT_ID (val);
593	default:
594	  break;
595	}
596      break;
597
598    case LEVEL_GROUP:
599      switch (offset)
600	{
601	case 0: /* request/detect */
602	  /* Clear any ID bits and then set them according to IR */
603	  HW_TRACE ((me, "write-icr group=%d:0 level 0x%02x %x:%x:%x",
604		     group->gid, val,
605		     group->request, EXTRACT_IR (val), EXTRACT_ID (val)));
606	  group->request =
607	    ((EXTRACT_IR (val) & EXTRACT_ID (val))
608	     | (EXTRACT_IR (val) & group->request)
609	     | (~EXTRACT_IR (val) & ~EXTRACT_ID (val) & group->request));
610	  break;
611	case 1: /* level/enable */
612	  HW_TRACE ((me, "write-icr group=%d:1 level 0x%02x",
613		     group->gid, val));
614	  group->level = EXTRACT_LV (val);
615	  group->enable = EXTRACT_IE (val);
616	  break;
617	default:
618	  /* ignore */
619	  break;
620	}
621      push_interrupt_level (me, controller);
622      break;
623
624    default:
625      break;
626
627    }
628}
629
630
631/* Read the IAGR (Interrupt accepted group register) */
632
633static unsigned8
634read_iagr (struct hw *me,
635	   struct mn103int *controller,
636	   unsigned_word offset)
637{
638  unsigned8 val;
639  switch (offset)
640    {
641    case 0:
642      {
643	if (!(controller->group[controller->interrupt_accepted_group].request
644	      & controller->group[controller->interrupt_accepted_group].enable))
645	  {
646	    /* oops, lost the request */
647	    val = 0;
648	    HW_TRACE ((me, "read-iagr:0 lost-0"));
649	  }
650	else
651	  {
652	    val = (controller->interrupt_accepted_group << 2);
653	    HW_TRACE ((me, "read-iagr:0 %d", (int) val));
654	  }
655	break;
656      }
657    case 1:
658      val = 0;
659      HW_TRACE ((me, "read-iagr:1 %d", (int) val));
660      break;
661    default:
662      val = 0;
663      HW_TRACE ((me, "read-iagr 0x%08lx bad offset", (long) offset));
664      break;
665    }
666  return val;
667}
668
669
670/* Reads/writes to the EXTMD (external interrupt trigger configuration
671   register) */
672
673static struct mn103int_group *
674external_group (struct mn103int *controller,
675		unsigned_word offset)
676{
677  switch (offset)
678    {
679    case 0:
680      return &controller->group[IRQ0_PORT/4];
681    case 1:
682      return &controller->group[IRQ4_PORT/4];
683    default:
684      return NULL;
685    }
686}
687
688static unsigned8
689read_extmd (struct hw *me,
690	    struct mn103int *controller,
691	    unsigned_word offset)
692{
693  int gid;
694  unsigned8 val = 0;
695  struct mn103int_group *group = external_group (controller, offset);
696  if (group != NULL)
697    {
698      for (gid = 0; gid < 4; gid++)
699	{
700	  val |= (group[gid].trigger << (gid * 2));
701	}
702    }
703  HW_TRACE ((me, "read-extmd 0x%02lx", (long) val));
704  return val;
705}
706
707static void
708write_extmd (struct hw *me,
709	     struct mn103int *controller,
710	     unsigned_word offset,
711	     unsigned8 val)
712{
713  int gid;
714  struct mn103int_group *group = external_group (controller, offset);
715  if (group != NULL)
716    {
717      for (gid = 0; gid < 4; gid++)
718	{
719	  group[gid].trigger = (val >> (gid * 2)) & 0x3;
720	  /* MAYBE: interrupts already pending? */
721	}
722    }
723  HW_TRACE ((me, "write-extmd 0x%02lx", (long) val));
724}
725
726
727/* generic read/write */
728
729static int
730decode_addr (struct hw *me,
731	     struct mn103int *controller,
732	     unsigned_word address,
733	     unsigned_word *offset)
734{
735  int i;
736  for (i = 0; i < NR_BLOCKS; i++)
737    {
738      if (address >= controller->block[i].base
739	  && address <= controller->block[i].bound)
740	{
741	  *offset = address - controller->block[i].base;
742	  return i;
743	}
744    }
745  hw_abort (me, "bad address");
746  return -1;
747}
748
749static unsigned
750mn103int_io_read_buffer (struct hw *me,
751			 void *dest,
752			 int space,
753			 unsigned_word base,
754			 unsigned nr_bytes)
755{
756  struct mn103int *controller = hw_data (me);
757  unsigned8 *buf = dest;
758  unsigned byte;
759  /* HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); */
760  for (byte = 0; byte < nr_bytes; byte++)
761    {
762      unsigned_word address = base + byte;
763      unsigned_word offset;
764      switch (decode_addr (me, controller, address, &offset))
765	{
766	case ICR_BLOCK:
767	  buf[byte] = read_icr (me, controller, offset);
768	  break;
769	case IAGR_BLOCK:
770	  buf[byte] = read_iagr (me, controller, offset);
771	  break;
772	case EXTMD_BLOCK:
773	  buf[byte] = read_extmd (me, controller, offset);
774	  break;
775	default:
776	  hw_abort (me, "bad switch");
777	}
778    }
779  return nr_bytes;
780}
781
782static unsigned
783mn103int_io_write_buffer (struct hw *me,
784			  const void *source,
785			  int space,
786			  unsigned_word base,
787			  unsigned nr_bytes)
788{
789  struct mn103int *controller = hw_data (me);
790  const unsigned8 *buf = source;
791  unsigned byte;
792  /* HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); */
793  for (byte = 0; byte < nr_bytes; byte++)
794    {
795      unsigned_word address = base + byte;
796      unsigned_word offset;
797      switch (decode_addr (me, controller, address, &offset))
798	{
799	case ICR_BLOCK:
800	  write_icr (me, controller, offset, buf[byte]);
801	  break;
802	case IAGR_BLOCK:
803	  /* not allowed */
804	  break;
805	case EXTMD_BLOCK:
806	  write_extmd (me, controller, offset, buf[byte]);
807	  break;
808	default:
809	  hw_abort (me, "bad switch");
810	}
811    }
812  return nr_bytes;
813}
814
815static int
816mn103int_ioctl(struct hw *me,
817	       hw_ioctl_request request,
818	       va_list ap)
819{
820  struct mn103int *controller = (struct mn103int *)hw_data(me);
821  controller->group[0].request = EXTRACT_ID(4);
822  mn103int_port_event(me, 2 /* nmi_port(syserr) */, NULL, 0, 0);
823  return 0;
824}
825
826
827const struct hw_descriptor dv_mn103int_descriptor[] = {
828  { "mn103int", mn103int_finish, },
829  { NULL },
830};
831