]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/iio/adc/at91-sama5d2_adc.c
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
[linux-beck.git] / drivers / iio / adc / at91-sama5d2_adc.c
index 2e154cb5168567ce3f09ef095bb65a639802d7e4..e10dca3ed74be4633e7d9cccb28200dca685ee90 100644 (file)
 #define        AT91_SAMA5D2_MR_PRESCAL(v)      ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
 #define        AT91_SAMA5D2_MR_PRESCAL_OFFSET  8
 #define        AT91_SAMA5D2_MR_PRESCAL_MAX     0xff
+#define AT91_SAMA5D2_MR_PRESCAL_MASK   GENMASK(15, 8)
 /* Startup Time */
 #define        AT91_SAMA5D2_MR_STARTUP(v)      ((v) << 16)
+#define AT91_SAMA5D2_MR_STARTUP_MASK   GENMASK(19, 16)
 /* Analog Change */
 #define        AT91_SAMA5D2_MR_ANACH           BIT(23)
 /* Tracking Time */
 /* Last Converted Data Register */
 #define AT91_SAMA5D2_LCDR      0x20
 /* Interrupt Enable Register */
-#define AT91_SAMA5D2_IER               0x24
+#define AT91_SAMA5D2_IER       0x24
 /* Interrupt Disable Register */
-#define AT91_SAMA5D2_IDR               0x28
+#define AT91_SAMA5D2_IDR       0x28
 /* Interrupt Mask Register */
-#define AT91_SAMA5D2_IMR               0x2c
+#define AT91_SAMA5D2_IMR       0x2c
 /* Interrupt Status Register */
-#define AT91_SAMA5D2_ISR               0x30
+#define AT91_SAMA5D2_ISR       0x30
 /* Last Channel Trigger Mode Register */
 #define AT91_SAMA5D2_LCTMR     0x34
 /* Last Channel Compare Window Register */
 /* Overrun Status Register */
 #define AT91_SAMA5D2_OVER      0x3c
 /* Extended Mode Register */
-#define AT91_SAMA5D2_EMR               0x40
+#define AT91_SAMA5D2_EMR       0x40
 /* Compare Window Register */
-#define AT91_SAMA5D2_CWR               0x44
+#define AT91_SAMA5D2_CWR       0x44
 /* Channel Gain Register */
-#define AT91_SAMA5D2_CGR               0x48
+#define AT91_SAMA5D2_CGR       0x48
+
 /* Channel Offset Register */
-#define AT91_SAMA5D2_COR               0x4c
+#define AT91_SAMA5D2_COR       0x4c
+#define AT91_SAMA5D2_COR_DIFF_OFFSET   16
+
 /* Channel Data Register 0 */
 #define AT91_SAMA5D2_CDR0      0x50
 /* Analog Control Register */
-#define AT91_SAMA5D2_ACR               0x94
+#define AT91_SAMA5D2_ACR       0x94
 /* Touchscreen Mode Register */
 #define AT91_SAMA5D2_TSMR      0xb0
 /* Touchscreen X Position Register */
 /* Correction Select Register */
 #define AT91_SAMA5D2_COSR      0xd0
 /* Correction Value Register */
-#define AT91_SAMA5D2_CVR               0xd4
+#define AT91_SAMA5D2_CVR       0xd4
 /* Channel Error Correction Register */
 #define AT91_SAMA5D2_CECR      0xd8
 /* Write Protection Mode Register */
 /* Version Register */
 #define AT91_SAMA5D2_VERSION   0xfc
 
-#define AT91_AT91_SAMA5D2_CHAN(num, addr)                              \
+#define AT91_SAMA5D2_CHAN_SINGLE(num, addr)                            \
        {                                                               \
                .type = IIO_VOLTAGE,                                    \
                .channel = num,                                         \
                .indexed = 1,                                           \
        }
 
+#define AT91_SAMA5D2_CHAN_DIFF(num, num2, addr)                                \
+       {                                                               \
+               .type = IIO_VOLTAGE,                                    \
+               .differential = 1,                                      \
+               .channel = num,                                         \
+               .channel2 = num2,                                       \
+               .address = addr,                                        \
+               .scan_type = {                                          \
+                       .sign = 's',                                    \
+                       .realbits = 12,                                 \
+               },                                                      \
+               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+               .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+               .datasheet_name = "CH"#num"-CH"#num2,                   \
+               .indexed = 1,                                           \
+       }
+
 #define at91_adc_readl(st, reg)                readl_relaxed(st->base + reg)
 #define at91_adc_writel(st, reg, val)  writel_relaxed(val, st->base + reg)
 
