Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  at91_adc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Driver for the ADC present in the Atmel AT91 evaluation boards.
 *
 * Copyright 2011 Free Electrons
 */


#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>

#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/pinctrl/consumer.h>

/* Registers */
#define AT91_ADC_CR  0x00  /* Control Register */
#define  AT91_ADC_SWRST  (1 << 0) /* Software Reset */
#define  AT91_ADC_START  (1 << 1) /* Start Conversion */

#define AT91_ADC_MR  0x04  /* Mode Register */
#define  AT91_ADC_TSAMOD  (3 << 0) /* ADC mode */
#define  AT91_ADC_TSAMOD_ADC_ONLY_MODE  (0 << 0) /* ADC Mode */
#define  AT91_ADC_TSAMOD_TS_ONLY_MODE  (1 << 0) /* Touch Screen Only Mode */
#define  AT91_ADC_TRGEN  (1 << 0) /* Trigger Enable */
#define  AT91_ADC_TRGSEL  (7 << 1) /* Trigger Selection */
#define   AT91_ADC_TRGSEL_TC0  (0 << 1)
#define   AT91_ADC_TRGSEL_TC1  (1 << 1)
#define   AT91_ADC_TRGSEL_TC2  (2 << 1)
#define   AT91_ADC_TRGSEL_EXTERNAL (6 << 1)
#define  AT91_ADC_LOWRES  (1 << 4) /* Low Resolution */
#define  AT91_ADC_SLEEP  (1 << 5) /* Sleep Mode */
#define  AT91_ADC_PENDET  (1 << 6) /* Pen contact detection enable */
#define  AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */
#define  AT91_ADC_PRESCAL_9G45 (0xff << 8)
#define   AT91_ADC_PRESCAL_(x) ((x) << 8)
#define  AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */
#define  AT91_ADC_STARTUP_9G45 (0x7f << 16)
#define  AT91_ADC_STARTUP_9X5 (0xf << 16)
#define   AT91_ADC_STARTUP_(x) ((x) << 16)
#define  AT91_ADC_SHTIM  (0xf  << 24) /* Sample & Hold Time */
#define   AT91_ADC_SHTIM_(x) ((x) << 24)
#define  AT91_ADC_PENDBC  (0x0f << 28) /* Pen Debounce time */
#define   AT91_ADC_PENDBC_(x) ((x) << 28)

#define AT91_ADC_TSR  0x0C
#define  AT91_ADC_TSR_SHTIM (0xf  << 24) /* Sample & Hold Time */
#define   AT91_ADC_TSR_SHTIM_(x) ((x) << 24)

#define AT91_ADC_CHER  0x10  /* Channel Enable Register */
#define AT91_ADC_CHDR  0x14  /* Channel Disable Register */
#define AT91_ADC_CHSR  0x18  /* Channel Status Register */
#define  AT91_ADC_CH(n)  (1 << (n)) /* Channel Number */

#define AT91_ADC_SR  0x1C  /* Status Register */
#define  AT91_ADC_EOC(n)  (1 << (n)) /* End of Conversion on Channel N */
#define  AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */
#define  AT91_ADC_DRDY  (1 << 16) /* Data Ready */
#define  AT91_ADC_GOVRE  (1 << 17) /* General Overrun Error */
#define  AT91_ADC_ENDRX  (1 << 18) /* End of RX Buffer */
#define  AT91_ADC_RXFUFF  (1 << 19) /* RX Buffer Full */

#define AT91_ADC_SR_9X5  0x30  /* Status Register for 9x5 */
#define  AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */

#define AT91_ADC_LCDR  0x20  /* Last Converted Data Register */
#define  AT91_ADC_LDATA  (0x3ff)

#define AT91_ADC_IER  0x24  /* Interrupt Enable Register */
#define AT91_ADC_IDR  0x28  /* Interrupt Disable Register */
#define AT91_ADC_IMR  0x2C  /* Interrupt Mask Register */
#define  AT91RL_ADC_IER_PEN (1 << 20)
#define  AT91RL_ADC_IER_NOPEN (1 << 21)
#define  AT91_ADC_IER_PEN (1 << 29)
#define  AT91_ADC_IER_NOPEN (1 << 30)
#define  AT91_ADC_IER_XRDY (1 << 20)
#define  AT91_ADC_IER_YRDY (1 << 21)
#define  AT91_ADC_IER_PRDY (1 << 22)
#define  AT91_ADC_ISR_PENS (1 << 31)

#define AT91_ADC_CHR(n)  (0x30 + ((n) * 4)) /* Channel Data Register N */
#define  AT91_ADC_DATA  (0x3ff)

#define AT91_ADC_CDR0_9X5 (0x50)   /* Channel Data Register 0 for 9X5 */

#define AT91_ADC_ACR  0x94 /* Analog Control Register */
#define  AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */

#define AT91_ADC_TSMR  0xB0
#define  AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */
#define   AT91_ADC_TSMR_TSMODE_NONE  (0 << 0)
#define   AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0)
#define   AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0)
#define   AT91_ADC_TSMR_TSMODE_5WIRE  (3 << 0)
#define  AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
#define   AT91_ADC_TSMR_TSAV_(x)  ((x) << 4)
#define  AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
#define   AT91_ADC_TSMR_SCTIM_(x)  ((x) << 16)
#define  AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
#define   AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
#define  AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
#define  AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */
#define  AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */

