* [PATCH v3 0/5] Introduce at91_adc8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Hi,
This is the very basic support for the adc introduced with the SAMA5D2 SoC
family.
The goal is to provide something to the user as soon as possible instead of
waiting for a full featured driver.
Only unsigned conversions on a software tigger are supported. Next steps are
signed conversions, differential channels, hardware triggers, dma support,
touchscreen support, and others.
Changes:
- v3:
- Driver file renaming.
- Syntax cleanup.
- Sampling frequency accessed through read/write_raw.
- Cleanup in the IRQ handler.
- v2:
- Cleanup thanks to Peter Meerwald-Stadler comments (add prefix for macros,
remove useless stuff, etc).
- Move parameters relative to the SoC to the device tree (min/max sampling
rate and startup time).
- Use sysfs to configure the sampling frequency.
- vddana supply is no more optionnal.
- No change for irq since I am not sure it will simplify the code (interrupt
is cleared when reading channel conversion result). Jonathan, tell me if
I need to rework this part.
Ludovic Desroches (5):
iio:adc:at91_adc8xx: introduce new atmel adc driver
MAINTAINERS: add entry for Atmel ADC 8xx driver
ARM: at91/dt: sama5d2: add adc device
ARM: at91/dt: sama5d2 Xplained: enable the adc device
ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
MAINTAINERS | 6 +
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 +
arch/arm/boot/dts/sama5d2.dtsi | 18 +-
arch/arm/configs/sama5_defconfig | 1 +
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
8 files changed, 590 insertions(+), 6 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
--
2.5.0
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 0/5] Introduce at91_adc8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Hi,
This is the very basic support for the adc introduced with the SAMA5D2 SoC
family.
The goal is to provide something to the user as soon as possible instead of
waiting for a full featured driver.
Only unsigned conversions on a software tigger are supported. Next steps are
signed conversions, differential channels, hardware triggers, dma support,
touchscreen support, and others.
Changes:
- v3:
- Driver file renaming.
- Syntax cleanup.
- Sampling frequency accessed through read/write_raw.
- Cleanup in the IRQ handler.
- v2:
- Cleanup thanks to Peter Meerwald-Stadler comments (add prefix for macros,
remove useless stuff, etc).
- Move parameters relative to the SoC to the device tree (min/max sampling
rate and startup time).
- Use sysfs to configure the sampling frequency.
- vddana supply is no more optionnal.
- No change for irq since I am not sure it will simplify the code (interrupt
is cleared when reading channel conversion result). Jonathan, tell me if
I need to rework this part.
Ludovic Desroches (5):
iio:adc:at91_adc8xx: introduce new atmel adc driver
MAINTAINERS: add entry for Atmel ADC 8xx driver
ARM: at91/dt: sama5d2: add adc device
ARM: at91/dt: sama5d2 Xplained: enable the adc device
ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
MAINTAINERS | 6 +
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 +
arch/arm/boot/dts/sama5d2.dtsi | 18 +-
arch/arm/configs/sama5_defconfig | 1 +
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
8 files changed, 590 insertions(+), 6 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
--
2.5.0
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 0/5] Introduce at91_adc8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
This is the very basic support for the adc introduced with the SAMA5D2 SoC
family.
The goal is to provide something to the user as soon as possible instead of
waiting for a full featured driver.
Only unsigned conversions on a software tigger are supported. Next steps are
signed conversions, differential channels, hardware triggers, dma support,
touchscreen support, and others.
Changes:
- v3:
- Driver file renaming.
- Syntax cleanup.
- Sampling frequency accessed through read/write_raw.
- Cleanup in the IRQ handler.
- v2:
- Cleanup thanks to Peter Meerwald-Stadler comments (add prefix for macros,
remove useless stuff, etc).
- Move parameters relative to the SoC to the device tree (min/max sampling
rate and startup time).
- Use sysfs to configure the sampling frequency.
- vddana supply is no more optionnal.
- No change for irq since I am not sure it will simplify the code (interrupt
is cleared when reading channel conversion result). Jonathan, tell me if
I need to rework this part.
Ludovic Desroches (5):
iio:adc:at91_adc8xx: introduce new atmel adc driver
MAINTAINERS: add entry for Atmel ADC 8xx driver
ARM: at91/dt: sama5d2: add adc device
ARM: at91/dt: sama5d2 Xplained: enable the adc device
ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
MAINTAINERS | 6 +
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 +
arch/arm/boot/dts/sama5d2.dtsi | 18 +-
arch/arm/configs/sama5_defconfig | 1 +
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
8 files changed, 590 insertions(+), 6 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
--
2.5.0
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
2016-01-14 15:38 ` Ludovic Desroches
(?)
@ 2016-01-14 15:38 ` Ludovic Desroches
-1 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
This driver supports the new version of the Atmel ADC device introduced
with the SAMA5D2 SoC family.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
4 files changed, 549 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
new file mode 100644
index 0000000..3223684
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
@@ -0,0 +1,28 @@
+* AT91 SAMA5D2 Analog to Digital Converter (ADC)
+
+Required properties:
+ - compatible: Should be "atmel,sama5d2-adc".
+ - reg: Should contain ADC registers location and length.
+ - interrupts: Should contain the IRQ line for the ADC.
+ - clocks: phandle to device clock.
+ - clock-names: Must be "adc_clk".
+ - vref-supply: Supply used as reference for conversions.
+ - vddana-supply: Supply for the adc device.
+ - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
+ - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
+ - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
+
+Example:
+
+adc: adc@fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 605ff42..f57b4ea 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -131,6 +131,17 @@ config AT91_ADC
To compile this driver as a module, choose M here: the module will be
called at91_adc.
+config AT91_SAMA5D2_ADC
+ tristate "Atmel AT91 SAMA5D2 ADC"
+ depends on ARCH_AT91
+ depends on INPUT
+ help
+ Say yes here to build support for Atmel SAMA5D2 ADC which is
+ available on SAMA5D2 SoC family.
+
+ To compile this driver as a module, choose M here: the module will be
+ called at91-sama5d2_adc.
+
config AXP288_ADC
tristate "X-Powers AXP288 ADC driver"
depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 6435780..fb57e12 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
new file mode 100644
index 0000000..ecb2d90
--- /dev/null
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -0,0 +1,509 @@
+/*
+ * Atmel ADC driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+/* Control Register */
+#define AT91_SAMA5D2_CR 0x00
+/* Software Reset */
+#define AT91_SAMA5D2_CR_SWRST BIT(0)
+/* Start Conversion */
+#define AT91_SAMA5D2_CR_START BIT(1)
+/* Touchscreen Calibration */
+#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
+/* Comparison Restart */
+#define AT91_SAMA5D2_CR_CMPRST BIT(4)
+
+/* Mode Register */
+#define AT91_SAMA5D2_MR 0x04
+/* Trigger Selection */
+#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
+/* ADTRG */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
+/* TIOA0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
+/* TIOA1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
+/* TIOA2 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
+/* PWM event line 0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
+/* PWM event line 1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
+/* TIOA3 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
+/* RTCOUT0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
+/* Sleep Mode */
+#define AT91_SAMA5D2_MR_SLEEP BIT(5)
+/* Fast Wake Up */
+#define AT91_SAMA5D2_MR_FWUP BIT(6)
+/* Prescaler Rate Selection */
+#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
+/* Startup Time */
+#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
+/* Analog Change */
+#define AT91_SAMA5D2_MR_ANACH BIT(23)
+/* Tracking Time */
+#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
+#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
+/* Transfer Time */
+#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
+#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
+/* Use Sequence Enable */
+#define AT91_SAMA5D2_MR_USEQ BIT(31)
+
+/* Channel Sequence Register 1 */
+#define AT91_SAMA5D2_SEQR1 0x08
+/* Channel Sequence Register 2 */
+#define AT91_SAMA5D2_SEQR2 0x0c
+/* Channel Enable Register */
+#define AT91_SAMA5D2_CHER 0x10
+/* Channel Disable Register */
+#define AT91_SAMA5D2_CHDR 0x14
+/* Channel Status Register */
+#define AT91_SAMA5D2_CHSR 0x18
+/* Last Converted Data Register */
+#define AT91_SAMA5D2_LCDR 0x20
+/* Interrupt Enable Register */
+#define AT91_SAMA5D2_IER 0x24
+/* Interrupt Disable Register */
+#define AT91_SAMA5D2_IDR 0x28
+/* Interrupt Mask Register */
+#define AT91_SAMA5D2_IMR 0x2c
+/* Interrupt Status Register */
+#define AT91_SAMA5D2_ISR 0x30
+/* Last Channel Trigger Mode Register */
+#define AT91_SAMA5D2_LCTMR 0x34
+/* Last Channel Compare Window Register */
+#define AT91_SAMA5D2_LCCWR 0x38
+/* Overrun Status Register */
+#define AT91_SAMA5D2_OVER 0x3c
+/* Extended Mode Register */
+#define AT91_SAMA5D2_EMR 0x40
+/* Compare Window Register */
+#define AT91_SAMA5D2_CWR 0x44
+/* Channel Gain Register */
+#define AT91_SAMA5D2_CGR 0x48
+/* Channel Offset Register */
+#define AT91_SAMA5D2_COR 0x4c
+/* Channel Data Register 0 */
+#define AT91_SAMA5D2_CDR0 0x50
+/* Analog Control Register */
+#define AT91_SAMA5D2_ACR 0x94
+/* Touchscreen Mode Register */
+#define AT91_SAMA5D2_TSMR 0xb0
+/* Touchscreen X Position Register */
+#define AT91_SAMA5D2_XPOSR 0xb4
+/* Touchscreen Y Position Register */
+#define AT91_SAMA5D2_YPOSR 0xb8
+/* Touchscreen Pressure Register */
+#define AT91_SAMA5D2_PRESSR 0xbc
+/* Trigger Register */
+#define AT91_SAMA5D2_TRGR 0xc0
+/* Correction Select Register */
+#define AT91_SAMA5D2_COSR 0xd0
+/* Correction Value Register */
+#define AT91_SAMA5D2_CVR 0xd4
+/* Channel Error Correction Register */
+#define AT91_SAMA5D2_CECR 0xd8
+/* Write Protection Mode Register */
+#define AT91_SAMA5D2_WPMR 0xe4
+/* Write Protection Status Register */
+#define AT91_SAMA5D2_WPSR 0xe8
+/* Version Register */
+#define AT91_SAMA5D2_VERSION 0xfc
+
+#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = addr, \
+ .scan_type = { \
+ .sign = 'u', \
+ .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, \
+ .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)
+
+struct at91_adc_soc_info {
+ unsigned startup_time;
+ unsigned min_sample_rate;
+ unsigned max_sample_rate;
+};
+
+struct at91_adc_state {
+ void __iomem *base;
+ int irq;
+ struct clk *per_clk;
+ struct regulator *reg;
+ struct regulator *vref;
+ u32 vref_uv;
+ const struct iio_chan_spec *chan;
+ bool conversion_done;
+ u32 conversion_value;
+ struct at91_adc_soc_info soc_info;
+ wait_queue_head_t wq_data_available;
+ /*
+ * lock to prevent concurrent 'single conversion' requests through
+ * sysfs.
+ */
+ struct mutex lock;
+};
+
+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),
+};
+
+static unsigned at91_adc_startup_time(unsigned startup_time_min,
+ unsigned adc_clk_khz)
+{
+ const unsigned startup_lookup[] = {
+ 0, 8, 16, 24,
+ 64, 80, 96, 112,
+ 512, 576, 640, 704,
+ 768, 832, 896, 960
+ };
+ unsigned ticks_min, i;
+
+ /*
+ * Since the adc frequency is checked before, there is no reason
+ * to not meet the startup time constraint.
+ */
+
+ ticks_min = startup_time_min * adc_clk_khz / 1000;
+ for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
+ if (startup_lookup[i] > ticks_min)
+ break;
+
+ return i;
+}
+
+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;
+
+ f_per = clk_get_rate(st->per_clk);
+ prescal = (f_per / (2 * freq)) - 1;
+
+ 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));
+
+ dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
+ freq, startup, prescal);
+}
+
+static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
+{
+ unsigned f_adc, f_per = clk_get_rate(st->per_clk);
+ unsigned mr, prescal;
+
+ mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
+ prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+ & AT91_SAMA5D2_MR_PRESCAL_MAX;
+ f_adc = f_per / (2 * (prescal + 1));
+
+ return f_adc;
+}
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio = private;
+ struct at91_adc_state *st = iio_priv(indio);
+ u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
+ u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
+
+ if (status & imr) {
+ st->conversion_value = at91_adc_readl(st, st->chan->address);
+ st->conversion_done = true;
+ wake_up_interruptible(&st->wq_data_available);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int at91_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ st->chan = chan;
+
+ 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);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_available,
+ st->conversion_done,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+
+ if (ret > 0) {
+ *val = st->conversion_value;
+ ret = IIO_VAL_INT;
+ st->conversion_done = false;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
+ at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
+
+ mutex_unlock(&st->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = at91_adc_get_sample_freq(st);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int at91_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ if (val < st->soc_info.min_sample_rate ||
+ val > st->soc_info.max_sample_rate)
+ return -EINVAL;
+
+ at91_adc_setup_samp_freq(st, val);
+
+ return 0;
+}
+
+static const struct iio_info at91_adc_info = {
+ .read_raw = &at91_adc_read_raw,
+ .write_raw = &at91_adc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct at91_adc_state *st;
+ struct resource *res;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct at91_adc_state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &at91_adc_info;
+ indio_dev->channels = at91_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
+
+ st = iio_priv(indio_dev);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,min-sample-rate-hz",
+ &st->soc_info.min_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,min-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,max-sample-rate-hz",
+ &st->soc_info.max_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,max-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
+ &st->soc_info.startup_time);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,startup-time-ms\n");
+ return ret;
+ }
+
+ init_waitqueue_head(&st->wq_data_available);
+ mutex_init(&st->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ st->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(st->base))
+ return PTR_ERR(st->base);
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq <= 0) {
+ if (!st->irq)
+ st->irq = -ENXIO;
+
+ return st->irq;
+ }
+
+ st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->per_clk))
+ return PTR_ERR(st->per_clk);
+
+ st->reg = devm_regulator_get(&pdev->dev, "vddana");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto reg_disable;
+
+ st->vref_uv = regulator_get_voltage(st->vref);
+ if (st->vref_uv <= 0) {
+ ret = -EINVAL;
+ goto vref_disable;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
+
+ at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+
+ ret = clk_prepare_enable(st->per_clk);
+ if (ret)
+ goto vref_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto per_clk_disable_unprepare;
+
+ dev_info(&pdev->dev, "version: %x\n",
+ readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
+
+ return 0;
+
+per_clk_disable_unprepare:
+ clk_disable_unprepare(st->per_clk);
+vref_disable:
+ regulator_disable(st->vref);
+reg_disable:
+ regulator_disable(st->reg);
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(st->per_clk);
+
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct of_device_id at91_adc_dt_match[] = {
+ {
+ .compatible = "atmel,sama5d2-adc",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = "at91-sama5d2_adc",
+ .of_match_table = at91_adc_dt_match,
+ },
+};
+module_platform_driver(at91_adc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
+MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
+MODULE_LICENSE("GPL v2");
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
This driver supports the new version of the Atmel ADC device introduced
with the SAMA5D2 SoC family.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
4 files changed, 549 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
new file mode 100644
index 0000000..3223684
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
@@ -0,0 +1,28 @@
+* AT91 SAMA5D2 Analog to Digital Converter (ADC)
+
+Required properties:
+ - compatible: Should be "atmel,sama5d2-adc".
+ - reg: Should contain ADC registers location and length.
+ - interrupts: Should contain the IRQ line for the ADC.
+ - clocks: phandle to device clock.
+ - clock-names: Must be "adc_clk".
+ - vref-supply: Supply used as reference for conversions.
+ - vddana-supply: Supply for the adc device.
+ - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
+ - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
+ - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
+
+Example:
+
+adc: adc@fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 605ff42..f57b4ea 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -131,6 +131,17 @@ config AT91_ADC
To compile this driver as a module, choose M here: the module will be
called at91_adc.
+config AT91_SAMA5D2_ADC
+ tristate "Atmel AT91 SAMA5D2 ADC"
+ depends on ARCH_AT91
+ depends on INPUT
+ help
+ Say yes here to build support for Atmel SAMA5D2 ADC which is
+ available on SAMA5D2 SoC family.
+
+ To compile this driver as a module, choose M here: the module will be
+ called at91-sama5d2_adc.
+
config AXP288_ADC
tristate "X-Powers AXP288 ADC driver"
depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 6435780..fb57e12 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
new file mode 100644
index 0000000..ecb2d90
--- /dev/null
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -0,0 +1,509 @@
+/*
+ * Atmel ADC driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+/* Control Register */
+#define AT91_SAMA5D2_CR 0x00
+/* Software Reset */
+#define AT91_SAMA5D2_CR_SWRST BIT(0)
+/* Start Conversion */
+#define AT91_SAMA5D2_CR_START BIT(1)
+/* Touchscreen Calibration */
+#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
+/* Comparison Restart */
+#define AT91_SAMA5D2_CR_CMPRST BIT(4)
+
+/* Mode Register */
+#define AT91_SAMA5D2_MR 0x04
+/* Trigger Selection */
+#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
+/* ADTRG */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
+/* TIOA0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
+/* TIOA1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
+/* TIOA2 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
+/* PWM event line 0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
+/* PWM event line 1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
+/* TIOA3 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
+/* RTCOUT0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
+/* Sleep Mode */
+#define AT91_SAMA5D2_MR_SLEEP BIT(5)
+/* Fast Wake Up */
+#define AT91_SAMA5D2_MR_FWUP BIT(6)
+/* Prescaler Rate Selection */
+#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
+/* Startup Time */
+#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
+/* Analog Change */
+#define AT91_SAMA5D2_MR_ANACH BIT(23)
+/* Tracking Time */
+#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
+#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
+/* Transfer Time */
+#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
+#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
+/* Use Sequence Enable */
+#define AT91_SAMA5D2_MR_USEQ BIT(31)
+
+/* Channel Sequence Register 1 */
+#define AT91_SAMA5D2_SEQR1 0x08
+/* Channel Sequence Register 2 */
+#define AT91_SAMA5D2_SEQR2 0x0c
+/* Channel Enable Register */
+#define AT91_SAMA5D2_CHER 0x10
+/* Channel Disable Register */
+#define AT91_SAMA5D2_CHDR 0x14
+/* Channel Status Register */
+#define AT91_SAMA5D2_CHSR 0x18
+/* Last Converted Data Register */
+#define AT91_SAMA5D2_LCDR 0x20
+/* Interrupt Enable Register */
+#define AT91_SAMA5D2_IER 0x24
+/* Interrupt Disable Register */
+#define AT91_SAMA5D2_IDR 0x28
+/* Interrupt Mask Register */
+#define AT91_SAMA5D2_IMR 0x2c
+/* Interrupt Status Register */
+#define AT91_SAMA5D2_ISR 0x30
+/* Last Channel Trigger Mode Register */
+#define AT91_SAMA5D2_LCTMR 0x34
+/* Last Channel Compare Window Register */
+#define AT91_SAMA5D2_LCCWR 0x38
+/* Overrun Status Register */
+#define AT91_SAMA5D2_OVER 0x3c
+/* Extended Mode Register */
+#define AT91_SAMA5D2_EMR 0x40
+/* Compare Window Register */
+#define AT91_SAMA5D2_CWR 0x44
+/* Channel Gain Register */
+#define AT91_SAMA5D2_CGR 0x48
+/* Channel Offset Register */
+#define AT91_SAMA5D2_COR 0x4c
+/* Channel Data Register 0 */
+#define AT91_SAMA5D2_CDR0 0x50
+/* Analog Control Register */
+#define AT91_SAMA5D2_ACR 0x94
+/* Touchscreen Mode Register */
+#define AT91_SAMA5D2_TSMR 0xb0
+/* Touchscreen X Position Register */
+#define AT91_SAMA5D2_XPOSR 0xb4
+/* Touchscreen Y Position Register */
+#define AT91_SAMA5D2_YPOSR 0xb8
+/* Touchscreen Pressure Register */
+#define AT91_SAMA5D2_PRESSR 0xbc
+/* Trigger Register */
+#define AT91_SAMA5D2_TRGR 0xc0
+/* Correction Select Register */
+#define AT91_SAMA5D2_COSR 0xd0
+/* Correction Value Register */
+#define AT91_SAMA5D2_CVR 0xd4
+/* Channel Error Correction Register */
+#define AT91_SAMA5D2_CECR 0xd8
+/* Write Protection Mode Register */
+#define AT91_SAMA5D2_WPMR 0xe4
+/* Write Protection Status Register */
+#define AT91_SAMA5D2_WPSR 0xe8
+/* Version Register */
+#define AT91_SAMA5D2_VERSION 0xfc
+
+#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = addr, \
+ .scan_type = { \
+ .sign = 'u', \
+ .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, \
+ .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)
+
+struct at91_adc_soc_info {
+ unsigned startup_time;
+ unsigned min_sample_rate;
+ unsigned max_sample_rate;
+};
+
+struct at91_adc_state {
+ void __iomem *base;
+ int irq;
+ struct clk *per_clk;
+ struct regulator *reg;
+ struct regulator *vref;
+ u32 vref_uv;
+ const struct iio_chan_spec *chan;
+ bool conversion_done;
+ u32 conversion_value;
+ struct at91_adc_soc_info soc_info;
+ wait_queue_head_t wq_data_available;
+ /*
+ * lock to prevent concurrent 'single conversion' requests through
+ * sysfs.
+ */
+ struct mutex lock;
+};
+
+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),
+};
+
+static unsigned at91_adc_startup_time(unsigned startup_time_min,
+ unsigned adc_clk_khz)
+{
+ const unsigned startup_lookup[] = {
+ 0, 8, 16, 24,
+ 64, 80, 96, 112,
+ 512, 576, 640, 704,
+ 768, 832, 896, 960
+ };
+ unsigned ticks_min, i;
+
+ /*
+ * Since the adc frequency is checked before, there is no reason
+ * to not meet the startup time constraint.
+ */
+
+ ticks_min = startup_time_min * adc_clk_khz / 1000;
+ for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
+ if (startup_lookup[i] > ticks_min)
+ break;
+
+ return i;
+}
+
+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;
+
+ f_per = clk_get_rate(st->per_clk);
+ prescal = (f_per / (2 * freq)) - 1;
+
+ 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));
+
+ dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
+ freq, startup, prescal);
+}
+
+static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
+{
+ unsigned f_adc, f_per = clk_get_rate(st->per_clk);
+ unsigned mr, prescal;
+
+ mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
+ prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+ & AT91_SAMA5D2_MR_PRESCAL_MAX;
+ f_adc = f_per / (2 * (prescal + 1));
+
+ return f_adc;
+}
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio = private;
+ struct at91_adc_state *st = iio_priv(indio);
+ u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
+ u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
+
+ if (status & imr) {
+ st->conversion_value = at91_adc_readl(st, st->chan->address);
+ st->conversion_done = true;
+ wake_up_interruptible(&st->wq_data_available);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int at91_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ st->chan = chan;
+
+ 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);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_available,
+ st->conversion_done,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+
+ if (ret > 0) {
+ *val = st->conversion_value;
+ ret = IIO_VAL_INT;
+ st->conversion_done = false;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
+ at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
+
+ mutex_unlock(&st->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = at91_adc_get_sample_freq(st);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int at91_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ if (val < st->soc_info.min_sample_rate ||
+ val > st->soc_info.max_sample_rate)
+ return -EINVAL;
+
+ at91_adc_setup_samp_freq(st, val);
+
+ return 0;
+}
+
+static const struct iio_info at91_adc_info = {
+ .read_raw = &at91_adc_read_raw,
+ .write_raw = &at91_adc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct at91_adc_state *st;
+ struct resource *res;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct at91_adc_state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &at91_adc_info;
+ indio_dev->channels = at91_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
+
+ st = iio_priv(indio_dev);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,min-sample-rate-hz",
+ &st->soc_info.min_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,min-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,max-sample-rate-hz",
+ &st->soc_info.max_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,max-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
+ &st->soc_info.startup_time);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,startup-time-ms\n");
+ return ret;
+ }
+
+ init_waitqueue_head(&st->wq_data_available);
+ mutex_init(&st->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ st->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(st->base))
+ return PTR_ERR(st->base);
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq <= 0) {
+ if (!st->irq)
+ st->irq = -ENXIO;
+
+ return st->irq;
+ }
+
+ st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->per_clk))
+ return PTR_ERR(st->per_clk);
+
+ st->reg = devm_regulator_get(&pdev->dev, "vddana");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto reg_disable;
+
+ st->vref_uv = regulator_get_voltage(st->vref);
+ if (st->vref_uv <= 0) {
+ ret = -EINVAL;
+ goto vref_disable;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
+
+ at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+
+ ret = clk_prepare_enable(st->per_clk);
+ if (ret)
+ goto vref_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto per_clk_disable_unprepare;
+
+ dev_info(&pdev->dev, "version: %x\n",
+ readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
+
+ return 0;
+
+per_clk_disable_unprepare:
+ clk_disable_unprepare(st->per_clk);
+vref_disable:
+ regulator_disable(st->vref);
+reg_disable:
+ regulator_disable(st->reg);
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(st->per_clk);
+
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct of_device_id at91_adc_dt_match[] = {
+ {
+ .compatible = "atmel,sama5d2-adc",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = "at91-sama5d2_adc",
+ .of_match_table = at91_adc_dt_match,
+ },
+};
+module_platform_driver(at91_adc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
+MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
+MODULE_LICENSE("GPL v2");
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
This driver supports the new version of the Atmel ADC device introduced
with the SAMA5D2 SoC family.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
4 files changed, 549 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
new file mode 100644
index 0000000..3223684
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
@@ -0,0 +1,28 @@
+* AT91 SAMA5D2 Analog to Digital Converter (ADC)
+
+Required properties:
+ - compatible: Should be "atmel,sama5d2-adc".
+ - reg: Should contain ADC registers location and length.
+ - interrupts: Should contain the IRQ line for the ADC.
+ - clocks: phandle to device clock.
+ - clock-names: Must be "adc_clk".
+ - vref-supply: Supply used as reference for conversions.
+ - vddana-supply: Supply for the adc device.
+ - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
+ - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
+ - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
+
+Example:
+
+adc: adc at fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 605ff42..f57b4ea 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -131,6 +131,17 @@ config AT91_ADC
To compile this driver as a module, choose M here: the module will be
called at91_adc.
+config AT91_SAMA5D2_ADC
+ tristate "Atmel AT91 SAMA5D2 ADC"
+ depends on ARCH_AT91
+ depends on INPUT
+ help
+ Say yes here to build support for Atmel SAMA5D2 ADC which is
+ available on SAMA5D2 SoC family.
+
+ To compile this driver as a module, choose M here: the module will be
+ called at91-sama5d2_adc.
+
config AXP288_ADC
tristate "X-Powers AXP288 ADC driver"
depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 6435780..fb57e12 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
new file mode 100644
index 0000000..ecb2d90
--- /dev/null
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -0,0 +1,509 @@
+/*
+ * Atmel ADC driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+/* Control Register */
+#define AT91_SAMA5D2_CR 0x00
+/* Software Reset */
+#define AT91_SAMA5D2_CR_SWRST BIT(0)
+/* Start Conversion */
+#define AT91_SAMA5D2_CR_START BIT(1)
+/* Touchscreen Calibration */
+#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
+/* Comparison Restart */
+#define AT91_SAMA5D2_CR_CMPRST BIT(4)
+
+/* Mode Register */
+#define AT91_SAMA5D2_MR 0x04
+/* Trigger Selection */
+#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
+/* ADTRG */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
+/* TIOA0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
+/* TIOA1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
+/* TIOA2 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
+/* PWM event line 0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
+/* PWM event line 1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
+/* TIOA3 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
+/* RTCOUT0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
+/* Sleep Mode */
+#define AT91_SAMA5D2_MR_SLEEP BIT(5)
+/* Fast Wake Up */
+#define AT91_SAMA5D2_MR_FWUP BIT(6)
+/* Prescaler Rate Selection */
+#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
+/* Startup Time */
+#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
+/* Analog Change */
+#define AT91_SAMA5D2_MR_ANACH BIT(23)
+/* Tracking Time */
+#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
+#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
+/* Transfer Time */
+#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
+#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
+/* Use Sequence Enable */
+#define AT91_SAMA5D2_MR_USEQ BIT(31)
+
+/* Channel Sequence Register 1 */
+#define AT91_SAMA5D2_SEQR1 0x08
+/* Channel Sequence Register 2 */
+#define AT91_SAMA5D2_SEQR2 0x0c
+/* Channel Enable Register */
+#define AT91_SAMA5D2_CHER 0x10
+/* Channel Disable Register */
+#define AT91_SAMA5D2_CHDR 0x14
+/* Channel Status Register */
+#define AT91_SAMA5D2_CHSR 0x18
+/* Last Converted Data Register */
+#define AT91_SAMA5D2_LCDR 0x20
+/* Interrupt Enable Register */
+#define AT91_SAMA5D2_IER 0x24
+/* Interrupt Disable Register */
+#define AT91_SAMA5D2_IDR 0x28
+/* Interrupt Mask Register */
+#define AT91_SAMA5D2_IMR 0x2c
+/* Interrupt Status Register */
+#define AT91_SAMA5D2_ISR 0x30
+/* Last Channel Trigger Mode Register */
+#define AT91_SAMA5D2_LCTMR 0x34
+/* Last Channel Compare Window Register */
+#define AT91_SAMA5D2_LCCWR 0x38
+/* Overrun Status Register */
+#define AT91_SAMA5D2_OVER 0x3c
+/* Extended Mode Register */
+#define AT91_SAMA5D2_EMR 0x40
+/* Compare Window Register */
+#define AT91_SAMA5D2_CWR 0x44
+/* Channel Gain Register */
+#define AT91_SAMA5D2_CGR 0x48
+/* Channel Offset Register */
+#define AT91_SAMA5D2_COR 0x4c
+/* Channel Data Register 0 */
+#define AT91_SAMA5D2_CDR0 0x50
+/* Analog Control Register */
+#define AT91_SAMA5D2_ACR 0x94
+/* Touchscreen Mode Register */
+#define AT91_SAMA5D2_TSMR 0xb0
+/* Touchscreen X Position Register */
+#define AT91_SAMA5D2_XPOSR 0xb4
+/* Touchscreen Y Position Register */
+#define AT91_SAMA5D2_YPOSR 0xb8
+/* Touchscreen Pressure Register */
+#define AT91_SAMA5D2_PRESSR 0xbc
+/* Trigger Register */
+#define AT91_SAMA5D2_TRGR 0xc0
+/* Correction Select Register */
+#define AT91_SAMA5D2_COSR 0xd0
+/* Correction Value Register */
+#define AT91_SAMA5D2_CVR 0xd4
+/* Channel Error Correction Register */
+#define AT91_SAMA5D2_CECR 0xd8
+/* Write Protection Mode Register */
+#define AT91_SAMA5D2_WPMR 0xe4
+/* Write Protection Status Register */
+#define AT91_SAMA5D2_WPSR 0xe8
+/* Version Register */
+#define AT91_SAMA5D2_VERSION 0xfc
+
+#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = addr, \
+ .scan_type = { \
+ .sign = 'u', \
+ .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, \
+ .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)
+
+struct at91_adc_soc_info {
+ unsigned startup_time;
+ unsigned min_sample_rate;
+ unsigned max_sample_rate;
+};
+
+struct at91_adc_state {
+ void __iomem *base;
+ int irq;
+ struct clk *per_clk;
+ struct regulator *reg;
+ struct regulator *vref;
+ u32 vref_uv;
+ const struct iio_chan_spec *chan;
+ bool conversion_done;
+ u32 conversion_value;
+ struct at91_adc_soc_info soc_info;
+ wait_queue_head_t wq_data_available;
+ /*
+ * lock to prevent concurrent 'single conversion' requests through
+ * sysfs.
+ */
+ struct mutex lock;
+};
+
+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),
+};
+
+static unsigned at91_adc_startup_time(unsigned startup_time_min,
+ unsigned adc_clk_khz)
+{
+ const unsigned startup_lookup[] = {
+ 0, 8, 16, 24,
+ 64, 80, 96, 112,
+ 512, 576, 640, 704,
+ 768, 832, 896, 960
+ };
+ unsigned ticks_min, i;
+
+ /*
+ * Since the adc frequency is checked before, there is no reason
+ * to not meet the startup time constraint.
+ */
+
+ ticks_min = startup_time_min * adc_clk_khz / 1000;
+ for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
+ if (startup_lookup[i] > ticks_min)
+ break;
+
+ return i;
+}
+
+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;
+
+ f_per = clk_get_rate(st->per_clk);
+ prescal = (f_per / (2 * freq)) - 1;
+
+ 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));
+
+ dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
+ freq, startup, prescal);
+}
+
+static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
+{
+ unsigned f_adc, f_per = clk_get_rate(st->per_clk);
+ unsigned mr, prescal;
+
+ mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
+ prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+ & AT91_SAMA5D2_MR_PRESCAL_MAX;
+ f_adc = f_per / (2 * (prescal + 1));
+
+ return f_adc;
+}
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio = private;
+ struct at91_adc_state *st = iio_priv(indio);
+ u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
+ u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
+
+ if (status & imr) {
+ st->conversion_value = at91_adc_readl(st, st->chan->address);
+ st->conversion_done = true;
+ wake_up_interruptible(&st->wq_data_available);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int at91_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ st->chan = chan;
+
+ 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);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_available,
+ st->conversion_done,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+
+ if (ret > 0) {
+ *val = st->conversion_value;
+ ret = IIO_VAL_INT;
+ st->conversion_done = false;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
+ at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
+
+ mutex_unlock(&st->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = at91_adc_get_sample_freq(st);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int at91_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ if (val < st->soc_info.min_sample_rate ||
+ val > st->soc_info.max_sample_rate)
+ return -EINVAL;
+
+ at91_adc_setup_samp_freq(st, val);
+
+ return 0;
+}
+
+static const struct iio_info at91_adc_info = {
+ .read_raw = &at91_adc_read_raw,
+ .write_raw = &at91_adc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct at91_adc_state *st;
+ struct resource *res;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct at91_adc_state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &at91_adc_info;
+ indio_dev->channels = at91_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
+
+ st = iio_priv(indio_dev);
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,min-sample-rate-hz",
+ &st->soc_info.min_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,min-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "atmel,max-sample-rate-hz",
+ &st->soc_info.max_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,max-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
+ &st->soc_info.startup_time);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,startup-time-ms\n");
+ return ret;
+ }
+
+ init_waitqueue_head(&st->wq_data_available);
+ mutex_init(&st->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ st->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(st->base))
+ return PTR_ERR(st->base);
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq <= 0) {
+ if (!st->irq)
+ st->irq = -ENXIO;
+
+ return st->irq;
+ }
+
+ st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->per_clk))
+ return PTR_ERR(st->per_clk);
+
+ st->reg = devm_regulator_get(&pdev->dev, "vddana");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto reg_disable;
+
+ st->vref_uv = regulator_get_voltage(st->vref);
+ if (st->vref_uv <= 0) {
+ ret = -EINVAL;
+ goto vref_disable;
+ }
+
+ at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
+ at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
+
+ at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
+
+ ret = clk_prepare_enable(st->per_clk);
+ if (ret)
+ goto vref_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto per_clk_disable_unprepare;
+
+ dev_info(&pdev->dev, "version: %x\n",
+ readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
+
+ return 0;
+
+per_clk_disable_unprepare:
+ clk_disable_unprepare(st->per_clk);
+vref_disable:
+ regulator_disable(st->vref);
+reg_disable:
+ regulator_disable(st->reg);
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(st->per_clk);
+
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct of_device_id at91_adc_dt_match[] = {
+ {
+ .compatible = "atmel,sama5d2-adc",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = "at91-sama5d2_adc",
+ .of_match_table = at91_adc_dt_match,
+ },
+};
+module_platform_driver(at91_adc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
+MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
+MODULE_LICENSE("GPL v2");
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 2/5] MAINTAINERS: add entry for Atmel ADC 8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..113900c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL ADC 8xx DRIVER
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+F: drivers/iio/adc/at91_adc8xx.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 2/5] MAINTAINERS: add entry for Atmel ADC 8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A,
nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A,
Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..113900c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL ADC 8xx DRIVER
+M: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
+L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Supported
+F: drivers/iio/adc/at91_adc8xx.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
L: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org (moderated for non-subscribers)
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 2/5] MAINTAINERS: add entry for Atmel ADC 8xx driver
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..113900c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL ADC 8xx DRIVER
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
+L: linux-iio at vger.kernel.org
+S: Supported
+F: drivers/iio/adc/at91_adc8xx.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel at alsa-project.org (moderated for non-subscribers)
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
2016-01-14 15:38 ` Ludovic Desroches
(?)
@ 2016-01-14 15:38 ` Ludovic Desroches
-1 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Add the ADC device, and remove the adc_op_clk which is useless since the
adc sampling frequency is configured with sysfs.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 3f750f6..46dd430 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -88,12 +88,6 @@
#clock-cells = <0>;
clock-frequency = <0>;
};
-
- adc_op_clk: adc_op_clk{
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <1000000>;
- };
};
ns_sram: sram@00200000 {
@@ -1085,6 +1079,18 @@
status = "disabled";
};
+ adc: adc@fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ status = "disabled";
+ };
+
pioA: pinctrl@fc038000 {
compatible = "atmel,sama5d2-pinctrl";
reg = <0xfc038000 0x600>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Add the ADC device, and remove the adc_op_clk which is useless since the
adc sampling frequency is configured with sysfs.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 3f750f6..46dd430 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -88,12 +88,6 @@
#clock-cells = <0>;
clock-frequency = <0>;
};
-
- adc_op_clk: adc_op_clk{
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <1000000>;
- };
};
ns_sram: sram@00200000 {
@@ -1085,6 +1079,18 @@
status = "disabled";
};
+ adc: adc@fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ status = "disabled";
+ };
+
pioA: pinctrl@fc038000 {
compatible = "atmel,sama5d2-pinctrl";
reg = <0xfc038000 0x600>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
Add the ADC device, and remove the adc_op_clk which is useless since the
adc sampling frequency is configured with sysfs.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 3f750f6..46dd430 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -88,12 +88,6 @@
#clock-cells = <0>;
clock-frequency = <0>;
};
-
- adc_op_clk: adc_op_clk{
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <1000000>;
- };
};
ns_sram: sram at 00200000 {
@@ -1085,6 +1079,18 @@
status = "disabled";
};
+ adc: adc at fc030000 {
+ compatible = "atmel,sama5d2-adc";
+ reg = <0xfc030000 0x100>;
+ interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&adc_clk>;
+ clock-names = "adc_clk";
+ atmel,min-sample-rate-hz = <200000>;
+ atmel,max-sample-rate-hz = <20000000>;
+ atmel,startup-time-ms = <4>;
+ status = "disabled";
+ };
+
pioA: pinctrl at fc038000 {
compatible = "atmel,sama5d2-pinctrl";
reg = <0xfc038000 0x600>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 4/5] ARM: at91/dt: sama5d2 Xplained: enable the adc device
2016-01-14 15:38 ` Ludovic Desroches
(?)
@ 2016-01-14 15:38 ` Ludovic Desroches
-1 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Enable the adc on the sama5d2 Xplained board.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 77ddff0..c3102a0 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -261,7 +261,29 @@
};
};
+ adc: adc@fc030000 {
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc_default>;
+ status = "okay";
+ };
+
pinctrl@fc038000 {
+ /*
+ * There is no real pinmux for ADC, if the pin
+ * is not requested by another peripheral then
+ * the muxing is done when channel is enabled.
+ * Requesting pins for ADC is GPIO is
+ * encouraged to prevent conflicts and to
+ * disable bias in order to be in the same
+ * state when the pin is not muxed to the adc.
+ */
+ pinctrl_adc_default: adc_default {
+ pinmux = <PIN_PD23__GPIO>;
+ bias-disable;
+ };
+
pinctrl_flx0_default: flx0_default {
pinmux = <PIN_PB28__FLEXCOM0_IO0>,
<PIN_PB29__FLEXCOM0_IO1>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 4/5] ARM: at91/dt: sama5d2 Xplained: enable the adc device
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Enable the adc on the sama5d2 Xplained board.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 77ddff0..c3102a0 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -261,7 +261,29 @@
};
};
+ adc: adc@fc030000 {
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc_default>;
+ status = "okay";
+ };
+
pinctrl@fc038000 {
+ /*
+ * There is no real pinmux for ADC, if the pin
+ * is not requested by another peripheral then
+ * the muxing is done when channel is enabled.
+ * Requesting pins for ADC is GPIO is
+ * encouraged to prevent conflicts and to
+ * disable bias in order to be in the same
+ * state when the pin is not muxed to the adc.
+ */
+ pinctrl_adc_default: adc_default {
+ pinmux = <PIN_PD23__GPIO>;
+ bias-disable;
+ };
+
pinctrl_flx0_default: flx0_default {
pinmux = <PIN_PB28__FLEXCOM0_IO0>,
<PIN_PB29__FLEXCOM0_IO1>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 4/5] ARM: at91/dt: sama5d2 Xplained: enable the adc device
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
Enable the adc on the sama5d2 Xplained board.
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 77ddff0..c3102a0 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -261,7 +261,29 @@
};
};
+ adc: adc at fc030000 {
+ vddana-supply = <&vdd_3v3_lp_reg>;
+ vref-supply = <&vdd_3v3_lp_reg>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_adc_default>;
+ status = "okay";
+ };
+
pinctrl at fc038000 {
+ /*
+ * There is no real pinmux for ADC, if the pin
+ * is not requested by another peripheral then
+ * the muxing is done when channel is enabled.
+ * Requesting pins for ADC is GPIO is
+ * encouraged to prevent conflicts and to
+ * disable bias in order to be in the same
+ * state when the pin is not muxed to the adc.
+ */
+ pinctrl_adc_default: adc_default {
+ pinmux = <PIN_PD23__GPIO>;
+ bias-disable;
+ };
+
pinctrl_flx0_default: flx0_default {
pinmux = <PIN_PB28__FLEXCOM0_IO0>,
<PIN_PB29__FLEXCOM0_IO1>;
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 5/5] ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
2016-01-14 15:38 ` Ludovic Desroches
(?)
@ 2016-01-14 15:38 ` Ludovic Desroches
-1 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/configs/sama5_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index c11bab7..afbda41 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -187,6 +187,7 @@ CONFIG_AT_XDMAC=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_IIO=y
CONFIG_AT91_ADC=y
+CONFIG_AT91_SAMA5D2_ADC=y
CONFIG_PWM=y
CONFIG_PWM_ATMEL=y
CONFIG_PWM_ATMEL_TCB=y
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 5/5] ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/configs/sama5_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index c11bab7..afbda41 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -187,6 +187,7 @@ CONFIG_AT_XDMAC=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_IIO=y
CONFIG_AT91_ADC=y
+CONFIG_AT91_SAMA5D2_ADC=y
CONFIG_PWM=y
CONFIG_PWM_ATMEL=y
CONFIG_PWM_ATMEL_TCB=y
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v3 5/5] ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig
@ 2016-01-14 15:38 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-14 15:38 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
arch/arm/configs/sama5_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index c11bab7..afbda41 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -187,6 +187,7 @@ CONFIG_AT_XDMAC=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_IIO=y
CONFIG_AT91_ADC=y
+CONFIG_AT91_SAMA5D2_ADC=y
CONFIG_PWM=y
CONFIG_PWM_ATMEL=y
CONFIG_PWM_ATMEL_TCB=y
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [RESEND PATCH v3 2/5] MAINTAINERS: add entry for Atmel SAMA5D2 ADC driver
@ 2016-01-15 8:54 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-15 8:54 UTC (permalink / raw)
To: jic23, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh, Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
I have forgotten to update this patch with the new name of the driver.
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..144422c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL SAMA5D2 ADC DRIVER
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+F: drivers/iio/adc/at91-sama5d2_adc.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [RESEND PATCH v3 2/5] MAINTAINERS: add entry for Atmel SAMA5D2 ADC driver
@ 2016-01-15 8:54 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-15 8:54 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A,
nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A,
Ludovic Desroches
Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
I have forgotten to update this patch with the new name of the driver.
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..144422c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL SAMA5D2 ADC DRIVER
+M: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
+L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Supported
+F: drivers/iio/adc/at91-sama5d2_adc.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
L: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org (moderated for non-subscribers)
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [RESEND PATCH v3 2/5] MAINTAINERS: add entry for Atmel SAMA5D2 ADC driver
@ 2016-01-15 8:54 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-15 8:54 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
---
I have forgotten to update this patch with the new name of the driver.
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index dac3be0..144422c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1951,6 +1951,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/tty/serial/atmel_serial.c
+ATMEL SAMA5D2 ADC DRIVER
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
+L: linux-iio at vger.kernel.org
+S: Supported
+F: drivers/iio/adc/at91-sama5d2_adc.c
+
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel at alsa-project.org (moderated for non-subscribers)
--
2.5.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-16 11:27 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:27 UTC (permalink / raw)
To: Ludovic Desroches, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh
On 14/01/16 15:38, Ludovic Desroches wrote:
> This driver supports the new version of the Atmel ADC device introduced
> with the SAMA5D2 SoC family.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> Acked-by: Rob Herring <robh@kernel.org>
Looks good to me. One totally trivial note inline (which I'm not even going
to bother changing whilst applying the patch - a good measure of how trivial ;)
Applied to the togreg branch of iio.git - initially pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> drivers/iio/adc/Kconfig | 11 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> 4 files changed, 549 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> new file mode 100644
> index 0000000..3223684
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> @@ -0,0 +1,28 @@
> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> +
> +Required properties:
> + - compatible: Should be "atmel,sama5d2-adc".
> + - reg: Should contain ADC registers location and length.
> + - interrupts: Should contain the IRQ line for the ADC.
> + - clocks: phandle to device clock.
> + - clock-names: Must be "adc_clk".
> + - vref-supply: Supply used as reference for conversions.
> + - vddana-supply: Supply for the adc device.
> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> +
> +Example:
> +
> +adc: adc@fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + vddana-supply = <&vdd_3v3_lp_reg>;
> + vref-supply = <&vdd_3v3_lp_reg>;
> +}
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 605ff42..f57b4ea 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -131,6 +131,17 @@ config AT91_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91_adc.
>
> +config AT91_SAMA5D2_ADC
> + tristate "Atmel AT91 SAMA5D2 ADC"
> + depends on ARCH_AT91
> + depends on INPUT
> + help
> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> + available on SAMA5D2 SoC family.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called at91-sama5d2_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 6435780..fb57e12 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> new file mode 100644
> index 0000000..ecb2d90
> --- /dev/null
> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> @@ -0,0 +1,509 @@
> +/*
> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> + *
> + * Copyright (C) 2015 Atmel,
> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* Control Register */
> +#define AT91_SAMA5D2_CR 0x00
> +/* Software Reset */
> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> +/* Start Conversion */
> +#define AT91_SAMA5D2_CR_START BIT(1)
> +/* Touchscreen Calibration */
> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> +/* Comparison Restart */
> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> +
> +/* Mode Register */
> +#define AT91_SAMA5D2_MR 0x04
> +/* Trigger Selection */
> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> +/* ADTRG */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> +/* TIOA0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> +/* TIOA1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> +/* TIOA2 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> +/* PWM event line 0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> +/* PWM event line 1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> +/* TIOA3 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> +/* RTCOUT0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> +/* Sleep Mode */
> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> +/* Fast Wake Up */
> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> +/* Prescaler Rate Selection */
> +#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
> +/* Startup Time */
> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> +/* Analog Change */
> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> +/* Tracking Time */
> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> +/* Transfer Time */
> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> +/* Use Sequence Enable */
> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> +
> +/* Channel Sequence Register 1 */
> +#define AT91_SAMA5D2_SEQR1 0x08
> +/* Channel Sequence Register 2 */
> +#define AT91_SAMA5D2_SEQR2 0x0c
> +/* Channel Enable Register */
> +#define AT91_SAMA5D2_CHER 0x10
> +/* Channel Disable Register */
> +#define AT91_SAMA5D2_CHDR 0x14
> +/* Channel Status Register */
> +#define AT91_SAMA5D2_CHSR 0x18
> +/* Last Converted Data Register */
> +#define AT91_SAMA5D2_LCDR 0x20
> +/* Interrupt Enable Register */
> +#define AT91_SAMA5D2_IER 0x24
> +/* Interrupt Disable Register */
> +#define AT91_SAMA5D2_IDR 0x28
> +/* Interrupt Mask Register */
> +#define AT91_SAMA5D2_IMR 0x2c
> +/* Interrupt Status Register */
> +#define AT91_SAMA5D2_ISR 0x30
> +/* Last Channel Trigger Mode Register */
> +#define AT91_SAMA5D2_LCTMR 0x34
> +/* Last Channel Compare Window Register */
> +#define AT91_SAMA5D2_LCCWR 0x38
> +/* Overrun Status Register */
> +#define AT91_SAMA5D2_OVER 0x3c
> +/* Extended Mode Register */
> +#define AT91_SAMA5D2_EMR 0x40
> +/* Compare Window Register */
> +#define AT91_SAMA5D2_CWR 0x44
> +/* Channel Gain Register */
> +#define AT91_SAMA5D2_CGR 0x48
> +/* Channel Offset Register */
> +#define AT91_SAMA5D2_COR 0x4c
> +/* Channel Data Register 0 */
> +#define AT91_SAMA5D2_CDR0 0x50
> +/* Analog Control Register */
> +#define AT91_SAMA5D2_ACR 0x94
> +/* Touchscreen Mode Register */
> +#define AT91_SAMA5D2_TSMR 0xb0
> +/* Touchscreen X Position Register */
> +#define AT91_SAMA5D2_XPOSR 0xb4
> +/* Touchscreen Y Position Register */
> +#define AT91_SAMA5D2_YPOSR 0xb8
> +/* Touchscreen Pressure Register */
> +#define AT91_SAMA5D2_PRESSR 0xbc
> +/* Trigger Register */
> +#define AT91_SAMA5D2_TRGR 0xc0
> +/* Correction Select Register */
> +#define AT91_SAMA5D2_COSR 0xd0
> +/* Correction Value Register */
> +#define AT91_SAMA5D2_CVR 0xd4
> +/* Channel Error Correction Register */
> +#define AT91_SAMA5D2_CECR 0xd8
> +/* Write Protection Mode Register */
> +#define AT91_SAMA5D2_WPMR 0xe4
> +/* Write Protection Status Register */
> +#define AT91_SAMA5D2_WPSR 0xe8
> +/* Version Register */
> +#define AT91_SAMA5D2_VERSION 0xfc
> +
> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .channel = num, \
> + .address = addr, \
> + .scan_type = { \
> + .sign = 'u', \
> + .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, \
> + .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)
> +
> +struct at91_adc_soc_info {
> + unsigned startup_time;
> + unsigned min_sample_rate;
> + unsigned max_sample_rate;
> +};
> +
> +struct at91_adc_state {
> + void __iomem *base;
> + int irq;
> + struct clk *per_clk;
> + struct regulator *reg;
> + struct regulator *vref;
> + u32 vref_uv;
> + const struct iio_chan_spec *chan;
> + bool conversion_done;
> + u32 conversion_value;
> + struct at91_adc_soc_info soc_info;
> + wait_queue_head_t wq_data_available;
> + /*
> + * lock to prevent concurrent 'single conversion' requests through
> + * sysfs.
> + */
> + struct mutex lock;
> +};
> +
> +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),
> +};
> +
> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> + unsigned adc_clk_khz)
> +{
> + const unsigned startup_lookup[] = {
> + 0, 8, 16, 24,
> + 64, 80, 96, 112,
> + 512, 576, 640, 704,
> + 768, 832, 896, 960
> + };
> + unsigned ticks_min, i;
> +
> + /*
> + * Since the adc frequency is checked before, there is no reason
> + * to not meet the startup time constraint.
> + */
> +
> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> + if (startup_lookup[i] > ticks_min)
> + break;
> +
> + return i;
> +}
> +
> +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;
> +
> + f_per = clk_get_rate(st->per_clk);
> + prescal = (f_per / (2 * freq)) - 1;
> +
> + 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));
> +
> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> + freq, startup, prescal);
> +}
> +
> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> +{
> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> + unsigned mr, prescal;
> +
> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> + f_adc = f_per / (2 * (prescal + 1));
> +
> + return f_adc;
> +}
> +
> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> +{
> + struct iio_dev *indio = private;
> + struct at91_adc_state *st = iio_priv(indio);
> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> +
> + if (status & imr) {
> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> + st->conversion_done = true;
> + wake_up_interruptible(&st->wq_data_available);
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}
> +
> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + mutex_lock(&st->lock);
> +
> + st->chan = chan;
> +
> + 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);
> +
> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> + st->conversion_done,
> + msecs_to_jiffies(1000));
> + if (ret == 0)
> + ret = -ETIMEDOUT;
> +
> + if (ret > 0) {
> + *val = st->conversion_value;
> + ret = IIO_VAL_INT;
> + st->conversion_done = false;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> +
> + mutex_unlock(&st->lock);
> + return ret;
> +
> + case IIO_CHAN_INFO_SCALE:
> + *val = st->vref_uv / 1000;
> + *val2 = chan->scan_type.realbits;
> + return IIO_VAL_FRACTIONAL_LOG2;
> +
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = at91_adc_get_sample_freq(st);
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> + return -EINVAL;
> +
> + if (val < st->soc_info.min_sample_rate ||
> + val > st->soc_info.max_sample_rate)
> + return -EINVAL;
> +
> + at91_adc_setup_samp_freq(st, val);
> +
> + return 0;
> +}
> +
> +static const struct iio_info at91_adc_info = {
> + .read_raw = &at91_adc_read_raw,
> + .write_raw = &at91_adc_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int at91_adc_probe(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev;
> + struct at91_adc_state *st;
> + struct resource *res;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> + sizeof(struct at91_adc_state));
Utter nitpick that isn't going to hold up the patch...
I'd have done this as sizeof(*st)) but really doesn't matter.
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &at91_adc_info;
> + indio_dev->channels = at91_adc_channels;
> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> +
> + st = iio_priv(indio_dev);
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,min-sample-rate-hz",
> + &st->soc_info.min_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,max-sample-rate-hz",
> + &st->soc_info.max_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> + &st->soc_info.startup_time);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,startup-time-ms\n");
> + return ret;
> + }
> +
> + init_waitqueue_head(&st->wq_data_available);
> + mutex_init(&st->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -EINVAL;
> +
> + st->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(st->base))
> + return PTR_ERR(st->base);
> +
> + st->irq = platform_get_irq(pdev, 0);
> + if (st->irq <= 0) {
> + if (!st->irq)
> + st->irq = -ENXIO;
> +
> + return st->irq;
> + }
> +
> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> + if (IS_ERR(st->per_clk))
> + return PTR_ERR(st->per_clk);
> +
> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> + if (IS_ERR(st->reg))
> + return PTR_ERR(st->reg);
> +
> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> + if (IS_ERR(st->vref))
> + return PTR_ERR(st->vref);
> +
> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> + pdev->dev.driver->name, indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->reg);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->vref);
> + if (ret)
> + goto reg_disable;
> +
> + st->vref_uv = regulator_get_voltage(st->vref);
> + if (st->vref_uv <= 0) {
> + ret = -EINVAL;
> + goto vref_disable;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> +
> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> +
> + ret = clk_prepare_enable(st->per_clk);
> + if (ret)
> + goto vref_disable;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret < 0)
> + goto per_clk_disable_unprepare;
> +
> + dev_info(&pdev->dev, "version: %x\n",
> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> +
> + return 0;
> +
> +per_clk_disable_unprepare:
> + clk_disable_unprepare(st->per_clk);
> +vref_disable:
> + regulator_disable(st->vref);
> +reg_disable:
> + regulator_disable(st->reg);
> + return ret;
> +}
> +
> +static int at91_adc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + clk_disable_unprepare(st->per_clk);
> +
> + regulator_disable(st->vref);
> + regulator_disable(st->reg);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id at91_adc_dt_match[] = {
> + {
> + .compatible = "atmel,sama5d2-adc",
> + }, {
> + /* sentinel */
> + }
> +};
> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> +
> +static struct platform_driver at91_adc_driver = {
> + .probe = at91_adc_probe,
> + .remove = at91_adc_remove,
> + .driver = {
> + .name = "at91-sama5d2_adc",
> + .of_match_table = at91_adc_dt_match,
> + },
> +};
> +module_platform_driver(at91_adc_driver)
> +
> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-16 11:27 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:27 UTC (permalink / raw)
To: Ludovic Desroches, nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A
On 14/01/16 15:38, Ludovic Desroches wrote:
> This driver supports the new version of the Atmel ADC device introduced
> with the SAMA5D2 SoC family.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Looks good to me. One totally trivial note inline (which I'm not even going
to bother changing whilst applying the patch - a good measure of how trivial ;)
Applied to the togreg branch of iio.git - initially pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> drivers/iio/adc/Kconfig | 11 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> 4 files changed, 549 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> new file mode 100644
> index 0000000..3223684
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> @@ -0,0 +1,28 @@
> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> +
> +Required properties:
> + - compatible: Should be "atmel,sama5d2-adc".
> + - reg: Should contain ADC registers location and length.
> + - interrupts: Should contain the IRQ line for the ADC.
> + - clocks: phandle to device clock.
> + - clock-names: Must be "adc_clk".
> + - vref-supply: Supply used as reference for conversions.
> + - vddana-supply: Supply for the adc device.
> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> +
> +Example:
> +
> +adc: adc@fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + vddana-supply = <&vdd_3v3_lp_reg>;
> + vref-supply = <&vdd_3v3_lp_reg>;
> +}
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 605ff42..f57b4ea 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -131,6 +131,17 @@ config AT91_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91_adc.
>
> +config AT91_SAMA5D2_ADC
> + tristate "Atmel AT91 SAMA5D2 ADC"
> + depends on ARCH_AT91
> + depends on INPUT
> + help
> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> + available on SAMA5D2 SoC family.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called at91-sama5d2_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 6435780..fb57e12 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> new file mode 100644
> index 0000000..ecb2d90
> --- /dev/null
> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> @@ -0,0 +1,509 @@
> +/*
> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> + *
> + * Copyright (C) 2015 Atmel,
> + * 2015 Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* Control Register */
> +#define AT91_SAMA5D2_CR 0x00
> +/* Software Reset */
> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> +/* Start Conversion */
> +#define AT91_SAMA5D2_CR_START BIT(1)
> +/* Touchscreen Calibration */
> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> +/* Comparison Restart */
> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> +
> +/* Mode Register */
> +#define AT91_SAMA5D2_MR 0x04
> +/* Trigger Selection */
> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> +/* ADTRG */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> +/* TIOA0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> +/* TIOA1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> +/* TIOA2 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> +/* PWM event line 0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> +/* PWM event line 1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> +/* TIOA3 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> +/* RTCOUT0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> +/* Sleep Mode */
> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> +/* Fast Wake Up */
> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> +/* Prescaler Rate Selection */
> +#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
> +/* Startup Time */
> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> +/* Analog Change */
> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> +/* Tracking Time */
> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> +/* Transfer Time */
> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> +/* Use Sequence Enable */
> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> +
> +/* Channel Sequence Register 1 */
> +#define AT91_SAMA5D2_SEQR1 0x08
> +/* Channel Sequence Register 2 */
> +#define AT91_SAMA5D2_SEQR2 0x0c
> +/* Channel Enable Register */
> +#define AT91_SAMA5D2_CHER 0x10
> +/* Channel Disable Register */
> +#define AT91_SAMA5D2_CHDR 0x14
> +/* Channel Status Register */
> +#define AT91_SAMA5D2_CHSR 0x18
> +/* Last Converted Data Register */
> +#define AT91_SAMA5D2_LCDR 0x20
> +/* Interrupt Enable Register */
> +#define AT91_SAMA5D2_IER 0x24
> +/* Interrupt Disable Register */
> +#define AT91_SAMA5D2_IDR 0x28
> +/* Interrupt Mask Register */
> +#define AT91_SAMA5D2_IMR 0x2c
> +/* Interrupt Status Register */
> +#define AT91_SAMA5D2_ISR 0x30
> +/* Last Channel Trigger Mode Register */
> +#define AT91_SAMA5D2_LCTMR 0x34
> +/* Last Channel Compare Window Register */
> +#define AT91_SAMA5D2_LCCWR 0x38
> +/* Overrun Status Register */
> +#define AT91_SAMA5D2_OVER 0x3c
> +/* Extended Mode Register */
> +#define AT91_SAMA5D2_EMR 0x40
> +/* Compare Window Register */
> +#define AT91_SAMA5D2_CWR 0x44
> +/* Channel Gain Register */
> +#define AT91_SAMA5D2_CGR 0x48
> +/* Channel Offset Register */
> +#define AT91_SAMA5D2_COR 0x4c
> +/* Channel Data Register 0 */
> +#define AT91_SAMA5D2_CDR0 0x50
> +/* Analog Control Register */
> +#define AT91_SAMA5D2_ACR 0x94
> +/* Touchscreen Mode Register */
> +#define AT91_SAMA5D2_TSMR 0xb0
> +/* Touchscreen X Position Register */
> +#define AT91_SAMA5D2_XPOSR 0xb4
> +/* Touchscreen Y Position Register */
> +#define AT91_SAMA5D2_YPOSR 0xb8
> +/* Touchscreen Pressure Register */
> +#define AT91_SAMA5D2_PRESSR 0xbc
> +/* Trigger Register */
> +#define AT91_SAMA5D2_TRGR 0xc0
> +/* Correction Select Register */
> +#define AT91_SAMA5D2_COSR 0xd0
> +/* Correction Value Register */
> +#define AT91_SAMA5D2_CVR 0xd4
> +/* Channel Error Correction Register */
> +#define AT91_SAMA5D2_CECR 0xd8
> +/* Write Protection Mode Register */
> +#define AT91_SAMA5D2_WPMR 0xe4
> +/* Write Protection Status Register */
> +#define AT91_SAMA5D2_WPSR 0xe8
> +/* Version Register */
> +#define AT91_SAMA5D2_VERSION 0xfc
> +
> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .channel = num, \
> + .address = addr, \
> + .scan_type = { \
> + .sign = 'u', \
> + .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, \
> + .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)
> +
> +struct at91_adc_soc_info {
> + unsigned startup_time;
> + unsigned min_sample_rate;
> + unsigned max_sample_rate;
> +};
> +
> +struct at91_adc_state {
> + void __iomem *base;
> + int irq;
> + struct clk *per_clk;
> + struct regulator *reg;
> + struct regulator *vref;
> + u32 vref_uv;
> + const struct iio_chan_spec *chan;
> + bool conversion_done;
> + u32 conversion_value;
> + struct at91_adc_soc_info soc_info;
> + wait_queue_head_t wq_data_available;
> + /*
> + * lock to prevent concurrent 'single conversion' requests through
> + * sysfs.
> + */
> + struct mutex lock;
> +};
> +
> +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),
> +};
> +
> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> + unsigned adc_clk_khz)
> +{
> + const unsigned startup_lookup[] = {
> + 0, 8, 16, 24,
> + 64, 80, 96, 112,
> + 512, 576, 640, 704,
> + 768, 832, 896, 960
> + };
> + unsigned ticks_min, i;
> +
> + /*
> + * Since the adc frequency is checked before, there is no reason
> + * to not meet the startup time constraint.
> + */
> +
> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> + if (startup_lookup[i] > ticks_min)
> + break;
> +
> + return i;
> +}
> +
> +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;
> +
> + f_per = clk_get_rate(st->per_clk);
> + prescal = (f_per / (2 * freq)) - 1;
> +
> + 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));
> +
> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> + freq, startup, prescal);
> +}
> +
> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> +{
> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> + unsigned mr, prescal;
> +
> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> + f_adc = f_per / (2 * (prescal + 1));
> +
> + return f_adc;
> +}
> +
> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> +{
> + struct iio_dev *indio = private;
> + struct at91_adc_state *st = iio_priv(indio);
> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> +
> + if (status & imr) {
> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> + st->conversion_done = true;
> + wake_up_interruptible(&st->wq_data_available);
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}
> +
> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + mutex_lock(&st->lock);
> +
> + st->chan = chan;
> +
> + 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);
> +
> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> + st->conversion_done,
> + msecs_to_jiffies(1000));
> + if (ret == 0)
> + ret = -ETIMEDOUT;
> +
> + if (ret > 0) {
> + *val = st->conversion_value;
> + ret = IIO_VAL_INT;
> + st->conversion_done = false;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> +
> + mutex_unlock(&st->lock);
> + return ret;
> +
> + case IIO_CHAN_INFO_SCALE:
> + *val = st->vref_uv / 1000;
> + *val2 = chan->scan_type.realbits;
> + return IIO_VAL_FRACTIONAL_LOG2;
> +
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = at91_adc_get_sample_freq(st);
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> + return -EINVAL;
> +
> + if (val < st->soc_info.min_sample_rate ||
> + val > st->soc_info.max_sample_rate)
> + return -EINVAL;
> +
> + at91_adc_setup_samp_freq(st, val);
> +
> + return 0;
> +}
> +
> +static const struct iio_info at91_adc_info = {
> + .read_raw = &at91_adc_read_raw,
> + .write_raw = &at91_adc_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int at91_adc_probe(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev;
> + struct at91_adc_state *st;
> + struct resource *res;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> + sizeof(struct at91_adc_state));
Utter nitpick that isn't going to hold up the patch...
I'd have done this as sizeof(*st)) but really doesn't matter.
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &at91_adc_info;
> + indio_dev->channels = at91_adc_channels;
> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> +
> + st = iio_priv(indio_dev);
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,min-sample-rate-hz",
> + &st->soc_info.min_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,max-sample-rate-hz",
> + &st->soc_info.max_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> + &st->soc_info.startup_time);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,startup-time-ms\n");
> + return ret;
> + }
> +
> + init_waitqueue_head(&st->wq_data_available);
> + mutex_init(&st->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -EINVAL;
> +
> + st->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(st->base))
> + return PTR_ERR(st->base);
> +
> + st->irq = platform_get_irq(pdev, 0);
> + if (st->irq <= 0) {
> + if (!st->irq)
> + st->irq = -ENXIO;
> +
> + return st->irq;
> + }
> +
> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> + if (IS_ERR(st->per_clk))
> + return PTR_ERR(st->per_clk);
> +
> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> + if (IS_ERR(st->reg))
> + return PTR_ERR(st->reg);
> +
> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> + if (IS_ERR(st->vref))
> + return PTR_ERR(st->vref);
> +
> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> + pdev->dev.driver->name, indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->reg);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->vref);
> + if (ret)
> + goto reg_disable;
> +
> + st->vref_uv = regulator_get_voltage(st->vref);
> + if (st->vref_uv <= 0) {
> + ret = -EINVAL;
> + goto vref_disable;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> +
> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> +
> + ret = clk_prepare_enable(st->per_clk);
> + if (ret)
> + goto vref_disable;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret < 0)
> + goto per_clk_disable_unprepare;
> +
> + dev_info(&pdev->dev, "version: %x\n",
> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> +
> + return 0;
> +
> +per_clk_disable_unprepare:
> + clk_disable_unprepare(st->per_clk);
> +vref_disable:
> + regulator_disable(st->vref);
> +reg_disable:
> + regulator_disable(st->reg);
> + return ret;
> +}
> +
> +static int at91_adc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + clk_disable_unprepare(st->per_clk);
> +
> + regulator_disable(st->vref);
> + regulator_disable(st->reg);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id at91_adc_dt_match[] = {
> + {
> + .compatible = "atmel,sama5d2-adc",
> + }, {
> + /* sentinel */
> + }
> +};
> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> +
> +static struct platform_driver at91_adc_driver = {
> + .probe = at91_adc_probe,
> + .remove = at91_adc_remove,
> + .driver = {
> + .name = "at91-sama5d2_adc",
> + .of_match_table = at91_adc_dt_match,
> + },
> +};
> +module_platform_driver(at91_adc_driver)
> +
> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>");
> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-16 11:27 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:27 UTC (permalink / raw)
To: linux-arm-kernel
On 14/01/16 15:38, Ludovic Desroches wrote:
> This driver supports the new version of the Atmel ADC device introduced
> with the SAMA5D2 SoC family.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> Acked-by: Rob Herring <robh@kernel.org>
Looks good to me. One totally trivial note inline (which I'm not even going
to bother changing whilst applying the patch - a good measure of how trivial ;)
Applied to the togreg branch of iio.git - initially pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> drivers/iio/adc/Kconfig | 11 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> 4 files changed, 549 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> new file mode 100644
> index 0000000..3223684
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> @@ -0,0 +1,28 @@
> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> +
> +Required properties:
> + - compatible: Should be "atmel,sama5d2-adc".
> + - reg: Should contain ADC registers location and length.
> + - interrupts: Should contain the IRQ line for the ADC.
> + - clocks: phandle to device clock.
> + - clock-names: Must be "adc_clk".
> + - vref-supply: Supply used as reference for conversions.
> + - vddana-supply: Supply for the adc device.
> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> +
> +Example:
> +
> +adc: adc at fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + vddana-supply = <&vdd_3v3_lp_reg>;
> + vref-supply = <&vdd_3v3_lp_reg>;
> +}
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 605ff42..f57b4ea 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -131,6 +131,17 @@ config AT91_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91_adc.
>
> +config AT91_SAMA5D2_ADC
> + tristate "Atmel AT91 SAMA5D2 ADC"
> + depends on ARCH_AT91
> + depends on INPUT
> + help
> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> + available on SAMA5D2 SoC family.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called at91-sama5d2_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 6435780..fb57e12 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> new file mode 100644
> index 0000000..ecb2d90
> --- /dev/null
> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> @@ -0,0 +1,509 @@
> +/*
> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> + *
> + * Copyright (C) 2015 Atmel,
> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* Control Register */
> +#define AT91_SAMA5D2_CR 0x00
> +/* Software Reset */
> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> +/* Start Conversion */
> +#define AT91_SAMA5D2_CR_START BIT(1)
> +/* Touchscreen Calibration */
> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> +/* Comparison Restart */
> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> +
> +/* Mode Register */
> +#define AT91_SAMA5D2_MR 0x04
> +/* Trigger Selection */
> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> +/* ADTRG */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> +/* TIOA0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> +/* TIOA1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> +/* TIOA2 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> +/* PWM event line 0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> +/* PWM event line 1 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> +/* TIOA3 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> +/* RTCOUT0 */
> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> +/* Sleep Mode */
> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> +/* Fast Wake Up */
> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> +/* Prescaler Rate Selection */
> +#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
> +/* Startup Time */
> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> +/* Analog Change */
> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> +/* Tracking Time */
> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> +/* Transfer Time */
> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> +/* Use Sequence Enable */
> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> +
> +/* Channel Sequence Register 1 */
> +#define AT91_SAMA5D2_SEQR1 0x08
> +/* Channel Sequence Register 2 */
> +#define AT91_SAMA5D2_SEQR2 0x0c
> +/* Channel Enable Register */
> +#define AT91_SAMA5D2_CHER 0x10
> +/* Channel Disable Register */
> +#define AT91_SAMA5D2_CHDR 0x14
> +/* Channel Status Register */
> +#define AT91_SAMA5D2_CHSR 0x18
> +/* Last Converted Data Register */
> +#define AT91_SAMA5D2_LCDR 0x20
> +/* Interrupt Enable Register */
> +#define AT91_SAMA5D2_IER 0x24
> +/* Interrupt Disable Register */
> +#define AT91_SAMA5D2_IDR 0x28
> +/* Interrupt Mask Register */
> +#define AT91_SAMA5D2_IMR 0x2c
> +/* Interrupt Status Register */
> +#define AT91_SAMA5D2_ISR 0x30
> +/* Last Channel Trigger Mode Register */
> +#define AT91_SAMA5D2_LCTMR 0x34
> +/* Last Channel Compare Window Register */
> +#define AT91_SAMA5D2_LCCWR 0x38
> +/* Overrun Status Register */
> +#define AT91_SAMA5D2_OVER 0x3c
> +/* Extended Mode Register */
> +#define AT91_SAMA5D2_EMR 0x40
> +/* Compare Window Register */
> +#define AT91_SAMA5D2_CWR 0x44
> +/* Channel Gain Register */
> +#define AT91_SAMA5D2_CGR 0x48
> +/* Channel Offset Register */
> +#define AT91_SAMA5D2_COR 0x4c
> +/* Channel Data Register 0 */
> +#define AT91_SAMA5D2_CDR0 0x50
> +/* Analog Control Register */
> +#define AT91_SAMA5D2_ACR 0x94
> +/* Touchscreen Mode Register */
> +#define AT91_SAMA5D2_TSMR 0xb0
> +/* Touchscreen X Position Register */
> +#define AT91_SAMA5D2_XPOSR 0xb4
> +/* Touchscreen Y Position Register */
> +#define AT91_SAMA5D2_YPOSR 0xb8
> +/* Touchscreen Pressure Register */
> +#define AT91_SAMA5D2_PRESSR 0xbc
> +/* Trigger Register */
> +#define AT91_SAMA5D2_TRGR 0xc0
> +/* Correction Select Register */
> +#define AT91_SAMA5D2_COSR 0xd0
> +/* Correction Value Register */
> +#define AT91_SAMA5D2_CVR 0xd4
> +/* Channel Error Correction Register */
> +#define AT91_SAMA5D2_CECR 0xd8
> +/* Write Protection Mode Register */
> +#define AT91_SAMA5D2_WPMR 0xe4
> +/* Write Protection Status Register */
> +#define AT91_SAMA5D2_WPSR 0xe8
> +/* Version Register */
> +#define AT91_SAMA5D2_VERSION 0xfc
> +
> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> + { \
> + .type = IIO_VOLTAGE, \
> + .channel = num, \
> + .address = addr, \
> + .scan_type = { \
> + .sign = 'u', \
> + .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, \
> + .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)
> +
> +struct at91_adc_soc_info {
> + unsigned startup_time;
> + unsigned min_sample_rate;
> + unsigned max_sample_rate;
> +};
> +
> +struct at91_adc_state {
> + void __iomem *base;
> + int irq;
> + struct clk *per_clk;
> + struct regulator *reg;
> + struct regulator *vref;
> + u32 vref_uv;
> + const struct iio_chan_spec *chan;
> + bool conversion_done;
> + u32 conversion_value;
> + struct at91_adc_soc_info soc_info;
> + wait_queue_head_t wq_data_available;
> + /*
> + * lock to prevent concurrent 'single conversion' requests through
> + * sysfs.
> + */
> + struct mutex lock;
> +};
> +
> +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),
> +};
> +
> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> + unsigned adc_clk_khz)
> +{
> + const unsigned startup_lookup[] = {
> + 0, 8, 16, 24,
> + 64, 80, 96, 112,
> + 512, 576, 640, 704,
> + 768, 832, 896, 960
> + };
> + unsigned ticks_min, i;
> +
> + /*
> + * Since the adc frequency is checked before, there is no reason
> + * to not meet the startup time constraint.
> + */
> +
> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> + if (startup_lookup[i] > ticks_min)
> + break;
> +
> + return i;
> +}
> +
> +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;
> +
> + f_per = clk_get_rate(st->per_clk);
> + prescal = (f_per / (2 * freq)) - 1;
> +
> + 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));
> +
> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> + freq, startup, prescal);
> +}
> +
> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> +{
> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> + unsigned mr, prescal;
> +
> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> + f_adc = f_per / (2 * (prescal + 1));
> +
> + return f_adc;
> +}
> +
> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> +{
> + struct iio_dev *indio = private;
> + struct at91_adc_state *st = iio_priv(indio);
> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> +
> + if (status & imr) {
> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> + st->conversion_done = true;
> + wake_up_interruptible(&st->wq_data_available);
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}
> +
> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + mutex_lock(&st->lock);
> +
> + st->chan = chan;
> +
> + 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);
> +
> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> + st->conversion_done,
> + msecs_to_jiffies(1000));
> + if (ret == 0)
> + ret = -ETIMEDOUT;
> +
> + if (ret > 0) {
> + *val = st->conversion_value;
> + ret = IIO_VAL_INT;
> + st->conversion_done = false;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> +
> + mutex_unlock(&st->lock);
> + return ret;
> +
> + case IIO_CHAN_INFO_SCALE:
> + *val = st->vref_uv / 1000;
> + *val2 = chan->scan_type.realbits;
> + return IIO_VAL_FRACTIONAL_LOG2;
> +
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *val = at91_adc_get_sample_freq(st);
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> + return -EINVAL;
> +
> + if (val < st->soc_info.min_sample_rate ||
> + val > st->soc_info.max_sample_rate)
> + return -EINVAL;
> +
> + at91_adc_setup_samp_freq(st, val);
> +
> + return 0;
> +}
> +
> +static const struct iio_info at91_adc_info = {
> + .read_raw = &at91_adc_read_raw,
> + .write_raw = &at91_adc_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int at91_adc_probe(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev;
> + struct at91_adc_state *st;
> + struct resource *res;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> + sizeof(struct at91_adc_state));
Utter nitpick that isn't going to hold up the patch...
I'd have done this as sizeof(*st)) but really doesn't matter.
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &at91_adc_info;
> + indio_dev->channels = at91_adc_channels;
> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> +
> + st = iio_priv(indio_dev);
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,min-sample-rate-hz",
> + &st->soc_info.min_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "atmel,max-sample-rate-hz",
> + &st->soc_info.max_sample_rate);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> + &st->soc_info.startup_time);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "invalid or missing value for atmel,startup-time-ms\n");
> + return ret;
> + }
> +
> + init_waitqueue_head(&st->wq_data_available);
> + mutex_init(&st->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -EINVAL;
> +
> + st->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(st->base))
> + return PTR_ERR(st->base);
> +
> + st->irq = platform_get_irq(pdev, 0);
> + if (st->irq <= 0) {
> + if (!st->irq)
> + st->irq = -ENXIO;
> +
> + return st->irq;
> + }
> +
> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> + if (IS_ERR(st->per_clk))
> + return PTR_ERR(st->per_clk);
> +
> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> + if (IS_ERR(st->reg))
> + return PTR_ERR(st->reg);
> +
> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> + if (IS_ERR(st->vref))
> + return PTR_ERR(st->vref);
> +
> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> + pdev->dev.driver->name, indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->reg);
> + if (ret)
> + return ret;
> +
> + ret = regulator_enable(st->vref);
> + if (ret)
> + goto reg_disable;
> +
> + st->vref_uv = regulator_get_voltage(st->vref);
> + if (st->vref_uv <= 0) {
> + ret = -EINVAL;
> + goto vref_disable;
> + }
> +
> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> +
> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> +
> + ret = clk_prepare_enable(st->per_clk);
> + if (ret)
> + goto vref_disable;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret < 0)
> + goto per_clk_disable_unprepare;
> +
> + dev_info(&pdev->dev, "version: %x\n",
> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> +
> + return 0;
> +
> +per_clk_disable_unprepare:
> + clk_disable_unprepare(st->per_clk);
> +vref_disable:
> + regulator_disable(st->vref);
> +reg_disable:
> + regulator_disable(st->reg);
> + return ret;
> +}
> +
> +static int at91_adc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct at91_adc_state *st = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + clk_disable_unprepare(st->per_clk);
> +
> + regulator_disable(st->vref);
> + regulator_disable(st->reg);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id at91_adc_dt_match[] = {
> + {
> + .compatible = "atmel,sama5d2-adc",
> + }, {
> + /* sentinel */
> + }
> +};
> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> +
> +static struct platform_driver at91_adc_driver = {
> + .probe = at91_adc_probe,
> + .remove = at91_adc_remove,
> + .driver = {
> + .name = "at91-sama5d2_adc",
> + .of_match_table = at91_adc_dt_match,
> + },
> +};
> +module_platform_driver(at91_adc_driver)
> +
> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
2016-01-16 11:27 ` Jonathan Cameron
@ 2016-01-16 11:35 ` Jonathan Cameron
-1 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:35 UTC (permalink / raw)
To: Ludovic Desroches, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh
On 16/01/16 11:27, Jonathan Cameron wrote:
> On 14/01/16 15:38, Ludovic Desroches wrote:
>> This driver supports the new version of the Atmel ADC device introduced
>> with the SAMA5D2 SoC family.
>>
>> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>> Acked-by: Rob Herring <robh@kernel.org>
> Looks good to me. One totally trivial note inline (which I'm not even going
> to bother changing whilst applying the patch - a good measure of how trivial ;)
>
> Applied to the togreg branch of iio.git - initially pushed out as testing
> for the autobuilders to play with it.
Having said that - just noticed an oddity - why the DEPENDS on INPUT?
I'll drop that as a probable cut and paste issue as right now it doesn't
depend on INPUT (might at some later date of course!) Whilst there - to
improve general build coverage I've added COMPILE_TEST as an alternative
to at91 (works fine on my x86_64 compiler for starters)
Doing the COMPILE_TEST option tends to highlight any 'interesting' dependencies
that happen to get pulled in by other at91 dependencies at the moment and
reduces the chance of an IIO / kernel wide change breaking the driver.
Jonathan
>
> Thanks,
>
> Jonathan
>> ---
>> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
>> drivers/iio/adc/Kconfig | 11 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
>> 4 files changed, 549 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> new file mode 100644
>> index 0000000..3223684
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> @@ -0,0 +1,28 @@
>> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
>> +
>> +Required properties:
>> + - compatible: Should be "atmel,sama5d2-adc".
>> + - reg: Should contain ADC registers location and length.
>> + - interrupts: Should contain the IRQ line for the ADC.
>> + - clocks: phandle to device clock.
>> + - clock-names: Must be "adc_clk".
>> + - vref-supply: Supply used as reference for conversions.
>> + - vddana-supply: Supply for the adc device.
>> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
>> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
>> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
>> +
>> +Example:
>> +
>> +adc: adc@fc030000 {
>> + compatible = "atmel,sama5d2-adc";
>> + reg = <0xfc030000 0x100>;
>> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
>> + clocks = <&adc_clk>;
>> + clock-names = "adc_clk";
>> + atmel,min-sample-rate-hz = <200000>;
>> + atmel,max-sample-rate-hz = <20000000>;
>> + atmel,startup-time-ms = <4>;
>> + vddana-supply = <&vdd_3v3_lp_reg>;
>> + vref-supply = <&vdd_3v3_lp_reg>;
>> +}
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 605ff42..f57b4ea 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -131,6 +131,17 @@ config AT91_ADC
>> To compile this driver as a module, choose M here: the module will be
>> called at91_adc.
>>
>> +config AT91_SAMA5D2_ADC
>> + tristate "Atmel AT91 SAMA5D2 ADC"
>> + depends on ARCH_AT91
>> + depends on INPUT
>> + help
>> + Say yes here to build support for Atmel SAMA5D2 ADC which is
>> + available on SAMA5D2 SoC family.
>> +
>> + To compile this driver as a module, choose M here: the module will be
>> + called at91-sama5d2_adc.
>> +
>> config AXP288_ADC
>> tristate "X-Powers AXP288 ADC driver"
>> depends on MFD_AXP20X
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 6435780..fb57e12 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
>> obj-$(CONFIG_AD7887) += ad7887.o
>> obj-$(CONFIG_AD799X) += ad799x.o
>> obj-$(CONFIG_AT91_ADC) += at91_adc.o
>> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
>> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
>> new file mode 100644
>> index 0000000..ecb2d90
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
>> @@ -0,0 +1,509 @@
>> +/*
>> + * Atmel ADC driver for SAMA5D2 devices and compatible.
>> + *
>> + * Copyright (C) 2015 Atmel,
>> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/sched.h>
>> +#include <linux/wait.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +/* Control Register */
>> +#define AT91_SAMA5D2_CR 0x00
>> +/* Software Reset */
>> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
>> +/* Start Conversion */
>> +#define AT91_SAMA5D2_CR_START BIT(1)
>> +/* Touchscreen Calibration */
>> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
>> +/* Comparison Restart */
>> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
>> +
>> +/* Mode Register */
>> +#define AT91_SAMA5D2_MR 0x04
>> +/* Trigger Selection */
>> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
>> +/* ADTRG */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
>> +/* TIOA0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
>> +/* TIOA1 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
>> +/* TIOA2 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
>> +/* PWM event line 0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
>> +/* PWM event line 1 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
>> +/* TIOA3 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
>> +/* RTCOUT0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
>> +/* Sleep Mode */
>> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
>> +/* Fast Wake Up */
>> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
>> +/* Prescaler Rate Selection */
>> +#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
>> +/* Startup Time */
>> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
>> +/* Analog Change */
>> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
>> +/* Tracking Time */
>> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
>> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
>> +/* Transfer Time */
>> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
>> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
>> +/* Use Sequence Enable */
>> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
>> +
>> +/* Channel Sequence Register 1 */
>> +#define AT91_SAMA5D2_SEQR1 0x08
>> +/* Channel Sequence Register 2 */
>> +#define AT91_SAMA5D2_SEQR2 0x0c
>> +/* Channel Enable Register */
>> +#define AT91_SAMA5D2_CHER 0x10
>> +/* Channel Disable Register */
>> +#define AT91_SAMA5D2_CHDR 0x14
>> +/* Channel Status Register */
>> +#define AT91_SAMA5D2_CHSR 0x18
>> +/* Last Converted Data Register */
>> +#define AT91_SAMA5D2_LCDR 0x20
>> +/* Interrupt Enable Register */
>> +#define AT91_SAMA5D2_IER 0x24
>> +/* Interrupt Disable Register */
>> +#define AT91_SAMA5D2_IDR 0x28
>> +/* Interrupt Mask Register */
>> +#define AT91_SAMA5D2_IMR 0x2c
>> +/* Interrupt Status Register */
>> +#define AT91_SAMA5D2_ISR 0x30
>> +/* Last Channel Trigger Mode Register */
>> +#define AT91_SAMA5D2_LCTMR 0x34
>> +/* Last Channel Compare Window Register */
>> +#define AT91_SAMA5D2_LCCWR 0x38
>> +/* Overrun Status Register */
>> +#define AT91_SAMA5D2_OVER 0x3c
>> +/* Extended Mode Register */
>> +#define AT91_SAMA5D2_EMR 0x40
>> +/* Compare Window Register */
>> +#define AT91_SAMA5D2_CWR 0x44
>> +/* Channel Gain Register */
>> +#define AT91_SAMA5D2_CGR 0x48
>> +/* Channel Offset Register */
>> +#define AT91_SAMA5D2_COR 0x4c
>> +/* Channel Data Register 0 */
>> +#define AT91_SAMA5D2_CDR0 0x50
>> +/* Analog Control Register */
>> +#define AT91_SAMA5D2_ACR 0x94
>> +/* Touchscreen Mode Register */
>> +#define AT91_SAMA5D2_TSMR 0xb0
>> +/* Touchscreen X Position Register */
>> +#define AT91_SAMA5D2_XPOSR 0xb4
>> +/* Touchscreen Y Position Register */
>> +#define AT91_SAMA5D2_YPOSR 0xb8
>> +/* Touchscreen Pressure Register */
>> +#define AT91_SAMA5D2_PRESSR 0xbc
>> +/* Trigger Register */
>> +#define AT91_SAMA5D2_TRGR 0xc0
>> +/* Correction Select Register */
>> +#define AT91_SAMA5D2_COSR 0xd0
>> +/* Correction Value Register */
>> +#define AT91_SAMA5D2_CVR 0xd4
>> +/* Channel Error Correction Register */
>> +#define AT91_SAMA5D2_CECR 0xd8
>> +/* Write Protection Mode Register */
>> +#define AT91_SAMA5D2_WPMR 0xe4
>> +/* Write Protection Status Register */
>> +#define AT91_SAMA5D2_WPSR 0xe8
>> +/* Version Register */
>> +#define AT91_SAMA5D2_VERSION 0xfc
>> +
>> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
>> + { \
>> + .type = IIO_VOLTAGE, \
>> + .channel = num, \
>> + .address = addr, \
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .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, \
>> + .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)
>> +
>> +struct at91_adc_soc_info {
>> + unsigned startup_time;
>> + unsigned min_sample_rate;
>> + unsigned max_sample_rate;
>> +};
>> +
>> +struct at91_adc_state {
>> + void __iomem *base;
>> + int irq;
>> + struct clk *per_clk;
>> + struct regulator *reg;
>> + struct regulator *vref;
>> + u32 vref_uv;
>> + const struct iio_chan_spec *chan;
>> + bool conversion_done;
>> + u32 conversion_value;
>> + struct at91_adc_soc_info soc_info;
>> + wait_queue_head_t wq_data_available;
>> + /*
>> + * lock to prevent concurrent 'single conversion' requests through
>> + * sysfs.
>> + */
>> + struct mutex lock;
>> +};
>> +
>> +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),
>> +};
>> +
>> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
>> + unsigned adc_clk_khz)
>> +{
>> + const unsigned startup_lookup[] = {
>> + 0, 8, 16, 24,
>> + 64, 80, 96, 112,
>> + 512, 576, 640, 704,
>> + 768, 832, 896, 960
>> + };
>> + unsigned ticks_min, i;
>> +
>> + /*
>> + * Since the adc frequency is checked before, there is no reason
>> + * to not meet the startup time constraint.
>> + */
>> +
>> + ticks_min = startup_time_min * adc_clk_khz / 1000;
>> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
>> + if (startup_lookup[i] > ticks_min)
>> + break;
>> +
>> + return i;
>> +}
>> +
>> +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;
>> +
>> + f_per = clk_get_rate(st->per_clk);
>> + prescal = (f_per / (2 * freq)) - 1;
>> +
>> + 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));
>> +
>> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
>> + freq, startup, prescal);
>> +}
>> +
>> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
>> +{
>> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
>> + unsigned mr, prescal;
>> +
>> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
>> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
>> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
>> + f_adc = f_per / (2 * (prescal + 1));
>> +
>> + return f_adc;
>> +}
>> +
>> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
>> +{
>> + struct iio_dev *indio = private;
>> + struct at91_adc_state *st = iio_priv(indio);
>> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
>> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
>> +
>> + if (status & imr) {
>> + st->conversion_value = at91_adc_readl(st, st->chan->address);
>> + st->conversion_done = true;
>> + wake_up_interruptible(&st->wq_data_available);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + return IRQ_NONE;
>> +}
>> +
>> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> + int ret;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + mutex_lock(&st->lock);
>> +
>> + st->chan = chan;
>> +
>> + 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);
>> +
>> + ret = wait_event_interruptible_timeout(st->wq_data_available,
>> + st->conversion_done,
>> + msecs_to_jiffies(1000));
>> + if (ret == 0)
>> + ret = -ETIMEDOUT;
>> +
>> + if (ret > 0) {
>> + *val = st->conversion_value;
>> + ret = IIO_VAL_INT;
>> + st->conversion_done = false;
>> + }
>> +
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
>> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
>> +
>> + mutex_unlock(&st->lock);
>> + return ret;
>> +
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = st->vref_uv / 1000;
>> + *val2 = chan->scan_type.realbits;
>> + return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + *val = at91_adc_get_sample_freq(st);
>> + return IIO_VAL_INT;
>> +
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int val, int val2, long mask)
>> +{
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
>> + return -EINVAL;
>> +
>> + if (val < st->soc_info.min_sample_rate ||
>> + val > st->soc_info.max_sample_rate)
>> + return -EINVAL;
>> +
>> + at91_adc_setup_samp_freq(st, val);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info at91_adc_info = {
>> + .read_raw = &at91_adc_read_raw,
>> + .write_raw = &at91_adc_write_raw,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static int at91_adc_probe(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct at91_adc_state *st;
>> + struct resource *res;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev,
>> + sizeof(struct at91_adc_state));
> Utter nitpick that isn't going to hold up the patch...
> I'd have done this as sizeof(*st)) but really doesn't matter.
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + indio_dev->info = &at91_adc_info;
>> + indio_dev->channels = at91_adc_channels;
>> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
>> +
>> + st = iio_priv(indio_dev);
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node,
>> + "atmel,min-sample-rate-hz",
>> + &st->soc_info.min_sample_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,min-sample-rate-hz\n");
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node,
>> + "atmel,max-sample-rate-hz",
>> + &st->soc_info.max_sample_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,max-sample-rate-hz\n");
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
>> + &st->soc_info.startup_time);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,startup-time-ms\n");
>> + return ret;
>> + }
>> +
>> + init_waitqueue_head(&st->wq_data_available);
>> + mutex_init(&st->lock);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res)
>> + return -EINVAL;
>> +
>> + st->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(st->base))
>> + return PTR_ERR(st->base);
>> +
>> + st->irq = platform_get_irq(pdev, 0);
>> + if (st->irq <= 0) {
>> + if (!st->irq)
>> + st->irq = -ENXIO;
>> +
>> + return st->irq;
>> + }
>> +
>> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
>> + if (IS_ERR(st->per_clk))
>> + return PTR_ERR(st->per_clk);
>> +
>> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
>> + if (IS_ERR(st->reg))
>> + return PTR_ERR(st->reg);
>> +
>> + st->vref = devm_regulator_get(&pdev->dev, "vref");
>> + if (IS_ERR(st->vref))
>> + return PTR_ERR(st->vref);
>> +
>> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
>> + pdev->dev.driver->name, indio_dev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regulator_enable(st->reg);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regulator_enable(st->vref);
>> + if (ret)
>> + goto reg_disable;
>> +
>> + st->vref_uv = regulator_get_voltage(st->vref);
>> + if (st->vref_uv <= 0) {
>> + ret = -EINVAL;
>> + goto vref_disable;
>> + }
>> +
>> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
>> +
>> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
>> +
>> + ret = clk_prepare_enable(st->per_clk);
>> + if (ret)
>> + goto vref_disable;
>> +
>> + ret = iio_device_register(indio_dev);
>> + if (ret < 0)
>> + goto per_clk_disable_unprepare;
>> +
>> + dev_info(&pdev->dev, "version: %x\n",
>> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
>> +
>> + return 0;
>> +
>> +per_clk_disable_unprepare:
>> + clk_disable_unprepare(st->per_clk);
>> +vref_disable:
>> + regulator_disable(st->vref);
>> +reg_disable:
>> + regulator_disable(st->reg);
>> + return ret;
>> +}
>> +
>> +static int at91_adc_remove(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + iio_device_unregister(indio_dev);
>> +
>> + clk_disable_unprepare(st->per_clk);
>> +
>> + regulator_disable(st->vref);
>> + regulator_disable(st->reg);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id at91_adc_dt_match[] = {
>> + {
>> + .compatible = "atmel,sama5d2-adc",
>> + }, {
>> + /* sentinel */
>> + }
>> +};
>> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
>> +
>> +static struct platform_driver at91_adc_driver = {
>> + .probe = at91_adc_probe,
>> + .remove = at91_adc_remove,
>> + .driver = {
>> + .name = "at91-sama5d2_adc",
>> + .of_match_table = at91_adc_dt_match,
>> + },
>> +};
>> +module_platform_driver(at91_adc_driver)
>> +
>> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
>> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
>> +MODULE_LICENSE("GPL v2");
>>
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-16 11:35 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:35 UTC (permalink / raw)
To: linux-arm-kernel
On 16/01/16 11:27, Jonathan Cameron wrote:
> On 14/01/16 15:38, Ludovic Desroches wrote:
>> This driver supports the new version of the Atmel ADC device introduced
>> with the SAMA5D2 SoC family.
>>
>> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>> Acked-by: Rob Herring <robh@kernel.org>
> Looks good to me. One totally trivial note inline (which I'm not even going
> to bother changing whilst applying the patch - a good measure of how trivial ;)
>
> Applied to the togreg branch of iio.git - initially pushed out as testing
> for the autobuilders to play with it.
Having said that - just noticed an oddity - why the DEPENDS on INPUT?
I'll drop that as a probable cut and paste issue as right now it doesn't
depend on INPUT (might at some later date of course!) Whilst there - to
improve general build coverage I've added COMPILE_TEST as an alternative
to at91 (works fine on my x86_64 compiler for starters)
Doing the COMPILE_TEST option tends to highlight any 'interesting' dependencies
that happen to get pulled in by other at91 dependencies at the moment and
reduces the chance of an IIO / kernel wide change breaking the driver.
Jonathan
>
> Thanks,
>
> Jonathan
>> ---
>> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
>> drivers/iio/adc/Kconfig | 11 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
>> 4 files changed, 549 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> new file mode 100644
>> index 0000000..3223684
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
>> @@ -0,0 +1,28 @@
>> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
>> +
>> +Required properties:
>> + - compatible: Should be "atmel,sama5d2-adc".
>> + - reg: Should contain ADC registers location and length.
>> + - interrupts: Should contain the IRQ line for the ADC.
>> + - clocks: phandle to device clock.
>> + - clock-names: Must be "adc_clk".
>> + - vref-supply: Supply used as reference for conversions.
>> + - vddana-supply: Supply for the adc device.
>> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
>> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
>> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
>> +
>> +Example:
>> +
>> +adc: adc at fc030000 {
>> + compatible = "atmel,sama5d2-adc";
>> + reg = <0xfc030000 0x100>;
>> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
>> + clocks = <&adc_clk>;
>> + clock-names = "adc_clk";
>> + atmel,min-sample-rate-hz = <200000>;
>> + atmel,max-sample-rate-hz = <20000000>;
>> + atmel,startup-time-ms = <4>;
>> + vddana-supply = <&vdd_3v3_lp_reg>;
>> + vref-supply = <&vdd_3v3_lp_reg>;
>> +}
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 605ff42..f57b4ea 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -131,6 +131,17 @@ config AT91_ADC
>> To compile this driver as a module, choose M here: the module will be
>> called at91_adc.
>>
>> +config AT91_SAMA5D2_ADC
>> + tristate "Atmel AT91 SAMA5D2 ADC"
>> + depends on ARCH_AT91
>> + depends on INPUT
>> + help
>> + Say yes here to build support for Atmel SAMA5D2 ADC which is
>> + available on SAMA5D2 SoC family.
>> +
>> + To compile this driver as a module, choose M here: the module will be
>> + called at91-sama5d2_adc.
>> +
>> config AXP288_ADC
>> tristate "X-Powers AXP288 ADC driver"
>> depends on MFD_AXP20X
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 6435780..fb57e12 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
>> obj-$(CONFIG_AD7887) += ad7887.o
>> obj-$(CONFIG_AD799X) += ad799x.o
>> obj-$(CONFIG_AT91_ADC) += at91_adc.o
>> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
>> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
>> new file mode 100644
>> index 0000000..ecb2d90
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
>> @@ -0,0 +1,509 @@
>> +/*
>> + * Atmel ADC driver for SAMA5D2 devices and compatible.
>> + *
>> + * Copyright (C) 2015 Atmel,
>> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/sched.h>
>> +#include <linux/wait.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +/* Control Register */
>> +#define AT91_SAMA5D2_CR 0x00
>> +/* Software Reset */
>> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
>> +/* Start Conversion */
>> +#define AT91_SAMA5D2_CR_START BIT(1)
>> +/* Touchscreen Calibration */
>> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
>> +/* Comparison Restart */
>> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
>> +
>> +/* Mode Register */
>> +#define AT91_SAMA5D2_MR 0x04
>> +/* Trigger Selection */
>> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
>> +/* ADTRG */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
>> +/* TIOA0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
>> +/* TIOA1 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
>> +/* TIOA2 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
>> +/* PWM event line 0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
>> +/* PWM event line 1 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
>> +/* TIOA3 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
>> +/* RTCOUT0 */
>> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
>> +/* Sleep Mode */
>> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
>> +/* Fast Wake Up */
>> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
>> +/* Prescaler Rate Selection */
>> +#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
>> +/* Startup Time */
>> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
>> +/* Analog Change */
>> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
>> +/* Tracking Time */
>> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
>> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
>> +/* Transfer Time */
>> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
>> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
>> +/* Use Sequence Enable */
>> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
>> +
>> +/* Channel Sequence Register 1 */
>> +#define AT91_SAMA5D2_SEQR1 0x08
>> +/* Channel Sequence Register 2 */
>> +#define AT91_SAMA5D2_SEQR2 0x0c
>> +/* Channel Enable Register */
>> +#define AT91_SAMA5D2_CHER 0x10
>> +/* Channel Disable Register */
>> +#define AT91_SAMA5D2_CHDR 0x14
>> +/* Channel Status Register */
>> +#define AT91_SAMA5D2_CHSR 0x18
>> +/* Last Converted Data Register */
>> +#define AT91_SAMA5D2_LCDR 0x20
>> +/* Interrupt Enable Register */
>> +#define AT91_SAMA5D2_IER 0x24
>> +/* Interrupt Disable Register */
>> +#define AT91_SAMA5D2_IDR 0x28
>> +/* Interrupt Mask Register */
>> +#define AT91_SAMA5D2_IMR 0x2c
>> +/* Interrupt Status Register */
>> +#define AT91_SAMA5D2_ISR 0x30
>> +/* Last Channel Trigger Mode Register */
>> +#define AT91_SAMA5D2_LCTMR 0x34
>> +/* Last Channel Compare Window Register */
>> +#define AT91_SAMA5D2_LCCWR 0x38
>> +/* Overrun Status Register */
>> +#define AT91_SAMA5D2_OVER 0x3c
>> +/* Extended Mode Register */
>> +#define AT91_SAMA5D2_EMR 0x40
>> +/* Compare Window Register */
>> +#define AT91_SAMA5D2_CWR 0x44
>> +/* Channel Gain Register */
>> +#define AT91_SAMA5D2_CGR 0x48
>> +/* Channel Offset Register */
>> +#define AT91_SAMA5D2_COR 0x4c
>> +/* Channel Data Register 0 */
>> +#define AT91_SAMA5D2_CDR0 0x50
>> +/* Analog Control Register */
>> +#define AT91_SAMA5D2_ACR 0x94
>> +/* Touchscreen Mode Register */
>> +#define AT91_SAMA5D2_TSMR 0xb0
>> +/* Touchscreen X Position Register */
>> +#define AT91_SAMA5D2_XPOSR 0xb4
>> +/* Touchscreen Y Position Register */
>> +#define AT91_SAMA5D2_YPOSR 0xb8
>> +/* Touchscreen Pressure Register */
>> +#define AT91_SAMA5D2_PRESSR 0xbc
>> +/* Trigger Register */
>> +#define AT91_SAMA5D2_TRGR 0xc0
>> +/* Correction Select Register */
>> +#define AT91_SAMA5D2_COSR 0xd0
>> +/* Correction Value Register */
>> +#define AT91_SAMA5D2_CVR 0xd4
>> +/* Channel Error Correction Register */
>> +#define AT91_SAMA5D2_CECR 0xd8
>> +/* Write Protection Mode Register */
>> +#define AT91_SAMA5D2_WPMR 0xe4
>> +/* Write Protection Status Register */
>> +#define AT91_SAMA5D2_WPSR 0xe8
>> +/* Version Register */
>> +#define AT91_SAMA5D2_VERSION 0xfc
>> +
>> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
>> + { \
>> + .type = IIO_VOLTAGE, \
>> + .channel = num, \
>> + .address = addr, \
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .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, \
>> + .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)
>> +
>> +struct at91_adc_soc_info {
>> + unsigned startup_time;
>> + unsigned min_sample_rate;
>> + unsigned max_sample_rate;
>> +};
>> +
>> +struct at91_adc_state {
>> + void __iomem *base;
>> + int irq;
>> + struct clk *per_clk;
>> + struct regulator *reg;
>> + struct regulator *vref;
>> + u32 vref_uv;
>> + const struct iio_chan_spec *chan;
>> + bool conversion_done;
>> + u32 conversion_value;
>> + struct at91_adc_soc_info soc_info;
>> + wait_queue_head_t wq_data_available;
>> + /*
>> + * lock to prevent concurrent 'single conversion' requests through
>> + * sysfs.
>> + */
>> + struct mutex lock;
>> +};
>> +
>> +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),
>> +};
>> +
>> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
>> + unsigned adc_clk_khz)
>> +{
>> + const unsigned startup_lookup[] = {
>> + 0, 8, 16, 24,
>> + 64, 80, 96, 112,
>> + 512, 576, 640, 704,
>> + 768, 832, 896, 960
>> + };
>> + unsigned ticks_min, i;
>> +
>> + /*
>> + * Since the adc frequency is checked before, there is no reason
>> + * to not meet the startup time constraint.
>> + */
>> +
>> + ticks_min = startup_time_min * adc_clk_khz / 1000;
>> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
>> + if (startup_lookup[i] > ticks_min)
>> + break;
>> +
>> + return i;
>> +}
>> +
>> +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;
>> +
>> + f_per = clk_get_rate(st->per_clk);
>> + prescal = (f_per / (2 * freq)) - 1;
>> +
>> + 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));
>> +
>> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
>> + freq, startup, prescal);
>> +}
>> +
>> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
>> +{
>> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
>> + unsigned mr, prescal;
>> +
>> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
>> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
>> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
>> + f_adc = f_per / (2 * (prescal + 1));
>> +
>> + return f_adc;
>> +}
>> +
>> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
>> +{
>> + struct iio_dev *indio = private;
>> + struct at91_adc_state *st = iio_priv(indio);
>> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
>> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
>> +
>> + if (status & imr) {
>> + st->conversion_value = at91_adc_readl(st, st->chan->address);
>> + st->conversion_done = true;
>> + wake_up_interruptible(&st->wq_data_available);
>> + return IRQ_HANDLED;
>> + }
>> +
>> + return IRQ_NONE;
>> +}
>> +
>> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> + int ret;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + mutex_lock(&st->lock);
>> +
>> + st->chan = chan;
>> +
>> + 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);
>> +
>> + ret = wait_event_interruptible_timeout(st->wq_data_available,
>> + st->conversion_done,
>> + msecs_to_jiffies(1000));
>> + if (ret == 0)
>> + ret = -ETIMEDOUT;
>> +
>> + if (ret > 0) {
>> + *val = st->conversion_value;
>> + ret = IIO_VAL_INT;
>> + st->conversion_done = false;
>> + }
>> +
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
>> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
>> +
>> + mutex_unlock(&st->lock);
>> + return ret;
>> +
>> + case IIO_CHAN_INFO_SCALE:
>> + *val = st->vref_uv / 1000;
>> + *val2 = chan->scan_type.realbits;
>> + return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + *val = at91_adc_get_sample_freq(st);
>> + return IIO_VAL_INT;
>> +
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int val, int val2, long mask)
>> +{
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
>> + return -EINVAL;
>> +
>> + if (val < st->soc_info.min_sample_rate ||
>> + val > st->soc_info.max_sample_rate)
>> + return -EINVAL;
>> +
>> + at91_adc_setup_samp_freq(st, val);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_info at91_adc_info = {
>> + .read_raw = &at91_adc_read_raw,
>> + .write_raw = &at91_adc_write_raw,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static int at91_adc_probe(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct at91_adc_state *st;
>> + struct resource *res;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(&pdev->dev,
>> + sizeof(struct at91_adc_state));
> Utter nitpick that isn't going to hold up the patch...
> I'd have done this as sizeof(*st)) but really doesn't matter.
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + indio_dev->dev.parent = &pdev->dev;
>> + indio_dev->name = dev_name(&pdev->dev);
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + indio_dev->info = &at91_adc_info;
>> + indio_dev->channels = at91_adc_channels;
>> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
>> +
>> + st = iio_priv(indio_dev);
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node,
>> + "atmel,min-sample-rate-hz",
>> + &st->soc_info.min_sample_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,min-sample-rate-hz\n");
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node,
>> + "atmel,max-sample-rate-hz",
>> + &st->soc_info.max_sample_rate);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,max-sample-rate-hz\n");
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
>> + &st->soc_info.startup_time);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "invalid or missing value for atmel,startup-time-ms\n");
>> + return ret;
>> + }
>> +
>> + init_waitqueue_head(&st->wq_data_available);
>> + mutex_init(&st->lock);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res)
>> + return -EINVAL;
>> +
>> + st->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(st->base))
>> + return PTR_ERR(st->base);
>> +
>> + st->irq = platform_get_irq(pdev, 0);
>> + if (st->irq <= 0) {
>> + if (!st->irq)
>> + st->irq = -ENXIO;
>> +
>> + return st->irq;
>> + }
>> +
>> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
>> + if (IS_ERR(st->per_clk))
>> + return PTR_ERR(st->per_clk);
>> +
>> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
>> + if (IS_ERR(st->reg))
>> + return PTR_ERR(st->reg);
>> +
>> + st->vref = devm_regulator_get(&pdev->dev, "vref");
>> + if (IS_ERR(st->vref))
>> + return PTR_ERR(st->vref);
>> +
>> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
>> + pdev->dev.driver->name, indio_dev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regulator_enable(st->reg);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regulator_enable(st->vref);
>> + if (ret)
>> + goto reg_disable;
>> +
>> + st->vref_uv = regulator_get_voltage(st->vref);
>> + if (st->vref_uv <= 0) {
>> + ret = -EINVAL;
>> + goto vref_disable;
>> + }
>> +
>> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
>> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
>> +
>> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
>> +
>> + ret = clk_prepare_enable(st->per_clk);
>> + if (ret)
>> + goto vref_disable;
>> +
>> + ret = iio_device_register(indio_dev);
>> + if (ret < 0)
>> + goto per_clk_disable_unprepare;
>> +
>> + dev_info(&pdev->dev, "version: %x\n",
>> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
>> +
>> + return 0;
>> +
>> +per_clk_disable_unprepare:
>> + clk_disable_unprepare(st->per_clk);
>> +vref_disable:
>> + regulator_disable(st->vref);
>> +reg_disable:
>> + regulator_disable(st->reg);
>> + return ret;
>> +}
>> +
>> +static int at91_adc_remove(struct platform_device *pdev)
>> +{
>> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> + struct at91_adc_state *st = iio_priv(indio_dev);
>> +
>> + iio_device_unregister(indio_dev);
>> +
>> + clk_disable_unprepare(st->per_clk);
>> +
>> + regulator_disable(st->vref);
>> + regulator_disable(st->reg);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id at91_adc_dt_match[] = {
>> + {
>> + .compatible = "atmel,sama5d2-adc",
>> + }, {
>> + /* sentinel */
>> + }
>> +};
>> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
>> +
>> +static struct platform_driver at91_adc_driver = {
>> + .probe = at91_adc_probe,
>> + .remove = at91_adc_remove,
>> + .driver = {
>> + .name = "at91-sama5d2_adc",
>> + .of_match_table = at91_adc_dt_match,
>> + },
>> +};
>> +module_platform_driver(at91_adc_driver)
>> +
>> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
>> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
>> +MODULE_LICENSE("GPL v2");
>>
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-16 11:38 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:38 UTC (permalink / raw)
To: Ludovic Desroches, nicolas.ferre, alexandre.belloni
Cc: devicetree, linux-kernel, linux-iio, plagnioj, linux-arm-kernel,
pmeerw, robh
On 14/01/16 15:38, Ludovic Desroches wrote:
> Add the ADC device, and remove the adc_op_clk which is useless since the
> adc sampling frequency is configured with sysfs.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
I've now applied the driver to the togreg branch of iio.git.
Should get a pull request out to Greg in about 2 weeks time and it'll
probably be in linux-next a week after that. I'm assuming all the device
tree changes will got through the at91 tree and arm-soc etc.
Shout if you want me to take them through IIO.
Thanks,
Jonathan
> ---
> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> index 3f750f6..46dd430 100644
> --- a/arch/arm/boot/dts/sama5d2.dtsi
> +++ b/arch/arm/boot/dts/sama5d2.dtsi
> @@ -88,12 +88,6 @@
> #clock-cells = <0>;
> clock-frequency = <0>;
> };
> -
> - adc_op_clk: adc_op_clk{
> - compatible = "fixed-clock";
> - #clock-cells = <0>;
> - clock-frequency = <1000000>;
> - };
> };
>
> ns_sram: sram@00200000 {
> @@ -1085,6 +1079,18 @@
> status = "disabled";
> };
>
> + adc: adc@fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + status = "disabled";
> + };
> +
> pioA: pinctrl@fc038000 {
> compatible = "atmel,sama5d2-pinctrl";
> reg = <0xfc038000 0x600>;
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-16 11:38 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:38 UTC (permalink / raw)
To: Ludovic Desroches, nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A
On 14/01/16 15:38, Ludovic Desroches wrote:
> Add the ADC device, and remove the adc_op_clk which is useless since the
> adc sampling frequency is configured with sysfs.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
I've now applied the driver to the togreg branch of iio.git.
Should get a pull request out to Greg in about 2 weeks time and it'll
probably be in linux-next a week after that. I'm assuming all the device
tree changes will got through the at91 tree and arm-soc etc.
Shout if you want me to take them through IIO.
Thanks,
Jonathan
> ---
> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> index 3f750f6..46dd430 100644
> --- a/arch/arm/boot/dts/sama5d2.dtsi
> +++ b/arch/arm/boot/dts/sama5d2.dtsi
> @@ -88,12 +88,6 @@
> #clock-cells = <0>;
> clock-frequency = <0>;
> };
> -
> - adc_op_clk: adc_op_clk{
> - compatible = "fixed-clock";
> - #clock-cells = <0>;
> - clock-frequency = <1000000>;
> - };
> };
>
> ns_sram: sram@00200000 {
> @@ -1085,6 +1079,18 @@
> status = "disabled";
> };
>
> + adc: adc@fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + status = "disabled";
> + };
> +
> pioA: pinctrl@fc038000 {
> compatible = "atmel,sama5d2-pinctrl";
> reg = <0xfc038000 0x600>;
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-16 11:38 ` Jonathan Cameron
0 siblings, 0 replies; 38+ messages in thread
From: Jonathan Cameron @ 2016-01-16 11:38 UTC (permalink / raw)
To: linux-arm-kernel
On 14/01/16 15:38, Ludovic Desroches wrote:
> Add the ADC device, and remove the adc_op_clk which is useless since the
> adc sampling frequency is configured with sysfs.
>
> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
I've now applied the driver to the togreg branch of iio.git.
Should get a pull request out to Greg in about 2 weeks time and it'll
probably be in linux-next a week after that. I'm assuming all the device
tree changes will got through the at91 tree and arm-soc etc.
Shout if you want me to take them through IIO.
Thanks,
Jonathan
> ---
> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> index 3f750f6..46dd430 100644
> --- a/arch/arm/boot/dts/sama5d2.dtsi
> +++ b/arch/arm/boot/dts/sama5d2.dtsi
> @@ -88,12 +88,6 @@
> #clock-cells = <0>;
> clock-frequency = <0>;
> };
> -
> - adc_op_clk: adc_op_clk{
> - compatible = "fixed-clock";
> - #clock-cells = <0>;
> - clock-frequency = <1000000>;
> - };
> };
>
> ns_sram: sram at 00200000 {
> @@ -1085,6 +1079,18 @@
> status = "disabled";
> };
>
> + adc: adc at fc030000 {
> + compatible = "atmel,sama5d2-adc";
> + reg = <0xfc030000 0x100>;
> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> + clocks = <&adc_clk>;
> + clock-names = "adc_clk";
> + atmel,min-sample-rate-hz = <200000>;
> + atmel,max-sample-rate-hz = <20000000>;
> + atmel,startup-time-ms = <4>;
> + status = "disabled";
> + };
> +
> pioA: pinctrl at fc038000 {
> compatible = "atmel,sama5d2-pinctrl";
> reg = <0xfc038000 0x600>;
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-18 7:55 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:55 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Ludovic Desroches, nicolas.ferre, alexandre.belloni, devicetree,
linux-kernel, linux-iio, plagnioj, linux-arm-kernel, pmeerw,
robh
On Sat, Jan 16, 2016 at 11:35:24AM +0000, Jonathan Cameron wrote:
> On 16/01/16 11:27, Jonathan Cameron wrote:
> > On 14/01/16 15:38, Ludovic Desroches wrote:
> >> This driver supports the new version of the Atmel ADC device introduced
> >> with the SAMA5D2 SoC family.
> >>
> >> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >> Acked-by: Rob Herring <robh@kernel.org>
> > Looks good to me. One totally trivial note inline (which I'm not even going
> > to bother changing whilst applying the patch - a good measure of how trivial ;)
> >
> > Applied to the togreg branch of iio.git - initially pushed out as testing
> > for the autobuilders to play with it.
> Having said that - just noticed an oddity - why the DEPENDS on INPUT?
> I'll drop that as a probable cut and paste issue as right now it doesn't
> depend on INPUT (might at some later date of course!) Whilst there - to
> improve general build coverage I've added COMPILE_TEST as an alternative
> to at91 (works fine on my x86_64 compiler for starters)
>
Yes copy/paste issue. Thanks for the addition of COMPILE_TEST.
Regards
Ludovic
> Doing the COMPILE_TEST option tends to highlight any 'interesting' dependencies
> that happen to get pulled in by other at91 dependencies at the moment and
> reduces the chance of an IIO / kernel wide change breaking the driver.
>
> Jonathan
> >
> > Thanks,
> >
> > Jonathan
> >> ---
> >> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> >> drivers/iio/adc/Kconfig | 11 +
> >> drivers/iio/adc/Makefile | 1 +
> >> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> >> 4 files changed, 549 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> new file mode 100644
> >> index 0000000..3223684
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> @@ -0,0 +1,28 @@
> >> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> >> +
> >> +Required properties:
> >> + - compatible: Should be "atmel,sama5d2-adc".
> >> + - reg: Should contain ADC registers location and length.
> >> + - interrupts: Should contain the IRQ line for the ADC.
> >> + - clocks: phandle to device clock.
> >> + - clock-names: Must be "adc_clk".
> >> + - vref-supply: Supply used as reference for conversions.
> >> + - vddana-supply: Supply for the adc device.
> >> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> >> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> >> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> >> +
> >> +Example:
> >> +
> >> +adc: adc@fc030000 {
> >> + compatible = "atmel,sama5d2-adc";
> >> + reg = <0xfc030000 0x100>;
> >> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> >> + clocks = <&adc_clk>;
> >> + clock-names = "adc_clk";
> >> + atmel,min-sample-rate-hz = <200000>;
> >> + atmel,max-sample-rate-hz = <20000000>;
> >> + atmel,startup-time-ms = <4>;
> >> + vddana-supply = <&vdd_3v3_lp_reg>;
> >> + vref-supply = <&vdd_3v3_lp_reg>;
> >> +}
> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> index 605ff42..f57b4ea 100644
> >> --- a/drivers/iio/adc/Kconfig
> >> +++ b/drivers/iio/adc/Kconfig
> >> @@ -131,6 +131,17 @@ config AT91_ADC
> >> To compile this driver as a module, choose M here: the module will be
> >> called at91_adc.
> >>
> >> +config AT91_SAMA5D2_ADC
> >> + tristate "Atmel AT91 SAMA5D2 ADC"
> >> + depends on ARCH_AT91
> >> + depends on INPUT
> >> + help
> >> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> >> + available on SAMA5D2 SoC family.
> >> +
> >> + To compile this driver as a module, choose M here: the module will be
> >> + called at91-sama5d2_adc.
> >> +
> >> config AXP288_ADC
> >> tristate "X-Powers AXP288 ADC driver"
> >> depends on MFD_AXP20X
> >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >> index 6435780..fb57e12 100644
> >> --- a/drivers/iio/adc/Makefile
> >> +++ b/drivers/iio/adc/Makefile
> >> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> >> obj-$(CONFIG_AD7887) += ad7887.o
> >> obj-$(CONFIG_AD799X) += ad799x.o
> >> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> >> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> >> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> >> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> >> new file mode 100644
> >> index 0000000..ecb2d90
> >> --- /dev/null
> >> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> >> @@ -0,0 +1,509 @@
> >> +/*
> >> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> >> + *
> >> + * Copyright (C) 2015 Atmel,
> >> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
> >> + *
> >> + * This software is licensed under the terms of the GNU General Public
> >> + * License version 2, as published by the Free Software Foundation, and
> >> + * may be copied, distributed, and modified under those terms.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#include <linux/bitops.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/sched.h>
> >> +#include <linux/wait.h>
> >> +#include <linux/iio/iio.h>
> >> +#include <linux/iio/sysfs.h>
> >> +#include <linux/regulator/consumer.h>
> >> +
> >> +/* Control Register */
> >> +#define AT91_SAMA5D2_CR 0x00
> >> +/* Software Reset */
> >> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> >> +/* Start Conversion */
> >> +#define AT91_SAMA5D2_CR_START BIT(1)
> >> +/* Touchscreen Calibration */
> >> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> >> +/* Comparison Restart */
> >> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> >> +
> >> +/* Mode Register */
> >> +#define AT91_SAMA5D2_MR 0x04
> >> +/* Trigger Selection */
> >> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> >> +/* ADTRG */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> >> +/* TIOA0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> >> +/* TIOA1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> >> +/* TIOA2 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> >> +/* PWM event line 0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> >> +/* PWM event line 1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> >> +/* TIOA3 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> >> +/* RTCOUT0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> >> +/* Sleep Mode */
> >> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> >> +/* Fast Wake Up */
> >> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> >> +/* Prescaler Rate Selection */
> >> +#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
> >> +/* Startup Time */
> >> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> >> +/* Analog Change */
> >> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> >> +/* Tracking Time */
> >> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> >> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> >> +/* Transfer Time */
> >> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> >> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> >> +/* Use Sequence Enable */
> >> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> >> +
> >> +/* Channel Sequence Register 1 */
> >> +#define AT91_SAMA5D2_SEQR1 0x08
> >> +/* Channel Sequence Register 2 */
> >> +#define AT91_SAMA5D2_SEQR2 0x0c
> >> +/* Channel Enable Register */
> >> +#define AT91_SAMA5D2_CHER 0x10
> >> +/* Channel Disable Register */
> >> +#define AT91_SAMA5D2_CHDR 0x14
> >> +/* Channel Status Register */
> >> +#define AT91_SAMA5D2_CHSR 0x18
> >> +/* Last Converted Data Register */
> >> +#define AT91_SAMA5D2_LCDR 0x20
> >> +/* Interrupt Enable Register */
> >> +#define AT91_SAMA5D2_IER 0x24
> >> +/* Interrupt Disable Register */
> >> +#define AT91_SAMA5D2_IDR 0x28
> >> +/* Interrupt Mask Register */
> >> +#define AT91_SAMA5D2_IMR 0x2c
> >> +/* Interrupt Status Register */
> >> +#define AT91_SAMA5D2_ISR 0x30
> >> +/* Last Channel Trigger Mode Register */
> >> +#define AT91_SAMA5D2_LCTMR 0x34
> >> +/* Last Channel Compare Window Register */
> >> +#define AT91_SAMA5D2_LCCWR 0x38
> >> +/* Overrun Status Register */
> >> +#define AT91_SAMA5D2_OVER 0x3c
> >> +/* Extended Mode Register */
> >> +#define AT91_SAMA5D2_EMR 0x40
> >> +/* Compare Window Register */
> >> +#define AT91_SAMA5D2_CWR 0x44
> >> +/* Channel Gain Register */
> >> +#define AT91_SAMA5D2_CGR 0x48
> >> +/* Channel Offset Register */
> >> +#define AT91_SAMA5D2_COR 0x4c
> >> +/* Channel Data Register 0 */
> >> +#define AT91_SAMA5D2_CDR0 0x50
> >> +/* Analog Control Register */
> >> +#define AT91_SAMA5D2_ACR 0x94
> >> +/* Touchscreen Mode Register */
> >> +#define AT91_SAMA5D2_TSMR 0xb0
> >> +/* Touchscreen X Position Register */
> >> +#define AT91_SAMA5D2_XPOSR 0xb4
> >> +/* Touchscreen Y Position Register */
> >> +#define AT91_SAMA5D2_YPOSR 0xb8
> >> +/* Touchscreen Pressure Register */
> >> +#define AT91_SAMA5D2_PRESSR 0xbc
> >> +/* Trigger Register */
> >> +#define AT91_SAMA5D2_TRGR 0xc0
> >> +/* Correction Select Register */
> >> +#define AT91_SAMA5D2_COSR 0xd0
> >> +/* Correction Value Register */
> >> +#define AT91_SAMA5D2_CVR 0xd4
> >> +/* Channel Error Correction Register */
> >> +#define AT91_SAMA5D2_CECR 0xd8
> >> +/* Write Protection Mode Register */
> >> +#define AT91_SAMA5D2_WPMR 0xe4
> >> +/* Write Protection Status Register */
> >> +#define AT91_SAMA5D2_WPSR 0xe8
> >> +/* Version Register */
> >> +#define AT91_SAMA5D2_VERSION 0xfc
> >> +
> >> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> >> + { \
> >> + .type = IIO_VOLTAGE, \
> >> + .channel = num, \
> >> + .address = addr, \
> >> + .scan_type = { \
> >> + .sign = 'u', \
> >> + .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, \
> >> + .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)
> >> +
> >> +struct at91_adc_soc_info {
> >> + unsigned startup_time;
> >> + unsigned min_sample_rate;
> >> + unsigned max_sample_rate;
> >> +};
> >> +
> >> +struct at91_adc_state {
> >> + void __iomem *base;
> >> + int irq;
> >> + struct clk *per_clk;
> >> + struct regulator *reg;
> >> + struct regulator *vref;
> >> + u32 vref_uv;
> >> + const struct iio_chan_spec *chan;
> >> + bool conversion_done;
> >> + u32 conversion_value;
> >> + struct at91_adc_soc_info soc_info;
> >> + wait_queue_head_t wq_data_available;
> >> + /*
> >> + * lock to prevent concurrent 'single conversion' requests through
> >> + * sysfs.
> >> + */
> >> + struct mutex lock;
> >> +};
> >> +
> >> +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),
> >> +};
> >> +
> >> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> >> + unsigned adc_clk_khz)
> >> +{
> >> + const unsigned startup_lookup[] = {
> >> + 0, 8, 16, 24,
> >> + 64, 80, 96, 112,
> >> + 512, 576, 640, 704,
> >> + 768, 832, 896, 960
> >> + };
> >> + unsigned ticks_min, i;
> >> +
> >> + /*
> >> + * Since the adc frequency is checked before, there is no reason
> >> + * to not meet the startup time constraint.
> >> + */
> >> +
> >> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> >> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> >> + if (startup_lookup[i] > ticks_min)
> >> + break;
> >> +
> >> + return i;
> >> +}
> >> +
> >> +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;
> >> +
> >> + f_per = clk_get_rate(st->per_clk);
> >> + prescal = (f_per / (2 * freq)) - 1;
> >> +
> >> + 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));
> >> +
> >> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> >> + freq, startup, prescal);
> >> +}
> >> +
> >> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> >> +{
> >> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> >> + unsigned mr, prescal;
> >> +
> >> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> >> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> >> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> >> + f_adc = f_per / (2 * (prescal + 1));
> >> +
> >> + return f_adc;
> >> +}
> >> +
> >> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> >> +{
> >> + struct iio_dev *indio = private;
> >> + struct at91_adc_state *st = iio_priv(indio);
> >> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> >> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> >> +
> >> + if (status & imr) {
> >> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> >> + st->conversion_done = true;
> >> + wake_up_interruptible(&st->wq_data_available);
> >> + return IRQ_HANDLED;
> >> + }
> >> +
> >> + return IRQ_NONE;
> >> +}
> >> +
> >> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int *val, int *val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> + int ret;
> >> +
> >> + switch (mask) {
> >> + case IIO_CHAN_INFO_RAW:
> >> + mutex_lock(&st->lock);
> >> +
> >> + st->chan = chan;
> >> +
> >> + 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);
> >> +
> >> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> >> + st->conversion_done,
> >> + msecs_to_jiffies(1000));
> >> + if (ret == 0)
> >> + ret = -ETIMEDOUT;
> >> +
> >> + if (ret > 0) {
> >> + *val = st->conversion_value;
> >> + ret = IIO_VAL_INT;
> >> + st->conversion_done = false;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> >> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> >> +
> >> + mutex_unlock(&st->lock);
> >> + return ret;
> >> +
> >> + case IIO_CHAN_INFO_SCALE:
> >> + *val = st->vref_uv / 1000;
> >> + *val2 = chan->scan_type.realbits;
> >> + return IIO_VAL_FRACTIONAL_LOG2;
> >> +
> >> + case IIO_CHAN_INFO_SAMP_FREQ:
> >> + *val = at91_adc_get_sample_freq(st);
> >> + return IIO_VAL_INT;
> >> +
> >> + default:
> >> + return -EINVAL;
> >> + }
> >> +}
> >> +
> >> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int val, int val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> >> + return -EINVAL;
> >> +
> >> + if (val < st->soc_info.min_sample_rate ||
> >> + val > st->soc_info.max_sample_rate)
> >> + return -EINVAL;
> >> +
> >> + at91_adc_setup_samp_freq(st, val);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct iio_info at91_adc_info = {
> >> + .read_raw = &at91_adc_read_raw,
> >> + .write_raw = &at91_adc_write_raw,
> >> + .driver_module = THIS_MODULE,
> >> +};
> >> +
> >> +static int at91_adc_probe(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev;
> >> + struct at91_adc_state *st;
> >> + struct resource *res;
> >> + int ret;
> >> +
> >> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> >> + sizeof(struct at91_adc_state));
> > Utter nitpick that isn't going to hold up the patch...
> > I'd have done this as sizeof(*st)) but really doesn't matter.
> >> + if (!indio_dev)
> >> + return -ENOMEM;
> >> +
> >> + indio_dev->dev.parent = &pdev->dev;
> >> + indio_dev->name = dev_name(&pdev->dev);
> >> + indio_dev->modes = INDIO_DIRECT_MODE;
> >> + indio_dev->info = &at91_adc_info;
> >> + indio_dev->channels = at91_adc_channels;
> >> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> >> +
> >> + st = iio_priv(indio_dev);
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,min-sample-rate-hz",
> >> + &st->soc_info.min_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,max-sample-rate-hz",
> >> + &st->soc_info.max_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> >> + &st->soc_info.startup_time);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,startup-time-ms\n");
> >> + return ret;
> >> + }
> >> +
> >> + init_waitqueue_head(&st->wq_data_available);
> >> + mutex_init(&st->lock);
> >> +
> >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> + if (!res)
> >> + return -EINVAL;
> >> +
> >> + st->base = devm_ioremap_resource(&pdev->dev, res);
> >> + if (IS_ERR(st->base))
> >> + return PTR_ERR(st->base);
> >> +
> >> + st->irq = platform_get_irq(pdev, 0);
> >> + if (st->irq <= 0) {
> >> + if (!st->irq)
> >> + st->irq = -ENXIO;
> >> +
> >> + return st->irq;
> >> + }
> >> +
> >> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> >> + if (IS_ERR(st->per_clk))
> >> + return PTR_ERR(st->per_clk);
> >> +
> >> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> >> + if (IS_ERR(st->reg))
> >> + return PTR_ERR(st->reg);
> >> +
> >> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> >> + if (IS_ERR(st->vref))
> >> + return PTR_ERR(st->vref);
> >> +
> >> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> >> + pdev->dev.driver->name, indio_dev);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->reg);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->vref);
> >> + if (ret)
> >> + goto reg_disable;
> >> +
> >> + st->vref_uv = regulator_get_voltage(st->vref);
> >> + if (st->vref_uv <= 0) {
> >> + ret = -EINVAL;
> >> + goto vref_disable;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> >> +
> >> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> >> +
> >> + ret = clk_prepare_enable(st->per_clk);
> >> + if (ret)
> >> + goto vref_disable;
> >> +
> >> + ret = iio_device_register(indio_dev);
> >> + if (ret < 0)
> >> + goto per_clk_disable_unprepare;
> >> +
> >> + dev_info(&pdev->dev, "version: %x\n",
> >> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> >> +
> >> + return 0;
> >> +
> >> +per_clk_disable_unprepare:
> >> + clk_disable_unprepare(st->per_clk);
> >> +vref_disable:
> >> + regulator_disable(st->vref);
> >> +reg_disable:
> >> + regulator_disable(st->reg);
> >> + return ret;
> >> +}
> >> +
> >> +static int at91_adc_remove(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + iio_device_unregister(indio_dev);
> >> +
> >> + clk_disable_unprepare(st->per_clk);
> >> +
> >> + regulator_disable(st->vref);
> >> + regulator_disable(st->reg);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id at91_adc_dt_match[] = {
> >> + {
> >> + .compatible = "atmel,sama5d2-adc",
> >> + }, {
> >> + /* sentinel */
> >> + }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> >> +
> >> +static struct platform_driver at91_adc_driver = {
> >> + .probe = at91_adc_probe,
> >> + .remove = at91_adc_remove,
> >> + .driver = {
> >> + .name = "at91-sama5d2_adc",
> >> + .of_match_table = at91_adc_dt_match,
> >> + },
> >> +};
> >> +module_platform_driver(at91_adc_driver)
> >> +
> >> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
> >> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> >> +MODULE_LICENSE("GPL v2");
> >>
> >
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-18 7:55 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:55 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Ludovic Desroches, nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A
On Sat, Jan 16, 2016 at 11:35:24AM +0000, Jonathan Cameron wrote:
> On 16/01/16 11:27, Jonathan Cameron wrote:
> > On 14/01/16 15:38, Ludovic Desroches wrote:
> >> This driver supports the new version of the Atmel ADC device introduced
> >> with the SAMA5D2 SoC family.
> >>
> >> Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> >> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > Looks good to me. One totally trivial note inline (which I'm not even going
> > to bother changing whilst applying the patch - a good measure of how trivial ;)
> >
> > Applied to the togreg branch of iio.git - initially pushed out as testing
> > for the autobuilders to play with it.
> Having said that - just noticed an oddity - why the DEPENDS on INPUT?
> I'll drop that as a probable cut and paste issue as right now it doesn't
> depend on INPUT (might at some later date of course!) Whilst there - to
> improve general build coverage I've added COMPILE_TEST as an alternative
> to at91 (works fine on my x86_64 compiler for starters)
>
Yes copy/paste issue. Thanks for the addition of COMPILE_TEST.
Regards
Ludovic
> Doing the COMPILE_TEST option tends to highlight any 'interesting' dependencies
> that happen to get pulled in by other at91 dependencies at the moment and
> reduces the chance of an IIO / kernel wide change breaking the driver.
>
> Jonathan
> >
> > Thanks,
> >
> > Jonathan
> >> ---
> >> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> >> drivers/iio/adc/Kconfig | 11 +
> >> drivers/iio/adc/Makefile | 1 +
> >> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> >> 4 files changed, 549 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> new file mode 100644
> >> index 0000000..3223684
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> @@ -0,0 +1,28 @@
> >> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> >> +
> >> +Required properties:
> >> + - compatible: Should be "atmel,sama5d2-adc".
> >> + - reg: Should contain ADC registers location and length.
> >> + - interrupts: Should contain the IRQ line for the ADC.
> >> + - clocks: phandle to device clock.
> >> + - clock-names: Must be "adc_clk".
> >> + - vref-supply: Supply used as reference for conversions.
> >> + - vddana-supply: Supply for the adc device.
> >> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> >> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> >> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> >> +
> >> +Example:
> >> +
> >> +adc: adc@fc030000 {
> >> + compatible = "atmel,sama5d2-adc";
> >> + reg = <0xfc030000 0x100>;
> >> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> >> + clocks = <&adc_clk>;
> >> + clock-names = "adc_clk";
> >> + atmel,min-sample-rate-hz = <200000>;
> >> + atmel,max-sample-rate-hz = <20000000>;
> >> + atmel,startup-time-ms = <4>;
> >> + vddana-supply = <&vdd_3v3_lp_reg>;
> >> + vref-supply = <&vdd_3v3_lp_reg>;
> >> +}
> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> index 605ff42..f57b4ea 100644
> >> --- a/drivers/iio/adc/Kconfig
> >> +++ b/drivers/iio/adc/Kconfig
> >> @@ -131,6 +131,17 @@ config AT91_ADC
> >> To compile this driver as a module, choose M here: the module will be
> >> called at91_adc.
> >>
> >> +config AT91_SAMA5D2_ADC
> >> + tristate "Atmel AT91 SAMA5D2 ADC"
> >> + depends on ARCH_AT91
> >> + depends on INPUT
> >> + help
> >> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> >> + available on SAMA5D2 SoC family.
> >> +
> >> + To compile this driver as a module, choose M here: the module will be
> >> + called at91-sama5d2_adc.
> >> +
> >> config AXP288_ADC
> >> tristate "X-Powers AXP288 ADC driver"
> >> depends on MFD_AXP20X
> >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >> index 6435780..fb57e12 100644
> >> --- a/drivers/iio/adc/Makefile
> >> +++ b/drivers/iio/adc/Makefile
> >> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> >> obj-$(CONFIG_AD7887) += ad7887.o
> >> obj-$(CONFIG_AD799X) += ad799x.o
> >> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> >> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> >> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> >> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> >> new file mode 100644
> >> index 0000000..ecb2d90
> >> --- /dev/null
> >> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> >> @@ -0,0 +1,509 @@
> >> +/*
> >> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> >> + *
> >> + * Copyright (C) 2015 Atmel,
> >> + * 2015 Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> >> + *
> >> + * This software is licensed under the terms of the GNU General Public
> >> + * License version 2, as published by the Free Software Foundation, and
> >> + * may be copied, distributed, and modified under those terms.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#include <linux/bitops.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/sched.h>
> >> +#include <linux/wait.h>
> >> +#include <linux/iio/iio.h>
> >> +#include <linux/iio/sysfs.h>
> >> +#include <linux/regulator/consumer.h>
> >> +
> >> +/* Control Register */
> >> +#define AT91_SAMA5D2_CR 0x00
> >> +/* Software Reset */
> >> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> >> +/* Start Conversion */
> >> +#define AT91_SAMA5D2_CR_START BIT(1)
> >> +/* Touchscreen Calibration */
> >> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> >> +/* Comparison Restart */
> >> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> >> +
> >> +/* Mode Register */
> >> +#define AT91_SAMA5D2_MR 0x04
> >> +/* Trigger Selection */
> >> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> >> +/* ADTRG */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> >> +/* TIOA0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> >> +/* TIOA1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> >> +/* TIOA2 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> >> +/* PWM event line 0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> >> +/* PWM event line 1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> >> +/* TIOA3 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> >> +/* RTCOUT0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> >> +/* Sleep Mode */
> >> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> >> +/* Fast Wake Up */
> >> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> >> +/* Prescaler Rate Selection */
> >> +#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
> >> +/* Startup Time */
> >> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> >> +/* Analog Change */
> >> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> >> +/* Tracking Time */
> >> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> >> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> >> +/* Transfer Time */
> >> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> >> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> >> +/* Use Sequence Enable */
> >> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> >> +
> >> +/* Channel Sequence Register 1 */
> >> +#define AT91_SAMA5D2_SEQR1 0x08
> >> +/* Channel Sequence Register 2 */
> >> +#define AT91_SAMA5D2_SEQR2 0x0c
> >> +/* Channel Enable Register */
> >> +#define AT91_SAMA5D2_CHER 0x10
> >> +/* Channel Disable Register */
> >> +#define AT91_SAMA5D2_CHDR 0x14
> >> +/* Channel Status Register */
> >> +#define AT91_SAMA5D2_CHSR 0x18
> >> +/* Last Converted Data Register */
> >> +#define AT91_SAMA5D2_LCDR 0x20
> >> +/* Interrupt Enable Register */
> >> +#define AT91_SAMA5D2_IER 0x24
> >> +/* Interrupt Disable Register */
> >> +#define AT91_SAMA5D2_IDR 0x28
> >> +/* Interrupt Mask Register */
> >> +#define AT91_SAMA5D2_IMR 0x2c
> >> +/* Interrupt Status Register */
> >> +#define AT91_SAMA5D2_ISR 0x30
> >> +/* Last Channel Trigger Mode Register */
> >> +#define AT91_SAMA5D2_LCTMR 0x34
> >> +/* Last Channel Compare Window Register */
> >> +#define AT91_SAMA5D2_LCCWR 0x38
> >> +/* Overrun Status Register */
> >> +#define AT91_SAMA5D2_OVER 0x3c
> >> +/* Extended Mode Register */
> >> +#define AT91_SAMA5D2_EMR 0x40
> >> +/* Compare Window Register */
> >> +#define AT91_SAMA5D2_CWR 0x44
> >> +/* Channel Gain Register */
> >> +#define AT91_SAMA5D2_CGR 0x48
> >> +/* Channel Offset Register */
> >> +#define AT91_SAMA5D2_COR 0x4c
> >> +/* Channel Data Register 0 */
> >> +#define AT91_SAMA5D2_CDR0 0x50
> >> +/* Analog Control Register */
> >> +#define AT91_SAMA5D2_ACR 0x94
> >> +/* Touchscreen Mode Register */
> >> +#define AT91_SAMA5D2_TSMR 0xb0
> >> +/* Touchscreen X Position Register */
> >> +#define AT91_SAMA5D2_XPOSR 0xb4
> >> +/* Touchscreen Y Position Register */
> >> +#define AT91_SAMA5D2_YPOSR 0xb8
> >> +/* Touchscreen Pressure Register */
> >> +#define AT91_SAMA5D2_PRESSR 0xbc
> >> +/* Trigger Register */
> >> +#define AT91_SAMA5D2_TRGR 0xc0
> >> +/* Correction Select Register */
> >> +#define AT91_SAMA5D2_COSR 0xd0
> >> +/* Correction Value Register */
> >> +#define AT91_SAMA5D2_CVR 0xd4
> >> +/* Channel Error Correction Register */
> >> +#define AT91_SAMA5D2_CECR 0xd8
> >> +/* Write Protection Mode Register */
> >> +#define AT91_SAMA5D2_WPMR 0xe4
> >> +/* Write Protection Status Register */
> >> +#define AT91_SAMA5D2_WPSR 0xe8
> >> +/* Version Register */
> >> +#define AT91_SAMA5D2_VERSION 0xfc
> >> +
> >> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> >> + { \
> >> + .type = IIO_VOLTAGE, \
> >> + .channel = num, \
> >> + .address = addr, \
> >> + .scan_type = { \
> >> + .sign = 'u', \
> >> + .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, \
> >> + .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)
> >> +
> >> +struct at91_adc_soc_info {
> >> + unsigned startup_time;
> >> + unsigned min_sample_rate;
> >> + unsigned max_sample_rate;
> >> +};
> >> +
> >> +struct at91_adc_state {
> >> + void __iomem *base;
> >> + int irq;
> >> + struct clk *per_clk;
> >> + struct regulator *reg;
> >> + struct regulator *vref;
> >> + u32 vref_uv;
> >> + const struct iio_chan_spec *chan;
> >> + bool conversion_done;
> >> + u32 conversion_value;
> >> + struct at91_adc_soc_info soc_info;
> >> + wait_queue_head_t wq_data_available;
> >> + /*
> >> + * lock to prevent concurrent 'single conversion' requests through
> >> + * sysfs.
> >> + */
> >> + struct mutex lock;
> >> +};
> >> +
> >> +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),
> >> +};
> >> +
> >> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> >> + unsigned adc_clk_khz)
> >> +{
> >> + const unsigned startup_lookup[] = {
> >> + 0, 8, 16, 24,
> >> + 64, 80, 96, 112,
> >> + 512, 576, 640, 704,
> >> + 768, 832, 896, 960
> >> + };
> >> + unsigned ticks_min, i;
> >> +
> >> + /*
> >> + * Since the adc frequency is checked before, there is no reason
> >> + * to not meet the startup time constraint.
> >> + */
> >> +
> >> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> >> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> >> + if (startup_lookup[i] > ticks_min)
> >> + break;
> >> +
> >> + return i;
> >> +}
> >> +
> >> +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;
> >> +
> >> + f_per = clk_get_rate(st->per_clk);
> >> + prescal = (f_per / (2 * freq)) - 1;
> >> +
> >> + 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));
> >> +
> >> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> >> + freq, startup, prescal);
> >> +}
> >> +
> >> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> >> +{
> >> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> >> + unsigned mr, prescal;
> >> +
> >> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> >> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> >> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> >> + f_adc = f_per / (2 * (prescal + 1));
> >> +
> >> + return f_adc;
> >> +}
> >> +
> >> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> >> +{
> >> + struct iio_dev *indio = private;
> >> + struct at91_adc_state *st = iio_priv(indio);
> >> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> >> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> >> +
> >> + if (status & imr) {
> >> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> >> + st->conversion_done = true;
> >> + wake_up_interruptible(&st->wq_data_available);
> >> + return IRQ_HANDLED;
> >> + }
> >> +
> >> + return IRQ_NONE;
> >> +}
> >> +
> >> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int *val, int *val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> + int ret;
> >> +
> >> + switch (mask) {
> >> + case IIO_CHAN_INFO_RAW:
> >> + mutex_lock(&st->lock);
> >> +
> >> + st->chan = chan;
> >> +
> >> + 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);
> >> +
> >> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> >> + st->conversion_done,
> >> + msecs_to_jiffies(1000));
> >> + if (ret == 0)
> >> + ret = -ETIMEDOUT;
> >> +
> >> + if (ret > 0) {
> >> + *val = st->conversion_value;
> >> + ret = IIO_VAL_INT;
> >> + st->conversion_done = false;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> >> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> >> +
> >> + mutex_unlock(&st->lock);
> >> + return ret;
> >> +
> >> + case IIO_CHAN_INFO_SCALE:
> >> + *val = st->vref_uv / 1000;
> >> + *val2 = chan->scan_type.realbits;
> >> + return IIO_VAL_FRACTIONAL_LOG2;
> >> +
> >> + case IIO_CHAN_INFO_SAMP_FREQ:
> >> + *val = at91_adc_get_sample_freq(st);
> >> + return IIO_VAL_INT;
> >> +
> >> + default:
> >> + return -EINVAL;
> >> + }
> >> +}
> >> +
> >> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int val, int val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> >> + return -EINVAL;
> >> +
> >> + if (val < st->soc_info.min_sample_rate ||
> >> + val > st->soc_info.max_sample_rate)
> >> + return -EINVAL;
> >> +
> >> + at91_adc_setup_samp_freq(st, val);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct iio_info at91_adc_info = {
> >> + .read_raw = &at91_adc_read_raw,
> >> + .write_raw = &at91_adc_write_raw,
> >> + .driver_module = THIS_MODULE,
> >> +};
> >> +
> >> +static int at91_adc_probe(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev;
> >> + struct at91_adc_state *st;
> >> + struct resource *res;
> >> + int ret;
> >> +
> >> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> >> + sizeof(struct at91_adc_state));
> > Utter nitpick that isn't going to hold up the patch...
> > I'd have done this as sizeof(*st)) but really doesn't matter.
> >> + if (!indio_dev)
> >> + return -ENOMEM;
> >> +
> >> + indio_dev->dev.parent = &pdev->dev;
> >> + indio_dev->name = dev_name(&pdev->dev);
> >> + indio_dev->modes = INDIO_DIRECT_MODE;
> >> + indio_dev->info = &at91_adc_info;
> >> + indio_dev->channels = at91_adc_channels;
> >> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> >> +
> >> + st = iio_priv(indio_dev);
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,min-sample-rate-hz",
> >> + &st->soc_info.min_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,max-sample-rate-hz",
> >> + &st->soc_info.max_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> >> + &st->soc_info.startup_time);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,startup-time-ms\n");
> >> + return ret;
> >> + }
> >> +
> >> + init_waitqueue_head(&st->wq_data_available);
> >> + mutex_init(&st->lock);
> >> +
> >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> + if (!res)
> >> + return -EINVAL;
> >> +
> >> + st->base = devm_ioremap_resource(&pdev->dev, res);
> >> + if (IS_ERR(st->base))
> >> + return PTR_ERR(st->base);
> >> +
> >> + st->irq = platform_get_irq(pdev, 0);
> >> + if (st->irq <= 0) {
> >> + if (!st->irq)
> >> + st->irq = -ENXIO;
> >> +
> >> + return st->irq;
> >> + }
> >> +
> >> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> >> + if (IS_ERR(st->per_clk))
> >> + return PTR_ERR(st->per_clk);
> >> +
> >> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> >> + if (IS_ERR(st->reg))
> >> + return PTR_ERR(st->reg);
> >> +
> >> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> >> + if (IS_ERR(st->vref))
> >> + return PTR_ERR(st->vref);
> >> +
> >> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> >> + pdev->dev.driver->name, indio_dev);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->reg);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->vref);
> >> + if (ret)
> >> + goto reg_disable;
> >> +
> >> + st->vref_uv = regulator_get_voltage(st->vref);
> >> + if (st->vref_uv <= 0) {
> >> + ret = -EINVAL;
> >> + goto vref_disable;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> >> +
> >> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> >> +
> >> + ret = clk_prepare_enable(st->per_clk);
> >> + if (ret)
> >> + goto vref_disable;
> >> +
> >> + ret = iio_device_register(indio_dev);
> >> + if (ret < 0)
> >> + goto per_clk_disable_unprepare;
> >> +
> >> + dev_info(&pdev->dev, "version: %x\n",
> >> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> >> +
> >> + return 0;
> >> +
> >> +per_clk_disable_unprepare:
> >> + clk_disable_unprepare(st->per_clk);
> >> +vref_disable:
> >> + regulator_disable(st->vref);
> >> +reg_disable:
> >> + regulator_disable(st->reg);
> >> + return ret;
> >> +}
> >> +
> >> +static int at91_adc_remove(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + iio_device_unregister(indio_dev);
> >> +
> >> + clk_disable_unprepare(st->per_clk);
> >> +
> >> + regulator_disable(st->vref);
> >> + regulator_disable(st->reg);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id at91_adc_dt_match[] = {
> >> + {
> >> + .compatible = "atmel,sama5d2-adc",
> >> + }, {
> >> + /* sentinel */
> >> + }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> >> +
> >> +static struct platform_driver at91_adc_driver = {
> >> + .probe = at91_adc_probe,
> >> + .remove = at91_adc_remove,
> >> + .driver = {
> >> + .name = "at91-sama5d2_adc",
> >> + .of_match_table = at91_adc_dt_match,
> >> + },
> >> +};
> >> +module_platform_driver(at91_adc_driver)
> >> +
> >> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>");
> >> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> >> +MODULE_LICENSE("GPL v2");
> >>
> >
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver
@ 2016-01-18 7:55 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:55 UTC (permalink / raw)
To: linux-arm-kernel
On Sat, Jan 16, 2016 at 11:35:24AM +0000, Jonathan Cameron wrote:
> On 16/01/16 11:27, Jonathan Cameron wrote:
> > On 14/01/16 15:38, Ludovic Desroches wrote:
> >> This driver supports the new version of the Atmel ADC device introduced
> >> with the SAMA5D2 SoC family.
> >>
> >> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >> Acked-by: Rob Herring <robh@kernel.org>
> > Looks good to me. One totally trivial note inline (which I'm not even going
> > to bother changing whilst applying the patch - a good measure of how trivial ;)
> >
> > Applied to the togreg branch of iio.git - initially pushed out as testing
> > for the autobuilders to play with it.
> Having said that - just noticed an oddity - why the DEPENDS on INPUT?
> I'll drop that as a probable cut and paste issue as right now it doesn't
> depend on INPUT (might at some later date of course!) Whilst there - to
> improve general build coverage I've added COMPILE_TEST as an alternative
> to at91 (works fine on my x86_64 compiler for starters)
>
Yes copy/paste issue. Thanks for the addition of COMPILE_TEST.
Regards
Ludovic
> Doing the COMPILE_TEST option tends to highlight any 'interesting' dependencies
> that happen to get pulled in by other at91 dependencies at the moment and
> reduces the chance of an IIO / kernel wide change breaking the driver.
>
> Jonathan
> >
> > Thanks,
> >
> > Jonathan
> >> ---
> >> .../bindings/iio/adc/at91-sama5d2_adc.txt | 28 ++
> >> drivers/iio/adc/Kconfig | 11 +
> >> drivers/iio/adc/Makefile | 1 +
> >> drivers/iio/adc/at91-sama5d2_adc.c | 509 +++++++++++++++++++++
> >> 4 files changed, 549 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c
> >>
> >> diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> new file mode 100644
> >> index 0000000..3223684
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt
> >> @@ -0,0 +1,28 @@
> >> +* AT91 SAMA5D2 Analog to Digital Converter (ADC)
> >> +
> >> +Required properties:
> >> + - compatible: Should be "atmel,sama5d2-adc".
> >> + - reg: Should contain ADC registers location and length.
> >> + - interrupts: Should contain the IRQ line for the ADC.
> >> + - clocks: phandle to device clock.
> >> + - clock-names: Must be "adc_clk".
> >> + - vref-supply: Supply used as reference for conversions.
> >> + - vddana-supply: Supply for the adc device.
> >> + - atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
> >> + - atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
> >> + - atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
> >> +
> >> +Example:
> >> +
> >> +adc: adc at fc030000 {
> >> + compatible = "atmel,sama5d2-adc";
> >> + reg = <0xfc030000 0x100>;
> >> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> >> + clocks = <&adc_clk>;
> >> + clock-names = "adc_clk";
> >> + atmel,min-sample-rate-hz = <200000>;
> >> + atmel,max-sample-rate-hz = <20000000>;
> >> + atmel,startup-time-ms = <4>;
> >> + vddana-supply = <&vdd_3v3_lp_reg>;
> >> + vref-supply = <&vdd_3v3_lp_reg>;
> >> +}
> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> index 605ff42..f57b4ea 100644
> >> --- a/drivers/iio/adc/Kconfig
> >> +++ b/drivers/iio/adc/Kconfig
> >> @@ -131,6 +131,17 @@ config AT91_ADC
> >> To compile this driver as a module, choose M here: the module will be
> >> called at91_adc.
> >>
> >> +config AT91_SAMA5D2_ADC
> >> + tristate "Atmel AT91 SAMA5D2 ADC"
> >> + depends on ARCH_AT91
> >> + depends on INPUT
> >> + help
> >> + Say yes here to build support for Atmel SAMA5D2 ADC which is
> >> + available on SAMA5D2 SoC family.
> >> +
> >> + To compile this driver as a module, choose M here: the module will be
> >> + called at91-sama5d2_adc.
> >> +
> >> config AXP288_ADC
> >> tristate "X-Powers AXP288 ADC driver"
> >> depends on MFD_AXP20X
> >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> >> index 6435780..fb57e12 100644
> >> --- a/drivers/iio/adc/Makefile
> >> +++ b/drivers/iio/adc/Makefile
> >> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
> >> obj-$(CONFIG_AD7887) += ad7887.o
> >> obj-$(CONFIG_AD799X) += ad799x.o
> >> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> >> +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> >> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> >> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> >> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
> >> diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
> >> new file mode 100644
> >> index 0000000..ecb2d90
> >> --- /dev/null
> >> +++ b/drivers/iio/adc/at91-sama5d2_adc.c
> >> @@ -0,0 +1,509 @@
> >> +/*
> >> + * Atmel ADC driver for SAMA5D2 devices and compatible.
> >> + *
> >> + * Copyright (C) 2015 Atmel,
> >> + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
> >> + *
> >> + * This software is licensed under the terms of the GNU General Public
> >> + * License version 2, as published by the Free Software Foundation, and
> >> + * may be copied, distributed, and modified under those terms.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#include <linux/bitops.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/sched.h>
> >> +#include <linux/wait.h>
> >> +#include <linux/iio/iio.h>
> >> +#include <linux/iio/sysfs.h>
> >> +#include <linux/regulator/consumer.h>
> >> +
> >> +/* Control Register */
> >> +#define AT91_SAMA5D2_CR 0x00
> >> +/* Software Reset */
> >> +#define AT91_SAMA5D2_CR_SWRST BIT(0)
> >> +/* Start Conversion */
> >> +#define AT91_SAMA5D2_CR_START BIT(1)
> >> +/* Touchscreen Calibration */
> >> +#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
> >> +/* Comparison Restart */
> >> +#define AT91_SAMA5D2_CR_CMPRST BIT(4)
> >> +
> >> +/* Mode Register */
> >> +#define AT91_SAMA5D2_MR 0x04
> >> +/* Trigger Selection */
> >> +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
> >> +/* ADTRG */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
> >> +/* TIOA0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
> >> +/* TIOA1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
> >> +/* TIOA2 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
> >> +/* PWM event line 0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
> >> +/* PWM event line 1 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
> >> +/* TIOA3 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
> >> +/* RTCOUT0 */
> >> +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
> >> +/* Sleep Mode */
> >> +#define AT91_SAMA5D2_MR_SLEEP BIT(5)
> >> +/* Fast Wake Up */
> >> +#define AT91_SAMA5D2_MR_FWUP BIT(6)
> >> +/* Prescaler Rate Selection */
> >> +#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
> >> +/* Startup Time */
> >> +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
> >> +/* Analog Change */
> >> +#define AT91_SAMA5D2_MR_ANACH BIT(23)
> >> +/* Tracking Time */
> >> +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
> >> +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
> >> +/* Transfer Time */
> >> +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
> >> +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
> >> +/* Use Sequence Enable */
> >> +#define AT91_SAMA5D2_MR_USEQ BIT(31)
> >> +
> >> +/* Channel Sequence Register 1 */
> >> +#define AT91_SAMA5D2_SEQR1 0x08
> >> +/* Channel Sequence Register 2 */
> >> +#define AT91_SAMA5D2_SEQR2 0x0c
> >> +/* Channel Enable Register */
> >> +#define AT91_SAMA5D2_CHER 0x10
> >> +/* Channel Disable Register */
> >> +#define AT91_SAMA5D2_CHDR 0x14
> >> +/* Channel Status Register */
> >> +#define AT91_SAMA5D2_CHSR 0x18
> >> +/* Last Converted Data Register */
> >> +#define AT91_SAMA5D2_LCDR 0x20
> >> +/* Interrupt Enable Register */
> >> +#define AT91_SAMA5D2_IER 0x24
> >> +/* Interrupt Disable Register */
> >> +#define AT91_SAMA5D2_IDR 0x28
> >> +/* Interrupt Mask Register */
> >> +#define AT91_SAMA5D2_IMR 0x2c
> >> +/* Interrupt Status Register */
> >> +#define AT91_SAMA5D2_ISR 0x30
> >> +/* Last Channel Trigger Mode Register */
> >> +#define AT91_SAMA5D2_LCTMR 0x34
> >> +/* Last Channel Compare Window Register */
> >> +#define AT91_SAMA5D2_LCCWR 0x38
> >> +/* Overrun Status Register */
> >> +#define AT91_SAMA5D2_OVER 0x3c
> >> +/* Extended Mode Register */
> >> +#define AT91_SAMA5D2_EMR 0x40
> >> +/* Compare Window Register */
> >> +#define AT91_SAMA5D2_CWR 0x44
> >> +/* Channel Gain Register */
> >> +#define AT91_SAMA5D2_CGR 0x48
> >> +/* Channel Offset Register */
> >> +#define AT91_SAMA5D2_COR 0x4c
> >> +/* Channel Data Register 0 */
> >> +#define AT91_SAMA5D2_CDR0 0x50
> >> +/* Analog Control Register */
> >> +#define AT91_SAMA5D2_ACR 0x94
> >> +/* Touchscreen Mode Register */
> >> +#define AT91_SAMA5D2_TSMR 0xb0
> >> +/* Touchscreen X Position Register */
> >> +#define AT91_SAMA5D2_XPOSR 0xb4
> >> +/* Touchscreen Y Position Register */
> >> +#define AT91_SAMA5D2_YPOSR 0xb8
> >> +/* Touchscreen Pressure Register */
> >> +#define AT91_SAMA5D2_PRESSR 0xbc
> >> +/* Trigger Register */
> >> +#define AT91_SAMA5D2_TRGR 0xc0
> >> +/* Correction Select Register */
> >> +#define AT91_SAMA5D2_COSR 0xd0
> >> +/* Correction Value Register */
> >> +#define AT91_SAMA5D2_CVR 0xd4
> >> +/* Channel Error Correction Register */
> >> +#define AT91_SAMA5D2_CECR 0xd8
> >> +/* Write Protection Mode Register */
> >> +#define AT91_SAMA5D2_WPMR 0xe4
> >> +/* Write Protection Status Register */
> >> +#define AT91_SAMA5D2_WPSR 0xe8
> >> +/* Version Register */
> >> +#define AT91_SAMA5D2_VERSION 0xfc
> >> +
> >> +#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
> >> + { \
> >> + .type = IIO_VOLTAGE, \
> >> + .channel = num, \
> >> + .address = addr, \
> >> + .scan_type = { \
> >> + .sign = 'u', \
> >> + .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, \
> >> + .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)
> >> +
> >> +struct at91_adc_soc_info {
> >> + unsigned startup_time;
> >> + unsigned min_sample_rate;
> >> + unsigned max_sample_rate;
> >> +};
> >> +
> >> +struct at91_adc_state {
> >> + void __iomem *base;
> >> + int irq;
> >> + struct clk *per_clk;
> >> + struct regulator *reg;
> >> + struct regulator *vref;
> >> + u32 vref_uv;
> >> + const struct iio_chan_spec *chan;
> >> + bool conversion_done;
> >> + u32 conversion_value;
> >> + struct at91_adc_soc_info soc_info;
> >> + wait_queue_head_t wq_data_available;
> >> + /*
> >> + * lock to prevent concurrent 'single conversion' requests through
> >> + * sysfs.
> >> + */
> >> + struct mutex lock;
> >> +};
> >> +
> >> +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),
> >> +};
> >> +
> >> +static unsigned at91_adc_startup_time(unsigned startup_time_min,
> >> + unsigned adc_clk_khz)
> >> +{
> >> + const unsigned startup_lookup[] = {
> >> + 0, 8, 16, 24,
> >> + 64, 80, 96, 112,
> >> + 512, 576, 640, 704,
> >> + 768, 832, 896, 960
> >> + };
> >> + unsigned ticks_min, i;
> >> +
> >> + /*
> >> + * Since the adc frequency is checked before, there is no reason
> >> + * to not meet the startup time constraint.
> >> + */
> >> +
> >> + ticks_min = startup_time_min * adc_clk_khz / 1000;
> >> + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
> >> + if (startup_lookup[i] > ticks_min)
> >> + break;
> >> +
> >> + return i;
> >> +}
> >> +
> >> +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;
> >> +
> >> + f_per = clk_get_rate(st->per_clk);
> >> + prescal = (f_per / (2 * freq)) - 1;
> >> +
> >> + 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));
> >> +
> >> + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
> >> + freq, startup, prescal);
> >> +}
> >> +
> >> +static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
> >> +{
> >> + unsigned f_adc, f_per = clk_get_rate(st->per_clk);
> >> + unsigned mr, prescal;
> >> +
> >> + mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
> >> + prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
> >> + & AT91_SAMA5D2_MR_PRESCAL_MAX;
> >> + f_adc = f_per / (2 * (prescal + 1));
> >> +
> >> + return f_adc;
> >> +}
> >> +
> >> +static irqreturn_t at91_adc_interrupt(int irq, void *private)
> >> +{
> >> + struct iio_dev *indio = private;
> >> + struct at91_adc_state *st = iio_priv(indio);
> >> + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
> >> + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
> >> +
> >> + if (status & imr) {
> >> + st->conversion_value = at91_adc_readl(st, st->chan->address);
> >> + st->conversion_done = true;
> >> + wake_up_interruptible(&st->wq_data_available);
> >> + return IRQ_HANDLED;
> >> + }
> >> +
> >> + return IRQ_NONE;
> >> +}
> >> +
> >> +static int at91_adc_read_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int *val, int *val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> + int ret;
> >> +
> >> + switch (mask) {
> >> + case IIO_CHAN_INFO_RAW:
> >> + mutex_lock(&st->lock);
> >> +
> >> + st->chan = chan;
> >> +
> >> + 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);
> >> +
> >> + ret = wait_event_interruptible_timeout(st->wq_data_available,
> >> + st->conversion_done,
> >> + msecs_to_jiffies(1000));
> >> + if (ret == 0)
> >> + ret = -ETIMEDOUT;
> >> +
> >> + if (ret > 0) {
> >> + *val = st->conversion_value;
> >> + ret = IIO_VAL_INT;
> >> + st->conversion_done = false;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
> >> + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
> >> +
> >> + mutex_unlock(&st->lock);
> >> + return ret;
> >> +
> >> + case IIO_CHAN_INFO_SCALE:
> >> + *val = st->vref_uv / 1000;
> >> + *val2 = chan->scan_type.realbits;
> >> + return IIO_VAL_FRACTIONAL_LOG2;
> >> +
> >> + case IIO_CHAN_INFO_SAMP_FREQ:
> >> + *val = at91_adc_get_sample_freq(st);
> >> + return IIO_VAL_INT;
> >> +
> >> + default:
> >> + return -EINVAL;
> >> + }
> >> +}
> >> +
> >> +static int at91_adc_write_raw(struct iio_dev *indio_dev,
> >> + struct iio_chan_spec const *chan,
> >> + int val, int val2, long mask)
> >> +{
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + if (mask != IIO_CHAN_INFO_SAMP_FREQ)
> >> + return -EINVAL;
> >> +
> >> + if (val < st->soc_info.min_sample_rate ||
> >> + val > st->soc_info.max_sample_rate)
> >> + return -EINVAL;
> >> +
> >> + at91_adc_setup_samp_freq(st, val);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct iio_info at91_adc_info = {
> >> + .read_raw = &at91_adc_read_raw,
> >> + .write_raw = &at91_adc_write_raw,
> >> + .driver_module = THIS_MODULE,
> >> +};
> >> +
> >> +static int at91_adc_probe(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev;
> >> + struct at91_adc_state *st;
> >> + struct resource *res;
> >> + int ret;
> >> +
> >> + indio_dev = devm_iio_device_alloc(&pdev->dev,
> >> + sizeof(struct at91_adc_state));
> > Utter nitpick that isn't going to hold up the patch...
> > I'd have done this as sizeof(*st)) but really doesn't matter.
> >> + if (!indio_dev)
> >> + return -ENOMEM;
> >> +
> >> + indio_dev->dev.parent = &pdev->dev;
> >> + indio_dev->name = dev_name(&pdev->dev);
> >> + indio_dev->modes = INDIO_DIRECT_MODE;
> >> + indio_dev->info = &at91_adc_info;
> >> + indio_dev->channels = at91_adc_channels;
> >> + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
> >> +
> >> + st = iio_priv(indio_dev);
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,min-sample-rate-hz",
> >> + &st->soc_info.min_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,min-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node,
> >> + "atmel,max-sample-rate-hz",
> >> + &st->soc_info.max_sample_rate);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,max-sample-rate-hz\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
> >> + &st->soc_info.startup_time);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "invalid or missing value for atmel,startup-time-ms\n");
> >> + return ret;
> >> + }
> >> +
> >> + init_waitqueue_head(&st->wq_data_available);
> >> + mutex_init(&st->lock);
> >> +
> >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >> + if (!res)
> >> + return -EINVAL;
> >> +
> >> + st->base = devm_ioremap_resource(&pdev->dev, res);
> >> + if (IS_ERR(st->base))
> >> + return PTR_ERR(st->base);
> >> +
> >> + st->irq = platform_get_irq(pdev, 0);
> >> + if (st->irq <= 0) {
> >> + if (!st->irq)
> >> + st->irq = -ENXIO;
> >> +
> >> + return st->irq;
> >> + }
> >> +
> >> + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
> >> + if (IS_ERR(st->per_clk))
> >> + return PTR_ERR(st->per_clk);
> >> +
> >> + st->reg = devm_regulator_get(&pdev->dev, "vddana");
> >> + if (IS_ERR(st->reg))
> >> + return PTR_ERR(st->reg);
> >> +
> >> + st->vref = devm_regulator_get(&pdev->dev, "vref");
> >> + if (IS_ERR(st->vref))
> >> + return PTR_ERR(st->vref);
> >> +
> >> + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
> >> + pdev->dev.driver->name, indio_dev);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->reg);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + ret = regulator_enable(st->vref);
> >> + if (ret)
> >> + goto reg_disable;
> >> +
> >> + st->vref_uv = regulator_get_voltage(st->vref);
> >> + if (st->vref_uv <= 0) {
> >> + ret = -EINVAL;
> >> + goto vref_disable;
> >> + }
> >> +
> >> + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
> >> + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
> >> +
> >> + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
> >> +
> >> + ret = clk_prepare_enable(st->per_clk);
> >> + if (ret)
> >> + goto vref_disable;
> >> +
> >> + ret = iio_device_register(indio_dev);
> >> + if (ret < 0)
> >> + goto per_clk_disable_unprepare;
> >> +
> >> + dev_info(&pdev->dev, "version: %x\n",
> >> + readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
> >> +
> >> + return 0;
> >> +
> >> +per_clk_disable_unprepare:
> >> + clk_disable_unprepare(st->per_clk);
> >> +vref_disable:
> >> + regulator_disable(st->vref);
> >> +reg_disable:
> >> + regulator_disable(st->reg);
> >> + return ret;
> >> +}
> >> +
> >> +static int at91_adc_remove(struct platform_device *pdev)
> >> +{
> >> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> >> + struct at91_adc_state *st = iio_priv(indio_dev);
> >> +
> >> + iio_device_unregister(indio_dev);
> >> +
> >> + clk_disable_unprepare(st->per_clk);
> >> +
> >> + regulator_disable(st->vref);
> >> + regulator_disable(st->reg);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id at91_adc_dt_match[] = {
> >> + {
> >> + .compatible = "atmel,sama5d2-adc",
> >> + }, {
> >> + /* sentinel */
> >> + }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
> >> +
> >> +static struct platform_driver at91_adc_driver = {
> >> + .probe = at91_adc_probe,
> >> + .remove = at91_adc_remove,
> >> + .driver = {
> >> + .name = "at91-sama5d2_adc",
> >> + .of_match_table = at91_adc_dt_match,
> >> + },
> >> +};
> >> +module_platform_driver(at91_adc_driver)
> >> +
> >> +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
> >> +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
> >> +MODULE_LICENSE("GPL v2");
> >>
> >
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-18 7:59 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:59 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Ludovic Desroches, nicolas.ferre, alexandre.belloni, devicetree,
linux-kernel, linux-iio, plagnioj, linux-arm-kernel, pmeerw,
robh
On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
> On 14/01/16 15:38, Ludovic Desroches wrote:
> > Add the ADC device, and remove the adc_op_clk which is useless since the
> > adc sampling frequency is configured with sysfs.
> >
> > Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> I've now applied the driver to the togreg branch of iio.git.
> Should get a pull request out to Greg in about 2 weeks time and it'll
> probably be in linux-next a week after that. I'm assuming all the device
> tree changes will got through the at91 tree and arm-soc etc.
> Shout if you want me to take them through IIO.
>
Ok, I think Nicolas would take this patch.
Regards
Ludovic
> Thanks,
>
> Jonathan
> > ---
> > arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> > 1 file changed, 12 insertions(+), 6 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> > index 3f750f6..46dd430 100644
> > --- a/arch/arm/boot/dts/sama5d2.dtsi
> > +++ b/arch/arm/boot/dts/sama5d2.dtsi
> > @@ -88,12 +88,6 @@
> > #clock-cells = <0>;
> > clock-frequency = <0>;
> > };
> > -
> > - adc_op_clk: adc_op_clk{
> > - compatible = "fixed-clock";
> > - #clock-cells = <0>;
> > - clock-frequency = <1000000>;
> > - };
> > };
> >
> > ns_sram: sram@00200000 {
> > @@ -1085,6 +1079,18 @@
> > status = "disabled";
> > };
> >
> > + adc: adc@fc030000 {
> > + compatible = "atmel,sama5d2-adc";
> > + reg = <0xfc030000 0x100>;
> > + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> > + clocks = <&adc_clk>;
> > + clock-names = "adc_clk";
> > + atmel,min-sample-rate-hz = <200000>;
> > + atmel,max-sample-rate-hz = <20000000>;
> > + atmel,startup-time-ms = <4>;
> > + status = "disabled";
> > + };
> > +
> > pioA: pinctrl@fc038000 {
> > compatible = "atmel,sama5d2-pinctrl";
> > reg = <0xfc038000 0x600>;
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-18 7:59 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:59 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Ludovic Desroches, nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
plagnioj-sclMFOaUSTBWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pmeerw-jW+XmwGofnusTnJN9+BGXg, robh-DgEjT+Ai2ygdnm+yROfE0A
On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
> On 14/01/16 15:38, Ludovic Desroches wrote:
> > Add the ADC device, and remove the adc_op_clk which is useless since the
> > adc sampling frequency is configured with sysfs.
> >
> > Signed-off-by: Ludovic Desroches <ludovic.desroches-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> I've now applied the driver to the togreg branch of iio.git.
> Should get a pull request out to Greg in about 2 weeks time and it'll
> probably be in linux-next a week after that. I'm assuming all the device
> tree changes will got through the at91 tree and arm-soc etc.
> Shout if you want me to take them through IIO.
>
Ok, I think Nicolas would take this patch.
Regards
Ludovic
> Thanks,
>
> Jonathan
> > ---
> > arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> > 1 file changed, 12 insertions(+), 6 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> > index 3f750f6..46dd430 100644
> > --- a/arch/arm/boot/dts/sama5d2.dtsi
> > +++ b/arch/arm/boot/dts/sama5d2.dtsi
> > @@ -88,12 +88,6 @@
> > #clock-cells = <0>;
> > clock-frequency = <0>;
> > };
> > -
> > - adc_op_clk: adc_op_clk{
> > - compatible = "fixed-clock";
> > - #clock-cells = <0>;
> > - clock-frequency = <1000000>;
> > - };
> > };
> >
> > ns_sram: sram@00200000 {
> > @@ -1085,6 +1079,18 @@
> > status = "disabled";
> > };
> >
> > + adc: adc@fc030000 {
> > + compatible = "atmel,sama5d2-adc";
> > + reg = <0xfc030000 0x100>;
> > + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> > + clocks = <&adc_clk>;
> > + clock-names = "adc_clk";
> > + atmel,min-sample-rate-hz = <200000>;
> > + atmel,max-sample-rate-hz = <20000000>;
> > + atmel,startup-time-ms = <4>;
> > + status = "disabled";
> > + };
> > +
> > pioA: pinctrl@fc038000 {
> > compatible = "atmel,sama5d2-pinctrl";
> > reg = <0xfc038000 0x600>;
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-18 7:59 ` Ludovic Desroches
0 siblings, 0 replies; 38+ messages in thread
From: Ludovic Desroches @ 2016-01-18 7:59 UTC (permalink / raw)
To: linux-arm-kernel
On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
> On 14/01/16 15:38, Ludovic Desroches wrote:
> > Add the ADC device, and remove the adc_op_clk which is useless since the
> > adc sampling frequency is configured with sysfs.
> >
> > Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> I've now applied the driver to the togreg branch of iio.git.
> Should get a pull request out to Greg in about 2 weeks time and it'll
> probably be in linux-next a week after that. I'm assuming all the device
> tree changes will got through the at91 tree and arm-soc etc.
> Shout if you want me to take them through IIO.
>
Ok, I think Nicolas would take this patch.
Regards
Ludovic
> Thanks,
>
> Jonathan
> > ---
> > arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
> > 1 file changed, 12 insertions(+), 6 deletions(-)
> >
> > diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
> > index 3f750f6..46dd430 100644
> > --- a/arch/arm/boot/dts/sama5d2.dtsi
> > +++ b/arch/arm/boot/dts/sama5d2.dtsi
> > @@ -88,12 +88,6 @@
> > #clock-cells = <0>;
> > clock-frequency = <0>;
> > };
> > -
> > - adc_op_clk: adc_op_clk{
> > - compatible = "fixed-clock";
> > - #clock-cells = <0>;
> > - clock-frequency = <1000000>;
> > - };
> > };
> >
> > ns_sram: sram at 00200000 {
> > @@ -1085,6 +1079,18 @@
> > status = "disabled";
> > };
> >
> > + adc: adc at fc030000 {
> > + compatible = "atmel,sama5d2-adc";
> > + reg = <0xfc030000 0x100>;
> > + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
> > + clocks = <&adc_clk>;
> > + clock-names = "adc_clk";
> > + atmel,min-sample-rate-hz = <200000>;
> > + atmel,max-sample-rate-hz = <20000000>;
> > + atmel,startup-time-ms = <4>;
> > + status = "disabled";
> > + };
> > +
> > pioA: pinctrl at fc038000 {
> > compatible = "atmel,sama5d2-pinctrl";
> > reg = <0xfc038000 0x600>;
> >
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
2016-01-18 7:59 ` Ludovic Desroches
(?)
@ 2016-01-18 8:09 ` Nicolas Ferre
-1 siblings, 0 replies; 38+ messages in thread
From: Nicolas Ferre @ 2016-01-18 8:09 UTC (permalink / raw)
To: Jonathan Cameron, alexandre.belloni, linux-iio
Cc: devicetree, linux-kernel, plagnioj, linux-arm-kernel, pmeerw, robh
Le 18/01/2016 08:59, Ludovic Desroches a écrit :
> On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
>> On 14/01/16 15:38, Ludovic Desroches wrote:
>>> Add the ADC device, and remove the adc_op_clk which is useless since the
>>> adc sampling frequency is configured with sysfs.
>>>
>>> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>> I've now applied the driver to the togreg branch of iio.git.
>> Should get a pull request out to Greg in about 2 weeks time and it'll
>> probably be in linux-next a week after that. I'm assuming all the device
>> tree changes will got through the at91 tree and arm-soc etc.
>> Shout if you want me to take them through IIO.
>>
>
> Ok, I think Nicolas would take this patch.
Sure, we'll take them through AT91 then arm-soc trees.
Thanks, bye.
>>> ---
>>> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
>>> 1 file changed, 12 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
>>> index 3f750f6..46dd430 100644
>>> --- a/arch/arm/boot/dts/sama5d2.dtsi
>>> +++ b/arch/arm/boot/dts/sama5d2.dtsi
>>> @@ -88,12 +88,6 @@
>>> #clock-cells = <0>;
>>> clock-frequency = <0>;
>>> };
>>> -
>>> - adc_op_clk: adc_op_clk{
>>> - compatible = "fixed-clock";
>>> - #clock-cells = <0>;
>>> - clock-frequency = <1000000>;
>>> - };
>>> };
>>>
>>> ns_sram: sram@00200000 {
>>> @@ -1085,6 +1079,18 @@
>>> status = "disabled";
>>> };
>>>
>>> + adc: adc@fc030000 {
>>> + compatible = "atmel,sama5d2-adc";
>>> + reg = <0xfc030000 0x100>;
>>> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
>>> + clocks = <&adc_clk>;
>>> + clock-names = "adc_clk";
>>> + atmel,min-sample-rate-hz = <200000>;
>>> + atmel,max-sample-rate-hz = <20000000>;
>>> + atmel,startup-time-ms = <4>;
>>> + status = "disabled";
>>> + };
>>> +
>>> pioA: pinctrl@fc038000 {
>>> compatible = "atmel,sama5d2-pinctrl";
>>> reg = <0xfc038000 0x600>;
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-18 8:09 ` Nicolas Ferre
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas Ferre @ 2016-01-18 8:09 UTC (permalink / raw)
To: Jonathan Cameron, alexandre.belloni, linux-iio
Cc: devicetree, linux-kernel, plagnioj, linux-arm-kernel, pmeerw, robh
Le 18/01/2016 08:59, Ludovic Desroches a écrit :
> On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
>> On 14/01/16 15:38, Ludovic Desroches wrote:
>>> Add the ADC device, and remove the adc_op_clk which is useless since the
>>> adc sampling frequency is configured with sysfs.
>>>
>>> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>> I've now applied the driver to the togreg branch of iio.git.
>> Should get a pull request out to Greg in about 2 weeks time and it'll
>> probably be in linux-next a week after that. I'm assuming all the device
>> tree changes will got through the at91 tree and arm-soc etc.
>> Shout if you want me to take them through IIO.
>>
>
> Ok, I think Nicolas would take this patch.
Sure, we'll take them through AT91 then arm-soc trees.
Thanks, bye.
>>> ---
>>> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
>>> 1 file changed, 12 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
>>> index 3f750f6..46dd430 100644
>>> --- a/arch/arm/boot/dts/sama5d2.dtsi
>>> +++ b/arch/arm/boot/dts/sama5d2.dtsi
>>> @@ -88,12 +88,6 @@
>>> #clock-cells = <0>;
>>> clock-frequency = <0>;
>>> };
>>> -
>>> - adc_op_clk: adc_op_clk{
>>> - compatible = "fixed-clock";
>>> - #clock-cells = <0>;
>>> - clock-frequency = <1000000>;
>>> - };
>>> };
>>>
>>> ns_sram: sram@00200000 {
>>> @@ -1085,6 +1079,18 @@
>>> status = "disabled";
>>> };
>>>
>>> + adc: adc@fc030000 {
>>> + compatible = "atmel,sama5d2-adc";
>>> + reg = <0xfc030000 0x100>;
>>> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
>>> + clocks = <&adc_clk>;
>>> + clock-names = "adc_clk";
>>> + atmel,min-sample-rate-hz = <200000>;
>>> + atmel,max-sample-rate-hz = <20000000>;
>>> + atmel,startup-time-ms = <4>;
>>> + status = "disabled";
>>> + };
>>> +
>>> pioA: pinctrl@fc038000 {
>>> compatible = "atmel,sama5d2-pinctrl";
>>> reg = <0xfc038000 0x600>;
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device
@ 2016-01-18 8:09 ` Nicolas Ferre
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas Ferre @ 2016-01-18 8:09 UTC (permalink / raw)
To: linux-arm-kernel
Le 18/01/2016 08:59, Ludovic Desroches a ?crit :
> On Sat, Jan 16, 2016 at 11:38:30AM +0000, Jonathan Cameron wrote:
>> On 14/01/16 15:38, Ludovic Desroches wrote:
>>> Add the ADC device, and remove the adc_op_clk which is useless since the
>>> adc sampling frequency is configured with sysfs.
>>>
>>> Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>> I've now applied the driver to the togreg branch of iio.git.
>> Should get a pull request out to Greg in about 2 weeks time and it'll
>> probably be in linux-next a week after that. I'm assuming all the device
>> tree changes will got through the at91 tree and arm-soc etc.
>> Shout if you want me to take them through IIO.
>>
>
> Ok, I think Nicolas would take this patch.
Sure, we'll take them through AT91 then arm-soc trees.
Thanks, bye.
>>> ---
>>> arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++------
>>> 1 file changed, 12 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
>>> index 3f750f6..46dd430 100644
>>> --- a/arch/arm/boot/dts/sama5d2.dtsi
>>> +++ b/arch/arm/boot/dts/sama5d2.dtsi
>>> @@ -88,12 +88,6 @@
>>> #clock-cells = <0>;
>>> clock-frequency = <0>;
>>> };
>>> -
>>> - adc_op_clk: adc_op_clk{
>>> - compatible = "fixed-clock";
>>> - #clock-cells = <0>;
>>> - clock-frequency = <1000000>;
>>> - };
>>> };
>>>
>>> ns_sram: sram at 00200000 {
>>> @@ -1085,6 +1079,18 @@
>>> status = "disabled";
>>> };
>>>
>>> + adc: adc at fc030000 {
>>> + compatible = "atmel,sama5d2-adc";
>>> + reg = <0xfc030000 0x100>;
>>> + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
>>> + clocks = <&adc_clk>;
>>> + clock-names = "adc_clk";
>>> + atmel,min-sample-rate-hz = <200000>;
>>> + atmel,max-sample-rate-hz = <20000000>;
>>> + atmel,startup-time-ms = <4>;
>>> + status = "disabled";
>>> + };
>>> +
>>> pioA: pinctrl at fc038000 {
>>> compatible = "atmel,sama5d2-pinctrl";
>>> reg = <0xfc038000 0x600>;
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2016-01-18 8:09 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-14 15:38 [PATCH v3 0/5] Introduce at91_adc8xx driver Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` [PATCH v3 1/5] iio:adc:at91_adc8xx: introduce new atmel adc driver Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-16 11:27 ` Jonathan Cameron
2016-01-16 11:27 ` Jonathan Cameron
2016-01-16 11:27 ` Jonathan Cameron
2016-01-16 11:35 ` Jonathan Cameron
2016-01-16 11:35 ` Jonathan Cameron
2016-01-18 7:55 ` Ludovic Desroches
2016-01-18 7:55 ` Ludovic Desroches
2016-01-18 7:55 ` Ludovic Desroches
2016-01-14 15:38 ` [PATCH v3 2/5] MAINTAINERS: add entry for Atmel ADC 8xx driver Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-15 8:54 ` [RESEND PATCH v3 2/5] MAINTAINERS: add entry for Atmel SAMA5D2 ADC driver Ludovic Desroches
2016-01-15 8:54 ` Ludovic Desroches
2016-01-15 8:54 ` Ludovic Desroches
2016-01-14 15:38 ` [PATCH v3 3/5] ARM: at91/dt: sama5d2: add adc device Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-16 11:38 ` Jonathan Cameron
2016-01-16 11:38 ` Jonathan Cameron
2016-01-16 11:38 ` Jonathan Cameron
2016-01-18 7:59 ` Ludovic Desroches
2016-01-18 7:59 ` Ludovic Desroches
2016-01-18 7:59 ` Ludovic Desroches
2016-01-18 8:09 ` Nicolas Ferre
2016-01-18 8:09 ` Nicolas Ferre
2016-01-18 8:09 ` Nicolas Ferre
2016-01-14 15:38 ` [PATCH v3 4/5] ARM: at91/dt: sama5d2 Xplained: enable the " Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` [PATCH v3 5/5] ARM: at91/defconfig: add sama5d2 adc support in sama5_defconfig Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
2016-01-14 15:38 ` Ludovic Desroches
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.