#include <linux/comedidev.h>
#include "8253.h"
#include "8255.h"
+#include "amcc_s5933.h"
//#define CB_PCIDAS_DEBUG // enable debugging code
#undef CB_PCIDAS_DEBUG // disable debugging code
#define PCI_VENDOR_ID_CB 0x1307
#define TIMER_BASE 100 // 10MHz master clock
static const int max_fifo_size = 1024; // maximum fifo size of any supported board
+#define NUM_CHANNELS_8800 8
+#define NUM_CHANNELS_7376 1
/* PCI-DAS base addresses */
#define PACER_BADRINDEX 3
#define AO_BADRINDEX 4
// sizes of io regions
-#define S5933_SIZE 64
#define CONT_STAT_SIZE 10
#define ADC_FIFO_SIZE 4
#define PACER_SIZE 12
#define AO_SIZE 4
-// amcc s5933 pci configuration registers
-#define INCOMING_MAILBOX(x) (0x10 + 4 * (x)) // incoming mailbox registers 0 to 3
-
-#define MBEF 0x34 // mailbox empty/full status register
-
-#define INTCSR 0x38 // interrupt control/status
-#define OUTBOX_BYTE(x) ((x) & 0x3)
-#define OUTBOX_SELECT(x) (((x) & 0x3) << 2)
-#define OUTBOX_EMPTY_INT 0x10 // enable outbox empty interrupt
-#define INBOX_BYTE(x) (((x) & 0x3) << 8)
-#define INBOX_SELECT(x) (((x) & 0x3) << 10)
-#define INBOX_FULL_INT 0x1000 // enable inbox full interrupt
-#define INBOX_INTR_STATUS 0x20000 // read, or write clear inbox full interrupt
-#define INTR_ASSERTED 0x800000 // read only, interrupt asserted
-
/* Control/Status registers */
#define INT_ADCFIFO 0 // INTERRUPT / ADC FIFO register
#define INT_EOS 0x1 // interrupt end of scan
#define BURSTE 0x20 // burst mode enable
#define XTRCL 0x80 // clear external trigger
-#define CALIBRATION 6 // CALIBRATION register
+#define CALIBRATION_REG 6 // CALIBRATION register
+#define SELECT_8800_BIT 0x100 // select 8800 caldac
+#define SELECT_7376_BIT 0x200 // select ad7376 trim pot
+#define CAL_SRC_BITS(x) (((x) & 0x7) << 11)
+#define CAL_EN_BIT 0x4000 // read calibration source instead of analog input channel 0
+#define SERIAL_DATA_IN_BIT 0x8000 // serial data stream going to 8800 and 7376
#define DAC_CSR 0x8 // dac control and status register
#define DACEN 0x2 // dac enable
unsigned int ao_divisor2;
volatile unsigned int ao_count; // number of analog output samples remaining
int ao_value[2]; // remember what the analog outputs are set to, to allow readback
+ unsigned int caldac_value[ NUM_CHANNELS_8800 ]; // for readback of caldac
+ unsigned int trimpot_value; // for readback of trimpot
} cb_pcidas_private;
/*
static int cb_pcidas_cancel(comedi_device *dev, comedi_subdevice *s);
static int cb_pcidas_ao_cancel(comedi_device *dev, comedi_subdevice *s);
static void cb_pcidas_load_counters(comedi_device *dev, unsigned int *ns, int round_flags);
+static int eeprom_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data );
+static int caldac_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data );
+static int caldac_write_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data );
+static int trimpot_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data );
+static int trimpot_write_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data );
+static int caldac_8800_write(comedi_device *dev, unsigned int address, uint8_t value);
+static int trimpot_7376_write(comedi_device *dev, uint8_t value);
+static int nvram_read( comedi_device *dev, unsigned int address, uint8_t *data );
/*
* Attach is called by the Comedi core to configure the driver
// reserve io ports
err = 0;
- if(check_region(s5933_config, S5933_SIZE) < 0)
+ if(check_region(s5933_config, AMCC_OP_REG_SIZE) < 0)
err++;
if(check_region(control_status, CONT_STAT_SIZE) < 0)
err++;
printk(" I/O port conflict\n");
return -EIO;
}
- request_region(s5933_config, S5933_SIZE, "cb_pcidas");
+ request_region(s5933_config, AMCC_OP_REG_SIZE, "cb_pcidas");
devpriv->s5933_config = s5933_config;
request_region(control_status, CONT_STAT_SIZE, "cb_pcidas");
devpriv->control_status = control_status;
/*
* Allocate the subdevice structures.
*/
- dev->n_subdevices = 3;
- if(alloc_subdevices(dev)<0)
+ dev->n_subdevices = 6;
+ if(alloc_subdevices(dev) < 0)
return -ENOMEM;
s = dev->subdevices + 0;
subdev_8255_init(dev, s, NULL,
(unsigned long)(devpriv->pacer_counter_dio + DIO_8255));
- // make sure mailbox 3 is empty
- inl(devpriv->s5933_config + INCOMING_MAILBOX(3));
+ // serial EEPROM,
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 128; // XXX may have more
+ s->maxdata = 0xff;
+ s->insn_read = eeprom_read_insn;
+
+ // caldac
+ s = dev->subdevices + 4;
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_8800;
+ s->maxdata = 0xff;
+ s->insn_read = caldac_read_insn;
+ s->insn_write = caldac_write_insn;
+
+ // trim potentiometer
+ s = dev->subdevices + 5;
+ if(0)
+ {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_7376;
+ s->insn_read = trimpot_read_insn;
+ s->insn_write = trimpot_write_insn;
+ s->maxdata = 0x7f;
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // make sure mailbox 4 is empty
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4 );
/* Set bits to enable incoming mailbox interrupts on amcc s5933.
* They don't actually get sent here, but in cmd code. */
- devpriv->s5933_intcsr_bits = INBOX_BYTE(3) | INBOX_SELECT(3) | INBOX_FULL_INT;
+ devpriv->s5933_intcsr_bits = INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) | INTCSR_INBOX_FULL_INT;
- // make sure CALEN is disabled
- outw(0, devpriv->control_status + CALIBRATION);
return 1;
}
if(devpriv->s5933_config)
{
// disable and clear interrupts on amcc s5933
- outl(INBOX_INTR_STATUS, devpriv->s5933_config + INTCSR);
+ outl(INTCSR_INBOX_INTR_STATUS, devpriv->s5933_config + AMCC_OP_REG_INTCSR);
#ifdef CB_PCIDAS_DEBUG
- rt_printk("detaching, incsr is 0x%x\n", inl(devpriv->s5933_config + INTCSR));
+ rt_printk("detaching, incsr is 0x%x\n", inl(devpriv->s5933_config + AMCC_OP_REG_INTCSR));
#endif
- release_region(devpriv->s5933_config, S5933_SIZE);
+ release_region(devpriv->s5933_config, AMCC_OP_REG_SIZE);
}
if(devpriv->control_status)
release_region(devpriv->control_status, CONT_STAT_SIZE);
unsigned int bits;
static const int timeout = 10000;
+ // make sure CALEN is disabled
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+
// set mux limits and gain
bits = BEGIN_SCAN(CR_CHAN(insn->chanspec)) |
END_SCAN(CR_CHAN(insn->chanspec)) |
return 1;
}
+static int eeprom_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data )
+{
+ uint8_t nvram_data;
+ int retval;
+
+ retval = nvram_read( dev, CR_CHAN( insn->chanspec ), &nvram_data );
+ if( retval < 0 )
+ return retval;
+
+ data[ 0 ] = nvram_data;
+
+ return 1;
+}
+
+static int caldac_write_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data )
+{
+ const unsigned int channel = CR_CHAN( insn->chanspec );
+
+ devpriv->caldac_value[ channel ] = data[0];
+ caldac_8800_write( dev, channel, data[0] );
+
+ return 1;
+}
+
+static int caldac_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data )
+{
+ data[ 0 ] = devpriv->caldac_value[ CR_CHAN( insn->chanspec ) ];
+
+ return 1;
+}
+
+static int trimpot_write_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data )
+{
+ devpriv->trimpot_value = data[0];
+ trimpot_7376_write( dev, data[0] );
+
+ return 1;
+}
+
+static int trimpot_read_insn( comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data )
+{
+ data[ 0 ] = devpriv->trimpot_value;
+
+ return 1;
+}
+
static int cb_pcidas_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
comedi_cmd *cmd)
{
comedi_cmd *cmd = &async->cmd;
unsigned int bits;
+ // make sure CALEN is disabled
+ outw(0, devpriv->control_status + CALIBRATION_REG);
// initialize before settings pacer source and count values
outw(0, devpriv->control_status + TRIG_CONTSTAT);
// clear fifo
// enable (and clear) interrupts
outw(devpriv->adc_fifo_bits | EOAI | INT | LADFUL, devpriv->control_status + INT_ADCFIFO);
// enable s5933 interrupt
- outl(devpriv->s5933_intcsr_bits, devpriv->s5933_config + INTCSR);
- // make sure mailbox 3 is empty
- inl(devpriv->s5933_config + INCOMING_MAILBOX(3));
+ outl(devpriv->s5933_intcsr_bits, devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+ // make sure mailbox 4 is empty
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
// set start trigger and burst mode
// enable and clear interrupts
outw(devpriv->adc_fifo_bits | DAEMI | DAHFI, devpriv->control_status + INT_ADCFIFO);
// enable s5933 interrupt
- outl(devpriv->s5933_intcsr_bits, devpriv->s5933_config + INTCSR);
- // make sure mailbox 3 is empty
- inl(devpriv->s5933_config + INCOMING_MAILBOX(3));
+ outl(devpriv->s5933_intcsr_bits, devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+ // make sure mailbox 4 is empty
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
// start dac
devpriv->ao_control_bits |= DAC_START | DACEN | DAC_EMPTY;
async = s->async;
async->events = 0;
- s5933_status = inl(devpriv->s5933_config + INTCSR);
+ s5933_status = inl(devpriv->s5933_config + AMCC_OP_REG_INTCSR);
#ifdef CB_PCIDAS_DEBUG
rt_printk("intcsr 0x%x\n", s5933_status);
- rt_printk("mbef 0x%x\n", inl(devpriv->s5933_config + MBEF));
+ rt_printk("mbef 0x%x\n", inl(devpriv->s5933_config + AMCC_OP_REG_MBEF));
#endif
- if(INTR_ASSERTED & s5933_status)
+ if(INTCSR_INTR_ASSERTED & s5933_status)
{
// clear interrupt on amcc s5933
- outl(devpriv->s5933_intcsr_bits | INBOX_INTR_STATUS, devpriv->s5933_config + INTCSR);
- // make sure mailbox 3 is empty
- inl(devpriv->s5933_config + INCOMING_MAILBOX(3));
+ outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS, devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+ // make sure mailbox 4 is empty
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
}
status = inw(devpriv->control_status + INT_ADCFIFO);
i8254_load(devpriv->pacer_counter_dio + ADC8254, 2, devpriv->divisor2, 2);
}
+static int caldac_8800_write(comedi_device *dev, unsigned int address, uint8_t value)
+{
+ static const int num_caldac_channels = 8;
+ static const int bitstream_length = 11;
+ unsigned int bitstream = ((address & 0x7) << 8) | value;
+ unsigned int bit, register_bits;
+ static const int caldac_8800_udelay = 1;
+
+ if(address >= num_caldac_channels)
+ {
+ comedi_error(dev, "illegal caldac channel");
+ return -1;
+ }
+
+ for( bit = 1 << (bitstream_length - 1); bit; bit >>= 1)
+ {
+ if(bitstream & bit)
+ register_bits = SERIAL_DATA_IN_BIT;
+ else
+ register_bits = 0;
+ udelay(caldac_8800_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+ }
+
+ udelay(caldac_8800_udelay);
+ outw(SELECT_8800_BIT, devpriv->control_status + CALIBRATION_REG);
+ udelay(caldac_8800_udelay);
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+static int trimpot_7376_write(comedi_device *dev, uint8_t value)
+{
+ static const int bitstream_length = 7;
+ unsigned int bitstream = value & 0x7f;
+ unsigned int bit, register_bits;
+ static const int ad7376_udelay = 1;
+
+ register_bits = SELECT_7376_BIT;
+ udelay( ad7376_udelay );
+ outw( register_bits, devpriv->control_status + CALIBRATION_REG);
+
+ for( bit = 1 << ( bitstream_length - 1 ); bit; bit >>= 1)
+ {
+ if( bitstream & bit )
+ register_bits |= SERIAL_DATA_IN_BIT;
+ else
+ register_bits &= ~SERIAL_DATA_IN_BIT;
+ udelay(ad7376_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+ }
+
+ udelay(ad7376_udelay);
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+static int wait_for_nvram_ready( unsigned long s5933_base_addr )
+{
+ static const int timeout = 1000;
+ unsigned int i;
+
+ for( i = 0; i < timeout; i++)
+ {
+ if( ( inb( s5933_base_addr + AMCC_OP_REG_MCSR_NVCMD ) & MCSR_NV_BUSY ) == 0 )
+ return 0;
+ udelay( 1 );
+ }
+ return -1;
+}
+
+static int nvram_read( comedi_device *dev, unsigned int address, uint8_t *data )
+{
+ unsigned long iobase = devpriv->s5933_config;
+
+ if( wait_for_nvram_ready( iobase ) < 0 )
+ return -ETIMEDOUT;
+
+ outb( MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR, iobase + AMCC_OP_REG_MCSR_NVCMD );
+ outb( address & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA );
+ outb( MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR, iobase + AMCC_OP_REG_MCSR_NVCMD );
+ outb( ( address >> 8 ) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA );
+ outb( MCSR_NV_ENABLE | MCSR_NV_READ, iobase + AMCC_OP_REG_MCSR_NVCMD );
+
+ if( wait_for_nvram_ready( iobase ) < 0 )
+ return -ETIMEDOUT;
+
+ *data = inb( iobase + AMCC_OP_REG_MCSR_NVDATA );
+
+ return 0;
+}
/*
* A convenient macro that defines init_module() and cleanup_module(),