#define AT91_ADC_TSXPOSR 0xB4
#define AT91_ADC_TSYPOSR 0xB8
#define AT91_ADC_TSPRESSR 0xBC

#define AT91_ADC_TRGR_9260 AT91_ADC_MR
#define AT91_ADC_TRGR_9G45 0x08
#define AT91_ADC_TRGR_9X5 0xC0

/* Trigger Register bit field */
#define  AT91_ADC_TRGR_TRGPER (0xffff << 16)
#define   AT91_ADC_TRGR_TRGPER_(x) ((x) << 16)
#define  AT91_ADC_TRGR_TRGMOD (0x7 << 0)
#define   AT91_ADC_TRGR_NONE  (0 << 0)
#define   AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0)

#define AT91_ADC_CHAN(st, ch) \
 (st->registers->channel_base + (ch * 4))
#define at91_adc_readl(st, reg) \
 (readl_relaxed(st->reg_base + reg))
#define at91_adc_writel(st, reg, val) \
 (writel_relaxed(val, st->reg_base + reg))

#define DRIVER_NAME  "at91_adc"
#define MAX_POS_BITS  12

#define TOUCH_SAMPLE_PERIOD_US  2000 /* 2ms */
#define TOUCH_PEN_DETECT_DEBOUNCE_US 200

#define MAX_RLPOS_BITS         10
#define TOUCH_SAMPLE_PERIOD_US_RL      10000   /* 10ms, the SoC can't keep up with 2ms */
#define TOUCH_SHTIM                    0xa
#define TOUCH_SCTIM_US  10  /* 10us for the Touchscreen Switches Closure Time */

enum atmel_adc_ts_type {
 ATMEL_ADC_TOUCHSCREEN_NONE = 0,
 ATMEL_ADC_TOUCHSCREEN_4WIRE = 4,
 ATMEL_ADC_TOUCHSCREEN_5WIRE = 5,
};

/**
 * struct at91_adc_trigger - description of triggers
 * @name: name of the trigger advertised to the user
 * @value: value to set in the ADC's trigger setup register
 * to enable the trigger
 * @is_external: Does the trigger rely on an external pin?
 */

struct at91_adc_trigger {
 const char *name;
 u8  value;
 bool  is_external;
};

/**
 * struct at91_adc_reg_desc - Various informations relative to registers
 * @channel_base: Base offset for the channel data registers
 * @drdy_mask: Mask of the DRDY field in the relevant registers
 * (Interruptions registers mostly)
 * @status_register: Offset of the Interrupt Status Register
 * @trigger_register: Offset of the Trigger setup register
 * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register
 * @mr_startup_mask: Mask of the STARTUP field in the adc MR register
 */

struct at91_adc_reg_desc {
 u8 channel_base;
 u32 drdy_mask;
 u8 status_register;
 u8 trigger_register;
 u32 mr_prescal_mask;
 u32 mr_startup_mask;
};

struct at91_adc_caps {
 bool has_ts;  /* Support touch screen */
 bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */
 /*
 * Numbers of sampling data will be averaged. Can be 0~3.
 * Hardware can average (2 ^ ts_filter_average) sample data.
 */

 u8 ts_filter_average;
 /* Pen Detection input pull-up resistor, can be 0~3 */
 u8 ts_pen_detect_sensitivity;

 /* startup time calculate function */
 u32 (*calc_startup_ticks)(u32 startup_time, u32 adc_clk_khz);

 u8 num_channels;

 u8 low_res_bits;
 u8 high_res_bits;
 u32 trigger_number;
 const struct at91_adc_trigger *triggers;
 struct at91_adc_reg_desc registers;
};

struct at91_adc_state {
 struct clk  *adc_clk;
 u16   *buffer;
 unsigned long  channels_mask;
 struct clk  *clk;
 bool   done;
 int   irq;
 u16   last_value;
 int   chnb;
 struct mutex  lock;
 u8   num_channels;
 void __iomem  *reg_base;
 const struct at91_adc_reg_desc *registers;
 u32   startup_time;
 u8   sample_hold_time;
 bool   sleep_mode;
 struct iio_trigger **trig;
 bool   use_external;
 u32   vref_mv;
 u32   res;  /* resolution used for convertions */
 wait_queue_head_t wq_data_avail;
 const struct at91_adc_caps *caps;

 /*
 * Following ADC channels are shared by touchscreen:
 *
 * CH0 -- Touch screen XP/UL
 * CH1 -- Touch screen XM/UR
 * CH2 -- Touch screen YP/LL
 * CH3 -- Touch screen YM/Sense
 * CH4 -- Touch screen LR(5-wire only)
 *
 * The bitfields below represents the reserved channel in the
 * touchscreen mode.
 */

#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0)
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0)
 enum atmel_adc_ts_type touchscreen_type;
 struct input_dev *ts_input;

 u16   ts_sample_period_val;
 u32   ts_pressure_threshold;
 u16   ts_pendbc;

 bool   ts_bufferedmeasure;
 u32   ts_prev_absx;
 u32   ts_prev_absy;
};

