From 7b6cd6fb5eea8bc56ff1f8b75523f7de48ee8953 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Tue, 3 Feb 2004 23:13:47 +0000 Subject: [PATCH] i8254 counter patch from Salvador Eduardo Tropea --- Contributors | 1 + comedi/drivers/das08.c | 156 ++++++++++++++++++++++++++++++++++++++++- comedi/drivers/das08.h | 14 ++++ include/linux/comedi.h | 32 +++++++++ 4 files changed, 202 insertions(+), 1 deletion(-) diff --git a/Contributors b/Contributors index aa822259..1015bed6 100644 --- a/Contributors +++ b/Contributors @@ -41,4 +41,5 @@ David Schleef Bernd Porr Steven Jenkins Caleb Tennis +Salvador Eduardo Tropea diff --git a/comedi/drivers/das08.c b/comedi/drivers/das08.c index 37cb6cbb..c7a8009b 100644 --- a/comedi/drivers/das08.c +++ b/comedi/drivers/das08.c @@ -5,6 +5,7 @@ COMEDI - Linux Control and Measurement Device Interface Copyright (C) 2000 David A. Schleef Copyright (C) 2001,2002,2003 Frank Mori Hess + Copyright (C) 2004 Salvador E. Tropea 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<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; } diff --git a/comedi/drivers/das08.h b/comedi/drivers/das08.h index 5afdb254..f70a5d5d 100644 --- a/comedi/drivers/das08.h +++ b/comedi/drivers/das08.h @@ -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 diff --git a/include/linux/comedi.h b/include/linux/comedi.h index 2711af4c..f344656c 100644 --- a/include/linux/comedi.h +++ b/include/linux/comedi.h @@ -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 -- 2.26.2