aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/s626.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/s626.c')
-rw-r--r--drivers/staging/comedi/drivers/s626.c755
1 files changed, 303 insertions, 452 deletions
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
index f90578e5e72..551d68b7837 100644
--- a/drivers/staging/comedi/drivers/s626.c
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -32,11 +32,7 @@ Authors: Gianluca Palli <gpalli@deis.unibo.it>,
Updated: Fri, 15 Feb 2008 10:28:42 +0000
Status: experimental
-Configuration options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration options: not applicable, uses PCI auto config
INSN_CONFIG instructions:
analog input:
@@ -82,45 +78,8 @@ INSN_CONFIG instructions:
#define PCI_SUBVENDOR_ID_S626 0x6000
#define PCI_SUBDEVICE_ID_S626 0x0272
-struct s626_board {
- const char *name;
- int vendor_id;
- int device_id;
- int subvendor_id;
- int subdevice_id;
- int ai_chans;
- int ai_bits;
- int ao_chans;
- int ao_bits;
- int dio_chans;
- int dio_banks;
- int enc_chans;
-};
-
-static const struct s626_board s626_boards[] = {
- {
- .name = "s626",
- .vendor_id = PCI_VENDOR_ID_S626,
- .device_id = PCI_DEVICE_ID_S626,
- .subvendor_id = PCI_SUBVENDOR_ID_S626,
- .subdevice_id = PCI_SUBDEVICE_ID_S626,
- .ai_chans = S626_ADC_CHANNELS,
- .ai_bits = 14,
- .ao_chans = S626_DAC_CHANNELS,
- .ao_bits = 13,
- .dio_chans = S626_DIO_CHANNELS,
- .dio_banks = S626_DIO_BANKS,
- .enc_chans = S626_ENCODER_CHANNELS,
- }
-};
-
-#define thisboard ((const struct s626_board *)dev->board_ptr)
-
struct s626_private {
- struct pci_dev *pdev;
void __iomem *base_addr;
- int got_regions;
- short allocatedBuf;
uint8_t ai_cmd_running; /* ai_cmd is running */
uint8_t ai_continous; /* continous acquisition */
int ai_sample_count; /* number of samples to acquire */
@@ -139,9 +98,7 @@ struct s626_private {
/* Pointer to logical adrs of DMA buffer used to hold DAC data. */
uint16_t Dacpol; /* Image of DAC polarity register. */
uint8_t TrimSetpoint[12]; /* Images of TrimDAC setpoints */
- uint16_t ChargeEnabled; /* Image of MISC2 Battery */
/* Charge Enabled (0 or WRMISC2_CHARGE_ENABLE). */
- uint16_t WDInterval; /* Image of MISC2 watchdog interval control bits. */
uint32_t I2CAdrs;
/* I2C device address for onboard EEPROM (board rev dependent). */
/* short I2Cards; */
@@ -1548,56 +1505,28 @@ static int s626_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* cmdtest tests a particular command to see if it is valid. Using
- * the cmdtest ioctl, a user can create a valid cmd and then have it
- * executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests the
- * command passes. */
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT
- && cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1847,6 +1776,9 @@ static int s626_dio_insn_config(struct comedi_device *dev,
/* Now this function initializes the value of the counter (data[0])
and set the subdevice. To complete with trigger and interrupt
configuration */
+/* FIXME: data[0] is supposed to be an INSN_CONFIG_xxx constant indicating
+ * what is being configured, but this function appears to be using data[0]
+ * as a variable. */
static int s626_enc_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -1868,7 +1800,7 @@ static int s626_enc_insn_config(struct comedi_device *dev,
/* (data==NULL) ? (Preloadvalue=0) : (Preloadvalue=data[0]); */
k->SetMode(dev, k, Setup, TRUE);
- Preload(dev, k, *(insn->data));
+ Preload(dev, k, data[0]);
k->PulseIndex(dev, k);
SetLatchSource(dev, k, valueSrclatch);
k->SetEnable(dev, k, (uint16_t) (enab != 0));
@@ -1920,6 +1852,7 @@ static void WriteMISC2(struct comedi_device *dev, uint16_t NewImage)
static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma,
size_t bsize)
{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
void *vbptr;
dma_addr_t vpptr;
@@ -1930,7 +1863,7 @@ static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma,
vbptr = pdma->LogicalBase;
vpptr = pdma->PhysicalBase;
if (vbptr) {
- pci_free_consistent(devpriv->pdev, bsize, vbptr, vpptr);
+ pci_free_consistent(pcidev, bsize, vbptr, vpptr);
pdma->LogicalBase = NULL;
pdma->PhysicalBase = 0;
}
@@ -2476,146 +2409,317 @@ static void CountersInit(struct comedi_device *dev)
}
}
-static struct pci_dev *s626_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int s626_allocate_dma_buffers(struct comedi_device *dev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ void *addr;
+ dma_addr_t appdma;
- for (i = 0; i < ARRAY_SIZE(s626_boards) && !pcidev; i++) {
- do {
- pcidev = pci_get_subsys(s626_boards[i].vendor_id,
- s626_boards[i].device_id,
- s626_boards[i].subvendor_id,
- s626_boards[i].subdevice_id,
- pcidev);
-
- if ((bus || slot) && pcidev) {
- /* matches requested bus/slot */
- if (pcidev->bus->number == bus &&
- PCI_SLOT(pcidev->devfn) == slot)
- break;
- } else {
- break;
- }
- } while (1);
- }
- return pcidev;
+ addr = pci_alloc_consistent(pcidev, DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->ANABuf.LogicalBase = addr;
+ devpriv->ANABuf.PhysicalBase = appdma;
+
+ addr = pci_alloc_consistent(pcidev, DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->RPSBuf.LogicalBase = addr;
+ devpriv->RPSBuf.PhysicalBase = appdma;
+
+ return 0;
}
-static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static void s626_initialize(struct comedi_device *dev)
{
-/* uint8_t PollList; */
-/* uint16_t AdcData; */
-/* uint16_t StartVal; */
-/* uint16_t index; */
-/* unsigned int data[16]; */
- int result;
+ dma_addr_t pPhysBuf;
+ uint16_t chan;
int i;
- int ret;
- resource_size_t resourceStart;
- dma_addr_t appdma;
- struct comedi_subdevice *s;
- if (alloc_private(dev, sizeof(struct s626_private)) < 0)
- return -ENOMEM;
+ /* Enable DEBI and audio pins, enable I2C interface */
+ MC_ENABLE(P_MC1, MC1_DEBI | MC1_AUDIO | MC1_I2C);
- devpriv->pdev = s626_find_pci(dev, it);
- if (!devpriv->pdev) {
- printk(KERN_ERR "s626_attach: Board not present!!!\n");
- return -ENODEV;
- }
+ /*
+ * Configure DEBI operating mode
+ *
+ * Local bus is 16 bits wide
+ * Declare DEBI transfer timeout interval
+ * Set up byte lane steering
+ * Intel-compatible local bus (DEBI never times out)
+ */
+ WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 |
+ (DEBI_TOUT << DEBI_CFG_TOUT_BIT) |
+ DEBI_SWAP | DEBI_CFG_INTEL);
+
+ /* Disable MMU paging */
+ WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE);
- result = comedi_pci_enable(devpriv->pdev, "s626");
- if (result < 0) {
- printk(KERN_ERR "s626_attach: comedi_pci_enable fails\n");
- return -ENODEV;
+ /* Init GPIO so that ADC Start* is negated */
+ WR7146(P_GPIO, GPIO_BASE | GPIO1_HI);
+
+ /* I2C device address for onboard eeprom (revb) */
+ devpriv->I2CAdrs = 0xA0;
+
+ /*
+ * Issue an I2C ABORT command to halt any I2C
+ * operation in progress and reset BUSY flag.
+ */
+ WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT);
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC);
+ while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0)
+ ;
+
+ /*
+ * Per SAA7146 data sheet, write to STATUS
+ * reg twice to reset all I2C error flags.
+ */
+ for (i = 0; i < 2; i++) {
+ WR7146(P_I2CSTAT, I2C_CLKSEL);
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC);
+ while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
+ ;
}
- devpriv->got_regions = 1;
- resourceStart = pci_resource_start(devpriv->pdev, 0);
+ /*
+ * Init audio interface functional attributes: set DAC/ADC
+ * serial clock rates, invert DAC serial clock so that
+ * DAC data setup times are satisfied, enable DAC serial
+ * clock out.
+ */
+ WR7146(P_ACON2, ACON2_INIT);
+
+ /*
+ * Set up TSL1 slot list, which is used to control the
+ * accumulation of ADC data: RSD1 = shift data in on SD1.
+ * SIB_A1 = store data uint8_t at next available location
+ * in FB BUFFER1 register.
+ */
+ WR7146(P_TSL1, RSD1 | SIB_A1);
+ WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS);
+
+ /* Enable TSL1 slot list so that it executes all the time */
+ WR7146(P_ACON1, ACON1_ADCSTART);
+
+ /*
+ * Initialize RPS registers used for ADC
+ */
+
+ /* Physical start of RPS program */
+ WR7146(P_RPSADDR1, (uint32_t)devpriv->RPSBuf.PhysicalBase);
+ /* RPS program performs no explicit mem writes */
+ WR7146(P_RPSPAGE1, 0);
+ /* Disable RPS timeouts */
+ WR7146(P_RPS1_TOUT, 0);
+
+#if 0
+ /*
+ * SAA7146 BUG WORKAROUND
+ *
+ * Initialize SAA7146 ADC interface to a known state by
+ * invoking ADCs until FB BUFFER 1 register shows that it
+ * is correctly receiving ADC data. This is necessary
+ * because the SAA7146 ADC interface does not start up in
+ * a defined state after a PCI reset.
+ */
+
+ {
+ uint8_t PollList;
+ uint16_t AdcData;
+ uint16_t StartVal;
+ uint16_t index;
+ unsigned int data[16];
+
+ /* Create a simple polling list for analog input channel 0 */
+ PollList = EOPL;
+ ResetADC(dev, &PollList);
- devpriv->base_addr = ioremap(resourceStart, SIZEOF_ADDRESS_SPACE);
- if (devpriv->base_addr == NULL) {
- printk(KERN_ERR "s626_attach: IOREMAP failed\n");
- return -ENODEV;
+ /* Get initial ADC value */
+ s626_ai_rinsn(dev, dev->subdevices, NULL, data);
+ StartVal = data[0];
+
+ /*
+ * VERSION 2.01 CHANGE: TIMEOUT ADDED TO PREVENT HANGED EXECUTION.
+ *
+ * Invoke ADCs until the new ADC value differs from the initial
+ * value or a timeout occurs. The timeout protects against the
+ * possibility that the driver is restarting and the ADC data is a
+ * fixed value resulting from the applied ADC analog input being
+ * unusually quiet or at the rail.
+ */
+ for (index = 0; index < 500; index++) {
+ s626_ai_rinsn(dev, dev->subdevices, NULL, data);
+ AdcData = data[0];
+ if (AdcData != StartVal)
+ break;
}
- if (devpriv->base_addr) {
- /* disable master interrupt */
- writel(0, devpriv->base_addr + P_IER);
+ }
+#endif /* SAA7146 BUG WORKAROUND */
- /* soft reset */
- writel(MC1_SOFT_RESET, devpriv->base_addr + P_MC1);
+ /*
+ * Initialize the DAC interface
+ */
- /* DMA FIXME DMA// */
+ /*
+ * Init Audio2's output DMAC attributes:
+ * burst length = 1 DWORD
+ * threshold = 1 DWORD.
+ */
+ WR7146(P_PCI_BT_A, 0);
- /* adc buffer allocation */
- devpriv->allocatedBuf = 0;
+ /*
+ * Init Audio2's output DMA physical addresses. The protection
+ * address is set to 1 DWORD past the base address so that a
+ * single DWORD will be transferred each time a DMA transfer is
+ * enabled.
+ */
+ pPhysBuf = devpriv->ANABuf.PhysicalBase +
+ (DAC_WDMABUF_OS * sizeof(uint32_t));
+ WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf);
+ WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t)));
- devpriv->ANABuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+ /*
+ * Cache Audio2's output DMA buffer logical address. This is
+ * where DAC data is buffered for A2 output DMA transfers.
+ */
+ devpriv->pDacWBuf = (uint32_t *)devpriv->ANABuf.LogicalBase +
+ DAC_WDMABUF_OS;
- if (devpriv->ANABuf.LogicalBase == NULL) {
- printk(KERN_ERR "s626_attach: DMA Memory mapping error\n");
- return -ENOMEM;
- }
+ /*
+ * Audio2's output channels does not use paging. The
+ * protection violation handling bit is set so that the
+ * DMAC will automatically halt and its PCI address pointer
+ * will be reset when the protection address is reached.
+ */
+ WR7146(P_PAGEA2_OUT, 8);
- devpriv->ANABuf.PhysicalBase = appdma;
+ /*
+ * Initialize time slot list 2 (TSL2), which is used to control
+ * the clock generation for and serialization of data to be sent
+ * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
+ * execution; this permits other slots to be safely modified
+ * without first turning off the TSL sequencer (which is
+ * apparently impossible to do). Also, SD3 (which is driven by a
+ * pull-up resistor) is shifted in and stored to the MSB of
+ * FB_BUFFER2 to be used as evidence that the slot sequence has
+ * not yet finished executing.
+ */
- devpriv->allocatedBuf++;
+ /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2 */
+ SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS);
- devpriv->RPSBuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+ /*
+ * Initialize slot 1, which is constant. Slot 1 causes a
+ * DWORD to be transferred from audio channel 2's output FIFO
+ * to the FIFO's output buffer so that it can be serialized
+ * and sent to the DAC during subsequent slots. All remaining
+ * slots are dynamically populated as required by the target
+ * DAC device.
+ */
- if (devpriv->RPSBuf.LogicalBase == NULL) {
- printk(KERN_ERR "s626_attach: DMA Memory mapping error\n");
- return -ENOMEM;
- }
+ /* Slot 1: Fetch DWORD from Audio2's output FIFO */
+ SETVECT(1, LF_A2);
+
+ /* Start DAC's audio interface (TSL2) running */
+ WR7146(P_ACON1, ACON1_DACSTART);
+
+ /*
+ * Init Trim DACs to calibrated values. Do it twice because the
+ * SAA7146 audio channel does not always reset properly and
+ * sometimes causes the first few TrimDAC writes to malfunction.
+ */
+ LoadTrimDACs(dev);
+ LoadTrimDACs(dev);
+
+ /*
+ * Manually init all gate array hardware in case this is a soft
+ * reset (we have no way of determining whether this is a warm
+ * or cold start). This is necessary because the gate array will
+ * reset only in response to a PCI hard reset; there is no soft
+ * reset function.
+ */
- devpriv->RPSBuf.PhysicalBase = appdma;
+ /*
+ * Init all DAC outputs to 0V and init all DAC setpoint and
+ * polarity images.
+ */
+ for (chan = 0; chan < S626_DAC_CHANNELS; chan++)
+ SetDAC(dev, chan, 0);
- devpriv->allocatedBuf++;
+ /* Init counters */
+ CountersInit(dev);
- }
+ /*
+ * Without modifying the state of the Battery Backup enab, disable
+ * the watchdog timer, set DIO channels 0-5 to operate in the
+ * standard DIO (vs. counter overflow) mode, disable the battery
+ * charger, and reset the watchdog interval selector to zero.
+ */
+ WriteMISC2(dev, (uint16_t)(DEBIread(dev, LP_RDMISC2) &
+ MISC2_BATT_ENABLE));
- dev->board_ptr = s626_boards;
- dev->board_name = thisboard->name;
+ /* Initialize the digital I/O subsystem */
+ s626_dio_init(dev);
- ret = comedi_alloc_subdevices(dev, 6);
+ /* enable interrupt test */
+ /* writel(IRQ_GPIO3 | IRQ_RPS1, devpriv->base_addr + P_IER); */
+}
+
+static int s626_attach_pci(struct comedi_device *dev, struct pci_dev *pcidev)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+ dev->board_name = dev->driver->driver_name;
+
+ if (alloc_private(dev, sizeof(struct s626_private)) < 0)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
+ dev->iobase = 1; /* detach needs this */
- dev->iobase = (unsigned long)devpriv->base_addr;
- dev->irq = devpriv->pdev->irq;
+ devpriv->base_addr = ioremap(pci_resource_start(pcidev, 0),
+ pci_resource_len(pcidev, 0));
+ if (!devpriv->base_addr)
+ return -ENOMEM;
- /* set up interrupt handler */
- if (dev->irq == 0) {
- printk(KERN_ERR " unknown irq (bad)\n");
- } else {
- ret = request_irq(dev->irq, s626_irq_handler, IRQF_SHARED,
- "s626", dev);
+ /* disable master interrupt */
+ writel(0, devpriv->base_addr + P_IER);
- if (ret < 0) {
- printk(KERN_ERR " irq not available\n");
- dev->irq = 0;
- }
+ /* soft reset */
+ writel(MC1_SOFT_RESET, devpriv->base_addr + P_MC1);
+
+ /* DMA FIXME DMA// */
+
+ ret = s626_allocate_dma_buffers(dev);
+ if (ret)
+ return ret;
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, s626_irq_handler, IRQF_SHARED,
+ dev->board_name, dev);
+
+ if (ret == 0)
+ dev->irq = pcidev->irq;
}
+ ret = comedi_alloc_subdevices(dev, 6);
+ if (ret)
+ return ret;
+
s = dev->subdevices + 0;
/* analog input subdevice */
dev->read_subdev = s;
/* we support single-ended (ground) and differential */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_CMD_READ;
- s->n_chan = thisboard->ai_chans;
+ s->n_chan = S626_ADC_CHANNELS;
s->maxdata = (0xffff >> 2);
s->range_table = &s626_range_table;
- s->len_chanlist = thisboard->ai_chans; /* This is the maximum chanlist
- length that the board can
- handle */
+ s->len_chanlist = S626_ADC_CHANNELS;
s->insn_config = s626_ai_insn_config;
s->insn_read = s626_ai_insn_read;
s->do_cmd = s626_ai_cmd;
@@ -2626,7 +2730,7 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
- s->n_chan = thisboard->ao_chans;
+ s->n_chan = S626_DAC_CHANNELS;
s->maxdata = (0x3fff);
s->range_table = &range_bipolar10;
s->insn_write = s626_ao_winsn;
@@ -2672,7 +2776,7 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* encoder (counter) subdevice */
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
- s->n_chan = thisboard->enc_chans;
+ s->n_chan = S626_ENCODER_CHANNELS;
s->private = enc_private_data;
s->insn_config = s626_enc_insn_config;
s->insn_read = s626_enc_insn_read;
@@ -2680,269 +2784,17 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->maxdata = 0xffffff;
s->range_table = &range_unknown;
- /* stop ai_command */
- devpriv->ai_cmd_running = 0;
-
- if (devpriv->base_addr && (devpriv->allocatedBuf == 2)) {
- dma_addr_t pPhysBuf;
- uint16_t chan;
-
- /* enab DEBI and audio pins, enable I2C interface. */
- MC_ENABLE(P_MC1, MC1_DEBI | MC1_AUDIO | MC1_I2C);
- /* Configure DEBI operating mode. */
- WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 /* Local bus is 16 */
- /* bits wide. */
- | (DEBI_TOUT << DEBI_CFG_TOUT_BIT)
-
- /* Declare DEBI */
- /* transfer timeout */
- /* interval. */
- |DEBI_SWAP /* Set up byte lane */
- /* steering. */
- | DEBI_CFG_INTEL); /* Intel-compatible */
- /* local bus (DEBI */
- /* never times out). */
-
- /* DEBI INIT S626 WR7146( P_DEBICFG, DEBI_CFG_INTEL | DEBI_CFG_TOQ */
- /* | DEBI_CFG_INCQ| DEBI_CFG_16Q); //end */
-
- /* Paging is disabled. */
- WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE); /* Disable MMU paging. */
-
- /* Init GPIO so that ADC Start* is negated. */
- WR7146(P_GPIO, GPIO_BASE | GPIO1_HI);
-
- /* IsBoardRevA is a boolean that indicates whether the board is RevA.
- *
- * VERSION 2.01 CHANGE: REV A & B BOARDS NOW SUPPORTED BY DYNAMIC
- * EEPROM ADDRESS SELECTION. Initialize the I2C interface, which
- * is used to access the onboard serial EEPROM. The EEPROM's I2C
- * DeviceAddress is hardwired to a value that is dependent on the
- * 626 board revision. On all board revisions, the EEPROM stores
- * TrimDAC calibration constants for analog I/O. On RevB and
- * higher boards, the DeviceAddress is hardwired to 0 to enable
- * the EEPROM to also store the PCI SubVendorID and SubDeviceID;
- * this is the address at which the SAA7146 expects a
- * configuration EEPROM to reside. On RevA boards, the EEPROM
- * device address, which is hardwired to 4, prevents the SAA7146
- * from retrieving PCI sub-IDs, so the SAA7146 uses its built-in
- * default values, instead.
- */
-
- /* devpriv->I2Cards= IsBoardRevA ? 0xA8 : 0xA0; // Set I2C EEPROM */
- /* DeviceType (0xA0) */
- /* and DeviceAddress<<1. */
-
- devpriv->I2CAdrs = 0xA0; /* I2C device address for onboard */
- /* eeprom(revb) */
+ s626_initialize(dev);
- /* Issue an I2C ABORT command to halt any I2C operation in */
- /* progress and reset BUSY flag. */
- WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT);
- /* Write I2C control: abort any I2C activity. */
- MC_ENABLE(P_MC2, MC2_UPLD_IIC);
- /* Invoke command upload */
- while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0)
- ;
- /* and wait for upload to complete. */
-
- /* Per SAA7146 data sheet, write to STATUS reg twice to
- * reset all I2C error flags. */
- for (i = 0; i < 2; i++) {
- WR7146(P_I2CSTAT, I2C_CLKSEL);
- /* Write I2C control: reset error flags. */
- MC_ENABLE(P_MC2, MC2_UPLD_IIC); /* Invoke command upload */
- while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
- ;
- /* and wait for upload to complete. */
- }
-
- /* Init audio interface functional attributes: set DAC/ADC
- * serial clock rates, invert DAC serial clock so that
- * DAC data setup times are satisfied, enable DAC serial
- * clock out.
- */
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
- WR7146(P_ACON2, ACON2_INIT);
-
- /* Set up TSL1 slot list, which is used to control the
- * accumulation of ADC data: RSD1 = shift data in on SD1.
- * SIB_A1 = store data uint8_t at next available location in
- * FB BUFFER1 register. */
- WR7146(P_TSL1, RSD1 | SIB_A1);
- /* Fetch ADC high data uint8_t. */
- WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS);
- /* Fetch ADC low data uint8_t; end of TSL1. */
-
- /* enab TSL1 slot list so that it executes all the time. */
- WR7146(P_ACON1, ACON1_ADCSTART);
-
- /* Initialize RPS registers used for ADC. */
-
- /* Physical start of RPS program. */
- WR7146(P_RPSADDR1, (uint32_t) devpriv->RPSBuf.PhysicalBase);
-
- WR7146(P_RPSPAGE1, 0);
- /* RPS program performs no explicit mem writes. */
- WR7146(P_RPS1_TOUT, 0); /* Disable RPS timeouts. */
-
- /* SAA7146 BUG WORKAROUND. Initialize SAA7146 ADC interface
- * to a known state by invoking ADCs until FB BUFFER 1
- * register shows that it is correctly receiving ADC data.
- * This is necessary because the SAA7146 ADC interface does
- * not start up in a defined state after a PCI reset.
- */
-
-/* PollList = EOPL; // Create a simple polling */
-/* // list for analog input */
-/* // channel 0. */
-/* ResetADC( dev, &PollList ); */
-
-/* s626_ai_rinsn(dev,dev->subdevices,NULL,data); //( &AdcData ); // */
-/* //Get initial ADC */
-/* //value. */
-
-/* StartVal = data[0]; */
-
-/* // VERSION 2.01 CHANGE: TIMEOUT ADDED TO PREVENT HANGED EXECUTION. */
-/* // Invoke ADCs until the new ADC value differs from the initial */
-/* // value or a timeout occurs. The timeout protects against the */
-/* // possibility that the driver is restarting and the ADC data is a */
-/* // fixed value resulting from the applied ADC analog input being */
-/* // unusually quiet or at the rail. */
-
-/* for ( index = 0; index < 500; index++ ) */
-/* { */
-/* s626_ai_rinsn(dev,dev->subdevices,NULL,data); */
-/* AdcData = data[0]; //ReadADC( &AdcData ); */
-/* if ( AdcData != StartVal ) */
-/* break; */
-/* } */
-
- /* end initADC */
-
- /* init the DAC interface */
-
- /* Init Audio2's output DMAC attributes: burst length = 1
- * DWORD, threshold = 1 DWORD.
- */
- WR7146(P_PCI_BT_A, 0);
-
- /* Init Audio2's output DMA physical addresses. The protection
- * address is set to 1 DWORD past the base address so that a
- * single DWORD will be transferred each time a DMA transfer is
- * enabled. */
-
- pPhysBuf =
- devpriv->ANABuf.PhysicalBase +
- (DAC_WDMABUF_OS * sizeof(uint32_t));
-
- WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf); /* Buffer base adrs. */
- WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t))); /* Protection address. */
-
- /* Cache Audio2's output DMA buffer logical address. This is
- * where DAC data is buffered for A2 output DMA transfers. */
- devpriv->pDacWBuf =
- (uint32_t *) devpriv->ANABuf.LogicalBase + DAC_WDMABUF_OS;
-
- /* Audio2's output channels does not use paging. The protection
- * violation handling bit is set so that the DMAC will
- * automatically halt and its PCI address pointer will be reset
- * when the protection address is reached. */
-
- WR7146(P_PAGEA2_OUT, 8);
-
- /* Initialize time slot list 2 (TSL2), which is used to control
- * the clock generation for and serialization of data to be sent
- * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
- * execution; this permits other slots to be safely modified
- * without first turning off the TSL sequencer (which is
- * apparently impossible to do). Also, SD3 (which is driven by a
- * pull-up resistor) is shifted in and stored to the MSB of
- * FB_BUFFER2 to be used as evidence that the slot sequence has
- * not yet finished executing.
- */
-
- SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS);
- /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2. */
-
- /* Initialize slot 1, which is constant. Slot 1 causes a
- * DWORD to be transferred from audio channel 2's output FIFO
- * to the FIFO's output buffer so that it can be serialized
- * and sent to the DAC during subsequent slots. All remaining
- * slots are dynamically populated as required by the target
- * DAC device.
- */
- SETVECT(1, LF_A2);
- /* Slot 1: Fetch DWORD from Audio2's output FIFO. */
-
- /* Start DAC's audio interface (TSL2) running. */
- WR7146(P_ACON1, ACON1_DACSTART);
-
- /* end init DAC interface */
-
- /* Init Trim DACs to calibrated values. Do it twice because the
- * SAA7146 audio channel does not always reset properly and
- * sometimes causes the first few TrimDAC writes to malfunction.
- */
-
- LoadTrimDACs(dev);
- LoadTrimDACs(dev); /* Insurance. */
-
- /* Manually init all gate array hardware in case this is a soft
- * reset (we have no way of determining whether this is a warm
- * or cold start). This is necessary because the gate array will
- * reset only in response to a PCI hard reset; there is no soft
- * reset function. */
-
- /* Init all DAC outputs to 0V and init all DAC setpoint and
- * polarity images.
- */
- for (chan = 0; chan < S626_DAC_CHANNELS; chan++)
- SetDAC(dev, chan, 0);
-
- /* Init image of WRMISC2 Battery Charger Enabled control bit.
- * This image is used when the state of the charger control bit,
- * which has no direct hardware readback mechanism, is queried.
- */
- devpriv->ChargeEnabled = 0;
-
- /* Init image of watchdog timer interval in WRMISC2. This image
- * maintains the value of the control bits of MISC2 are
- * continuously reset to zero as long as the WD timer is disabled.
- */
- devpriv->WDInterval = 0;
-
- /* Init Counter Interrupt enab mask for RDMISC2. This mask is
- * applied against MISC2 when testing to determine which timer
- * events are requesting interrupt service.
- */
- devpriv->CounterIntEnabs = 0;
-
- /* Init counters. */
- CountersInit(dev);
-
- /* Without modifying the state of the Battery Backup enab, disable
- * the watchdog timer, set DIO channels 0-5 to operate in the
- * standard DIO (vs. counter overflow) mode, disable the battery
- * charger, and reset the watchdog interval selector to zero.
- */
- WriteMISC2(dev, (uint16_t) (DEBIread(dev,
- LP_RDMISC2) &
- MISC2_BATT_ENABLE));
-
- /* Initialize the digital I/O subsystem. */
- s626_dio_init(dev);
-
- /* enable interrupt test */
- /* writel(IRQ_GPIO3 | IRQ_RPS1,devpriv->base_addr+P_IER); */
- }
-
- return 1;
+ return 0;
}
static void s626_detach(struct comedi_device *dev)
{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
if (devpriv) {
/* stop ai_command */
devpriv->ai_cmd_running = 0;
@@ -2967,18 +2819,17 @@ static void s626_detach(struct comedi_device *dev)
free_irq(dev->irq, dev);
if (devpriv->base_addr)
iounmap(devpriv->base_addr);
- if (devpriv->pdev) {
- if (devpriv->got_regions)
- comedi_pci_disable(devpriv->pdev);
- pci_dev_put(devpriv->pdev);
- }
+ }
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
}
}
static struct comedi_driver s626_driver = {
.driver_name = "s626",
.module = THIS_MODULE,
- .attach = s626_attach,
+ .attach_pci = s626_attach_pci,
.detach = s626_detach,
};