static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
{
 struct iio_poll_func *pf = p;
 struct iio_dev *idev = pf->indio_dev;
 struct at91_adc_state *st = iio_priv(idev);
 struct iio_chan_spec const *chan;
 int i, j = 0;

 iio_for_each_active_channel(idev, i) {
  chan = idev->channels + i;
  st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
  j++;
 }

 iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp);

 iio_trigger_notify_done(idev->trig);

 /* Needed to ACK the DRDY interruption */
 at91_adc_readl(st, AT91_ADC_LCDR);

 enable_irq(st->irq);

 return IRQ_HANDLED;
}

/* Handler for classic adc channel eoc trigger */
static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
{
 struct at91_adc_state *st = iio_priv(idev);

 if (iio_buffer_enabled(idev)) {
  disable_irq_nosync(irq);
  iio_trigger_poll(idev->trig);
 } else {
  st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
  /* Needed to ACK the DRDY interruption */
  at91_adc_readl(st, AT91_ADC_LCDR);
  st->done = true;
  wake_up_interruptible(&st->wq_data_avail);
 }
}

static int at91_ts_sample(struct iio_dev *idev)
{
 struct at91_adc_state *st = iio_priv(idev);
 unsigned int xscale, yscale, reg, z1, z2;
 unsigned int x, y, pres, xpos, ypos;
 unsigned int rxp = 1;
 unsigned int factor = 1000;

 unsigned int xyz_mask_bits = st->res;
 unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;

 /* calculate position */
 /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
 reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
 xpos = reg & xyz_mask;
 x = (xpos << MAX_POS_BITS) - xpos;
 xscale = (reg >> 16) & xyz_mask;
 if (xscale == 0) {
  dev_err(&idev->dev, "Error: xscale == 0!\n");
  return -1;
 }
 x /= xscale;

 /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
 reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
 ypos = reg & xyz_mask;
 y = (ypos << MAX_POS_BITS) - ypos;
 yscale = (reg >> 16) & xyz_mask;
 if (yscale == 0) {
  dev_err(&idev->dev, "Error: yscale == 0!\n");
  return -1;
 }
 y /= yscale;

 /* calculate the pressure */
 reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
 z1 = reg & xyz_mask;
 z2 = (reg >> 16) & xyz_mask;

 if (z1 != 0)
  pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
   / factor;
 else
  pres = st->ts_pressure_threshold; /* no pen contacted */

 dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
    xpos, xscale, ypos, yscale, z1, z2, pres);

 if (pres < st->ts_pressure_threshold) {
  dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
     x, y, pres / factor);
  input_report_abs(st->ts_input, ABS_X, x);
  input_report_abs(st->ts_input, ABS_Y, y);
  input_report_abs(st->ts_input, ABS_PRESSURE, pres);
  input_report_key(st->ts_input, BTN_TOUCH, 1);
  input_sync(st->ts_input);
 } else {
  dev_dbg(&idev->dev, "pressure too low: not reporting\n");
 }

 return 0;
}

static irqreturn_t at91_adc_rl_interrupt(int irq, void *private)
{
 struct iio_dev *idev = private;
 struct at91_adc_state *st = iio_priv(idev);
 u32 status = at91_adc_readl(st, st->registers->status_register);
 unsigned int reg;

 status &= at91_adc_readl(st, AT91_ADC_IMR);
 if (status & GENMASK(st->num_channels - 1, 0))
  handle_adc_eoc_trigger(irq, idev);

 if (status & AT91RL_ADC_IER_PEN) {
  /* Disabling pen debounce is required to get a NOPEN irq */
  reg = at91_adc_readl(st, AT91_ADC_MR);
  reg &= ~AT91_ADC_PENDBC;
  at91_adc_writel(st, AT91_ADC_MR, reg);

  at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
  at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN
    | AT91_ADC_EOC(3));
  /* Set up period trigger for sampling */
  at91_adc_writel(st, st->registers->trigger_register,
   AT91_ADC_TRGR_MOD_PERIOD_TRIG |
   AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
 } else if (status & AT91RL_ADC_IER_NOPEN) {
  reg = at91_adc_readl(st, AT91_ADC_MR);
  reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
  at91_adc_writel(st, AT91_ADC_MR, reg);
  at91_adc_writel(st, st->registers->trigger_register,
   AT91_ADC_TRGR_NONE);

  at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN
    | AT91_ADC_EOC(3));
  at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
  st->ts_bufferedmeasure = false;
  input_report_key(st->ts_input, BTN_TOUCH, 0);
  input_sync(st->ts_input);
 } else if (status & AT91_ADC_EOC(3) && st->ts_input) {
  /* Conversion finished and we've a touchscreen */
  if (st->ts_bufferedmeasure) {
   /*
 * Last measurement is always discarded, since it can
 * be erroneous.
 * Always report previous measurement
 */

   input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx);
   input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy);
   input_report_key(st->ts_input, BTN_TOUCH, 1);
   input_sync(st->ts_input);
  } else
   st->ts_bufferedmeasure = true;

  /* Now make new measurement */
  st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3))
       << MAX_RLPOS_BITS;
  st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2));

  st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1))
       << MAX_RLPOS_BITS;
  st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0));
 }

 return IRQ_HANDLED;
}

