i8254 counter patch from Salvador Eduardo Tropea <salvador@inti.gov.ar>
authorFrank Mori Hess <fmhess@speakeasy.net>
Tue, 3 Feb 2004 23:13:47 +0000 (23:13 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Tue, 3 Feb 2004 23:13:47 +0000 (23:13 +0000)
Contributors
comedi/drivers/das08.c
comedi/drivers/das08.h
include/linux/comedi.h

index aa822259c972b63a16365601097d1c7c79bb2d30..1015bed629096a3e25660600c049faf6c0167570 100644 (file)
@@ -41,4 +41,5 @@ David Schleef <ds@schleef.org>
 Bernd Porr <Bernd.Porr@cn.stir.ac.uk>
 Steven Jenkins <steven.jenkins@ieee.org>
 Caleb Tennis <caleb@aei-tech.com>
+Salvador Eduardo Tropea <salvador@inti.gov.ar>
 
index 37cb6cbbd245a6c86674b5ab173f95449ac4e55a..c7a8009baac4a5460a58f2cb2d963cd501887a71 100644 (file)
@@ -5,6 +5,7 @@
     COMEDI - Linux Control and Measurement Device Interface
     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
     Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+    Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -667,6 +668,135 @@ static int das08ao_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *
        return n;
 }
 
+
+static unsigned int i8254_read_channel_low(unsigned int base, int chan)
+{
+       unsigned int msb, lsb;
+
+        /* The following instructions must be in order.
+           We must avoid other process reading the counter's value in the
+           middle.
+           The spin_lock isn't needed since ioctl calls grab the big kernel 
+           lock automatically */
+        /*spin_lock(sp);*/
+        outb(chan<<6,base+I8254_CTRL);
+        base+=chan;
+       lsb=inb(base);
+       msb=inb(base);
+        /*spin_unlock(sp);*/
+
+       return lsb | (msb<<8);
+}
+
+static void i8254_write_channel_low(unsigned int base, int chan, unsigned int value)
+{
+       unsigned int msb, lsb;
+
+       lsb=value & 0xFF;
+       msb=value>>8;
+
+       /* write lsb, then msb */
+        base+=chan;
+        /* See comments in i8254_read_channel_low */
+        /*spin_lock(sp);*/
+       outb(lsb,base);
+       outb(msb,base);
+        /*spin_unlock(sp);*/
+}
+
+static unsigned int i8254_read_channel(struct i8254_struct *st, int channel)
+{
+        int chan=st->logic2phys[channel];
+
+        return i8254_read_channel_low(st->iobase,chan);
+}
+
+static void i8254_write_channel(struct i8254_struct *st, int channel, unsigned int value)
+{
+        int chan=st->logic2phys[channel];
+
+        i8254_write_channel_low(st->iobase,chan,value);
+}
+
+#define I8254_CH0_LM  0x30
+#define I8254_CH1_LM  0x60
+#define I8254_CH2_LM  0x80
+
+static void i8254_initialize(struct i8254_struct *st)
+{
+        unsigned int port=st->iobase+I8254_CTRL;
+        outb(I8254_CH0_LM | st->mode[0],port);
+        outb(I8254_CH1_LM | st->mode[1],port);
+        outb(I8254_CH2_LM | st->mode[2],port);
+}
+
+
+static void i8254_set_mode_low(unsigned int base, int channel, unsigned int mode)
+{
+        outb((channel<<6) | 0x30 | (mode & 0x0F),base+I8254_CTRL);
+}
+
+static void i8254_set_mode(struct i8254_struct *st, int channel, unsigned int mode)
+{
+        int chan=st->logic2phys[channel];
+
+        st->mode[chan]=mode;
+        return i8254_set_mode_low(st->iobase,chan,mode);
+}
+
+static unsigned int i8254_read_status_low(unsigned int base, int channel)
+{
+        outb(0xE0 | (2<<channel),base+I8254_CTRL);
+        return inb(base+channel);
+}
+
+static unsigned int i8254_read_status(struct i8254_struct *st, int channel)
+{
+        int chan=st->logic2phys[channel];
+        
+        return i8254_read_status_low(st->iobase,chan);
+}
+
+static int das08_counter_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+
+        //printk("Reading counter channel %d ",chan);
+        data[0]=i8254_read_channel(&devpriv->i8254,chan);
+        //printk("=> 0x%08X\n",data[0]);
+           
+       return 1;
+}
+
+static int das08_counter_write(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+
+        //printk("Writing counter channel %d with 0x%04X\n",chan,data[0]);
+        i8254_write_channel(&devpriv->i8254,chan,data[0]);
+           
+       return 1;
+}
+
+static int das08_counter_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+        int chan=insn->chanspec;
+
+       if(insn->n != 2)return -EINVAL;
+
+        switch (data[0]){
+                case INSN_CONFIG_8254_SET_MODE:
+                        i8254_set_mode(&devpriv->i8254,chan,data[1]);
+                        break;
+                case INSN_CONFIG_8254_READ_STATUS:
+                        data[1]=i8254_read_status(&devpriv->i8254,chan);
+                        break;
+                default:
+                        return -EINVAL;
+        }
+        return 1;
+}
+
 static int das08_attach(comedi_device *dev,comedi_devconfig *it);
 
 static comedi_driver driver_das08={
@@ -698,7 +828,7 @@ int das08_common_attach(comedi_device *dev, unsigned long iobase )
 
        dev->board_name = thisboard->name;
 
-       if((ret=alloc_subdevices(dev, 5))<0)
+       if((ret=alloc_subdevices(dev, 6))<0)
                return ret;
 
        s=dev->subdevices+0;
@@ -766,6 +896,30 @@ int das08_common_attach(comedi_device *dev, unsigned long iobase )
                s->type=COMEDI_SUBD_UNUSED;
        }
 
