source: multiio/pcmmio/original/mio_io_rtems.c @ da59d43

Last change on this file since da59d43 was da59d43, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 10, 2009 at 6:53:27 PM

2009-06-10 Joel Sherrill <joel.sherrill@…>

  • mio_io_rtems.c: Initial interrupt support added. Should now be on functional parity with Linux driver but not debugged.
  • Property mode set to 100644
File size: 15.1 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
22/*
23 *  These are configured by the initialization call.
24 */
25
26/* IRQ source or 0 ==> polled */
27static unsigned short irq = 0;
28/* This holds the base addresses of the board */
29static unsigned short base_port = 0;
30
31/* Function prototypes for local functions */
32int get_buffered_int(void);
33void init_io(unsigned short io_address);
34void clr_int(int bit_number);
35int get_int(void);
36
37/* RTEMS Ids for Wait Queues */
38rtems_id wq_a2d_1;
39rtems_id wq_a2d_2;
40rtems_id wq_dac_1;
41rtems_id wq_dac_2;
42rtems_id wq_dio;
43
44void interruptible_sleep_on(
45  rtems_id *id
46);
47void wake_up_interruptible(
48  rtems_id *id
49);
50
51///////////////////////////////////////////////////////////////////////////////
52//
53//    MIO_READ_IRQ_ASSIGNED
54//
55//////////////////////////////////////////////////////////////////////////////
56
57int mio_read_irq_assigned(void)
58{
59  mio_error_code = MIO_SUCCESS;
60
61  if (check_handle())   /* Check for chip available */
62    return -1;
63
64
65  /* All of our programming of the hardware is handled at this level so that
66     all of the routines that need to shove and IRQ value into hardware will
67     use this call.
68  */
69
70  return (irq & 0xff);
71}
72
73///////////////////////////////////////////////////////////////////////////////
74//
75//    READ_DIO_BYTE
76//
77//////////////////////////////////////////////////////////////////////////////
78
79unsigned char read_dio_byte(int offset)
80{
81  unsigned char byte_val;
82  unsigned char offset_val;
83
84  mio_error_code = MIO_SUCCESS;
85
86  if (check_handle())   /* Check for chip available */
87    return -1;
88
89  /* All bit operations are handled at this level so we need only
90     read and write bytes from the actual hardware.
91  */
92
93  offset_val = offset & 0xff;
94  byte_val = inb(base_port + 0x10 + offset_val);
95  return (byte_val & 0xff);
96}
97
98///////////////////////////////////////////////////////////////////////////////
99//
100//    MIO_READ_REG
101//
102//////////////////////////////////////////////////////////////////////////////
103
104unsigned char mio_read_reg(int offset)
105{
106  unsigned char byte_val;
107  unsigned char offset_val;
108
109  mio_error_code = MIO_SUCCESS;
110
111  if (check_handle())   /* Check for chip available */
112    return -1;
113
114
115  /* This is a catchall register read routine that allows reading of
116     ANY of the registers on the PCM-MIO. It is used primarily for
117     retreiving control and access values in the hardware.
118   */
119
120  offset_val = offset & 0xff;
121  byte_val = inb(base_port + offset_val);
122  return (byte_val & 0xff);
123}
124
125///////////////////////////////////////////////////////////////////////////////
126//
127//    MIO_WRITE_REG
128//
129//////////////////////////////////////////////////////////////////////////////
130
131int mio_write_reg(int offset, unsigned char value)
132{
133  unsigned char byte_val;
134  unsigned char offset_val;
135
136  mio_error_code = MIO_SUCCESS;
137
138  if (check_handle())   /* Check for chip available */
139    return -1;
140
141  /* This function like the previous allow unlimited
142     write access to ALL of the registers on the PCM-MIO
143   */
144
145  offset_val = offset & 0xff;
146  byte_val = value;
147  outb(byte_val, base_port + offset_val);
148 
149  return 0;
150}
151
152
153///////////////////////////////////////////////////////////////////////////////
154//
155//    WRITE_DIO_BYTE
156//
157//////////////////////////////////////////////////////////////////////////////
158
159int write_dio_byte(int offset, unsigned char value)
160{
161  unsigned char byte_val;
162  unsigned char offset_val;
163
164  mio_error_code = MIO_SUCCESS;
165
166  if (check_handle())   /* Check for chip available */
167    return -1;
168
169  /* All bit operations for the DIO are handled at this level
170     and we need the driver to allow access to the actual
171     DIO registers to update the value.
172  */
173
174  offset_val = offset & 0xff;
175  byte_val = value;
176  outb(byte_val, base_port + 0x10 + offset_val);
177
178  return 0;
179}
180
181
182///////////////////////////////////////////////////////////////////////////////
183//
184//    WRITE_DAC_COMMAND
185//
186//////////////////////////////////////////////////////////////////////////////
187
188int write_dac_command(int dac_num,unsigned char value)
189{
190  unsigned char  byte_val;
191  unsigned char  offset_val;
192
193  mio_error_code = MIO_SUCCESS;
194
195  if (check_handle())   /* Check for chip available */
196    return -1;
197
198  byte_val = dac_num & 0xff;            /* This is the DAC number */
199  offset_val = value;                   /* This is the data value */
200  if (byte_val)
201    outb(offset_val,base_port + 0x0e);
202  else
203    outb(offset_val,base_port + 0x0a);
204
205  return 0;
206}
207
208///////////////////////////////////////////////////////////////////////////////
209//
210//    WRITE_ADC_COMMAND
211//
212//////////////////////////////////////////////////////////////////////////////
213
214int write_adc_command(int adc_num,unsigned char value)
215{
216  unsigned char byte_val;
217  unsigned char offset_val;
218
219  mio_error_code = MIO_SUCCESS;
220
221  if (check_handle())   /* Check for chip available */
222    return -1;
223
224  byte_val = adc_num & 0xff;            /* This is the ADC number */
225  offset_val = value;                   /* This is the data value */
226
227  if(byte_val)
228    outb(offset_val,base_port + 0x06);
229  else
230    outb(offset_val,base_port + 0x02);
231  return 0;
232}
233
234///////////////////////////////////////////////////////////////////////////////
235//
236//    WRITE_DAC_DATA
237//
238//////////////////////////////////////////////////////////////////////////////
239
240int write_dac_data(int dac_num, unsigned short value)
241{
242  unsigned short word_val;
243  unsigned char byte_val;
244
245  mio_error_code = MIO_SUCCESS;
246
247  if (check_handle())   /* Check for chip available */
248    return -1;
249
250  byte_val = dac_num;
251  word_val = value;
252
253  if(byte_val)          /* DAC 1 */
254    outw(word_val,base_port+0x0c);
255  else
256    outw(word_val,base_port+8);
257 
258  return 0;
259}
260
261///////////////////////////////////////////////////////////////////////////////
262//
263//    DAC_READ_STATUS
264//
265//////////////////////////////////////////////////////////////////////////////
266
267unsigned char dac_read_status(int dac_num)
268{
269  mio_error_code = MIO_SUCCESS;
270
271  if (check_handle())   /* Check for chip available */
272    return -1;
273
274  if (dac_num)
275    return inb(base_port + 0x0f);
276
277  return inb(base_port + 0x0b);
278}
279
280///////////////////////////////////////////////////////////////////////////////
281//
282//    ADC_READ_STATUS
283//
284//////////////////////////////////////////////////////////////////////////////
285
286unsigned char adc_read_status(int adc_num)
287{
288  mio_error_code = MIO_SUCCESS;
289
290  if (check_handle())   /* Check for chip available */
291    return -1;
292
293  if (adc_num)
294    return inb(base_port + 7);
295  return inb(base_port + 3);
296}
297
298///////////////////////////////////////////////////////////////////////////////
299//
300//    ADC_READ_CONVERSION_DATA
301//
302//////////////////////////////////////////////////////////////////////////////
303
304unsigned short adc_read_conversion_data(int channel)
305{
306int adc_num;
307
308  mio_error_code = MIO_SUCCESS;
309
310  if (check_handle())   /* Check for chip available */
311    return -1;
312
313  if (channel > 7)
314    adc_num = 1;
315  else
316    adc_num = 0;
317
318  if (adc_num)
319    return inw(base_port + 4);
320
321  return inw(base_port);
322}
323
324
325int dio_get_int(void)
326{
327  if (check_handle())   /* Check for chip available */
328    return -1;
329
330  return get_buffered_int() & 0xff;
331
332}
333
334int wait_adc_int(int adc_num)
335{
336  if (check_handle())   /* Check for chip available */
337    return -1;
338
339  if (adc_num) {
340    interruptible_sleep_on(&wq_a2d_1);
341  } else {
342    interruptible_sleep_on(&wq_a2d_2);
343  }
344
345  return 0;
346}
347
348
349int wait_dac_int(int dac_num)
350{
351  if (check_handle())   /* Check for chip available */
352    return -1;
353
354  if (dac_num) {
355    interruptible_sleep_on(&wq_dac_1);
356  } else {
357    interruptible_sleep_on(&wq_dac_2);
358  }
359
360  return 0;
361}
362
363
364int wait_dio_int(void)
365{
366  int i;
367
368  if (check_handle())   /* Check for chip available */
369    return -1;
370
371  if((i = get_buffered_int()))
372    return i;
373
374  interruptible_sleep_on(&wq_dio);
375
376  i = get_buffered_int();
377
378  return i;
379}
380
381
382static int handle = 0; /* XXX move to lower */
383
384int check_handle(void)
385{
386  if (handle > 0)  /* If it's already a valid handle */
387    return 0;
388
389  if (handle == -1)  /* If it's already been tried */
390  {
391    mio_error_code = MIO_OPEN_ERROR;
392    sprintf(mio_error_string,"MIO - Unable to open device PCMMIO");
393    return -1;
394  }
395
396  /*
397   * 0  ==> not initialized
398   * 1+ ==> valid file handle, thus initialized
399   * -1 ==> already attempted to open
400   */
401  handle = 1;
402  return 0;
403
404  /* if an error happens, go here */
405  mio_error_code = MIO_OPEN_ERROR;
406  sprintf(mio_error_string,"MIO - Unable to open device PCMMIO");
407  handle = -1;
408  return -1;
409}
410
411/*
412 *  RTEMS barrier create helper
413 */
414void pcmmio_barrier_create(
415  rtems_name  name,
416  rtems_id   *id
417)
418{
419  rtems_status_code rc;
420
421  rc = rtems_barrier_create( name, RTEMS_BARRIER_MANUAL_RELEASE, 0, id );
422  if ( rc == RTEMS_SUCCESSFUL )
423    return;
424
425 printk( "Unable to create PCMMIO Barrier\n" );
426 exit(1);
427}
428
429void interruptible_sleep_on(
430  rtems_id *id
431)
432{
433  rtems_status_code rc;
434
435  rc = rtems_barrier_wait(*id, 0);
436}
437
438void wake_up_interruptible(
439  rtems_id *id
440)
441{
442  rtems_status_code rc;
443  uint32_t          unblocked;
444
445  rc = rtems_barrier_release(*id, &unblocked);
446}
447
448/*
449 *  RTEMS specific interrupt handler
450 */
451#include <bsp/irq.h>
452
453void common_handler(void);
454
455void pcmmio_interrupt_handler(
456  rtems_irq_hdl_param param
457)
458{
459}
460
461rtems_irq_connect_data pcmmio_irq = {
462  0,                            // name
463  pcmmio_interrupt_handler,     // handler
464  NULL,                         // parameter
465  NULL,                         // enable IRQ
466  NULL,                         // disable IRQ
467  NULL,                         // is IRQ enabled
468};
469
470/*
471 * RTEMS specific initialization routine
472 */
473void pcmmio_initialize(
474  unsigned short _base_port,
475  unsigned short _irq
476)
477{
478  /* hardware configuration information */
479  base_port = _base_port;
480  irq       = _irq;
481
482  /* Create RTEMS Objects */
483  pcmmio_barrier_create( rtems_build_name( 'a', '2', 'd', '1' ), &wq_a2d_1 );
484  pcmmio_barrier_create( rtems_build_name( 'd', 'a', 'c', '1' ), &wq_dac_1 );
485  pcmmio_barrier_create( rtems_build_name( 'd', 'a', 'c', '2' ), &wq_dac_2 );
486  pcmmio_barrier_create( rtems_build_name( 'd', 'i', 'o', ' ' ), &wq_dio );
487
488  /* install IRQ handler */
489  if ( irq ) {
490    pcmmio_irq.name = irq;
491    BSP_install_rtems_irq_handler( &pcmmio_irq );
492  }
493}
494
495
496/*
497 *  From this point down, we should be able to share easily with the Linux
498 *  driver but I haven't gone to the trouble to do surgery on it.  I have
499 *  no way to test it.
500 */
501
502/* We will buffer up the transition interrupts and will pass them on
503   to waiting applications
504*/
505
506#define MAX_INTS 1024
507
508static unsigned char int_buffer[MAX_INTS];
509static int inptr = 0;
510static int outptr = 0;
511
512static unsigned char adc2_port_image;
513
514/* This is the common interrupt handler. It is called by the
515 * actual hardware ISR.
516 */
517
518void common_handler(void)
519{
520  unsigned char status;
521  unsigned char int_num;
522
523  /* Read the interrupt ID register from ADC2 */
524
525  adc2_port_image = adc2_port_image | 0x20;
526  outb(adc2_port_image,base_port + 0x0f);
527
528  status = inb(base_port + 0x0f);
529  if (status & 1) {
530    /* Clear ADC1 interrupt */
531    inb(base_port+1);      /* Clear interrupt */
532
533    /* Wake up any holding processes */
534    wake_up_interruptible(&wq_a2d_1);
535  }
536
537  if (status & 2) {
538    /* Clear ADC1 interrupt */
539    inb(base_port+5);      /* Clear interrupt */
540
541    /* Wake up anybody waiting for ADC1 */
542    wake_up_interruptible(&wq_a2d_2);
543  }
544
545  if (status & 4) {
546    /* Clear DAC1 interrupt */
547    inb(base_port+9);    /* Clear interrupt */
548
549    /* Wake up if you're waiting on DAC1 */
550    wake_up_interruptible(&wq_dac_1);
551  }
552
553  if (status & 8) {
554
555    /* DIO interrupt. Find out which bit */
556    int_num = get_int();
557    if (int_num) {
558      #ifdef DEBUG
559        printk("<1>Buffering DIO interrupt on bit %d\n",int_num);
560      #endif
561
562      /* Buffer the interrupt */
563
564      int_buffer[inptr++] = int_num;
565      if (inptr == MAX_INTS)
566        inptr = 0;
567
568        /* Clear the interrupt */
569        clr_int(int_num);
570    }
571
572    /* Wake up anybody waiting for a DIO interrupt */
573    wake_up_interruptible(&wq_dio);
574  }
575
576  if (status & 0x10) {
577    /* Clear DAC2 Interrupt */
578    inb(base_port+0x0d);    /* Clear interrupt */
579
580    /* Wake up DAC2 holding processes */
581    wake_up_interruptible(&wq_dac_2);
582  }
583
584  /* Reset the access to the interrupt ID register */
585  adc2_port_image = adc2_port_image & 0xdf;
586  outb(adc2_port_image,base_port+0x0f);
587}
588
589
590void clr_int(int bit_number)
591{
592  unsigned short port;
593  unsigned short temp;
594  unsigned short mask;
595  unsigned short dio_port;
596
597  dio_port = base_port + 0x10;
598
599  /* Also adjust bit number */
600  --bit_number;
601
602  /* Calculate the I/O address based upon bit number */
603  port = (bit_number / 8) + dio_port + 8;
604
605  /* Calculate a bit mask based upon the specified bit number */
606  mask = (1 << (bit_number % 8));
607
608  /* Turn on page 2 access */
609  outb(0x80,dio_port+7);
610
611  /* Get the current state of the interrupt enable register */
612  temp = inb(port);
613
614  /* Temporarily clear only our enable. This clears the interrupt */
615  temp = temp & ~mask;    /* Clear the enable for this bit */
616
617  /* Now update the interrupt enable register */
618  outb(temp,port);
619
620  /* Re-enable our interrupt bit */
621  temp = temp | mask;
622  outb(temp,port);
623
624  /* Set access back to page 0 */
625  outb(0x00,dio_port+7);
626}
627
628int get_int(void)
629{
630  int temp;
631  int x;
632  unsigned short dio_port;
633
634  dio_port = base_port + 0x10;
635
636  /* Read the master interrupt pending register,
637           mask off undefined bits */
638  temp = inb(dio_port+6) & 0x07;
639
640  /* If there are no pending interrupts, return 0 */
641  if ((temp & 7) == 0)
642    return 0;
643
644  /* There is something pending, now we need to identify it */
645
646  /* Set access to page 3 for interrupt id register */
647  outb(0xc0, dio_port + 7);
648
649  /* Read the interrupt ID register for port 0 */
650  temp = inb(dio_port+8);
651
652  /* See if any bit set, if so return the bit number */
653  if (temp != 0) {
654    for (x=0; x<=7; x++) {
655      if (temp & (1 << x)) {
656        outb(0,dio_port+7);
657        return(x+1);
658       }
659    }
660  }
661
662  /* None in port 0, read port 1 interrupt ID register */
663  temp = inb(dio_port+9);
664
665  /* See if any bit set, if so return the bit number */
666  if (temp != 0) {
667    for (x=0; x<=7; x++) {
668      if (temp & (1 << x)) {
669        outb(0,dio_port+7);
670        return(x+9);
671      }
672    }
673  }
674
675  /* Lastly, read the statur of port 2 interrupt ID register */
676  temp = inb(dio_port+0x0a);
677
678  /* If any pending, return the appropriate bit number */
679  if (temp != 0) {
680    for (x=0; x<=7; x++) {
681      if (temp & (1 << x)) {
682         outb(0,dio_port+7);
683         return(x+17);
684      }
685    }
686  }
687
688  /* We should never get here unless the hardware is seriously
689     misbehaving, but just to be sure, we'll turn the page access
690     back to 0 and return a 0 for no interrupt found
691  */
692  outb(0,dio_port+7);
693  return 0;
694}
695
696
697int get_buffered_int(void)
698{
699  int temp;
700
701  if (irq == 0) {
702    temp = get_int();
703    if (temp)
704      clr_int(temp);
705    return temp;
706  }
707
708  if (outptr != inptr) {
709    temp = int_buffer[outptr++];
710    if (outptr == MAX_INTS)
711      outptr = 0;
712    return temp;
713  }
714
715  return 0;
716}
Note: See TracBrowser for help on using the repository browser.