static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private)
{
 struct iio_dev *idev = private;
 struct at91_adc_state *st = iio_priv(idev);
 u32 status = at91_adc_readl(st, st->registers->status_register);
 const uint32_t ts_data_irq_mask =
  AT91_ADC_IER_XRDY |
  AT91_ADC_IER_YRDY |
  AT91_ADC_IER_PRDY;

 if (status & GENMASK(st->num_channels - 1, 0))
  handle_adc_eoc_trigger(irq, idev);

 if (status & AT91_ADC_IER_PEN) {
  at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
  at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
   ts_data_irq_mask);
  /* Set up period trigger for sampling */
  at91_adc_writel(st, st->registers->trigger_register,
   AT91_ADC_TRGR_MOD_PERIOD_TRIG |
   AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
 } else if (status & AT91_ADC_IER_NOPEN) {
  at91_adc_writel(st, st->registers->trigger_register, 0);
  at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
   ts_data_irq_mask);
  at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);

  input_report_key(st->ts_input, BTN_TOUCH, 0);
  input_sync(st->ts_input);
 } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
  /* Now all touchscreen data is ready */

  if (status & AT91_ADC_ISR_PENS) {
   /* validate data by pen contact */
   at91_ts_sample(idev);
  } else {
   /* triggered by event that is no pen contact, just read
 * them to clean the interrupt and discard all.
 */

   at91_adc_readl(st, AT91_ADC_TSXPOSR);
   at91_adc_readl(st, AT91_ADC_TSYPOSR);
   at91_adc_readl(st, AT91_ADC_TSPRESSR);
  }
 }

 return IRQ_HANDLED;
}

static int at91_adc_channel_init(struct iio_dev *idev)
{
 struct at91_adc_state *st = iio_priv(idev);
 struct iio_chan_spec *chan_array, *timestamp;
 int bit, idx = 0;
 unsigned long rsvd_mask = 0;

 /* If touchscreen is enable, then reserve the adc channels */
 if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
  rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
 else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
  rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;

 /* set up the channel mask to reserve touchscreen channels */
 st->channels_mask &= ~rsvd_mask;

 idev->num_channels = bitmap_weight(&st->channels_mask,
        st->num_channels) + 1;

 chan_array = devm_kzalloc(&idev->dev,
      ((idev->num_channels + 1) *
     sizeof(struct iio_chan_spec)),
      GFP_KERNEL);

 if (!chan_array)
  return -ENOMEM;

 for_each_set_bit(bit, &st->channels_mask, st->num_channels) {
  struct iio_chan_spec *chan = chan_array + idx;

  chan->type = IIO_VOLTAGE;
  chan->indexed = 1;
  chan->channel = bit;
  chan->scan_index = idx;
  chan->scan_type.sign = 'u';
  chan->scan_type.realbits = st->res;
  chan->scan_type.storagebits = 16;
  chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
  chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
  idx++;
 }
 timestamp = chan_array + idx;

 timestamp->type = IIO_TIMESTAMP;
 timestamp->channel = -1;
 timestamp->scan_index = idx;
 timestamp->scan_type.sign = 's';
 timestamp->scan_type.realbits = 64;
 timestamp->scan_type.storagebits = 64;

 idev->channels = chan_array;
 return idev->num_channels;
}

static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
          const struct at91_adc_trigger *triggers,
          const char *trigger_name)
{
 struct at91_adc_state *st = iio_priv(idev);
 int i;

 for (i = 0; i < st->caps->trigger_number; i++) {
  char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s",
           idev->name,
           iio_device_id(idev),
           triggers[i].name);
  if (!name)
   return -ENOMEM;

  if (strcmp(trigger_name, name) == 0) {
   if (triggers[i].value == 0)
    return -EINVAL;
   return triggers[i].value;
  }
 }

 return -EINVAL;
}

static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
{
 struct iio_dev *idev = iio_trigger_get_drvdata(trig);
 struct at91_adc_state *st = iio_priv(idev);
 const struct at91_adc_reg_desc *reg = st->registers;
 u32 status = at91_adc_readl(st, reg->trigger_register);
 int value;
 u8 bit;

 value = at91_adc_get_trigger_value_by_name(idev,
         st->caps->triggers,
         idev->trig->name);
 if (value < 0)
  return value;

 if (state) {
  st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL);
  if (st->buffer == NULL)
   return -ENOMEM;

  at91_adc_writel(st, reg->trigger_register,
    status | value);

  for_each_set_bit(bit, idev->active_scan_mask,
     st->num_channels) {
   struct iio_chan_spec const *chan = idev->channels + bit;
   at91_adc_writel(st, AT91_ADC_CHER,
     AT91_ADC_CH(chan->channel));
  }

  at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask);

 } else {
  at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask);

  at91_adc_writel(st, reg->trigger_register,
    status & ~value);

  for_each_set_bit(bit, idev->active_scan_mask,
     st->num_channels) {
   struct iio_chan_spec const *chan = idev->channels + bit;
   at91_adc_writel(st, AT91_ADC_CHDR,
     AT91_ADC_CH(chan->channel));
  }
  kfree(st->buffer);
 }

 return 0;
}

static const struct iio_trigger_ops at91_adc_trigger_ops = {
 .set_trigger_state = &at91_adc_configure_trigger,
};