+       s=dev->subdevices+5;
+       /* 8254 */
+       if(thisboard->i8254_offset!=0){
+               s->type = COMEDI_SUBD_COUNTER;
+               s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+               s->n_chan = 3;
+               s->maxdata = 0xFFFF;
+               s->insn_read = das08_counter_read;
+               s->insn_write = das08_counter_write;
+               s->insn_config = das08_counter_config;
+                /* Set-up the 8254 structure */
+                devpriv->i8254.channels=3;
+                devpriv->i8254.logic2phys[0]=0;
+                devpriv->i8254.logic2phys[1]=1;
+                devpriv->i8254.logic2phys[2]=2;
+                devpriv->i8254.iobase=iobase+thisboard->i8254_offset;
+                devpriv->i8254.mode[0]=
+                devpriv->i8254.mode[1]=
+                devpriv->i8254.mode[2]=I8254_MODE0 | I8254_BINARY;
+                i8254_initialize(&devpriv->i8254);
+       }else{
+               s->type=COMEDI_SUBD_UNUSED;
+       }
+
        return 0;
 }
 
index 5afdb25481e778e5dc9167c806527cbed2e8c2c3..f70a5d5d5100f358fa325c0b09cbd0890996ab92 100644 (file)
@@ -47,12 +47,26 @@ typedef struct das08_board_struct{
        unsigned int    iosize; // number of ioports used
 } das08_board;
 
+struct i8254_struct
+{
+ int channels;        // available channels. Some could be used internally.
+ int logic2phys[3];   // to know which physical channel is.
+ int mode[3];         // the index is the real counter.
+ unsigned int iobase;
+};
+
+#define I8254_CNT0 0
+#define I8254_CNT1 1
+#define I8254_CNT2 2
+#define I8254_CTRL 3
+
 struct das08_private_struct{
        unsigned int    do_mux_bits;    // bits for do/mux register on boards without seperate do register
        unsigned int    do_bits;        // bits for do register on boards with register dedicated to digital out only
        unsigned int    *pg_gainlist;
        struct pci_dev *pdev;   // struct for pci-das08
        unsigned int    pci_iobase;     // additional base address for pci-das08
+        struct i8254_struct i8254;
 };
 
 #define NUM_DAS08_CS_BOARDS 2
index 2711af4ced6fccd614c0cb69f1263a92eb1e2f1e..f344656cbb1b0f2a0a22dc860c94bee0a2603918 100644 (file)
@@ -233,6 +233,8 @@ enum configuration_ids
        INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, // Use CTR as single pulsegenerator
        INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, // Use CTR as pulsetraingenerator
        INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, // Use the counter as encoder
+       INSN_CONFIG_8254_SET_MODE = 4097,
+       INSN_CONFIG_8254_READ_STATUS = 4098
 };
 
 /* ioctls */
@@ -446,6 +448,36 @@ struct comedi_bufinfo_struct{
 // Reset when index pulse arrives?
 #define GPCT_RESET_COUNTER_ON_INDEX 1
 
+/*
+  8254 specific configuration.
+
+  It supports two config commands:
+
+  0 ID: INSN_CONFIG_8254_SET_MODE
+  1 8254 Mode
+    I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+    OR'ed with:
+    I8254_BCD, I8254_BINARY
+
+  0 ID: INSN_CONFIG_8254_READ_STATUS
+  1 <-- Status byte returned here.
+    B7=Output
+    B6=NULL Count
+    B5-B0 Current mode.
+
+*/
+
+enum i8254_mode
+{
+       I8254_MODE0 = (0<<1),  /* Interrupt on terminal count */
+       I8254_MODE1 = (1<<1),  /* Hardware retriggerable one-shot */
+       I8254_MODE2 = (2<<1),  /* Rate generator */
+       I8254_MODE3 = (3<<1),  /* Square wave mode */
+       I8254_MODE4 = (4<<1),  /* Software triggered strobe */
+       I8254_MODE5 = (5<<1),  /* Hardware triggered strobe (retriggerable) */
+       I8254_BCD = 1, /* use binary-coded decimal instead of binary (pretty useless) */
+       I8254_BINARY = 0
+};
 #ifdef __cplusplus
 }
 #endif