static void s626_reset_adc()

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 */
}