static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
           const struct at91_adc_trigger *trigger)
{
 struct iio_trigger *trig;
 int ret;

 trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name,
     iio_device_id(idev), trigger->name);
 if (trig == NULL)
  return NULL;

 iio_trigger_set_drvdata(trig, idev);
 trig->ops = &at91_adc_trigger_ops;

 ret = iio_trigger_register(trig);
 if (ret) {
  iio_trigger_free(trig);
  return NULL;
 }

 return trig;
}

static int at91_adc_trigger_init(struct iio_dev *idev)
{
 struct at91_adc_state *st = iio_priv(idev);
 int i, ret;

 st->trig = devm_kcalloc(&idev->dev,
    st->caps->trigger_number, sizeof(*st->trig),
    GFP_KERNEL);

 if (st->trig == NULL) {
  ret = -ENOMEM;
  goto error_ret;
 }

 for (i = 0; i < st->caps->trigger_number; i++) {
  if (st->caps->triggers[i].is_external && !(st->use_external))
   continue;

  st->trig[i] = at91_adc_allocate_trigger(idev,
       st->caps->triggers + i);
  if (st->trig[i] == NULL) {
   dev_err(&idev->dev,
    "Could not allocate trigger %d\n", i);
   ret = -ENOMEM;
   goto error_trigger;
  }
 }

 return 0;

error_trigger:
 for (i--; i >= 0; i--) {
  iio_trigger_unregister(st->trig[i]);
  iio_trigger_free(st->trig[i]);
 }
error_ret:
 return ret;
}

static void at91_adc_trigger_remove(struct iio_dev *idev)
{
 struct at91_adc_state *st = iio_priv(idev);
 int i;

 for (i = 0; i < st->caps->trigger_number; i++) {
  iio_trigger_unregister(st->trig[i]);
  iio_trigger_free(st->trig[i]);
 }
}

static int at91_adc_buffer_init(struct iio_dev *idev)
{
 return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time,
  &at91_adc_trigger_handler, NULL);
}

static void at91_adc_buffer_remove(struct iio_dev *idev)
{
 iio_triggered_buffer_cleanup(idev);
}

static int at91_adc_read_raw(struct iio_dev *idev,
        struct iio_chan_spec const *chan,
        int *val, int *val2, long mask)
{
 struct at91_adc_state *st = iio_priv(idev);
 int ret;

 switch (mask) {
 case IIO_CHAN_INFO_RAW:
  mutex_lock(&st->lock);

  st->chnb = chan->channel;
  at91_adc_writel(st, AT91_ADC_CHER,
    AT91_ADC_CH(chan->channel));
  at91_adc_writel(st, AT91_ADC_IER, BIT(chan->channel));
  at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START);

  ret = wait_event_interruptible_timeout(st->wq_data_avail,
             st->done,
             msecs_to_jiffies(1000));

  /* Disable interrupts, regardless if adc conversion was
 * successful or not
 */

  at91_adc_writel(st, AT91_ADC_CHDR,
    AT91_ADC_CH(chan->channel));
  at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));

  if (ret > 0) {
   /* a valid conversion took place */
   *val = st->last_value;
   st->last_value = 0;
   st->done = false;
   ret = IIO_VAL_INT;
  } else if (ret == 0) {
   /* conversion timeout */
   dev_err(&idev->dev, "ADC Channel %d timeout.\n",
    chan->channel);
   ret = -ETIMEDOUT;
  }

  mutex_unlock(&st->lock);
  return ret;

 case IIO_CHAN_INFO_SCALE:
  *val = st->vref_mv;
  *val2 = chan->scan_type.realbits;
  return IIO_VAL_FRACTIONAL_LOG2;
 default:
  break;
 }
 return -EINVAL;
}


static u32 calc_startup_ticks_9260(u32 startup_time, u32 adc_clk_khz)
{
 /*
 * Number of ticks needed to cover the startup time of the ADC
 * as defined in the electrical characteristics of the board,
 * divided by 8. The formula thus is :
 *   Startup Time = (ticks + 1) * 8 / ADC Clock
 */

 return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8;
}

static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz)
{
 /*
 * For sama5d3x and at91sam9x5, the formula changes to:
 * Startup Time = <lookup_table_value> / ADC Clock
 */

 static const int startup_lookup[] = {
  0,   8,   16,  24,
  64,  80,  96,  112,
  512, 576, 640, 704,
  768, 832, 896, 960
  };
 int i, size = ARRAY_SIZE(startup_lookup);
 unsigned int ticks;

 ticks = startup_time * adc_clk_khz / 1000;
 for (i = 0; i < size; i++)
  if (ticks < startup_lookup[i])
   break;

 ticks = i;
 if (ticks == size)
  /* Reach the end of lookup table */
  ticks = size - 1;

 return ticks;
}

static int at91_adc_probe_dt_ts(struct device_node *node,
 struct at91_adc_state *st, struct device *dev)
{
 int ret;
 u32 prop;

 ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop);
 if (ret) {
  dev_info(dev, "ADC Touch screen is disabled.\n");
  return 0;
 }

 switch (prop) {
 case 4:
 case 5:
  st->touchscreen_type = prop;
  break;
 default:
  dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n"prop);
  return -EINVAL;
 }

 if (!st->caps->has_tsmr)
  return 0;
 prop = 0;
 of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop);
 st->ts_pressure_threshold = prop;
 if (st->ts_pressure_threshold) {
  return 0;
 } else {
  dev_err(dev, "Invalid pressure threshold for the touchscreen\n");
  return -EINVAL;
 }
}

