/* * This interrupt is shared with the touchscreen driver. * Make sure this interrupt is intended for us. * Handle only ADC channel specific interrupts.
*/
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status);
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &intr_mask);
intr_status = intr_status & intr_mask;
channel_intr_status = (intr_status & IPROC_ADC_INTR_MASK) >>
IPROC_ADC_INTR; if (channel_intr_status) return IRQ_WAKE_THREAD;
staticint iproc_adc_do_read(struct iio_dev *indio_dev, int channel,
u16 *p_adc_data)
{ int read_len = 0;
u32 val;
u32 mask;
u32 val_check; int failed_cnt = 0; struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
mutex_lock(&adc_priv->mutex);
/* * After a read is complete the ADC interrupts will be disabled so * we can assume this section of code is safe from interrupts.
*/
adc_priv->chan_val = -1;
adc_priv->chan_id = channel;
/* Set the Watermark for a channel */
regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL2 +
IPROC_ADC_CHANNEL_OFFSET * channel),
IPROC_ADC_CHANNEL_WATERMARK_MASK,
0x1);
/* Enable water mark interrupt */
regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_INTERRUPT_MASK +
IPROC_ADC_CHANNEL_OFFSET *
channel),
IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK,
IPROC_ADC_WATER_MARK_INTR_ENABLE);
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val);
/* Enable ADC interrupt for a channel */
val |= (BIT(channel) << IPROC_ADC_INTR);
regmap_write(adc_priv->regmap, IPROC_INTERRUPT_MASK, val);
/* * There seems to be a very rare issue where writing to this register * does not take effect. To work around the issue we will try multiple * writes. In total we will spend about 10*10 = 100 us attempting this. * Testing has shown that this may loop a few time, but we have never * hit the full count.
*/
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); while (val_check != val) {
failed_cnt++;
if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) break;
if (failed_cnt) {
dev_dbg(&indio_dev->dev, "IntMask failed (%d times)", failed_cnt); if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) {
dev_err(&indio_dev->dev, "IntMask set failed. Read will likely fail.");
read_len = -EIO; goto adc_err;
}
}
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check);
if (wait_for_completion_timeout(&adc_priv->completion,
IPROC_ADC_READ_TIMEOUT) > 0) {
/* Only the lower 16 bits are relevant */
*p_adc_data = adc_priv->chan_val & 0xFFFF;
read_len = sizeof(*p_adc_data);
} else { /* * We never got the interrupt, something went wrong. * Perhaps the interrupt may still be coming, we do not want * that now. Lets disable the ADC interrupt, and clear the * status to put it back in to normal state.
*/
read_len = -ETIMEDOUT; goto adc_err;
}
mutex_unlock(&adc_priv->mutex);
/* Set i_amux = 3b'000, select channel 0 */
ret = regmap_clear_bits(adc_priv->regmap, IPROC_ANALOG_CONTROL,
IPROC_ADC_CHANNEL_SEL_MASK); if (ret) {
dev_err(&indio_dev->dev, "failed to write IPROC_ANALOG_CONTROL %d\n", ret); return ret;
}
adc_priv->chan_val = -1;
/* * PWR up LDO, ADC, and Band Gap (0 to enable) * Also enable ADC controller (set high)
*/
ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); if (ret) {
dev_err(&indio_dev->dev, "failed to read IPROC_REGCTL2 %d\n", ret); return ret;
}
val &= ~(IPROC_ADC_PWR_LDO | IPROC_ADC_PWR_ADC | IPROC_ADC_PWR_BG);
ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); if (ret) {
dev_err(&indio_dev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); return ret;
}
ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); if (ret) {
dev_err(&indio_dev->dev, "failed to read IPROC_REGCTL2 %d\n", ret); return ret;
}
val |= IPROC_ADC_CONTROLLER_EN;
ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); if (ret) {
dev_err(&indio_dev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); return ret;
}
for (channel_id = 0; channel_id < indio_dev->num_channels;
channel_id++) {
ret = regmap_write(adc_priv->regmap,
IPROC_ADC_CHANNEL_INTERRUPT_MASK +
IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); if (ret) {
dev_err(&indio_dev->dev, "failed to write ADC_CHANNEL_INTERRUPT_MASK %d\n",
ret); return ret;
}
ret = regmap_write(adc_priv->regmap,
IPROC_ADC_CHANNEL_INTERRUPT_STATUS +
IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); if (ret) {
dev_err(&indio_dev->dev, "failed to write ADC_CHANNEL_INTERRUPT_STATUS %d\n",
ret); return ret;
}
}
adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "adc-syscon"); if (IS_ERR(adc_priv->regmap)) {
dev_err(&pdev->dev, "failed to get handle for tsc syscon\n");
ret = PTR_ERR(adc_priv->regmap); return ret;
}
adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); if (IS_ERR(adc_priv->adc_clk)) {
dev_err(&pdev->dev, "failed getting clock tsc_clk\n");
ret = PTR_ERR(adc_priv->adc_clk); return ret;
}
adc_priv->irqno = platform_get_irq(pdev, 0); if (adc_priv->irqno < 0) return adc_priv->irqno;
ret = regmap_clear_bits(adc_priv->regmap, IPROC_REGCTL2,
IPROC_ADC_AUXIN_SCAN_ENA); if (ret) {
dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno,
iproc_adc_interrupt_handler,
iproc_adc_interrupt_thread,
IRQF_SHARED, "iproc-adc", indio_dev); if (ret) {
dev_err(&pdev->dev, "request_irq error %d\n", ret); return ret;
}
ret = clk_prepare_enable(adc_priv->adc_clk); if (ret) {
dev_err(&pdev->dev, "clk_prepare_enable failed %d\n", ret); return ret;
}
ret = iproc_adc_enable(indio_dev); if (ret) {
dev_err(&pdev->dev, "failed to enable adc %d\n", ret); goto err_adc_enable;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.