in drivers/s626.c [1274:1469]
static void s626_reset_adc(struct comedi_device *dev, u8 *ppl)
{
struct s626_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
struct comedi_cmd *cmd = &s->async->cmd;
u32 *rps;
u32 jmp_adrs;
u16 i;
u16 n;
u32 local_ppl;
/* Stop RPS program in case it is currently running */
s626_mc_disable(dev, S626_MC1_ERPS1, S626_P_MC1);
/* Set starting logical address to write RPS commands. */
rps = (u32 *)devpriv->rps_buf.logical_base;
/* Initialize RPS instruction pointer */
writel((u32)devpriv->rps_buf.physical_base,
dev->mmio + S626_P_RPSADDR1);
/* Construct RPS program in rps_buf DMA buffer */
if (cmd->scan_begin_src != TRIG_FOLLOW) {
/* Wait for Start trigger. */
*rps++ = S626_RPS_PAUSE | S626_RPS_SIGADC;
*rps++ = S626_RPS_CLRSIGNAL | S626_RPS_SIGADC;
}
/*
* SAA7146 BUG WORKAROUND Do a dummy DEBI Write. This is necessary
* because the first RPS DEBI Write following a non-RPS DEBI write
* seems to always fail. If we don't do this dummy write, the ADC
* gain might not be set to the value required for the first slot in
* the poll list; the ADC gain would instead remain unchanged from
* the previously programmed value.
*/
/* Write DEBI Write command and address to shadow RAM. */
*rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
*rps++ = S626_DEBI_CMD_WRWORD | S626_LP_GSEL;
*rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
/* Write DEBI immediate data to shadow RAM: */
*rps++ = S626_GSEL_BIPOLAR5V; /* arbitrary immediate data value. */
*rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
/* Reset "shadow RAM uploaded" flag. */
/* Invoke shadow RAM upload. */
*rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
/* Wait for shadow upload to finish. */
*rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
/*
* Digitize all slots in the poll list. This is implemented as a
* for loop to limit the slot count to 16 in case the application
* forgot to set the S626_EOPL flag in the final slot.
*/
for (devpriv->adc_items = 0; devpriv->adc_items < 16;
devpriv->adc_items++) {
/*
* Convert application's poll list item to private board class
* format. Each app poll list item is an uint8_t with form
* (EOPL,x,x,RANGE,CHAN<3:0>), where RANGE code indicates 0 =
* +-10V, 1 = +-5V, and EOPL = End of Poll List marker.
*/
local_ppl = (*ppl << 8) | (*ppl & 0x10 ? S626_GSEL_BIPOLAR5V :
S626_GSEL_BIPOLAR10V);
/* Switch ADC analog gain. */
/* Write DEBI command and address to shadow RAM. */
*rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
*rps++ = S626_DEBI_CMD_WRWORD | S626_LP_GSEL;
/* Write DEBI immediate data to shadow RAM. */
*rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
*rps++ = local_ppl;
/* Reset "shadow RAM uploaded" flag. */
*rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
/* Invoke shadow RAM upload. */
*rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
/* Wait for shadow upload to finish. */
*rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
/* Select ADC analog input channel. */
*rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
/* Write DEBI command and address to shadow RAM. */
*rps++ = S626_DEBI_CMD_WRWORD | S626_LP_ISEL;
*rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
/* Write DEBI immediate data to shadow RAM. */
*rps++ = local_ppl;
/* Reset "shadow RAM uploaded" flag. */
*rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
/* Invoke shadow RAM upload. */
*rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
/* Wait for shadow upload to finish. */
*rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
/*
* Delay at least 10 microseconds for analog input settling.
* Instead of padding with NOPs, we use S626_RPS_JUMP
* instructions here; this allows us to produce a longer delay
* than is possible with NOPs because each S626_RPS_JUMP
* flushes the RPS' instruction prefetch pipeline.
*/
jmp_adrs =
(u32)devpriv->rps_buf.physical_base +
(u32)((unsigned long)rps -
(unsigned long)devpriv->rps_buf.logical_base);
for (i = 0; i < (10 * S626_RPSCLK_PER_US / 2); i++) {
jmp_adrs += 8; /* Repeat to implement time delay: */
/* Jump to next RPS instruction. */
*rps++ = S626_RPS_JUMP;
*rps++ = jmp_adrs;
}
if (cmd->convert_src != TRIG_NOW) {
/* Wait for Start trigger. */
*rps++ = S626_RPS_PAUSE | S626_RPS_SIGADC;
*rps++ = S626_RPS_CLRSIGNAL | S626_RPS_SIGADC;
}
/* Start ADC by pulsing GPIO1. */
/* Begin ADC Start pulse. */
*rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
*rps++ = S626_GPIO_BASE | S626_GPIO1_LO;
*rps++ = S626_RPS_NOP;
/* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
/* End ADC Start pulse. */
*rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
*rps++ = S626_GPIO_BASE | S626_GPIO1_HI;
/*
* Wait for ADC to complete (GPIO2 is asserted high when ADC not
* busy) and for data from previous conversion to shift into FB
* BUFFER 1 register.
*/
/* Wait for ADC done. */
*rps++ = S626_RPS_PAUSE | S626_RPS_GPIO2;
/* Transfer ADC data from FB BUFFER 1 register to DMA buffer. */
*rps++ = S626_RPS_STREG |
(S626_BUGFIX_STREG(S626_P_FB_BUFFER1) >> 2);
*rps++ = (u32)devpriv->ana_buf.physical_base +
(devpriv->adc_items << 2);
/*
* If this slot's EndOfPollList flag is set, all channels have
* now been processed.
*/
if (*ppl++ & S626_EOPL) {
devpriv->adc_items++; /* Adjust poll list item count. */
break; /* Exit poll list processing loop. */
}
}
/*
* VERSION 2.01 CHANGE: DELAY CHANGED FROM 250NS to 2US. Allow the
* ADC to stabilize for 2 microseconds before starting the final
* (dummy) conversion. This delay is necessary to allow sufficient
* time between last conversion finished and the start of the dummy
* conversion. Without this delay, the last conversion's data value
* is sometimes set to the previous conversion's data value.
*/
for (n = 0; n < (2 * S626_RPSCLK_PER_US); n++)
*rps++ = S626_RPS_NOP;
/*
* Start a dummy conversion to cause the data from the last
* conversion of interest to be shifted in.
*/
/* Begin ADC Start pulse. */
*rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
*rps++ = S626_GPIO_BASE | S626_GPIO1_LO;
*rps++ = S626_RPS_NOP;
/* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
*rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2); /* End ADC Start pulse. */
*rps++ = S626_GPIO_BASE | S626_GPIO1_HI;
/*
* Wait for the data from the last conversion of interest to arrive
* in FB BUFFER 1 register.
*/
*rps++ = S626_RPS_PAUSE | S626_RPS_GPIO2; /* Wait for ADC done. */
/* Transfer final ADC data from FB BUFFER 1 register to DMA buffer. */
*rps++ = S626_RPS_STREG | (S626_BUGFIX_STREG(S626_P_FB_BUFFER1) >> 2);
*rps++ = (u32)devpriv->ana_buf.physical_base +
(devpriv->adc_items << 2);
/* Indicate ADC scan loop is finished. */
/* Signal ReadADC() that scan is done. */
/* *rps++= S626_RPS_CLRSIGNAL | S626_RPS_SIGADC; */
/* invoke interrupt */
if (devpriv->ai_cmd_running == 1)
*rps++ = S626_RPS_IRQ;
/* Restart RPS program at its beginning. */
*rps++ = S626_RPS_JUMP; /* Branch to start of RPS program. */
*rps++ = (u32)devpriv->rps_buf.physical_base;
/* End of RPS program build */
}