static const struct iio_info at91_adc_info = {
 .read_raw = &at91_adc_read_raw,
};

/* Touchscreen related functions */
static int atmel_ts_open(struct input_dev *dev)
{
 struct at91_adc_state *st = input_get_drvdata(dev);

 if (st->caps->has_tsmr)
  at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
 else
  at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
 return 0;
}

static void atmel_ts_close(struct input_dev *dev)
{
 struct at91_adc_state *st = input_get_drvdata(dev);

 if (st->caps->has_tsmr)
  at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
 else
  at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
}

static int at91_ts_hw_init(struct iio_dev *idev, u32 adc_clk_khz)
{
 struct at91_adc_state *st = iio_priv(idev);
 u32 reg = 0;
 u32 tssctim = 0;
 int i = 0;

 /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
 * pen detect noise.
 * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
 */

 st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz /
     1000, 1);

 while (st->ts_pendbc >> ++i)
  ; /* Empty! Find the shift offset */
 if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1))))
  st->ts_pendbc = i;
 else
  st->ts_pendbc = i - 1;

 if (!st->caps->has_tsmr) {
  reg = at91_adc_readl(st, AT91_ADC_MR);
  reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET;

  reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
  at91_adc_writel(st, AT91_ADC_MR, reg);

  reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM;
  at91_adc_writel(st, AT91_ADC_TSR, reg);

  st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL *
          adc_clk_khz / 1000) - 1, 1);

  return 0;
 }

 /* Touchscreen Switches Closure time needed for allowing the value to
 * stabilize.
 * Switch Closure Time = (TSSCTIM * 4) ADCClock periods
 */

 tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4);
 dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n",
  adc_clk_khz, tssctim);

 if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
  reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
 else
  reg = AT91_ADC_TSMR_TSMODE_5WIRE;

 reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM;
 reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
        & AT91_ADC_TSMR_TSAV;
 reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC;
 reg |= AT91_ADC_TSMR_NOTSDMA;
 reg |= AT91_ADC_TSMR_PENDET_ENA;
 reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */

 at91_adc_writel(st, AT91_ADC_TSMR, reg);

 /* Change adc internal resistor value for better pen detection,
 * default value is 100 kOhm.
 * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
 * option only available on ES2 and higher
 */

 at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity
   & AT91_ADC_ACR_PENDETSENS);

 /* Sample Period Time = (TRGPER + 1) / ADCClock */
 st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US *
   adc_clk_khz / 1000) - 1, 1);

 return 0;
}

static int at91_ts_register(struct iio_dev *idev,
  struct platform_device *pdev)
{
 struct at91_adc_state *st = iio_priv(idev);
 struct input_dev *input;
 int ret;

 input = input_allocate_device();
 if (!input) {
  dev_err(&idev->dev, "Failed to allocate TS device!\n");
  return -ENOMEM;
 }

 input->name = DRIVER_NAME;
 input->id.bustype = BUS_HOST;
 input->dev.parent = &pdev->dev;
 input->open = atmel_ts_open;
 input->close = atmel_ts_close;

 __set_bit(EV_ABS, input->evbit);
 __set_bit(EV_KEY, input->evbit);
 __set_bit(BTN_TOUCH, input->keybit);
 if (st->caps->has_tsmr) {
  input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1,
         0, 0);
  input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1,
         0, 0);
  input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
 } else {
  if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) {
   dev_err(&pdev->dev,
    "This touchscreen controller only support 4 wires\n");
   ret = -EINVAL;
   goto err;
  }

  input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1,
         0, 0);
  input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1,
         0, 0);
 }

 st->ts_input = input;
 input_set_drvdata(input, st);

 ret = input_register_device(input);
 if (ret)
  goto err;

 return ret;

err:
 input_free_device(input);
 return ret;
}

static void at91_ts_unregister(struct at91_adc_state *st)
{
 input_unregister_device(st->ts_input);
}