@@ -185,18 +208,24 @@ struct at91_adc_state {
 };
 
 static const struct iio_chan_spec at91_adc_channels[] = {
-       AT91_AT91_SAMA5D2_CHAN(0, 0x50),
-       AT91_AT91_SAMA5D2_CHAN(1, 0x54),
-       AT91_AT91_SAMA5D2_CHAN(2, 0x58),
-       AT91_AT91_SAMA5D2_CHAN(3, 0x5c),
-       AT91_AT91_SAMA5D2_CHAN(4, 0x60),
-       AT91_AT91_SAMA5D2_CHAN(5, 0x64),
-       AT91_AT91_SAMA5D2_CHAN(6, 0x68),
-       AT91_AT91_SAMA5D2_CHAN(7, 0x6c),
-       AT91_AT91_SAMA5D2_CHAN(8, 0x70),
-       AT91_AT91_SAMA5D2_CHAN(9, 0x74),
-       AT91_AT91_SAMA5D2_CHAN(10, 0x78),
-       AT91_AT91_SAMA5D2_CHAN(11, 0x7c),
+       AT91_SAMA5D2_CHAN_SINGLE(0, 0x50),
+       AT91_SAMA5D2_CHAN_SINGLE(1, 0x54),
+       AT91_SAMA5D2_CHAN_SINGLE(2, 0x58),
+       AT91_SAMA5D2_CHAN_SINGLE(3, 0x5c),
+       AT91_SAMA5D2_CHAN_SINGLE(4, 0x60),
+       AT91_SAMA5D2_CHAN_SINGLE(5, 0x64),
+       AT91_SAMA5D2_CHAN_SINGLE(6, 0x68),
+       AT91_SAMA5D2_CHAN_SINGLE(7, 0x6c),
+       AT91_SAMA5D2_CHAN_SINGLE(8, 0x70),
+       AT91_SAMA5D2_CHAN_SINGLE(9, 0x74),
+       AT91_SAMA5D2_CHAN_SINGLE(10, 0x78),
+       AT91_SAMA5D2_CHAN_SINGLE(11, 0x7c),
+       AT91_SAMA5D2_CHAN_DIFF(0, 1, 0x50),
+       AT91_SAMA5D2_CHAN_DIFF(2, 3, 0x58),
+       AT91_SAMA5D2_CHAN_DIFF(4, 5, 0x60),
+       AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68),
+       AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70),
+       AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78),
 };
 
 static unsigned at91_adc_startup_time(unsigned startup_time_min,
@@ -226,7 +255,7 @@ static unsigned at91_adc_startup_time(unsigned startup_time_min,
 static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
 {
        struct iio_dev *indio_dev = iio_priv_to_dev(st);
-       unsigned f_per, prescal, startup;
+       unsigned f_per, prescal, startup, mr;
 
        f_per = clk_get_rate(st->per_clk);
        prescal = (f_per / (2 * freq)) - 1;
@@ -234,10 +263,11 @@ static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
        startup = at91_adc_startup_time(st->soc_info.startup_time,
                                        freq / 1000);
 
-       at91_adc_writel(st, AT91_SAMA5D2_MR,
-                       AT91_SAMA5D2_MR_TRANSFER(2)
-                       | AT91_SAMA5D2_MR_STARTUP(startup)
-                       | AT91_SAMA5D2_MR_PRESCAL(prescal));
+       mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
+       mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK);
+       mr |= AT91_SAMA5D2_MR_STARTUP(startup);
+       mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
+       at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
 
        dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
                freq, startup, prescal);
@@ -278,6 +308,7 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
                             int *val, int *val2, long mask)
 {
        struct at91_adc_state *st = iio_priv(indio_dev);
+       u32 cor = 0;
        int ret;
 
        switch (mask) {
@@ -286,6 +317,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
 
                st->chan = chan;
 
+               if (chan->differential)
+                       cor = (BIT(chan->channel) | BIT(chan->channel2)) <<
+                             AT91_SAMA5D2_COR_DIFF_OFFSET;
+
+               at91_adc_writel(st, AT91_SAMA5D2_COR, cor);
                at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
                at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
                at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START);
@@ -298,6 +334,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
 
                if (ret > 0) {
                        *val = st->conversion_value;
+                       if (chan->scan_type.sign == 's')
+                               *val = sign_extend32(*val, 11);
                        ret = IIO_VAL_INT;
                        st->conversion_done = false;
                }
@@ -310,6 +348,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
 
        case IIO_CHAN_INFO_SCALE:
                *val = st->vref_uv / 1000;
+               if (chan->differential)
+                       *val *= 2;
                *val2 = chan->scan_type.realbits;
                return IIO_VAL_FRACTIONAL_LOG2;
 
@@ -444,6 +484,12 @@ static int at91_adc_probe(struct platform_device *pdev)
 
        at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
        at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
+       /*
+        * Transfer field must be set to 2 according to the datasheet and
+        * allows different analog settings for each channel.
+        */
+       at91_adc_writel(st, AT91_SAMA5D2_MR,
+                       AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
 
        at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);