source: multiio/pcmmio/original/mio_io_rtems.c @ 5ebe1e8

Last change on this file since 5ebe1e8 was 5ebe1e8, checked in by Joel Sherrill <joel.sherrill@…>, on 12/11/09 at 19:30:22

2009-12-11 Joel Sherrill <joel.sherrill@…>

  • README: Add section on RTEMS Configuration needed by driver.
  • mio_io_rtems.c: Switch to using a message queue for transmitting discrete interrupt changes to the task level.
  • pcmmio_shell.c: Modify application configuration to take into account one less barrier and the new message queue.
  • Property mode set to 100644
File size: 19.2 KB
Line 
1/* mio_io.c WinSystems support module file for the  PCM-MIO RTEMS driver
2 *
3 *  $Id$
4 *
5 *  This file implements the hardware access routines as implemented for RTEMS.
6 *  This is very likely close to what is required with no OS.
7 */
8
9/* #define DEBUG 1 */
10
11#include "mio_io.h"   
12
13#include <stdio.h>
14#include <fcntl.h>      /* open */
15#include <unistd.h>     /* exit */
16#include <sys/ioctl.h>  /* ioctl */
17#include <stdlib.h>     /* for exit */
18
19#include <rtems.h>
20#include <i386_io.h>
21#include <bsp/irq.h>
22
23/*
24 *  These are configured by the initialization call.
25 */
26
27/* IRQ source or 0 ==> polled */
28static unsigned short irq = 0;
29/* This holds the base addresses of the board */
30static unsigned short base_port = 0;
31
32/* Function prototypes for local functions */
33int get_buffered_int(
34  unsigned long long *timestamp
35);
36void init_io(unsigned short io_address);
37void clr_int(int bit_number);
38int get_int(void);
39
40/* RTEMS Ids for Wait Queues */
41rtems_id wq_a2d_1;
42rtems_id wq_a2d_2;
43rtems_id wq_dac_1;
44rtems_id wq_dac_2;
45rtems_id wq_dio;
46
47/*
48 *  Limits on number of buffered discrete input interrupts in
49 *  the message queue.
50 */
51#define MAXIMUM_BUFFERED_DISCRETE_INTERRUPTS 1024
52
53///////////////////////////////////////////////////////////////////////////////
54typedef struct {
55  unsigned long long timestamp;
56  int                pin;
57} din_message_t;
58
59unsigned int pcmmio_dio_missed_interrupts;
60
61int interruptible_sleep_on(
62  rtems_id *id,
63  int       milliseconds
64);
65void wake_up_interruptible(
66  rtems_id *id
67);
68
69//
70//    MIO_READ_IRQ_ASSIGNED
71//
72//////////////////////////////////////////////////////////////////////////////
73
74int mio_read_irq_assigned(void)
75{
76  mio_error_code = MIO_SUCCESS;
77
78  if (check_handle())   /* Check for chip available */
79    return -1;
80
81  /* All of our programming of the hardware is handled at this level so that
82     all of the routines that need to shove and IRQ value into hardware will
83     use this call.
84  */
85
86  return (irq & 0xff);
87}
88
89///////////////////////////////////////////////////////////////////////////////
90//
91//    READ_DIO_BYTE
92//
93//////////////////////////////////////////////////////////////////////////////
94
95unsigned char read_dio_byte(int offset)
96{
97  unsigned char byte_val;
98  unsigned char offset_val;
99
100  mio_error_code = MIO_SUCCESS;
101
102  if (check_handle())   /* Check for chip available */
103    return -1;
104
105  /* All bit operations are handled at this level so we need only
106     read and write bytes from the actual hardware.
107  */
108
109  offset_val = offset & 0xff;
110  byte_val = inb(base_port + 0x10 + offset_val);
111  return (byte_val & 0xff);
112}
113
114///////////////////////////////////////////////////////////////////////////////
115//
116//    MIO_READ_REG
117//
118//////////////////////////////////////////////////////////////////////////////
119
120unsigned char mio_read_reg(int offset)
121{
122  unsigned char byte_val;
123  unsigned char offset_val;
124
125  mio_error_code = MIO_SUCCESS;
126
127  if (check_handle())   /* Check for chip available */
128    return -1;
129
130
131  /* This is a catchall register read routine that allows reading of
132     ANY of the registers on the PCM-MIO. It is used primarily for
133     retreiving control and access values in the hardware.
134   */
135
136  offset_val = offset & 0xff;
137  byte_val = inb(base_port + offset_val);
138  return (byte_val & 0xff);
139}
140
141///////////////////////////////////////////////////////////////////////////////
142//
143//    MIO_WRITE_REG
144//
145//////////////////////////////////////////////////////////////////////////////
146
147int mio_write_reg(int offset, unsigned char value)
148{
149  unsigned char byte_val;
150  unsigned char offset_val;
151
152  mio_error_code = MIO_SUCCESS;
153
154  if (check_handle())   /* Check for chip available */
155    return -1;
156
157  /* This function like the previous allow unlimited
158     write access to ALL of the registers on the PCM-MIO
159   */
160
161  offset_val = offset & 0xff;
162  byte_val = value;
163  outb(byte_val, base_port + offset_val);
164 
165  return 0;
166}
167
168
169///////////////////////////////////////////////////////////////////////////////
170//
171//    WRITE_DIO_BYTE
172//
173//////////////////////////////////////////////////////////////////////////////
174
175int write_dio_byte(int offset, unsigned char value)
176{
177  unsigned char byte_val;
178  unsigned char offset_val;
179
180  mio_error_code = MIO_SUCCESS;
181
182  if (check_handle())   /* Check for chip available */
183    return -1;
184
185  /* All bit operations for the DIO are handled at this level
186     and we need the driver to allow access to the actual
187     DIO registers to update the value.
188  */
189
190  offset_val = offset & 0xff;
191  byte_val = value;
192  outb(byte_val, base_port + 0x10 + offset_val);
193
194  return 0;
195}
196
197
198///////////////////////////////////////////////////////////////////////////////
199//
200//    WRITE_DAC_COMMAND
201//
202//////////////////////////////////////////////////////////////////////////////
203
204int write_dac_command(int dac_num,unsigned char value)
205{
206  unsigned char  byte_val;
207  unsigned char  offset_val;
208
209  mio_error_code = MIO_SUCCESS;
210
211  if (check_handle())   /* Check for chip available */
212    return -1;
213
214  byte_val = dac_num & 0xff;            /* This is the DAC number */
215  offset_val = value;                   /* This is the data value */
216  if (byte_val)
217    outb(offset_val,base_port + 0x0e);
218  else
219    outb(offset_val,base_port + 0x0a);
220
221  return 0;
222}
223
224///////////////////////////////////////////////////////////////////////////////
225//
226//    WRITE_ADC_COMMAND
227//
228//////////////////////////////////////////////////////////////////////////////
229
230int write_adc_command(int adc_num,unsigned char value)
231{
232  unsigned char byte_val;
233  unsigned char offset_val;
234
235  mio_error_code = MIO_SUCCESS;
236
237  if (check_handle())   /* Check for chip available */
238    return -1;
239
240  byte_val = adc_num & 0xff;            /* This is the ADC number */
241  offset_val = value;                   /* This is the data value */
242
243  if(byte_val)
244    outb(offset_val,base_port + 0x06);
245  else
246    outb(offset_val,base_port + 0x02);
247  return 0;
248}
249
250///////////////////////////////////////////////////////////////////////////////
251//
252//    WRITE_DAC_DATA
253//
254//////////////////////////////////////////////////////////////////////////////
255
256int write_dac_data(int dac_num, unsigned short value)
257{
258  unsigned short word_val;
259  unsigned char byte_val;
260
261  mio_error_code = MIO_SUCCESS;
262
263  if (check_handle())   /* Check for chip available */
264    return -1;
265
266  byte_val = dac_num;
267  word_val = value;
268
269  if(byte_val)          /* DAC 1 */
270    outw(word_val,base_port+0x0c);
271  else
272    outw(word_val,base_port+8);
273 
274  return 0;
275}
276
277///////////////////////////////////////////////////////////////////////////////
278//
279//    DAC_READ_STATUS
280//
281//////////////////////////////////////////////////////////////////////////////
282
283unsigned char dac_read_status(int dac_num)
284{
285  mio_error_code = MIO_SUCCESS;
286
287  if (check_handle())   /* Check for chip available */
288    return -1;
289
290  if (dac_num)
291    return inb(base_port + 0x0f);
292
293  return inb(base_port + 0x0b);
294}
295
296///////////////////////////////////////////////////////////////////////////////
297//
298//    ADC_READ_STATUS
299//
300//////////////////////////////////////////////////////////////////////////////
301
302unsigned char adc_read_status(int adc_num)
303{
304  mio_error_code = MIO_SUCCESS;
305
306  if (check_handle())   /* Check for chip available */
307    return -1;
308
309  if (adc_num)
310    return inb(base_port + 7);
311  return inb(base_port + 3);
312}
313
314///////////////////////////////////////////////////////////////////////////////
315//
316//    ADC_READ_CONVERSION_DATA
317//
318//////////////////////////////////////////////////////////////////////////////
319
320unsigned short adc_read_conversion_data(int channel)
321{
322int adc_num;
323
324  mio_error_code = MIO_SUCCESS;
325
326  if (check_handle())   /* Check for chip available */
327    return -1;
328
329  if (channel > 7)
330    adc_num = 1;
331  else
332    adc_num = 0;
333
334  if (adc_num)
335    return inw(base_port + 4);
336
337  return inw(base_port);
338}
339
340
341int dio_get_int_with_timestamp(
342  unsigned long long *timestamp
343)
344{
345  if (check_handle())   /* Check for chip available */
346    return -1;
347
348  return get_buffered_int(timestamp) & 0xff;
349}
350
351int dio_get_int(void)
352{
353  return dio_get_int_with_timestamp(NULL);
354}
355
356int wait_adc_int_with_timeout(int adc_num, int milliseconds)
357{
358  int sc;
359
360  if (check_handle())   /* Check for chip available */
361    return -1;
362
363  if (adc_num) {
364    sc = interruptible_sleep_on(&wq_a2d_1, milliseconds);
365  } else {
366    sc = interruptible_sleep_on(&wq_a2d_2, milliseconds);
367  }
368
369  return sc;
370}
371
372int wait_adc_int(int adc_num)
373{
374  return wait_adc_int_with_timeout(adc_num, 0);
375}
376
377int wait_dac_int_with_timeout(int dac_num, int milliseconds)
378{
379  int sc;
380
381  if (check_handle())   /* Check for chip available */
382    return -1;
383
384  if (dac_num) {
385    sc = interruptible_sleep_on(&wq_dac_1, milliseconds);
386  } else {
387    sc = interruptible_sleep_on(&wq_dac_2, milliseconds);
388  }
389
390  return sc;
391}
392
393int wait_dac_int(int dac_num)
394{
395  return wait_dac_int_with_timeout(dac_num, 0);
396}
397
398int wait_dio_int_with_timestamp(
399  int                 milliseconds,
400  unsigned long long *timestamp
401)
402{
403  rtems_status_code  rc;
404  din_message_t      din;
405  size_t             received;
406
407  if (check_handle())   /* Check for chip available */
408    return -1;
409
410  rc = rtems_message_queue_receive(
411    wq_dio,
412    &din,
413    &received,
414    RTEMS_DEFAULT_OPTIONS,
415    RTEMS_MILLISECONDS_TO_TICKS(milliseconds)
416  );
417  if ( rc == RTEMS_UNSATISFIED )
418    return -1;
419
420  if ( rc == RTEMS_TIMEOUT )
421    return -1;
422
423  if ( rc != RTEMS_SUCCESSFUL ) {
424    printk( "wait_dio_int_with_timestamp - error %d\n", rc );
425    exit( 0 );
426  }
427
428  if (timestamp)
429    *timestamp = din.timestamp;
430  return din.pin;
431}
432
433int wait_dio_int_with_timeout(int milliseconds)
434{
435  return wait_dio_int_with_timestamp(milliseconds, NULL);
436}
437
438int wait_dio_int(void)
439{
440  return wait_dio_int_with_timestamp(0, NULL);
441}
442
443static int handle = 0; /* XXX move to lower */
444
445int check_handle(void)
446{
447  if (handle > 0)  /* If it's already a valid handle */
448    return 0;
449
450  if (handle == -1)  /* If it's already been tried */
451  {
452    mio_error_code = MIO_OPEN_ERROR;
453    sprintf(mio_error_string,"MIO - Unable to open device PCMMIO");
454    return -1;
455  }
456
457  /*
458   * 0  ==> not initialized
459   * 1+ ==> valid file handle, thus initialized
460   * -1 ==> already attempted to open
461   */
462  handle = 1;
463  return 0;
464
465  /* if an error happens, go here */
466  mio_error_code = MIO_OPEN_ERROR;
467  sprintf(mio_error_string,"MIO - Unable to open device PCMMIO");
468  handle = -1;
469  return -1;
470}
471
472/*
473 *  RTEMS barrier create helper
474 */
475void pcmmio_barrier_create(
476  rtems_name  name,
477  rtems_id   *id
478)
479{
480  rtems_status_code rc;
481
482  rc = rtems_barrier_create( name, RTEMS_BARRIER_MANUAL_RELEASE, 0, id );
483  if ( rc == RTEMS_SUCCESSFUL )
484    return;
485
486 printk( "Unable to create PCMMIO Barrier\n" );
487 exit(1);
488}
489
490/*
491 *  RTEMS barrier create helper
492 */
493void pcmmio_din_queue_create(
494  rtems_name  name,
495  rtems_id   *id
496)
497{
498  rtems_status_code rc;
499
500  rc = rtems_message_queue_create(
501    name,
502    MAXIMUM_BUFFERED_DISCRETE_INTERRUPTS,
503    sizeof(din_message_t),
504    RTEMS_DEFAULT_ATTRIBUTES,
505    id
506  );
507  if ( rc == RTEMS_SUCCESSFUL )
508    return;
509
510  printk( "Unable to create PCMMIO DIN IRQ Message Queue\n" );
511  exit(1);
512}
513
514int interruptible_sleep_on(
515  rtems_id *id,
516  int       milliseconds
517)
518{
519  rtems_status_code rc;
520
521  rc = rtems_barrier_wait(*id, RTEMS_MILLISECONDS_TO_TICKS(milliseconds));
522  if ( rc == RTEMS_SUCCESSFUL )
523    return 0;
524  return -1;
525}
526
527void wake_up_interruptible(
528  rtems_id *id
529)
530{
531  rtems_status_code rc;
532  uint32_t          unblocked;
533
534  rc = rtems_barrier_release(*id, &unblocked);
535}
536
537/*
538 *  RTEMS specific interrupt handler
539 */
540#include <bsp/irq.h>
541
542void common_handler(void);
543
544void pcmmio_irq_handler(
545  rtems_irq_hdl_param param
546)
547{
548  common_handler();
549}
550
551static void pcmmio_irq_disable(const rtems_irq_connect_data *irq)
552{
553  BSP_irq_disable_at_i8259s(irq->name);
554}
555static void pcmmio_irq_enable(const rtems_irq_connect_data *irq)
556{
557  BSP_irq_enable_at_i8259s(irq->name);
558}
559
560static int pcmmio_irq_is_on(const rtems_irq_connect_data *irq)
561{
562  return BSP_irq_enabled_at_i8259s( irq->name );
563}
564
565rtems_irq_connect_data pcmmio_irq = {
566  0,                            // name
567  pcmmio_irq_handler,           // handler
568  NULL,                         // parameter
569  pcmmio_irq_enable,            // enable IRQ
570  pcmmio_irq_disable,           // disable IRQ
571  pcmmio_irq_is_on,             // is IRQ enabled
572};
573
574/* from pcmmio.c - GNU/Linux driver */
575void init_io(unsigned short io_address)
576{
577  int x;
578  unsigned short port;
579
580  /* save the address for later use */
581  port = io_address + 0X10;
582
583  /* Clear all of the I/O ports. This also makes them inputs */
584  for(x=0; x < 7; x++)
585    outb(0,port+x);
586
587  /* Set page 2 access, for interrupt enables */
588  outb(0x80,port+7);
589
590  /* Clear all interrupt enables */
591  outb(0,port+8);
592  outb(0,port+9);
593  outb(0,port+0x0a);
594
595  /* Restore page 0 register access */
596  outb(0,port+7);
597}
598
599/*
600 * RTEMS specific initialization routine
601 */
602void pcmmio_initialize(
603  unsigned short _base_port,
604  unsigned short _irq
605)
606{
607  /* hardware configuration information */
608  base_port                    = _base_port;
609  irq                          = _irq;
610  pcmmio_dio_missed_interrupts = 0;
611
612  /* Create RTEMS Objects */
613  pcmmio_barrier_create( rtems_build_name( 'a', '2', 'd', '1' ), &wq_a2d_1 );
614  pcmmio_barrier_create( rtems_build_name( 'd', 'a', 'c', '1' ), &wq_dac_1 );
615  pcmmio_barrier_create( rtems_build_name( 'd', 'a', 'c', '2' ), &wq_dac_2 );
616  pcmmio_din_queue_create( rtems_build_name( 'd', 'i', 'o', ' ' ), &wq_dio );
617
618  if ( base_port )
619    init_io( base_port );
620
621  /* install IRQ handler */
622  if ( base_port && irq ) {
623    int status = 0;
624    pcmmio_irq.name = irq;
625    #if defined(BSP_SHARED_HANDLER_SUPPORT)
626      printk( "PCMMIO Installing IRQ handler as shared\n" );
627      status = BSP_install_rtems_shared_irq_handler( &pcmmio_irq );
628    #else
629      printk( "PCMMIO Installing IRQ handler as non-shared\n" );
630      status = BSP_install_rtems_irq_handler( &pcmmio_irq );
631    #endif
632    if ( !status ) {
633      printk("Error installing PCMMIO interrupt handler! status=%d\n", status );
634    }
635  }
636}
637
638#include <libcpu/cpuModel.h> /* for rdtsc */
639
640/*
641 *  From this point down, we should be able to share easily with the Linux
642 *  driver but I haven't gone to the trouble to do surgery on it.  I have
643 *  no way to test it.
644 */
645
646/* real copy is in mio_io.c */
647extern unsigned char adc2_port_image;
648
649/* This is the common interrupt handler. It is called by the
650 * actual hardware ISR.
651 */
652
653void common_handler(void)
654{
655  unsigned char status;
656  unsigned char int_num;
657
658  /* Read the interrupt ID register from ADC2 */
659
660  adc2_port_image = adc2_port_image | 0x20;
661  outb(adc2_port_image,base_port + 0x0f);
662
663  status = inb(base_port + 0x0f);
664  if (status & 1) {
665    /* Clear ADC1 interrupt */
666    inb(base_port+1);      /* Clear interrupt */
667
668    /* Wake up any holding processes */
669    wake_up_interruptible(&wq_a2d_1);
670  }
671
672  if (status & 2) {
673    /* Clear ADC1 interrupt */
674    inb(base_port+5);      /* Clear interrupt */
675
676    /* Wake up anybody waiting for ADC1 */
677    wake_up_interruptible(&wq_a2d_2);
678  }
679
680  if (status & 4) {
681    /* Clear DAC1 interrupt */
682    inb(base_port+9);    /* Clear interrupt */
683
684    /* Wake up if you're waiting on DAC1 */
685    wake_up_interruptible(&wq_dac_1);
686  }
687
688  if (status & 8) {
689
690    /* DIO interrupt. Find out which bit */
691    int_num = get_int();
692    if (int_num) {
693      rtems_status_code  rc;
694      din_message_t      din;
695
696      din.timestamp = rdtsc();
697      din.pin       = int_num;
698
699      rc = rtems_message_queue_send( wq_dio, &din, sizeof(din_message_t) );
700      if ( rc != RTEMS_SUCCESSFUL ) {
701        pcmmio_dio_missed_interrupts++;
702        #ifdef DEBUG
703          printk("<1>Missed DIO interrupt\n" );
704        #endif
705     }
706     #ifdef DEBUG
707       printk("<1>Buffering DIO interrupt on bit %d\n",int_num);
708     #endif
709
710      /* Clear the interrupt */
711      clr_int(int_num);
712    }
713
714    /* Wake up anybody waiting for a DIO interrupt */
715    wake_up_interruptible(&wq_dio);
716  }
717
718  if (status & 0x10) {
719    /* Clear DAC2 Interrupt */
720    inb(base_port+0x0d);    /* Clear interrupt */
721
722    /* Wake up DAC2 holding processes */
723    wake_up_interruptible(&wq_dac_2);
724  }
725
726  /* Reset the access to the interrupt ID register */
727  adc2_port_image = adc2_port_image & 0xdf;
728  outb(adc2_port_image,base_port+0x0f);
729}
730
731
732void clr_int(int bit_number)
733{
734  unsigned short port;
735  unsigned short temp;
736  unsigned short mask;
737  unsigned short dio_port;
738
739  dio_port = base_port + 0x10;
740
741  /* Also adjust bit number */
742  --bit_number;
743
744  /* Calculate the I/O address based upon bit number */
745  port = (bit_number / 8) + dio_port + 8;
746
747  /* Calculate a bit mask based upon the specified bit number */
748  mask = (1 << (bit_number % 8));
749
750  /* Turn on page 2 access */
751  outb(0x80,dio_port+7);
752
753  /* Get the current state of the interrupt enable register */
754  temp = inb(port);
755
756  /* Temporarily clear only our enable. This clears the interrupt */
757  temp = temp & ~mask;    /* Clear the enable for this bit */
758
759  /* Now update the interrupt enable register */
760  outb(temp,port);
761
762  /* Re-enable our interrupt bit */
763  temp = temp | mask;
764  outb(temp,port);
765
766  /* Set access back to page 0 */
767  outb(0x00,dio_port+7);
768}
769
770int get_int(void)
771{
772  int temp;
773  int x;
774  unsigned short dio_port;
775
776  dio_port = base_port + 0x10;
777
778  /* Read the master interrupt pending register,
779           mask off undefined bits */
780  temp = inb(dio_port+6) & 0x07;
781
782  /* If there are no pending interrupts, return 0 */
783  if ((temp & 7) == 0)
784    return 0;
785
786  /* There is something pending, now we need to identify it */
787
788  /* Set access to page 3 for interrupt id register */
789  outb(0xc0, dio_port + 7);
790
791  /* Read the interrupt ID register for port 0 */
792  temp = inb(dio_port+8);
793
794  /* See if any bit set, if so return the bit number */
795  if (temp != 0) {
796    for (x=0; x<=7; x++) {
797      if (temp & (1 << x)) {
798        outb(0,dio_port+7);
799        return(x+1);
800       }
801    }
802  }
803
804  /* None in port 0, read port 1 interrupt ID register */
805  temp = inb(dio_port+9);
806
807  /* See if any bit set, if so return the bit number */
808  if (temp != 0) {
809    for (x=0; x<=7; x++) {
810      if (temp & (1 << x)) {
811        outb(0,dio_port+7);
812        return(x+9);
813      }
814    }
815  }
816
817  /* Lastly, read the status of port 2 interrupt ID register */
818  temp = inb(dio_port+0x0a);
819
820  /* If any pending, return the appropriate bit number */
821  if (temp != 0) {
822    for (x=0; x<=7; x++) {
823      if (temp & (1 << x)) {
824         outb(0,dio_port+7);
825         return(x+17);
826      }
827    }
828  }
829
830  /* We should never get here unless the hardware is seriously
831     misbehaving, but just to be sure, we'll turn the page access
832     back to 0 and return a 0 for no interrupt found
833  */
834  outb(0,dio_port+7);
835  return 0;
836}
837
838void flush_buffered_ints(void)
839{
840  rtems_status_code  rc;
841  size_t             flushed;
842
843  rc = rtems_message_queue_flush( wq_dio, &flushed );
844  if ( rc != RTEMS_SUCCESSFUL ) {
845    printk( "flushed_buffered_int - error %d\n", rc );
846    exit( 0 );
847  }
848}
849
850int get_buffered_int(
851  unsigned long long *timestamp
852)
853{
854  rtems_status_code  rc;
855  din_message_t      din;
856  int                line;
857  size_t             received;
858
859  if (irq == 0) {
860    line = get_int();
861    if (line)
862      clr_int(line);
863    return line;
864  }
865
866  rc = rtems_message_queue_receive(
867    wq_dio,
868    &din,
869    &received,
870    RTEMS_NO_WAIT,
871    0
872  );
873  if ( rc == RTEMS_UNSATISFIED )
874    return 0;
875
876  if ( rc != RTEMS_SUCCESSFUL ) {
877    printk( "get_buffered_int - error %d\n", rc );
878    exit( 0 );
879  }
880
881  if (timestamp)
882    *timestamp = din.timestamp;
883  return din.pin;
884}
885
886int dio_get_missed_interrupts(void)
887{
888  int isrs;
889
890  isrs = pcmmio_dio_missed_interrupts;
891
892  pcmmio_dio_missed_interrupts = 0;
893
894  return isrs;
895}
Note: See TracBrowser for help on using the repository browser.