static int at91_adc_probe(struct platform_device *pdev)
{
 unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
 struct device_node *node = pdev->dev.of_node;
 int ret;
 struct iio_dev *idev;
 struct at91_adc_state *st;
 u32 reg, prop;
 char *s;

 idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state));
 if (!idev)
  return -ENOMEM;

 st = iio_priv(idev);

 st->caps = of_device_get_match_data(&pdev->dev);

 st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");

 if (of_property_read_u32(node, "atmel,adc-channels-used", &prop))
  return dev_err_probe(&idev->dev, -EINVAL,
         "Missing adc-channels-used property in the DT.\n");
 st->channels_mask = prop;

 st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");

 if (of_property_read_u32(node, "atmel,adc-startup-time", &prop))
  return dev_err_probe(&idev->dev, -EINVAL,
         "Missing adc-startup-time property in the DT.\n");
 st->startup_time = prop;

 prop = 0;
 of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop);
 st->sample_hold_time = prop;

 if (of_property_read_u32(node, "atmel,adc-vref", &prop))
  return dev_err_probe(&idev->dev, -EINVAL,
         "Missing adc-vref property in the DT.\n");
 st->vref_mv = prop;

 st->res = st->caps->high_res_bits;
 if (st->caps->low_res_bits &&
     !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s)
     && !strcmp(s, "lowres"))
  st->res = st->caps->low_res_bits;

 dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);

 st->registers = &st->caps->registers;
 st->num_channels = st->caps->num_channels;

 /* Check if touchscreen is supported. */
 if (st->caps->has_ts) {
  ret = at91_adc_probe_dt_ts(node, st, &idev->dev);
  if (ret)
   return ret;
 }

 platform_set_drvdata(pdev, idev);

 idev->name = dev_name(&pdev->dev);
 idev->modes = INDIO_DIRECT_MODE;
 idev->info = &at91_adc_info;

 st->irq = platform_get_irq(pdev, 0);
 if (st->irq < 0)
  return -ENODEV;

 st->reg_base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(st->reg_base))
  return PTR_ERR(st->reg_base);

 /*
 * Disable all IRQs before setting up the handler
 */

 at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
 at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);

 if (st->caps->has_tsmr)
  ret = devm_request_irq(&pdev->dev, st->irq,
           at91_adc_9x5_interrupt, 0,
           pdev->dev.driver->name, idev);
 else
  ret = devm_request_irq(&pdev->dev, st->irq,
           at91_adc_rl_interrupt, 0,
           pdev->dev.driver->name, idev);
 if (ret)
  return dev_err_probe(&pdev->dev, ret,
         "Failed to allocate IRQ.\n");

 st->clk = devm_clk_get_enabled(&pdev->dev, "adc_clk");
 if (IS_ERR(st->clk))
  return dev_err_probe(&pdev->dev, PTR_ERR(st->clk),
         "Could not prepare or enable the clock.\n");

 st->adc_clk = devm_clk_get_enabled(&pdev->dev, "adc_op_clk");
 if (IS_ERR(st->adc_clk))
  return dev_err_probe(&pdev->dev, PTR_ERR(st->adc_clk),
         "Could not prepare or enable the ADC clock.\n");

 /*
 * Prescaler rate computation using the formula from the Atmel's
 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
 * specified by the electrical characteristics of the board.
 */

 mstrclk = clk_get_rate(st->clk);
 adc_clk = clk_get_rate(st->adc_clk);
 adc_clk_khz = adc_clk / 1000;

 dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
  mstrclk, adc_clk);

 prsc = (mstrclk / (2 * adc_clk)) - 1;

 if (!st->startup_time)
  return dev_err_probe(&pdev->dev, -EINVAL,
         "No startup time available.\n");
 ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz);

 /*
 * a minimal Sample and Hold Time is necessary for the ADC to guarantee
 * the best converted final value between two channels selection
 * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
 */

 if (st->sample_hold_time > 0)
  shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000)
     - 1, 1);
 else
  shtim = 0;

 reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask;
 reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask;
 if (st->res == st->caps->low_res_bits)
  reg |= AT91_ADC_LOWRES;
 if (st->sleep_mode)
  reg |= AT91_ADC_SLEEP;
 reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM;
 at91_adc_writel(st, AT91_ADC_MR, reg);

 /* Setup the ADC channels available on the board */
 ret = at91_adc_channel_init(idev);
 if (ret < 0)
  return dev_err_probe(&pdev->dev, ret,
         "Couldn't initialize the channels.\n");

 init_waitqueue_head(&st->wq_data_avail);
 mutex_init(&st->lock);

 /*
 * Since touch screen will set trigger register as period trigger. So
 * when touch screen is enabled, then we have to disable hardware
 * trigger for classic adc.
 */

 if (!st->touchscreen_type) {
  ret = at91_adc_buffer_init(idev);
  if (ret < 0)
   return dev_err_probe(&pdev->dev, ret,
          "Couldn't initialize the buffer.\n");

  ret = at91_adc_trigger_init(idev);
  if (ret < 0) {
   dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
   at91_adc_buffer_remove(idev);
   return ret;
  }
 } else {
  ret = at91_ts_register(idev, pdev);
  if (ret)
   return ret;

  at91_ts_hw_init(idev, adc_clk_khz);
 }

 ret = iio_device_register(idev);
 if (ret < 0) {
  dev_err(&pdev->dev, "Couldn't register the device.\n");
  goto error_iio_device_register;
 }

 return 0;

error_iio_device_register:
 if (!st->touchscreen_type) {
  at91_adc_trigger_remove(idev);
  at91_adc_buffer_remove(idev);
 } else {
  at91_ts_unregister(st);
 }
 return ret;
}

static void at91_adc_remove(struct platform_device *pdev)
{
 struct iio_dev *idev = platform_get_drvdata(pdev);
 struct at91_adc_state *st = iio_priv(idev);

 iio_device_unregister(idev);
 if (!st->touchscreen_type) {
  at91_adc_trigger_remove(idev);
  at91_adc_buffer_remove(idev);
 } else {
  at91_ts_unregister(st);
 }
}

static int at91_adc_suspend(struct device *dev)
{
 struct iio_dev *idev = dev_get_drvdata(dev);
 struct at91_adc_state *st = iio_priv(idev);

 pinctrl_pm_select_sleep_state(dev);
 clk_disable_unprepare(st->clk);

 return 0;
}

static int at91_adc_resume(struct device *dev)
{
 struct iio_dev *idev = dev_get_drvdata(dev);
 struct at91_adc_state *st = iio_priv(idev);

 clk_prepare_enable(st->clk);
 pinctrl_pm_select_default_state(dev);

 return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend,
    at91_adc_resume);

static const struct at91_adc_trigger at91sam9260_triggers[] = {
 { .name = "timer-counter-0", .value = 0x1 },
 { .name = "timer-counter-1", .value = 0x3 },
 { .name = "timer-counter-2", .value = 0x5 },
 { .name = "external", .value = 0xd, .is_external = true },
};

static const struct at91_adc_caps at91sam9260_caps = {
 .calc_startup_ticks = calc_startup_ticks_9260,
 .num_channels = 4,
 .low_res_bits = 8,
 .high_res_bits = 10,
 .registers = {
  .channel_base = AT91_ADC_CHR(0),
  .drdy_mask = AT91_ADC_DRDY,
  .status_register = AT91_ADC_SR,
  .trigger_register = AT91_ADC_TRGR_9260,
  .mr_prescal_mask = AT91_ADC_PRESCAL_9260,
  .mr_startup_mask = AT91_ADC_STARTUP_9260,
 },
 .triggers = at91sam9260_triggers,
 .trigger_number = ARRAY_SIZE(at91sam9260_triggers),
};

static const struct at91_adc_trigger at91sam9x5_triggers[] = {
 { .name = "external-rising", .value = 0x1, .is_external = true },
 { .name = "external-falling", .value = 0x2, .is_external = true },
 { .name = "external-any", .value = 0x3, .is_external = true },
 { .name = "continuous", .value = 0x6 },
};

static const struct at91_adc_caps at91sam9rl_caps = {
 .has_ts = true,
 .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
 .num_channels = 6,
 .low_res_bits = 8,
 .high_res_bits = 10,
 .registers = {
  .channel_base = AT91_ADC_CHR(0),
  .drdy_mask = AT91_ADC_DRDY,
  .status_register = AT91_ADC_SR,
  .trigger_register = AT91_ADC_TRGR_9G45,
  .mr_prescal_mask = AT91_ADC_PRESCAL_9260,
  .mr_startup_mask = AT91_ADC_STARTUP_9G45,
 },
 .triggers = at91sam9x5_triggers,
 .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};

static const struct at91_adc_caps at91sam9g45_caps = {
 .has_ts = true,
 .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
 .num_channels = 8,
 .low_res_bits = 8,
 .high_res_bits = 10,
 .registers = {
  .channel_base = AT91_ADC_CHR(0),
  .drdy_mask = AT91_ADC_DRDY,
  .status_register = AT91_ADC_SR,
  .trigger_register = AT91_ADC_TRGR_9G45,
  .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
  .mr_startup_mask = AT91_ADC_STARTUP_9G45,
 },
 .triggers = at91sam9x5_triggers,
 .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};

static const struct at91_adc_caps at91sam9x5_caps = {
 .has_ts = true,
 .has_tsmr = true,
 .ts_filter_average = 3,
 .ts_pen_detect_sensitivity = 2,
 .calc_startup_ticks = calc_startup_ticks_9x5,
 .num_channels = 12,
 .low_res_bits = 8,
 .high_res_bits = 10,
 .registers = {
  .channel_base = AT91_ADC_CDR0_9X5,
  .drdy_mask = AT91_ADC_SR_DRDY_9X5,
  .status_register = AT91_ADC_SR_9X5,
  .trigger_register = AT91_ADC_TRGR_9X5,
  /* prescal mask is same as 9G45 */
  .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
  .mr_startup_mask = AT91_ADC_STARTUP_9X5,
 },
 .triggers = at91sam9x5_triggers,
 .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};

static const struct at91_adc_caps sama5d3_caps = {
 .has_ts = true,
 .has_tsmr = true,
 .ts_filter_average = 3,
 .ts_pen_detect_sensitivity = 2,
 .calc_startup_ticks = calc_startup_ticks_9x5,
 .num_channels = 12,
 .low_res_bits = 0,
 .high_res_bits = 12,
 .registers = {
  .channel_base = AT91_ADC_CDR0_9X5,
  .drdy_mask = AT91_ADC_SR_DRDY_9X5,
  .status_register = AT91_ADC_SR_9X5,
  .trigger_register = AT91_ADC_TRGR_9X5,
  .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
  .mr_startup_mask = AT91_ADC_STARTUP_9X5,
 },
 .triggers = at91sam9x5_triggers,
 .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};

static const struct of_device_id at91_adc_dt_ids[] = {
 { .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps },
 { .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps },
 { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps },
 { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps },
 { .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps },
 { }
};
MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);

static struct platform_driver at91_adc_driver = {
 .probe = at91_adc_probe,
 .remove = at91_adc_remove,
 .driver = {
     .name = DRIVER_NAME,
     .of_match_table = at91_adc_dt_ids,
     .pm = pm_sleep_ptr(&at91_adc_pm_ops),
 },
};

module_platform_driver(at91_adc_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
MODULE_AUTHOR("Maxime Ripard ");

Messung V0.5
C=95 H=92 G=93

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge