diff --git a/arch/arc/soc/quark_se_c1000_ss/Kconfig.defconfig b/arch/arc/soc/quark_se_c1000_ss/Kconfig.defconfig index a42c46c7f4790..21403cf2eba17 100644 --- a/arch/arc/soc/quark_se_c1000_ss/Kconfig.defconfig +++ b/arch/arc/soc/quark_se_c1000_ss/Kconfig.defconfig @@ -126,10 +126,8 @@ endif endif # I2C if ADC -config ADC_QMSI_SS +config ADC_DW def_bool y -config ADC_0_IRQ_PRI - default 0 endif if BT_H4 diff --git a/arch/arc/soc/quark_se_c1000_ss/dts.fixup b/arch/arc/soc/quark_se_c1000_ss/dts.fixup index 8d7c477495dc8..3c215e699b85b 100644 --- a/arch/arc/soc/quark_se_c1000_ss/dts.fixup +++ b/arch/arc/soc/quark_se_c1000_ss/dts.fixup @@ -68,4 +68,10 @@ #define CONFIG_GPIO_QMSI_1_IRQ INTEL_QMSI_GPIO_B0800B00_IRQ_0 #define CONFIG_GPIO_QMSI_1_IRQ_PRI INTEL_QMSI_GPIO_B0800B00_IRQ_0_PRIORITY +#define CONFIG_ADC_0_IRQ SNPS_DW_ADC_80015000_IRQ_NORMAL +#define CONFIG_ADC_IRQ_ERR SNPS_DW_ADC_80015000_IRQ_ERROR +#define CONFIG_ADC_0_IRQ_PRI SNPS_DW_ADC_80015000_IRQ_0_PRIORITY +#define CONFIG_ADC_0_NAME SNPS_DW_ADC_80015000_LABEL +#define CONFIG_ADC_0_BASE_ADDRESS SNPS_DW_ADC_80015000_BASE_ADDRESS + /* End of SoC Level DTS fixup file */ diff --git a/arch/arm/soc/atmel_sam/same70/dts.fixup b/arch/arm/soc/atmel_sam/same70/dts.fixup index b8f25469ea1c7..0e2a449cdfb3a 100644 --- a/arch/arm/soc/atmel_sam/same70/dts.fixup +++ b/arch/arm/soc/atmel_sam/same70/dts.fixup @@ -95,10 +95,12 @@ #define CONFIG_ADC_0_IRQ ATMEL_SAM_AFEC_4003C000_IRQ_0 #define CONFIG_ADC_0_IRQ_PRI ATMEL_SAM_AFEC_4003C000_IRQ_0_PRIORITY #define CONFIG_ADC_0_NAME ATMEL_SAM_AFEC_4003C000_LABEL +#define CONFIG_ADC_0_PERIPHERAL_ID ATMEL_SAM_AFEC_4003C000_PERIPHERAL_ID #define CONFIG_ADC_1_BASE_ADDRESS ATMEL_SAM_AFEC_40064000_BASE_ADDRESS #define CONFIG_ADC_1_IRQ ATMEL_SAM_AFEC_40064000_IRQ_0 #define CONFIG_ADC_1_IRQ_PRI ATMEL_SAM_AFEC_40064000_IRQ_0_PRIORITY #define CONFIG_ADC_1_NAME ATMEL_SAM_AFEC_40064000_LABEL +#define CONFIG_ADC_1_PERIPHERAL_ID ATMEL_SAM_AFEC_40064000_PERIPHERAL_ID /* End of SoC Level DTS fixup file */ diff --git a/arch/arm/soc/nordic_nrf/nrf51/dts.fixup b/arch/arm/soc/nordic_nrf/nrf51/dts.fixup index f3c170d2a0da7..19a4a0352d61d 100644 --- a/arch/arm/soc/nordic_nrf/nrf51/dts.fixup +++ b/arch/arm/soc/nordic_nrf/nrf51/dts.fixup @@ -1,6 +1,11 @@ /* SoC level DTS fixup file */ #define CONFIG_NUM_IRQ_PRIO_BITS ARM_V6M_NVIC_E000E100_ARM_NUM_IRQ_PRIORITY_BITS + +#define CONFIG_ADC_0_IRQ NORDIC_NRF_ADC_40007000_IRQ_0 +#define CONFIG_ADC_0_IRQ_PRI NORDIC_NRF_ADC_40007000_IRQ_0_PRIORITY +#define CONFIG_ADC_0_NAME NORDIC_NRF_ADC_40007000_LABEL + #define CONFIG_UART_0_BASE NORDIC_NRF_UART_40002000_BASE_ADDRESS #define CONFIG_UART_0_IRQ_PRI NORDIC_NRF_UART_40002000_IRQ_0_PRIORITY #define CONFIG_UART_0_IRQ_NUM NORDIC_NRF_UART_40002000_IRQ_0 diff --git a/arch/arm/soc/nordic_nrf/nrf52/dts.fixup b/arch/arm/soc/nordic_nrf/nrf52/dts.fixup index 4016dec047e74..c921a68117728 100644 --- a/arch/arm/soc/nordic_nrf/nrf52/dts.fixup +++ b/arch/arm/soc/nordic_nrf/nrf52/dts.fixup @@ -1,6 +1,11 @@ /* SoC level DTS fixup file */ #define CONFIG_NUM_IRQ_PRIO_BITS ARM_V7M_NVIC_E000E100_ARM_NUM_IRQ_PRIORITY_BITS + +#define CONFIG_ADC_0_IRQ NORDIC_NRF_SAADC_40007000_IRQ_0 +#define CONFIG_ADC_0_IRQ_PRI NORDIC_NRF_SAADC_40007000_IRQ_0_PRIORITY +#define CONFIG_ADC_0_NAME NORDIC_NRF_SAADC_40007000_LABEL + #if defined(NORDIC_NRF_UARTE_40002000_BASE_ADDRESS) #define CONFIG_UART_0_BASE NORDIC_NRF_UARTE_40002000_BASE_ADDRESS #define CONFIG_UART_0_IRQ_PRI NORDIC_NRF_UARTE_40002000_IRQ_0_PRIORITY diff --git a/boards/arc/arduino_101_sss/arduino_101_sss.dts b/boards/arc/arduino_101_sss/arduino_101_sss.dts index 2f26717eee505..d28c2f8879ec5 100644 --- a/boards/arc/arduino_101_sss/arduino_101_sss.dts +++ b/boards/arc/arduino_101_sss/arduino_101_sss.dts @@ -44,3 +44,7 @@ &i2c3 { status = "ok"; }; + +&adc0 { + status = "ok"; +}; diff --git a/boards/arc/quark_se_c1000_ss_devboard/quark_se_c1000_ss_devboard.dts b/boards/arc/quark_se_c1000_ss_devboard/quark_se_c1000_ss_devboard.dts index 91ce23be886e3..1bfa1a71956f5 100644 --- a/boards/arc/quark_se_c1000_ss_devboard/quark_se_c1000_ss_devboard.dts +++ b/boards/arc/quark_se_c1000_ss_devboard/quark_se_c1000_ss_devboard.dts @@ -44,3 +44,7 @@ &i2c3 { status = "ok"; }; + +&adc0 { + status = "ok"; +}; diff --git a/boards/arm/nrf51_pca10028/nrf51_pca10028.dts b/boards/arm/nrf51_pca10028/nrf51_pca10028.dts index f0a77d03441f8..13f1903228f74 100644 --- a/boards/arm/nrf51_pca10028/nrf51_pca10028.dts +++ b/boards/arm/nrf51_pca10028/nrf51_pca10028.dts @@ -30,6 +30,10 @@ status ="ok"; }; +&adc { + status ="ok"; +}; + &uart0 { current-speed = <115200>; status = "ok"; diff --git a/boards/arm/nrf51_pca10028/nrf51_pca10028.yaml b/boards/arm/nrf51_pca10028/nrf51_pca10028.yaml index 2224f6a31fb9d..b8fede2aeec8d 100644 --- a/boards/arm/nrf51_pca10028/nrf51_pca10028.yaml +++ b/boards/arm/nrf51_pca10028/nrf51_pca10028.yaml @@ -7,5 +7,6 @@ toolchain: - gnuarmemb ram: 32 supported: + - adc - ble - nvs diff --git a/boards/arm/nrf52810_pca10040/nrf52810_pca10040.dts b/boards/arm/nrf52810_pca10040/nrf52810_pca10040.dts index 15cd471b2fed8..2374d641417b5 100644 --- a/boards/arm/nrf52810_pca10040/nrf52810_pca10040.dts +++ b/boards/arm/nrf52810_pca10040/nrf52810_pca10040.dts @@ -66,6 +66,10 @@ }; }; +&adc { + status = "ok"; +}; + &gpiote { status ="ok"; }; diff --git a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts index cf76b7aedaf73..1ef89fef8ff4e 100644 --- a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts +++ b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts @@ -22,6 +22,10 @@ }; }; +&adc { + status ="ok"; +}; + &gpiote { status ="ok"; }; diff --git a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.yaml b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.yaml index 96b6b31ed8315..9dfdca9c0de5b 100644 --- a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.yaml +++ b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.yaml @@ -6,6 +6,7 @@ toolchain: - zephyr - gnuarmemb supported: + - adc - usb_device - ble - ieee802154 diff --git a/boards/arm/nrf52_pca10040/nrf52_pca10040.dts b/boards/arm/nrf52_pca10040/nrf52_pca10040.dts index 4c98a86836355..984f75d5e04aa 100644 --- a/boards/arm/nrf52_pca10040/nrf52_pca10040.dts +++ b/boards/arm/nrf52_pca10040/nrf52_pca10040.dts @@ -23,6 +23,10 @@ }; }; +&adc { + status ="ok"; +}; + &gpiote { status ="ok"; }; diff --git a/boards/arm/nrf52_pca10040/nrf52_pca10040.yaml b/boards/arm/nrf52_pca10040/nrf52_pca10040.yaml index 69f545375f05a..7beaeee516998 100644 --- a/boards/arm/nrf52_pca10040/nrf52_pca10040.yaml +++ b/boards/arm/nrf52_pca10040/nrf52_pca10040.yaml @@ -8,4 +8,5 @@ toolchain: ram: 64 flash: 512 supported: + - adc - nvs diff --git a/boards/arm/nrf52_pca20020/nrf52_pca20020.dts b/boards/arm/nrf52_pca20020/nrf52_pca20020.dts index 41143a85c858e..d8197b27ef7d0 100644 --- a/boards/arm/nrf52_pca20020/nrf52_pca20020.dts +++ b/boards/arm/nrf52_pca20020/nrf52_pca20020.dts @@ -21,6 +21,10 @@ }; }; +&adc { + status = "ok"; +}; + &gpiote { status = "ok"; }; diff --git a/boards/arm/sam_e70_xplained/doc/sam_e70_xplained.rst b/boards/arm/sam_e70_xplained/doc/sam_e70_xplained.rst index c1bf6166b058b..6cbd63f1d1cea 100644 --- a/boards/arm/sam_e70_xplained/doc/sam_e70_xplained.rst +++ b/boards/arm/sam_e70_xplained/doc/sam_e70_xplained.rst @@ -56,6 +56,8 @@ features: +-----------+------------+-------------------------------------+ | GPIO | on-chip | gpio | +-----------+------------+-------------------------------------+ +| ADC | on-chip | ADC via AFEC | ++-----------+------------+-------------------------------------+ Other hardware features are not currently supported by Zephyr. diff --git a/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml b/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml index 5081c27112e52..178547051ac89 100644 --- a/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml +++ b/boards/arm/sam_e70_xplained/sam_e70_xplained.yaml @@ -7,3 +7,4 @@ toolchain: - gnuarmemb supported: - netif:eth + - adc diff --git a/boards/x86/quark_d2000_crb/quark_d2000_crb.yaml b/boards/x86/quark_d2000_crb/quark_d2000_crb.yaml index a63eeb8ac54e2..8f5e4a4ad7acb 100644 --- a/boards/x86/quark_d2000_crb/quark_d2000_crb.yaml +++ b/boards/x86/quark_d2000_crb/quark_d2000_crb.yaml @@ -9,7 +9,6 @@ toolchain: ram: 8 flash: 32 supported: - - adc - i2c - spi - gpio diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 787145630d39f..fc371eb1da86f 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -1,10 +1,9 @@ zephyr_library() -zephyr_library_sources_ifdef(CONFIG_ADC_DW adc_dw.c) -zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c) -zephyr_library_sources_ifdef(CONFIG_ADC_QMSI adc_qmsi.c) -zephyr_library_sources_ifdef(CONFIG_ADC_QMSI_SS adc_qmsi_ss.c) -zephyr_library_sources_ifdef(CONFIG_ADC_SAM_AFEC adc_sam_afec.c) -zephyr_library_sources_ifdef(CONFIG_ADC_TI_ADC108S102 adc_ti_adc108s102.c) - -zephyr_library_sources_ifdef(CONFIG_USERSPACE adc_handlers.c) +zephyr_library_sources_ifdef(CONFIG_ADC_DW adc_dw.c) +zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c) +zephyr_library_sources_ifdef(CONFIG_ADC_QMSI adc_qmsi.c) +zephyr_library_sources_ifdef(CONFIG_ADC_SAM_AFEC adc_sam_afec.c) +zephyr_library_sources_ifdef(CONFIG_ADC_TI_ADC108S102 adc_ti_adc108s102.c) +zephyr_library_sources_ifdef(CONFIG_ADC_NRFX_ADC adc_nrfx_adc.c) +zephyr_library_sources_ifdef(CONFIG_ADC_NRFX_SAADC adc_nrfx_saadc.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 5a72c1dbeb2b2..a7c4d90be9ada 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -11,11 +11,26 @@ # menuconfig ADC bool "ADC drivers" + # All platforms that implement the ADC driver are now required to + # provide relevant DTS entries. + select HAS_DTS_ADC help - Enable ADC (Analog to Digital Converter) driver configuration + Enable ADC (Analog to Digital Converter) driver configuration. if ADC +# By selecting or not this option particular ADC drivers indicate if it is +# required to explicitly specify analog inputs when configuring channels or +# just the channel identifier is sufficient. +config ADC_CONFIGURABLE_INPUTS + bool + +config ADC_ASYNC + bool "Enable asynchronous call support" + select POLL + help + This option enables the asynchronous API calls. + config SYS_LOG_ADC_LEVEL int "ADC drivers log level" depends on SYS_LOG @@ -73,6 +88,8 @@ source "drivers/adc/Kconfig.dw" source "drivers/adc/Kconfig.mcux" +source "drivers/adc/Kconfig.nrfx" + source "drivers/adc/Kconfig.qmsi" source "drivers/adc/Kconfig.sam_afec" diff --git a/drivers/adc/Kconfig.dw b/drivers/adc/Kconfig.dw index 370ef7e575841..1c235d91f3fb6 100644 --- a/drivers/adc/Kconfig.dw +++ b/drivers/adc/Kconfig.dw @@ -25,15 +25,6 @@ config ADC_DW_CALIBRATION If disabled, the ADC will require the application/system-integrator to provide a calibration method. -config ADC_DW_DUMMY_CONVERSION - bool "Enable dummy conversion" - default y - help - After awaking from low power state a dummy - conversion must be performed and discarded. - If disabled the user will have to discard the first - sample after a resume from a low power state. - choice prompt "Output Mode" default ADC_DW_SERIAL @@ -48,20 +39,6 @@ config ADC_DW_PARALLEL endchoice -choice - prompt "Sequence Mode" - default ADC_DW_SINGLESHOT - help - ADC sequence mode - single run/repetitive - -config ADC_DW_SINGLESHOT - bool "Single Ended" - -config ADC_DW_REPETITIVE - bool "Differential" - -endchoice - choice prompt "Capture Mode" default ADC_DW_RISING_EDGE @@ -77,20 +54,6 @@ config ADC_DW_FALLING_EDGE endchoice -config ADC_DW_SAMPLE_WIDTH - int "Sample Width" - default 31 - help - Defines ADC device data sample width (resolution): - - - 0 = 6 bits resolution - - - 1 = 8 bits resolution - - - 2 = 10 bits resolution - - - 3 = 12 bits resolution - config ADC_DW_SERIAL_DELAY int "Serial Delay" default 1 diff --git a/drivers/adc/Kconfig.mcux b/drivers/adc/Kconfig.mcux index 62704c256549f..a7a714b828476 100644 --- a/drivers/adc/Kconfig.mcux +++ b/drivers/adc/Kconfig.mcux @@ -9,6 +9,5 @@ config ADC_MCUX_ADC16 bool "MCUX ADC16 driver" depends on HAS_MCUX_ADC16 - select HAS_DTS_ADC help Enable the MCUX ADC16 driver. diff --git a/drivers/adc/Kconfig.nrfx b/drivers/adc/Kconfig.nrfx new file mode 100644 index 0000000000000..304b01c212ab7 --- /dev/null +++ b/drivers/adc/Kconfig.nrfx @@ -0,0 +1,32 @@ +# Kconfig - ADC configuration options + +# +# Copyright (c) 2018, Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +config ADC_NRFX_ADC + bool "nRF ADC nrfx driver" + depends on HAS_HW_NRF_ADC + select NRFX_ADC + select ADC_CONFIGURABLE_INPUTS + help + Enable support for nrfx ADC driver for nRF51 MCU series. + +config ADC_NRFX_ADC_CHANNEL_COUNT + int "Number of ADC channels" + depends on ADC_NRFX_ADC + range 1 8 + default 1 + help + Number of ADC channels to be supported by the driver. Each channel + needs a dedicated structure in RAM that stores the ADC settings + to be used when sampling this channel. + +config ADC_NRFX_SAADC + bool "nRF SAADC nrfx driver" + depends on HAS_HW_NRF_SAADC + select ADC_CONFIGURABLE_INPUTS + help + Enable support for nrfx SAADC driver for nRF52 MCU series. diff --git a/drivers/adc/Kconfig.qmsi b/drivers/adc/Kconfig.qmsi index 98d9592ac703b..c9debde634603 100644 --- a/drivers/adc/Kconfig.qmsi +++ b/drivers/adc/Kconfig.qmsi @@ -13,14 +13,7 @@ menuconfig ADC_QMSI help Enable the driver implementation of the QMSI ADC IP. -menuconfig ADC_QMSI_SS - bool "QMSI ADC Driver for the Sensor Subsystem" - depends on QMSI - select ADC_0 - help - Enable the driver implementation of the QMSI ADC IP. - -if ADC_QMSI || ADC_QMSI_SS +if ADC_QMSI choice prompt "Capturing Mode" @@ -59,7 +52,6 @@ config ADC_QMSI_SERIAL_DELAY the serial output is delayed after the conversion has started. -if ADC_QMSI config ADC_QMSI_SAMPLE_WIDTH int "Sample Width" default 3 @@ -73,22 +65,5 @@ config ADC_QMSI_SAMPLE_WIDTH - 2 = 10 bits resolution - 3 = 12 bits resolution -endif - -if ADC_QMSI_SS -config ADC_QMSI_SAMPLE_WIDTH - int "Sample Width" - default 11 - help - Defines ADC device data sample width (resolution): - - - 5 = 6 bits resolution - - - 7 = 8 bits resolution - - - 9 = 10 bits resolution - - - 11 = 12 bits resolution -endif -endif # ADC_QMSI || ADC_QMSI_SS +endif # ADC_QMSI diff --git a/drivers/adc/Kconfig.sam_afec b/drivers/adc/Kconfig.sam_afec index 18396a0f31467..d6100161c1a6d 100644 --- a/drivers/adc/Kconfig.sam_afec +++ b/drivers/adc/Kconfig.sam_afec @@ -9,7 +9,6 @@ menuconfig ADC_SAM_AFEC bool "SAM ADC Driver" depends on SOC_FAMILY_SAM - select HAS_DTS_ADC help Enable Atmel SAM MCU Family Analog-to-Digital Converter (ADC) driver based on AFEC module. diff --git a/drivers/adc/adc_context.h b/drivers/adc/adc_context.h new file mode 100644 index 0000000000000..bb7751759255a --- /dev/null +++ b/drivers/adc/adc_context.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ADC_CONTEXT_H__ +#define __ADC_CONTEXT_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct adc_context; + +/* + * Each driver should provide implementations of the following two functions: + * - adc_context_start_sampling() that will be called when a sampling (of one + * or more channels, depending on the realized sequence) is to be started + * - adc_context_update_buffer_pointer() that will be called when the sample + * buffer pointer should be prepared for writing of next sampling results, + * the "repeat_sampling" parameter indicates if the results should be written + * in the same place as before (when true) or as consecutive ones (otherwise). + */ +static void adc_context_start_sampling(struct adc_context *ctx); +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat_sampling); +/* + * If a given driver uses some dedicated hardware timer to trigger consecutive + * samplings, it should implement also the following two functions. Otherwise, + * it should define the ADC_CONTEXT_USES_KERNEL_TIMER macro to enable parts of + * this module that utilize a standard kernel timer. + */ +static void adc_context_enable_timer(struct adc_context *ctx); +static void adc_context_disable_timer(struct adc_context *ctx); + + +struct adc_context { + atomic_t sampling_requested; +#ifdef ADC_CONTEXT_USES_KERNEL_TIMER + struct k_timer timer; +#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ + + struct k_sem lock; + struct k_sem sync; + int status; + +#ifdef CONFIG_ADC_ASYNC + struct k_poll_signal *signal; + bool asynchronous; +#endif /* CONFIG_ADC_ASYNC */ + + const struct adc_sequence *sequence; + u16_t sampling_index; +}; + +#ifdef ADC_CONTEXT_USES_KERNEL_TIMER +#define ADC_CONTEXT_INIT_TIMER(_data, _ctx_name) \ + ._ctx_name.timer = _K_TIMER_INITIALIZER(_data._ctx_name.timer, \ + adc_context_on_timer_expired, \ + NULL) +#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ + +#define ADC_CONTEXT_INIT_LOCK(_data, _ctx_name) \ + ._ctx_name.lock = _K_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1) + +#define ADC_CONTEXT_INIT_SYNC(_data, _ctx_name) \ + ._ctx_name.sync = _K_SEM_INITIALIZER(_data._ctx_name.sync, 0, 1) + + +static inline void adc_context_request_next_sampling(struct adc_context *ctx) +{ + if (atomic_inc(&ctx->sampling_requested) == 0) { + adc_context_start_sampling(ctx); + } else { + /* + * If a sampling was already requested and was not finished yet, + * do not start another one from here, this will be done from + * adc_context_on_sampling_done() after the current sampling is + * complete. Instead, note this fact, and inform the user about + * it after the sequence is done. + */ + ctx->status = -EIO; + } +} + +#ifdef ADC_CONTEXT_USES_KERNEL_TIMER +static inline void adc_context_enable_timer(struct adc_context *ctx) +{ + u32_t interval_us = ctx->sequence->options->interval_us; + u32_t interval_ms = ceiling_fraction(interval_us, 1000UL); + + k_timer_start(&ctx->timer, 0, interval_ms); +} + +static inline void adc_context_disable_timer(struct adc_context *ctx) +{ + k_timer_stop(&ctx->timer); +} + +static void adc_context_on_timer_expired(struct k_timer *timer_id) +{ + struct adc_context *ctx = + CONTAINER_OF(timer_id, struct adc_context, timer); + + adc_context_request_next_sampling(ctx); +} +#endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ + + +static inline void adc_context_lock(struct adc_context *ctx, + bool asynchronous, + struct k_poll_signal *signal) +{ + k_sem_take(&ctx->lock, K_FOREVER); + +#ifdef CONFIG_ADC_ASYNC + ctx->asynchronous = asynchronous; + ctx->signal = signal; +#endif /* CONFIG_ADC_ASYNC */ +} + +static inline void adc_context_release(struct adc_context *ctx, int status) +{ +#ifdef CONFIG_ADC_ASYNC + if (ctx->asynchronous && (status == 0)) { + return; + } +#endif /* CONFIG_ADC_ASYNC */ + + k_sem_give(&ctx->lock); +} + +static inline void adc_context_unlock_unconditionally(struct adc_context *ctx) +{ + if (!k_sem_count_get(&ctx->lock)) { + k_sem_give(&ctx->lock); + } +} + +static inline int adc_context_wait_for_completion(struct adc_context *ctx) +{ +#ifdef CONFIG_ADC_ASYNC + if (ctx->asynchronous) { + return 0; + } +#endif /* CONFIG_ADC_ASYNC */ + + k_sem_take(&ctx->sync, K_FOREVER); + return ctx->status; +} + +static inline void adc_context_complete(struct adc_context *ctx, int status) +{ +#ifdef CONFIG_ADC_ASYNC + if (ctx->asynchronous) { + if (ctx->signal) { + k_poll_signal(ctx->signal, status); + } + + k_sem_give(&ctx->lock); + return; + } +#endif /* CONFIG_ADC_ASYNC */ + + /* + * Override the status only when an error is signaled to this function. + * Please note that adc_context_request_next_sampling() might have set + * this field. + */ + if (status != 0) { + ctx->status = status; + } + k_sem_give(&ctx->sync); +} + +static inline void adc_context_start_read(struct adc_context *ctx, + const struct adc_sequence *sequence) +{ + ctx->sequence = sequence; + ctx->status = 0; + + if (ctx->sequence->options) { + ctx->sampling_index = 0; + + if (ctx->sequence->options->interval_us != 0) { + atomic_set(&ctx->sampling_requested, 0); + adc_context_enable_timer(ctx); + return; + } + } + + adc_context_start_sampling(ctx); +} + +/* + * This function should be called after a sampling (of one or more channels, + * depending on the realized sequence) is done. It calls the defined callback + * function if required and takes further actions accordingly. + */ +static inline void adc_context_on_sampling_done(struct adc_context *ctx, + struct device *dev) +{ + if (ctx->sequence->options) { + adc_sequence_callback callback = + ctx->sequence->options->callback; + enum adc_action action; + bool finish = false; + bool repeat = false; + + if (callback) { + action = callback(dev, + ctx->sequence, + ctx->sampling_index); + } else { + action = ADC_ACTION_CONTINUE; + } + + switch (action) { + case ADC_ACTION_REPEAT: + repeat = true; + break; + case ADC_ACTION_FINISH: + finish = true; + break; + default: /* ADC_ACTION_CONTINUE */ + if (ctx->sampling_index < + ctx->sequence->options->extra_samplings) { + ++ctx->sampling_index; + } else { + finish = true; + } + } + + if (!finish) { + adc_context_update_buffer_pointer(ctx, repeat); + + /* + * Immediately start the next sampling if working with + * a zero interval or if the timer expired again while + * the current sampling was in progress. + */ + if (ctx->sequence->options->interval_us == 0) { + adc_context_start_sampling(ctx); + } else if (atomic_dec(&ctx->sampling_requested) > 1) { + adc_context_start_sampling(ctx); + } + + return; + } + + if (ctx->sequence->options->interval_us != 0) { + adc_context_disable_timer(ctx); + } + } + + adc_context_complete(ctx, 0); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __ADC_CONTEXT_H__ */ diff --git a/drivers/adc/adc_dw.c b/drivers/adc/adc_dw.c index 295aca1a488fa..f2e3212428460 100644 --- a/drivers/adc/adc_dw.c +++ b/drivers/adc/adc_dw.c @@ -15,9 +15,14 @@ #include #include #include + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" #include "adc_dw.h" +#include #define ADC_CLOCK_GATE (1 << 31) +#define ADC_DEEP_POWER_DOWN 0x01 #define ADC_POWER_DOWN 0x01 #define ADC_STANDBY 0x02 #define ADC_NORMAL_WITH_CALIB 0x03 @@ -51,6 +56,17 @@ #endif static void adc_config_irq(void); + +struct adc_info adc_info_dev = { + ADC_CONTEXT_INIT_TIMER(adc_info_dev, ctx), + ADC_CONTEXT_INIT_LOCK(adc_info_dev, ctx), + ADC_CONTEXT_INIT_SYNC(adc_info_dev, ctx), + .state = ADC_STATE_IDLE, +#ifdef CONFIG_ADC_DW_CALIBRATION + .calibration_value = ADC_NONE_CALIBRATION, +#endif +}; + #ifdef CONFIG_ADC_DW_CALIBRATION static void calibration_command(u8_t command) { @@ -66,7 +82,7 @@ static void calibration_command(u8_t command) /*Poll waiting for command*/ do { reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - } while ((reg_value & 0x10) == 0); + } while ((reg_value & BIT(4)) == 0); /*Clear Calibration Request*/ reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); @@ -83,7 +99,7 @@ static void adc_goto_normal_mode(struct device *dev) reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - if (((reg_value & 0xE) >> 1) != ADC_NORMAL_WITH_CALIB) { + if ((reg_value & ADC_MODE_MASK) != ADC_NORMAL_WITH_CALIB) { state = irq_lock(); /*Request Normal With Calibration Mode*/ @@ -96,7 +112,7 @@ static void adc_goto_normal_mode(struct device *dev) /*Poll waiting for normal mode*/ do { reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - } while ((reg_value & 0x1) == 0); + } while ((reg_value & BIT(3)) == 0); if (info->calibration_value == ADC_NONE_CALIBRATION) { /*Reset Calibration*/ @@ -123,10 +139,9 @@ static void adc_goto_normal_mode(struct device *dev) u32_t state; ARG_UNUSED(dev); - reg_value = sys_in32( - PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR); + reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - if (((reg_value & 0xE) >> 1) == ADC_NORMAL_WO_CALIB) { + if ((reg_value & ADC_MODE_MASK) == ADC_NORMAL_WO_CALIB) { state = irq_lock(); /*Request Power Down*/ reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); @@ -139,7 +154,7 @@ static void adc_goto_normal_mode(struct device *dev) do { reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - } while ((reg_value & 0x1) == 0); + } while ((reg_value & BIT(3)) == 0); } /*Request Normal With Calibration Mode*/ @@ -153,31 +168,39 @@ static void adc_goto_normal_mode(struct device *dev) /*Poll waiting for normal mode*/ do { reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - } while ((reg_value & 0x1) == 0); + } while ((reg_value & BIT(3)) == 0); } #endif -static void adc_goto_deep_power_down(void) +static int set_resolution(struct device *dev, + const struct adc_sequence *sequence) { - u32_t reg_value; - u32_t state; - - reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - if ((reg_value & 0xE >> 1) != 0) { - state = irq_lock(); - - reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); + u32_t tmp_val; + const struct adc_config *config = dev->config->config_info; + u32_t adc_base = config->reg_base; - reg_value &= ~(ADC_MODE_MASK); + tmp_val = sys_in32(adc_base + ADC_SET); + tmp_val &= ~FIVE_BITS_SET; + + switch (sequence->resolution) { + case 6: + break; + case 8: + tmp_val |= 1 & FIVE_BITS_SET; + break; + case 10: + tmp_val |= 2 & FIVE_BITS_SET; + break; + case 12: + tmp_val |= 3 & FIVE_BITS_SET; + break; + default: + return -EINVAL; + } - reg_value |= 0 | ADC_CLOCK_GATE; - sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0); + sys_out32(tmp_val, adc_base + ADC_SET); - irq_unlock(state); - do { - reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); - } while ((reg_value & 0x1) == 0); - } + return 0; } static void adc_dw_enable(struct device *dev) @@ -200,43 +223,125 @@ static void adc_dw_enable(struct device *dev) info->state = ADC_STATE_IDLE; } -static void adc_dw_disable(struct device *dev) +/* Implementation of the ADC driver API function: adc_channel_setup. */ +static int adc_dw_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) { - u32_t saved; + u8_t channel_id = channel_cfg->channel_id; struct adc_info *info = dev->driver_data; - const struct adc_config *config = dev->config->config_info; - u32_t adc_base = config->reg_base; - sys_out32(ADC_INT_DSB|ENABLE_ADC, adc_base + ADC_CTRL); - adc_goto_deep_power_down(); - sys_out32(ADC_INT_DSB|ADC_SEQ_PTR_RST, adc_base + ADC_CTRL); + if (channel_id >= DW_CHANNEL_COUNT) { + SYS_LOG_ERR("Invalid channel id"); + return -EINVAL; + } - saved = irq_lock(); + if (channel_cfg->gain != ADC_GAIN_1) { + SYS_LOG_ERR("Invalid channel gain"); + return -EINVAL; + } - sys_out32(sys_in32(adc_base + ADC_SET)|ADC_FLUSH_RX, adc_base + ADC_SET); - irq_unlock(saved); + if (channel_cfg->reference != ADC_REF_INTERNAL) { + SYS_LOG_ERR("Invalid channel reference"); + return -EINVAL; + } - info->state = ADC_STATE_DISABLED; + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + SYS_LOG_ERR("Invalid channel acquisition time"); + return -EINVAL; + } + + if (info->state != ADC_STATE_IDLE) { + SYS_LOG_ERR("ADC is busy or in error state"); + return -EAGAIN; + } + + info->active_channels |= 1 << channel_id; + return 0; } -static int adc_dw_read_request(struct device *dev, struct adc_seq_table *seq_tbl) +static int adc_dw_read_request(struct device *dev, + const struct adc_sequence *seq_tbl) { - u32_t i; - u32_t ctrl; - u32_t tmp_val; - u32_t num_iters; - u32_t saved; - struct adc_seq_entry *entry; struct adc_info *info = dev->driver_data; - const struct adc_config *config = dev->config->config_info; - u32_t adc_base = config->reg_base; + int error = 0; + u32_t saved; - if (info->state != ADC_STATE_IDLE) { - return 1; + /*hardware requires minimum 10 us delay between consecutive samples*/ + if (seq_tbl->options && + seq_tbl->options->extra_samplings && + seq_tbl->options->interval_us < 10) { + return -EINVAL; + } + + info->channels = seq_tbl->channels & info->active_channels; + + if (seq_tbl->channels != info->channels) { + return -EINVAL; + } + + error = set_resolution(dev, seq_tbl); + if (error) { + return error; } saved = irq_lock(); - info->seq_size = seq_tbl->num_entries; + info->entries = seq_tbl; + info->buffer = (u16_t *)seq_tbl->buffer; + + if (seq_tbl->options) { + info->seq_size = seq_tbl->options->extra_samplings + 1; + } else { + info->seq_size = 1; + } + + info->state = ADC_STATE_SAMPLING; + irq_unlock(saved); + + adc_context_start_read(&info->ctx, seq_tbl); + error = adc_context_wait_for_completion(&info->ctx); + adc_context_release(&info->ctx, error); + + if (info->state == ADC_STATE_ERROR) { + info->state = ADC_STATE_IDLE; + return -EIO; + } + + return 0; +} + +static int adc_dw_read(struct device *dev, const struct adc_sequence *seq_tbl) +{ + struct adc_info *info = dev->driver_data; + int ret; + + adc_context_lock(&info->ctx, false, NULL); + + ret = adc_dw_read_request(dev, seq_tbl); + return ret; +} + +#ifdef CONFIG_ADC_ASYNC +/* Implementation of the ADC driver API function: adc_read_async. */ +static int adc_dw_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + struct adc_info *info = dev->driver_data; + + adc_context_lock(&info->ctx, true, async); + return adc_dw_read_request(dev, sequence); +} +#endif + +static void adc_dw_start_conversion(struct device *dev) +{ + struct adc_info *info = dev->driver_data; + const struct adc_config *config = info->dev->config->config_info; + const struct adc_sequence *entry = info->ctx.sequence; + u32_t adc_base = config->reg_base; + u32_t ctrl, tmp_val, interval_us = 0; + + info->channel_id = find_lsb_set(info->channels) - 1; ctrl = sys_in32(adc_base + ADC_CTRL); ctrl |= ADC_SEQ_PTR_RST; @@ -244,72 +349,41 @@ static int adc_dw_read_request(struct device *dev, struct adc_seq_table *seq_tbl tmp_val = sys_in32(adc_base + ADC_SET); tmp_val &= ADC_SEQ_SIZE_SET_MASK; - tmp_val |= (((seq_tbl->num_entries - 1) & SIX_BITS_SET) - << SEQ_ENTRIES_POS); - tmp_val |= ((seq_tbl->num_entries - 1) << THRESHOLD_POS); sys_out32(tmp_val, adc_base + ADC_SET); - irq_unlock(saved); - - num_iters = seq_tbl->num_entries/2; - - for (i = 0, entry = seq_tbl->entries; - i < num_iters; i++, entry += 2) { - tmp_val = ((entry[1].sampling_delay & ELEVEN_BITS_SET) - << SEQ_DELAY_ODD_POS); - tmp_val |= ((entry[1].channel_id & FIVE_BITS_SET) - << SEQ_MUX_ODD_POS); - tmp_val |= ((entry[0].sampling_delay & ELEVEN_BITS_SET) - << SEQ_DELAY_EVEN_POS); - tmp_val |= (entry[0].channel_id & FIVE_BITS_SET); - sys_out32(tmp_val, adc_base + ADC_SEQ); + if (entry->options) { + interval_us = entry->options->interval_us; } - - if ((seq_tbl->num_entries % 2) != 0) { - tmp_val = ((entry[0].sampling_delay & ELEVEN_BITS_SET) + tmp_val = ((interval_us & ELEVEN_BITS_SET) << SEQ_DELAY_EVEN_POS); - tmp_val |= (entry[0].channel_id & FIVE_BITS_SET); - sys_out32(tmp_val, adc_base + ADC_SEQ); - } + tmp_val |= (info->channel_id & FIVE_BITS_SET); + sys_out32(tmp_val, adc_base + ADC_SEQ); sys_out32(ctrl | ADC_SEQ_PTR_RST, adc_base + ADC_CTRL); - info->entries = seq_tbl->entries; -#ifdef CONFIG_ADC_DW_REPETITIVE - memset(info->index, 0, seq_tbl->num_entries); -#endif - info->state = ADC_STATE_SAMPLING; sys_out32(START_ADC_SEQ, adc_base + ADC_CTRL); +} - k_sem_take(&info->device_sync_sem, K_FOREVER); +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_info *info = CONTAINER_OF(ctx, struct adc_info, ctx); - if (info->state == ADC_STATE_ERROR) { - info->state = ADC_STATE_IDLE; - return -EIO; - } + info->channels = ctx->sequence->channels; - return 0; + adc_dw_start_conversion(info->dev); } -static int adc_dw_read(struct device *dev, struct adc_seq_table *seq_tbl) +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat) { - struct adc_info *info = dev->driver_data; + struct adc_info *info = CONTAINER_OF(ctx, struct adc_info, ctx); + const struct adc_sequence *entry = ctx->sequence; -#ifdef CONFIG_ADC_DW_DUMMY_CONVERSION - if (info->dummy_conversion == ADC_NONE_DUMMY) { - adc_dw_read_request(dev, seq_tbl); - info->dummy_conversion = ADC_DONE_DUMMY; + if (repeat) { + info->buffer = (u16_t *)entry->buffer; } -#endif - return adc_dw_read_request(dev, seq_tbl); } -static struct adc_driver_api api_funcs = { - .enable = adc_dw_enable, - .disable = adc_dw_disable, - .read = adc_dw_read, -}; - int adc_dw_init(struct device *dev) { u32_t tmp_val; @@ -322,12 +396,11 @@ int adc_dw_init(struct device *dev) tmp_val = sys_in32(adc_base + ADC_SET); tmp_val &= ADC_CONFIG_SET_MASK; - val = (config->sample_width) & FIVE_BITS_SET; - val &= ~(1 << INPUT_MODE_POS); - val |= ((config->capture_mode & ONE_BIT_SET) << CAPTURE_MODE_POS); + val = ((config->capture_mode & ONE_BIT_SET) << CAPTURE_MODE_POS); val |= ((config->out_mode & ONE_BIT_SET) << OUTPUT_MODE_POS); val |= ((config->serial_dly & FIVE_BITS_SET) << SERIAL_DELAY_POS); val |= ((config->seq_mode & ONE_BIT_SET) << SEQUENCE_MODE_POS); + val &= ~(1 << INPUT_MODE_POS); sys_out32(tmp_val|val, adc_base + ADC_SET); sys_out32(config->clock_ratio & ADC_CLK_RATIO_MASK, @@ -338,154 +411,91 @@ int adc_dw_init(struct device *dev) config->config_func(); - k_sem_init(&info->device_sync_sem, 0, UINT_MAX); - int_unmask(config->reg_irq_mask); int_unmask(config->reg_err_mask); + info->dev = dev; + + adc_dw_enable(dev); + adc_context_unlock_unconditionally(&info->ctx); return 0; } -#ifdef CONFIG_ADC_DW_SINGLESHOT static void adc_dw_rx_isr(void *arg) { struct device *dev = (struct device *)arg; - struct device_config *dev_config = dev->config; - const struct adc_config *config = dev_config->config_info; struct adc_info *info = dev->driver_data; + const struct adc_config *config = dev->config->config_info; u32_t adc_base = config->reg_base; - struct adc_seq_entry *entries = info->entries; u32_t reg_val; - u32_t seq_index; - for (seq_index = 0; seq_index < info->seq_size; seq_index++) { - u32_t *adc_buffer; - - reg_val = sys_in32(adc_base + ADC_SET); - sys_out32(reg_val|ADC_POP_SAMPLE, adc_base + ADC_SET); - adc_buffer = (u32_t *)entries[seq_index].buffer; - *adc_buffer = sys_in32(adc_base + ADC_SAMPLE); - } + reg_val = sys_in32(adc_base + ADC_SET); + sys_out32(reg_val|ADC_POP_SAMPLE, adc_base + ADC_SET); + *info->buffer++ = sys_in32(adc_base + ADC_SAMPLE); /*Resume ADC state to continue new conversions*/ sys_out32(RESUME_ADC_CAPTURE, adc_base + ADC_CTRL); reg_val = sys_in32(adc_base + ADC_SET); sys_out32(reg_val | ADC_FLUSH_RX, adc_base + ADC_SET); - info->state = ADC_STATE_IDLE; /*Clear data A register*/ reg_val = sys_in32(adc_base + ADC_CTRL); sys_out32(reg_val | ADC_CLR_DATA_A, adc_base + ADC_CTRL); - k_sem_give(&info->device_sync_sem); -} -#else /*CONFIG_ADC_DW_REPETITIVE*/ -static void adc_dw_rx_isr(void *arg) -{ - struct device *dev = (struct device *)arg; - struct device_config *dev_config = dev->config; - const struct adc_config *config = dev_config->config_info; - struct adc_info *info = dev->driver_data; - u32_t adc_base = config->reg_base; - struct adc_seq_entry *entries = info->entries; - u32_t reg_val; - u32_t sequence_index; - u8_t full_buffer_flag = 0; - - for (sequence_index = 0; sequence_index < info->seq_size; sequence_index++) { - u32_t *adc_buffer; - u32_t repetitive_index; - - repetitive_index = info->index[sequence_index]; - /*API array is 8 bits array but ADC reads blocks of 32 bits with every sample.*/ - if (repetitive_index >= (entries[sequence_index].buffer_length >> 2)) { - full_buffer_flag = 1; - continue; - } - - reg_val = sys_in32(adc_base + ADC_SET); - sys_out32(reg_val|ADC_POP_SAMPLE, adc_base + ADC_SET); - adc_buffer = (u32_t *)entries[sequence_index].buffer; - adc_buffer[repetitive_index] = sys_in32(adc_base + ADC_SAMPLE); - repetitive_index++; - info->index[sequence_index] = repetitive_index; - } - - if (full_buffer_flag == 1) { - /*Resume ADC state to continue new conversions*/ - sys_out32(RESUME_ADC_CAPTURE, adc_base + ADC_CTRL); - reg_val = sys_in32(adc_base + ADC_SET); - sys_out32(reg_val | ADC_FLUSH_RX, adc_base + ADC_SET); - info->state = ADC_STATE_IDLE; - /*Clear data A register*/ - reg_val = sys_in32(adc_base + ADC_CTRL); - sys_out32(reg_val | ADC_CLR_DATA_A, adc_base + ADC_CTRL); + info->state = ADC_STATE_IDLE; + info->channels &= ~BIT(info->channel_id); - k_sem_give(&info->device_sync_sem); - return; + if (info->channels) { + adc_dw_start_conversion(dev); + } else { + adc_context_on_sampling_done(&info->ctx, dev); } - - /*Clear data A register*/ - reg_val = sys_in32(adc_base + ADC_CTRL); - sys_out32(reg_val | ADC_CLR_DATA_A, adc_base + ADC_CTRL); } -#endif - static void adc_dw_err_isr(void *arg) { struct device *dev = (struct device *) arg; - const struct adc_config *config = dev->config->config_info; - struct adc_info *info = dev->driver_data; + const struct adc_config *config = dev->config->config_info; + struct adc_info *info = dev->driver_data; u32_t adc_base = config->reg_base; u32_t reg_val = sys_in32(adc_base + ADC_SET); - sys_out32(RESUME_ADC_CAPTURE, adc_base + ADC_CTRL); sys_out32(reg_val | ADC_FLUSH_RX, adc_base + ADC_CTRL); sys_out32(FLUSH_ADC_ERRORS, adc_base + ADC_CTRL); info->state = ADC_STATE_ERROR; - - k_sem_give(&info->device_sync_sem); + adc_context_on_sampling_done(&info->ctx, dev); } -#ifdef CONFIG_ADC_DW - -struct adc_info adc_info_dev = { - .state = ADC_STATE_IDLE, -#ifdef CONFIG_ADC_DW_CALIBRATION - .calibration_value = ADC_NONE_CALIBRATION, +static const struct adc_driver_api api_funcs = { + .channel_setup = adc_dw_channel_setup, + .read = adc_dw_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_dw_read_async, #endif -#ifdef CONFIG_ADC_DW_DUMMY_CONVERSION - .dummy_conversion = ADC_NONE_DUMMY, -#endif - }; +}; -static struct adc_config adc_config_dev = { - .reg_base = PERIPH_ADDR_BASE_ADC, - .reg_irq_mask = SCSS_REGISTER_BASE + INT_SS_ADC_IRQ_MASK, - .reg_err_mask = SCSS_REGISTER_BASE + INT_SS_ADC_ERR_MASK, +const static struct adc_config adc_config_dev = { + .reg_base = CONFIG_ADC_0_BASE_ADDRESS, + .reg_irq_mask = SCSS_REGISTER_BASE + INT_SS_ADC_IRQ_MASK, + .reg_err_mask = SCSS_REGISTER_BASE + INT_SS_ADC_ERR_MASK, #ifdef CONFIG_ADC_DW_SERIAL - .out_mode = 0, + .out_mode = 0, #elif CONFIG_ADC_DW_PARALLEL - .out_mode = 1, -#endif -#ifdef CONFIG_ADC_DW_SINGLESHOT - .seq_mode = 0, -#elif CONFIG_ADC_DW_REPETITIVE - .seq_mode = 1, + .out_mode = 1, #endif + .seq_mode = 0, + #ifdef CONFIG_ADC_DW_RISING_EDGE - .capture_mode = 0, + .capture_mode = 0, #elif CONFIG_ADC_DW_FALLING_EDGE - .capture_mode = 1, + .capture_mode = 1, #endif - .sample_width = CONFIG_ADC_DW_SAMPLE_WIDTH, - .clock_ratio = CONFIG_ADC_DW_CLOCK_RATIO, - .serial_dly = CONFIG_ADC_DW_SERIAL_DELAY, - .config_func = adc_config_irq, - }; + .clock_ratio = CONFIG_ADC_DW_CLOCK_RATIO, + .serial_dly = CONFIG_ADC_DW_SERIAL_DELAY, + .config_func = adc_config_irq, +}; DEVICE_AND_API_INIT(adc_dw, CONFIG_ADC_0_NAME, &adc_dw_init, &adc_info_dev, &adc_config_dev, @@ -494,12 +504,11 @@ DEVICE_AND_API_INIT(adc_dw, CONFIG_ADC_0_NAME, &adc_dw_init, static void adc_config_irq(void) { - IRQ_CONNECT(IRQ_ADC_IRQ, CONFIG_ADC_0_IRQ_PRI, adc_dw_rx_isr, + IRQ_CONNECT(CONFIG_ADC_0_IRQ, CONFIG_ADC_0_IRQ_PRI, adc_dw_rx_isr, DEVICE_GET(adc_dw), 0); - irq_enable(IRQ_ADC_IRQ); + irq_enable(CONFIG_ADC_0_IRQ); - IRQ_CONNECT(IRQ_ADC_ERR, CONFIG_ADC_0_IRQ_PRI, + IRQ_CONNECT(CONFIG_ADC_IRQ_ERR, CONFIG_ADC_0_IRQ_PRI, adc_dw_err_isr, DEVICE_GET(adc_dw), 0); - irq_enable(IRQ_ADC_ERR); + irq_enable(CONFIG_ADC_IRQ_ERR); } -#endif diff --git a/drivers/adc/adc_dw.h b/drivers/adc/adc_dw.h index cdd5e2a5e04e5..797755da73d4e 100644 --- a/drivers/adc/adc_dw.h +++ b/drivers/adc/adc_dw.h @@ -135,6 +135,8 @@ extern "C" { #define RESUME_ADC_CAPTURE (ADC_INT_DSB|ADC_CLK_ENABLE|ADC_SEQ_PTR_RST) #define FLUSH_ADC_ERRORS (ADC_INT_DSB|ADC_CLK_ENABLE|ADC_CLR_OVERFLOW|ADC_CLR_UNDRFLOW) +#define DW_CHANNEL_COUNT 19 + /** mV = 3.3V*/ #define ADC_VREF 3300 @@ -188,13 +190,19 @@ struct adc_config { * during driver execution. */ struct adc_info { - struct k_sem device_sync_sem; + struct device *dev; + struct adc_context ctx; + u16_t *buffer; + u32_t active_channels; + u32_t channels; + u32_t channel_id; + #ifdef CONFIG_ADC_DW_REPETITIVE /**Current reception buffer index*/ u8_t index[BUFS_NUM]; #endif /**Sequence entries' array*/ - struct adc_seq_entry *entries; + const struct adc_sequence *entries; /**State of execution of the driver*/ u8_t state; /**Sequence size*/ @@ -209,7 +217,6 @@ struct adc_info { }; - /** * * @brief ADC Initialization function. diff --git a/drivers/adc/adc_handlers.c b/drivers/adc/adc_handlers.c deleted file mode 100644 index d88d955db8b96..0000000000000 --- a/drivers/adc/adc_handlers.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -Z_SYSCALL_HANDLER(adc_enable, dev) -{ - Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, enable)); - _impl_adc_enable((struct device *)dev); - return 0; -} - -Z_SYSCALL_HANDLER(adc_disable, dev) -{ - Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, disable)); - _impl_adc_disable((struct device *)dev); - return 0; -} - -Z_SYSCALL_HANDLER(adc_read, dev, seq_table_p) -{ - struct adc_seq_entry *entry, *entries_copy; - struct adc_seq_table *seq_table = (struct adc_seq_table *)seq_table_p; - struct adc_seq_table seq_table_copy; - unsigned int entries_bounds; - int i = 0, ret; - - Z_OOPS(Z_SYSCALL_DRIVER_ADC(dev, read)); - Z_OOPS(Z_SYSCALL_MEMORY_READ(seq_table, sizeof(struct adc_seq_table))); - - seq_table_copy = *seq_table; - if (Z_SYSCALL_VERIFY_MSG( - !__builtin_umul_overflow(seq_table_copy.num_entries, - sizeof(struct adc_seq_entry), - &entries_bounds), - "num_entries too large")) { - ret = -EINVAL; - goto out; - } - - Z_OOPS(Z_SYSCALL_MEMORY_READ(seq_table_copy.entries, entries_bounds)); - entries_copy = z_thread_malloc(entries_bounds); - if (!entries_copy) { - ret = -ENOMEM; - goto out; - } - - memcpy(entries_copy, seq_table_copy.entries, entries_bounds); - seq_table_copy.entries = entries_copy; - - for (entry = seq_table_copy.entries; i < seq_table_copy.num_entries; - i++, entry++) { - if (Z_SYSCALL_MEMORY_WRITE(entry->buffer, - entry->buffer_length)) { - k_free(entries_copy); - Z_OOPS(1); - } - } - - ret = _impl_adc_read((struct device *)dev, &seq_table_copy); - k_free(entries_copy); -out: - return ret; -} diff --git a/drivers/adc/adc_mcux_adc16.c b/drivers/adc/adc_mcux_adc16.c index c13cd2c6b6395..0d62499ef87b8 100644 --- a/drivers/adc/adc_mcux_adc16.c +++ b/drivers/adc/adc_mcux_adc16.c @@ -1,69 +1,170 @@ /* - * Copyright (c) 2017, NXP + * Copyright (c) 2017-2018, NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include -#include #include +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL +#include + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + struct mcux_adc16_config { ADC_Type *base; void (*irq_config_func)(struct device *dev); }; struct mcux_adc16_data { - struct k_sem sync; - u32_t channel_group; - u32_t result; + struct device *dev; + struct adc_context ctx; + u16_t *buffer; + u16_t *repeat_buffer; + u32_t channels; + u8_t channel_id; }; -static void mcux_adc16_enable(struct device *dev) +static int mcux_adc16_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + u8_t channel_id = channel_cfg->channel_id; + + if (channel_id > (ADC_SC1_ADCH_MASK >> ADC_SC1_ADCH_SHIFT)) { + SYS_LOG_ERR("Channel %d is not valid", channel_id); + return -EINVAL; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + SYS_LOG_ERR("Invalid channel acquisition time"); + return -EINVAL; + } + + if (channel_cfg->differential) { + SYS_LOG_ERR("Differential channels are not supported"); + return -EINVAL; + } + + if (channel_cfg->gain != ADC_GAIN_1) { + SYS_LOG_ERR("Invalid channel gain"); + return -EINVAL; + } + + if (channel_cfg->reference != ADC_REF_INTERNAL) { + SYS_LOG_ERR("Invalid channel reference"); + return -EINVAL; + } + + return 0; +} + +static int start_read(struct device *dev, const struct adc_sequence *sequence) +{ + const struct mcux_adc16_config *config = dev->config->config_info; + struct mcux_adc16_data *data = dev->driver_data; + adc16_hardware_average_mode_t mode; + int error; + + if (sequence->resolution != 12) { + SYS_LOG_ERR("Invalid resolution"); + return -EINVAL; + } + + switch (sequence->oversampling) { + case 0: + mode = kADC16_HardwareAverageDisabled; + break; + case 2: + mode = kADC16_HardwareAverageCount4; + break; + case 3: + mode = kADC16_HardwareAverageCount8; + break; + case 4: + mode = kADC16_HardwareAverageCount16; + break; + case 5: + mode = kADC16_HardwareAverageCount32; + break; + default: + SYS_LOG_ERR("Invalid oversampling"); + return -EINVAL; + } + ADC16_SetHardwareAverage(config->base, mode); + + data->buffer = sequence->buffer; + data->repeat_buffer = sequence->buffer; + + adc_context_start_read(&data->ctx, sequence); + error = adc_context_wait_for_completion(&data->ctx); + adc_context_release(&data->ctx, error); + + return error; +} + +static int mcux_adc16_read(struct device *dev, + const struct adc_sequence *sequence) { - ARG_UNUSED(dev); + struct mcux_adc16_data *data = dev->driver_data; + + adc_context_lock(&data->ctx, false, NULL); + return start_read(dev, sequence); } -static void mcux_adc16_disable(struct device *dev) +#ifdef CONFIG_ADC_ASYNC +static int mcux_adc16_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) { - ARG_UNUSED(dev); + struct mcux_adc16_data *data = dev->driver_data; + + adc_context_lock(&data->ctx, true, async); + return start_read(dev, sequence); } +#endif -static int mcux_adc16_read(struct device *dev, struct adc_seq_table *seq_table) +static void mcux_adc16_start_channel(struct device *dev) { const struct mcux_adc16_config *config = dev->config->config_info; struct mcux_adc16_data *data = dev->driver_data; - ADC_Type *base = config->base; - struct adc_seq_entry *entry = seq_table->entries; adc16_channel_config_t channel_config; u32_t channel_group = 0; - int i; - channel_config.enableInterruptOnConversionCompleted = true; + data->channel_id = find_lsb_set(data->channels) - 1; + + SYS_LOG_DBG("Starting channel %d", data->channel_id); + #if defined(FSL_FEATURE_ADC16_HAS_DIFF_MODE) && FSL_FEATURE_ADC16_HAS_DIFF_MODE channel_config.enableDifferentialConversion = false; #endif + channel_config.enableInterruptOnConversionCompleted = true; + channel_config.channelNumber = data->channel_id; + ADC16_SetChannelConfig(config->base, channel_group, &channel_config); +} - for (i = 0; i < seq_table->num_entries; i++) { - if (entry->buffer_length < sizeof(data->result)) { - return -EINVAL; - } - - channel_config.channelNumber = entry->channel_id; - ADC16_SetChannelConfig(base, channel_group, &channel_config); +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct mcux_adc16_data *data = + CONTAINER_OF(ctx, struct mcux_adc16_data, ctx); - data->channel_group = channel_group; + data->channels = ctx->sequence->channels; - k_sem_take(&data->sync, K_FOREVER); + mcux_adc16_start_channel(data->dev); +} - memcpy(entry->buffer, &data->result, sizeof(data->result)); +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat_sampling) +{ + struct mcux_adc16_data *data = + CONTAINER_OF(ctx, struct mcux_adc16_data, ctx); - entry++; + if (repeat_sampling) { + data->buffer = data->repeat_buffer; } - - return 0; } static void mcux_adc16_isr(void *arg) @@ -72,11 +173,21 @@ static void mcux_adc16_isr(void *arg) const struct mcux_adc16_config *config = dev->config->config_info; struct mcux_adc16_data *data = dev->driver_data; ADC_Type *base = config->base; - u32_t channel_group = data->channel_group; + u32_t channel_group = 0; + u16_t result; + + result = ADC16_GetChannelConversionValue(base, channel_group); + SYS_LOG_DBG("Finished channel %d. Result is 0x%04x", + data->channel_id, result); - data->result = ADC16_GetChannelConversionValue(base, channel_group); + *data->buffer++ = result; + data->channels &= ~BIT(data->channel_id); - k_sem_give(&data->sync); + if (data->channels) { + mcux_adc16_start_channel(dev); + } else { + adc_context_on_sampling_done(&data->ctx, dev); + } } static int mcux_adc16_init(struct device *dev) @@ -86,23 +197,25 @@ static int mcux_adc16_init(struct device *dev) ADC_Type *base = config->base; adc16_config_t adc_config; - k_sem_init(&data->sync, 0, UINT_MAX); - ADC16_GetDefaultConfig(&adc_config); ADC16_Init(base, &adc_config); ADC16_EnableHardwareTrigger(base, false); - ADC16_SetHardwareAverage(base, kADC16_HardwareAverageCount4); config->irq_config_func(dev); + data->dev = dev; + + adc_context_unlock_unconditionally(&data->ctx); return 0; } static const struct adc_driver_api mcux_adc16_driver_api = { - .enable = mcux_adc16_enable, - .disable = mcux_adc16_disable, + .channel_setup = mcux_adc16_channel_setup, .read = mcux_adc16_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = mcux_adc16_read_async, +#endif }; #if CONFIG_ADC_0 @@ -113,7 +226,11 @@ static const struct mcux_adc16_config mcux_adc16_config_0 = { .irq_config_func = mcux_adc16_config_func_0, }; -static struct mcux_adc16_data mcux_adc16_data_0; +static struct mcux_adc16_data mcux_adc16_data_0 = { + ADC_CONTEXT_INIT_TIMER(mcux_adc16_data_0, ctx), + ADC_CONTEXT_INIT_LOCK(mcux_adc16_data_0, ctx), + ADC_CONTEXT_INIT_SYNC(mcux_adc16_data_0, ctx), +}; DEVICE_AND_API_INIT(mcux_adc16_0, CONFIG_ADC_0_NAME, &mcux_adc16_init, &mcux_adc16_data_0, &mcux_adc16_config_0, @@ -137,7 +254,11 @@ static const struct mcux_adc16_config mcux_adc16_config_1 = { .irq_config_func = mcux_adc16_config_func_1, }; -static struct mcux_adc16_data mcux_adc16_data_1; +static struct mcux_adc16_data mcux_adc16_data_1 = { + ADC_CONTEXT_INIT_TIMER(mcux_adc16_data_1, ctx), + ADC_CONTEXT_INIT_LOCK(mcux_adc16_data_1, ctx), + ADC_CONTEXT_INIT_SYNC(mcux_adc16_data_1, ctx), +}; DEVICE_AND_API_INIT(mcux_adc16_1, CONFIG_ADC_1_NAME, &mcux_adc16_init, &mcux_adc16_data_1, &mcux_adc16_config_1, diff --git a/drivers/adc/adc_nrfx_adc.c b/drivers/adc/adc_nrfx_adc.c new file mode 100644 index 0000000000000..5fe4a19e59100 --- /dev/null +++ b/drivers/adc/adc_nrfx_adc.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" +#include + +#define SYS_LOG_DOMAIN "adc_nrfx_adc" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL +#include + +struct driver_data { + struct adc_context ctx; + + nrf_adc_value_t *buffer; + u8_t active_channels; +}; + +static struct driver_data m_data = { + ADC_CONTEXT_INIT_TIMER(m_data, ctx), + ADC_CONTEXT_INIT_LOCK(m_data, ctx), + ADC_CONTEXT_INIT_SYNC(m_data, ctx), +}; + +static nrfx_adc_channel_t m_channels[CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT]; + + +/* Implementation of the ADC driver API function: adc_channel_setup. */ +static int adc_nrfx_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + u8_t channel_id = channel_cfg->channel_id; + nrf_adc_config_t *config = &m_channels[channel_id].config; + + if (channel_id >= CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT) { + return -EINVAL; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + SYS_LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; + } + + if (channel_cfg->differential) { + SYS_LOG_ERR("Differential channels are not supported"); + return -EINVAL; + } + + switch (channel_cfg->gain) { + case ADC_GAIN_1_3: + config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD; + break; + case ADC_GAIN_2_3: + config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS; + break; + case ADC_GAIN_1: + config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE; + break; + default: + SYS_LOG_ERR("Selected ADC gain is not valid"); + return -EINVAL; + } + + switch (channel_cfg->reference) { + case ADC_REF_INTERNAL: + config->reference = NRF_ADC_CONFIG_REF_VBG; + config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE; + break; + case ADC_REF_VDD_1_2: + config->reference = NRF_ADC_CONFIG_REF_SUPPLY_ONE_HALF; + config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE; + break; + case ADC_REF_VDD_1_3: + config->reference = NRF_ADC_CONFIG_REF_SUPPLY_ONE_THIRD; + config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE; + break; + case ADC_REF_EXTERNAL0: + config->reference = NRF_ADC_CONFIG_REF_EXT; + config->extref = NRF_ADC_CONFIG_EXTREFSEL_AREF0; + break; + case ADC_REF_EXTERNAL1: + config->reference = NRF_ADC_CONFIG_REF_EXT; + config->extref = NRF_ADC_CONFIG_EXTREFSEL_AREF1; + break; + default: + SYS_LOG_ERR("Selected ADC reference is not valid"); + return -EINVAL; + } + + config->input = channel_cfg->input_positive; + + config->resolution = NRF_ADC_CONFIG_RES_8BIT; + + return 0; +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + ARG_UNUSED(ctx); + + nrfx_adc_buffer_convert(m_data.buffer, m_data.active_channels); + nrfx_adc_sample(); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat) +{ + ARG_UNUSED(ctx); + + if (!repeat) { + m_data.buffer += m_data.active_channels; + } +} + +static int check_buffer_size(const struct adc_sequence *sequence, + u8_t active_channels) +{ + size_t needed_buffer_size; + + needed_buffer_size = active_channels * sizeof(nrf_adc_value_t); + if (sequence->options) { + needed_buffer_size *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed_buffer_size) { + SYS_LOG_ERR("Provided buffer is too small (%u/%u)", + sequence->buffer_size, needed_buffer_size); + return -ENOMEM; + } + + return 0; +} + +static int start_read(struct device *dev, const struct adc_sequence *sequence) +{ + int error = 0; + u32_t selected_channels = sequence->channels; + u8_t active_channels; + u8_t channel_id; + nrf_adc_config_resolution_t nrf_resolution; + + /* Signal an error if channel selection is invalid (no channels or + * a non-existing one is selected). + */ + if (!selected_channels || + (selected_channels & + ~BIT_MASK(CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT))) { + SYS_LOG_ERR("Invalid selection of channels"); + return -EINVAL; + } + + if (sequence->oversampling != 0) { + SYS_LOG_ERR("Oversampling is not supported"); + return -EINVAL; + } + + switch (sequence->resolution) { + case 8: + nrf_resolution = NRF_ADC_CONFIG_RES_8BIT; + break; + case 9: + nrf_resolution = NRF_ADC_CONFIG_RES_9BIT; + break; + case 10: + nrf_resolution = NRF_ADC_CONFIG_RES_10BIT; + break; + default: + SYS_LOG_ERR("ADC resolution value %d is not valid", + sequence->resolution); + return -EINVAL; + } + + active_channels = 0; + nrfx_adc_all_channels_disable(); + + /* Enable the channels selected for the pointed sequence. + */ + channel_id = 0; + while (selected_channels) { + if (selected_channels & BIT(0)) { + /* The nrfx driver requires setting the resolution + * for each enabled channel individually. + */ + m_channels[channel_id].config.resolution = + nrf_resolution; + nrfx_adc_channel_enable(&m_channels[channel_id]); + ++active_channels; + } + selected_channels >>= 1; + ++channel_id; + } + + error = check_buffer_size(sequence, active_channels); + if (error) { + return error; + } + + m_data.buffer = sequence->buffer; + m_data.active_channels = active_channels; + + adc_context_start_read(&m_data.ctx, sequence); + + if (!error) { + error = adc_context_wait_for_completion(&m_data.ctx); + adc_context_release(&m_data.ctx, error); + } + + return error; +} + +/* Implementation of the ADC driver API function: adc_read. */ +static int adc_nrfx_read(struct device *dev, + const struct adc_sequence *sequence) +{ + adc_context_lock(&m_data.ctx, false, NULL); + return start_read(dev, sequence); +} + +#ifdef CONFIG_ADC_ASYNC +/* Implementation of the ADC driver API function: adc_read_sync. */ +static int adc_nrfx_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + adc_context_lock(&m_data.ctx, true, async); + return start_read(dev, sequence); +} +#endif + +DEVICE_DECLARE(adc_0); + +static void event_handler(const nrfx_adc_evt_t *p_event) +{ + struct device *dev = DEVICE_GET(adc_0); + + if (p_event->type == NRFX_ADC_EVT_DONE) { + adc_context_on_sampling_done(&m_data.ctx, dev); + } +} + +static int init_adc(struct device *dev) +{ + const nrfx_adc_config_t config = NRFX_ADC_DEFAULT_CONFIG; + + nrfx_err_t result = nrfx_adc_init(&config, event_handler); + + if (result != NRFX_SUCCESS) { + SYS_LOG_ERR("Failed to initialize device: %s", + dev->config->name); + return -EBUSY; + } + + IRQ_CONNECT(CONFIG_ADC_0_IRQ, CONFIG_ADC_0_IRQ_PRI, + nrfx_isr, nrfx_adc_irq_handler, 0); + + adc_context_unlock_unconditionally(&m_data.ctx); + + return 0; +} + +static const struct adc_driver_api adc_nrfx_driver_api = { + .channel_setup = adc_nrfx_channel_setup, + .read = adc_nrfx_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_nrfx_read_async, +#endif +}; + +#ifdef CONFIG_ADC_0 +DEVICE_AND_API_INIT(adc_0, CONFIG_ADC_0_NAME, + init_adc, NULL, NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &adc_nrfx_driver_api); +#endif /* CONFIG_ADC_0 */ diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c new file mode 100644 index 0000000000000..328d9fd51d667 --- /dev/null +++ b/drivers/adc/adc_nrfx_saadc.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" +#include + +#define SYS_LOG_DOMAIN "adc_nrfx_saadc" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL +#include + +struct driver_data { + struct adc_context ctx; + + u8_t positive_inputs[NRF_SAADC_CHANNEL_COUNT]; +}; + +static struct driver_data m_data = { + ADC_CONTEXT_INIT_TIMER(m_data, ctx), + ADC_CONTEXT_INIT_LOCK(m_data, ctx), + ADC_CONTEXT_INIT_SYNC(m_data, ctx), +}; + + +/* Implementation of the ADC driver API function: adc_channel_setup. */ +static int adc_nrfx_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + nrf_saadc_channel_config_t config = { + .resistor_p = NRF_SAADC_RESISTOR_DISABLED, + .resistor_n = NRF_SAADC_RESISTOR_DISABLED, + .burst = NRF_SAADC_BURST_DISABLED, + }; + u8_t channel_id = channel_cfg->channel_id; + + if (channel_id >= NRF_SAADC_CHANNEL_COUNT) { + return -EINVAL; + } + + switch (channel_cfg->gain) { + case ADC_GAIN_1_6: + config.gain = NRF_SAADC_GAIN1_6; + break; + case ADC_GAIN_1_5: + config.gain = NRF_SAADC_GAIN1_5; + break; + case ADC_GAIN_1_4: + config.gain = NRF_SAADC_GAIN1_4; + break; + case ADC_GAIN_1_3: + config.gain = NRF_SAADC_GAIN1_3; + break; + case ADC_GAIN_1_2: + config.gain = NRF_SAADC_GAIN1_2; + break; + case ADC_GAIN_1: + config.gain = NRF_SAADC_GAIN1; + break; + case ADC_GAIN_2: + config.gain = NRF_SAADC_GAIN2; + break; + case ADC_GAIN_4: + config.gain = NRF_SAADC_GAIN4; + break; + default: + SYS_LOG_ERR("Selected ADC gain is not valid"); + return -EINVAL; + } + + switch (channel_cfg->reference) { + case ADC_REF_INTERNAL: + config.reference = NRF_SAADC_REFERENCE_INTERNAL; + break; + case ADC_REF_VDD_1_4: + config.reference = NRF_SAADC_REFERENCE_VDD4; + break; + default: + SYS_LOG_ERR("Selected ADC reference is not valid"); + return -EINVAL; + } + + switch (channel_cfg->acquisition_time) { + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 3): + config.acq_time = NRF_SAADC_ACQTIME_3US; + break; + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5): + config.acq_time = NRF_SAADC_ACQTIME_5US; + break; + case ADC_ACQ_TIME_DEFAULT: + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10): + config.acq_time = NRF_SAADC_ACQTIME_10US; + break; + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 15): + config.acq_time = NRF_SAADC_ACQTIME_15US; + break; + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20): + config.acq_time = NRF_SAADC_ACQTIME_20US; + break; + case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40): + config.acq_time = NRF_SAADC_ACQTIME_40US; + break; + default: + SYS_LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; + } + + config.mode = (channel_cfg->differential ? + NRF_SAADC_MODE_DIFFERENTIAL : NRF_SAADC_MODE_SINGLE_ENDED); + + /* Keep the channel disabled in hardware (set positive input to + * NRF_SAADC_INPUT_DISABLED) until it is selected to be included + * in a sampling sequence. + */ + config.pin_p = NRF_SAADC_INPUT_DISABLED; + config.pin_n = channel_cfg->input_negative; + + nrf_saadc_channel_init(channel_id, &config); + + /* Store the positive input selection in a dedicated array, + * to get it later when the channel is selected for a sampling + * and to mark the channel as configured (ready to be selected). + */ + m_data.positive_inputs[channel_id] = channel_cfg->input_positive; + + return 0; +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + ARG_UNUSED(ctx); + + nrf_saadc_enable(); + + nrf_saadc_task_trigger(NRF_SAADC_TASK_START); + nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat) +{ + ARG_UNUSED(ctx); + + if (!repeat) { + nrf_saadc_buffer_pointer_set( + nrf_saadc_buffer_pointer_get() + + nrf_saadc_amount_get()); + } +} + +static int set_resolution(const struct adc_sequence *sequence) +{ + nrf_saadc_resolution_t nrf_resolution; + + switch (sequence->resolution) { + case 8: + nrf_resolution = NRF_SAADC_RESOLUTION_8BIT; + break; + case 10: + nrf_resolution = NRF_SAADC_RESOLUTION_10BIT; + break; + case 12: + nrf_resolution = NRF_SAADC_RESOLUTION_12BIT; + break; + case 14: + nrf_resolution = NRF_SAADC_RESOLUTION_14BIT; + break; + default: + SYS_LOG_ERR("ADC resolution value %d is not valid", + sequence->resolution); + return -EINVAL; + } + + nrf_saadc_resolution_set(nrf_resolution); + return 0; +} + +static int set_oversampling(const struct adc_sequence *sequence, + u8_t active_channels) +{ + nrf_saadc_oversample_t nrf_oversampling; + + if ((active_channels > 1) && (sequence->oversampling > 0)) { + SYS_LOG_ERR( + "Oversampling is supported for single channel only"); + return -EINVAL; + } + + switch (sequence->oversampling) { + case 0: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_DISABLED; + break; + case 1: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_2X; + break; + case 2: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_4X; + break; + case 3: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_8X; + break; + case 4: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_16X; + break; + case 5: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_32X; + break; + case 6: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_64X; + break; + case 7: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_128X; + break; + case 8: + nrf_oversampling = NRF_SAADC_OVERSAMPLE_256X; + break; + default: + SYS_LOG_ERR("Oversampling value %d is not valid", + sequence->oversampling); + return -EINVAL; + } + + nrf_saadc_oversample_set(nrf_oversampling); + return 0; +} + +static int check_buffer_size(const struct adc_sequence *sequence, + u8_t active_channels) +{ + size_t needed_buffer_size; + + needed_buffer_size = active_channels * sizeof(nrf_saadc_value_t); + if (sequence->options) { + needed_buffer_size *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed_buffer_size) { + SYS_LOG_ERR("Provided buffer is too small (%u/%u)", + sequence->buffer_size, needed_buffer_size); + return -ENOMEM; + } + + return 0; +} + +/* TODO: Move to . */ +static void _nrf_saadc_burst_set(uint8_t channel, + nrf_saadc_burst_t burst) +{ + NRF_SAADC->CH[channel].CONFIG = + (NRF_SAADC->CH[channel].CONFIG & ~SAADC_CH_CONFIG_BURST_Msk) | + (burst << SAADC_CH_CONFIG_BURST_Pos); +} + +static int start_read(struct device *dev, const struct adc_sequence *sequence) +{ + int error = 0; + u32_t selected_channels = sequence->channels; + u8_t active_channels; + u8_t channel_id; + + /* Signal an error if channel selection is invalid (no channels or + * a non-existing one is selected). + */ + if (!selected_channels || + (selected_channels & ~BIT_MASK(NRF_SAADC_CHANNEL_COUNT))) { + SYS_LOG_ERR("Invalid selection of channels"); + return -EINVAL; + } + + active_channels = 0; + + /* Enable only the channels selected for the pointed sequence. + * Disable all the rest. + */ + channel_id = 0; + do { + if (selected_channels & BIT(channel_id)) { + /* Signal an error if a selected channel has not been + * configured yet. + */ + if (m_data.positive_inputs[channel_id] == 0) { + SYS_LOG_ERR("Channel %u not configured", + channel_id); + return -EINVAL; + } + /* When oversampling is used, the burst mode needs to + * be activated. Unfortunately, this mode cannot be + * activated permanently in the channel setup, because + * then the multiple channel sampling fails (the END + * event is not generated) after switching to a single + * channel sampling and back. Thus, when oversampling + * is not used (hence, the multiple channel sampling is + * possible), the burst mode have to be deactivated. + */ + _nrf_saadc_burst_set(channel_id, + (sequence->oversampling != 0 ? + NRF_SAADC_BURST_ENABLED : + NRF_SAADC_BURST_DISABLED)); + nrf_saadc_channel_pos_input_set( + channel_id, + m_data.positive_inputs[channel_id]); + ++active_channels; + } else { + nrf_saadc_channel_pos_input_set( + channel_id, + NRF_SAADC_INPUT_DISABLED); + } + } while (++channel_id < NRF_SAADC_CHANNEL_COUNT); + + error = set_resolution(sequence); + if (error) { + return error; + } + + error = set_oversampling(sequence, active_channels); + if (error) { + return error; + } + + error = check_buffer_size(sequence, active_channels); + if (error) { + return error; + } + + nrf_saadc_buffer_init((nrf_saadc_value_t *)sequence->buffer, + active_channels); + + adc_context_start_read(&m_data.ctx, sequence); + + error = adc_context_wait_for_completion(&m_data.ctx); + adc_context_release(&m_data.ctx, error); + + return error; +} + +/* Implementation of the ADC driver API function: adc_read. */ +static int adc_nrfx_read(struct device *dev, + const struct adc_sequence *sequence) +{ + adc_context_lock(&m_data.ctx, false, NULL); + return start_read(dev, sequence); +} + +#ifdef CONFIG_ADC_ASYNC +/* Implementation of the ADC driver API function: adc_read_async. */ +static int adc_nrfx_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + adc_context_lock(&m_data.ctx, true, async); + return start_read(dev, sequence); +} +#endif + +static void saadc_irq_handler(void *param) +{ + struct device *dev = (struct device *)param; + + if (nrf_saadc_event_check(NRF_SAADC_EVENT_END)) { + nrf_saadc_event_clear(NRF_SAADC_EVENT_END); + + nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP); + nrf_saadc_disable(); + + adc_context_on_sampling_done(&m_data.ctx, dev); + } +} + +DEVICE_DECLARE(adc_0); + +static int init_saadc(struct device *dev) +{ + nrf_saadc_event_clear(NRF_SAADC_EVENT_END); + nrf_saadc_int_enable(NRF_SAADC_INT_END); + NRFX_IRQ_ENABLE(CONFIG_ADC_0_IRQ); + + IRQ_CONNECT(CONFIG_ADC_0_IRQ, CONFIG_ADC_0_IRQ_PRI, + saadc_irq_handler, DEVICE_GET(adc_0), 0); + + adc_context_unlock_unconditionally(&m_data.ctx); + + return 0; +} + +static const struct adc_driver_api adc_nrfx_driver_api = { + .channel_setup = adc_nrfx_channel_setup, + .read = adc_nrfx_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_nrfx_read_async, +#endif +}; + +#ifdef CONFIG_ADC_0 +DEVICE_AND_API_INIT(adc_0, CONFIG_ADC_0_NAME, + init_saadc, NULL, NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &adc_nrfx_driver_api); +#endif /* CONFIG_ADC_0 */ diff --git a/drivers/adc/adc_qmsi_ss.c b/drivers/adc/adc_qmsi_ss.c deleted file mode 100644 index 599d2eb23c7f0..0000000000000 --- a/drivers/adc/adc_qmsi_ss.c +++ /dev/null @@ -1,345 +0,0 @@ -/* adc_qmsi.c - QMSI ADC Sensor Subsystem driver */ - -/* - * Copyright (c) 2016 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qm_ss_isr.h" -#include "qm_ss_adc.h" -#include "ss_clk.h" - -enum { - ADC_STATE_IDLE, - ADC_STATE_BUSY, - ADC_STATE_ERROR -}; - -struct adc_info { - atomic_t state; - struct k_sem device_sync_sem; - struct k_sem sem; -#ifdef CONFIG_DEVICE_POWER_MANAGEMENT - u32_t device_power_state; - qm_ss_adc_context_t adc_ctx; -#endif -}; - -static void adc_config_irq(void); -static qm_ss_adc_config_t cfg; - -#if (CONFIG_ADC_QMSI_INTERRUPT) - -static void complete_callback(void *data, int error, qm_ss_adc_status_t status, - qm_ss_adc_cb_source_t source) -{ - ARG_UNUSED(status); - ARG_UNUSED(source); - - struct device *dev = data; - - struct adc_info *info = dev->driver_data; - - if (info) { - if (error) { - info->state = ADC_STATE_ERROR; - } - k_sem_give(&info->device_sync_sem); - } -} - -#endif - -static void adc_lock(struct adc_info *data) -{ - k_sem_take(&data->sem, K_FOREVER); - data->state = ADC_STATE_BUSY; - -} -static void adc_unlock(struct adc_info *data) -{ - k_sem_give(&data->sem); - data->state = ADC_STATE_IDLE; - -} -#if (CONFIG_ADC_QMSI_CALIBRATION) -static void adc_qmsi_ss_enable(struct device *dev) -{ - struct adc_info *info = dev->driver_data; - - adc_lock(info); - qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_NORM_CAL); - qm_ss_adc_calibrate(QM_SS_ADC_0); - adc_unlock(info); -} - -#else -static void adc_qmsi_ss_enable(struct device *dev) -{ - struct adc_info *info = dev->driver_data; - - adc_lock(info); - qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_NORM_NO_CAL); - adc_unlock(info); -} -#endif /* CONFIG_ADC_QMSI_CALIBRATION */ - -static void adc_qmsi_ss_disable(struct device *dev) -{ - struct adc_info *info = dev->driver_data; - - adc_lock(info); - /* Go to deep sleep */ - qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_DEEP_PWR_DOWN); - adc_unlock(info); -} - -#if (CONFIG_ADC_QMSI_POLL) -static int adc_qmsi_ss_read(struct device *dev, struct adc_seq_table *seq_tbl) -{ - int i, ret = 0; - qm_ss_adc_xfer_t xfer; - qm_ss_adc_status_t status; - - struct adc_info *info = dev->driver_data; - - - for (i = 0; i < seq_tbl->num_entries; i++) { - - xfer.ch = (qm_ss_adc_channel_t *)&seq_tbl->entries[i].channel_id; - /* Just one channel at the time using the Zephyr sequence table - */ - xfer.ch_len = 1; - xfer.samples = (qm_ss_adc_sample_t *)seq_tbl->entries[i].buffer; - - /* buffer length (bytes) the number of samples, the QMSI Driver - * does not allow more than QM_ADC_FIFO_LEN samples at the time - * in polling mode, if that happens, the qm_adc_convert api will - * return with an error - */ - xfer.samples_len = - (seq_tbl->entries[i].buffer_length)/sizeof(qm_ss_adc_sample_t); - - xfer.callback = NULL; - xfer.callback_data = NULL; - - cfg.window = seq_tbl->entries[i].sampling_delay; - - adc_lock(info); - - if (qm_ss_adc_set_config(QM_SS_ADC_0, &cfg) != 0) { - ret = -EINVAL; - adc_unlock(info); - break; - } - - /* Run the conversion, here the function will poll for the - * samples. The function will constantly read the status - * register to check if the number of samples required has been - * captured - */ - if (qm_ss_adc_convert(QM_SS_ADC_0, &xfer, &status) != 0) { - ret = -EIO; - adc_unlock(info); - break; - } - - /* Successful Analog to Digital conversion */ - adc_unlock(info); - } - - return ret; -} -#else -static int adc_qmsi_ss_read(struct device *dev, struct adc_seq_table *seq_tbl) -{ - int i, ret = 0; - qm_ss_adc_xfer_t xfer; - - struct adc_info *info = dev->driver_data; - - for (i = 0; i < seq_tbl->num_entries; i++) { - - xfer.ch = (qm_ss_adc_channel_t *)&seq_tbl->entries[i].channel_id; - /* Just one channel at the time using the Zephyr sequence table */ - xfer.ch_len = 1; - xfer.samples = - (qm_ss_adc_sample_t *)seq_tbl->entries[i].buffer; - - xfer.samples_len = - (seq_tbl->entries[i].buffer_length)/sizeof(qm_ss_adc_sample_t); - - xfer.callback = complete_callback; - xfer.callback_data = dev; - - cfg.window = seq_tbl->entries[i].sampling_delay; - - adc_lock(info); - - if (qm_ss_adc_set_config(QM_SS_ADC_0, &cfg) != 0) { - ret = -EINVAL; - adc_unlock(info); - break; - } - - /* This is the interrupt driven API, will generate and interrupt and - * call the complete_callback function once the samples have been - * obtained - */ - if (qm_ss_adc_irq_convert(QM_SS_ADC_0, &xfer) != 0) { - ret = -EIO; - adc_unlock(info); - break; - } - - /* Wait for the interrupt to finish */ - k_sem_take(&info->device_sync_sem, K_FOREVER); - - if (info->state == ADC_STATE_ERROR) { - ret = -EIO; - adc_unlock(info); - break; - } - /* Successful Analog to Digital conversion */ - adc_unlock(info); - } - - return ret; -} -#endif /* CONFIG_ADC_QMSI_POLL */ - -static void adc_qmsi_ss_rx_isr(void *arg) -{ - ARG_UNUSED(arg); - qm_ss_adc_0_isr(NULL); -} - -static void adc_qmsi_ss_err_isr(void *arg) -{ - ARG_UNUSED(arg); - qm_ss_adc_0_error_isr(NULL); -} - -static const struct adc_driver_api api_funcs = { - .enable = adc_qmsi_ss_enable, - .disable = adc_qmsi_ss_disable, - .read = adc_qmsi_ss_read, -}; - -#ifdef CONFIG_DEVICE_POWER_MANAGEMENT -static void adc_qmsi_ss_set_power_state(struct device *dev, - u32_t power_state) -{ - struct adc_info *context = dev->driver_data; - - context->device_power_state = power_state; -} - -static u32_t adc_qmsi_ss_get_power_state(struct device *dev) -{ - struct adc_info *context = dev->driver_data; - - return context->device_power_state; -} - -static int adc_qmsi_ss_suspend_device(struct device *dev) -{ - struct adc_info *context = dev->driver_data; - - qm_ss_adc_save_context(QM_SS_ADC_0, &context->adc_ctx); - - adc_qmsi_ss_set_power_state(dev, DEVICE_PM_SUSPEND_STATE); - - return 0; -} - -static int adc_qmsi_ss_resume_device_from_suspend(struct device *dev) -{ - struct adc_info *context = dev->driver_data; - - qm_ss_adc_restore_context(QM_SS_ADC_0, &context->adc_ctx); - - adc_qmsi_ss_set_power_state(dev, DEVICE_PM_ACTIVE_STATE); - - return 0; -} - -static int adc_qmsi_ss_device_ctrl(struct device *dev, u32_t ctrl_command, - void *context) -{ - if (ctrl_command == DEVICE_PM_SET_POWER_STATE) { - if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) { - return adc_qmsi_ss_suspend_device(dev); - } else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) { - return adc_qmsi_ss_resume_device_from_suspend(dev); - } - } else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) { - *((u32_t *)context) = adc_qmsi_ss_get_power_state(dev); - } - - return 0; -} -#else -#define adc_qmsi_ss_set_power_state(...) -#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */ -static int adc_qmsi_ss_init(struct device *dev) -{ - struct adc_info *info = dev->driver_data; - - /* Set up config */ - /* Clock cycles between the start of each sample */ - cfg.window = CONFIG_ADC_QMSI_SERIAL_DELAY; - cfg.resolution = CONFIG_ADC_QMSI_SAMPLE_WIDTH; - - qm_ss_adc_set_config(QM_SS_ADC_0, &cfg); - - ss_clk_adc_enable(); - ss_clk_adc_set_div(CONFIG_ADC_QMSI_CLOCK_RATIO); - k_sem_init(&info->device_sync_sem, 0, UINT_MAX); - - k_sem_init(&info->sem, 1, UINT_MAX); - info->state = ADC_STATE_IDLE; - - adc_config_irq(); - - adc_qmsi_ss_set_power_state(dev, DEVICE_PM_ACTIVE_STATE); - - return 0; -} - -static struct adc_info adc_info_dev; - -DEVICE_DEFINE(adc_qmsi_ss, CONFIG_ADC_0_NAME, &adc_qmsi_ss_init, - adc_qmsi_ss_device_ctrl, &adc_info_dev, NULL, POST_KERNEL, - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &api_funcs); - -static void adc_config_irq(void) -{ - u32_t *scss_intmask; - - IRQ_CONNECT(IRQ_ADC_IRQ, CONFIG_ADC_0_IRQ_PRI, - adc_qmsi_ss_rx_isr, DEVICE_GET(adc_qmsi_ss), 0); - irq_enable(IRQ_ADC_IRQ); - - IRQ_CONNECT(IRQ_ADC_ERR, CONFIG_ADC_0_IRQ_PRI, - adc_qmsi_ss_err_isr, DEVICE_GET(adc_qmsi_ss), 0); - irq_enable(IRQ_ADC_ERR); - - scss_intmask = - (u32_t *)&QM_INTERRUPT_ROUTER->ss_adc_0_error_int_mask; - *scss_intmask &= ~BIT(8); - - scss_intmask = (u32_t *)&QM_INTERRUPT_ROUTER->ss_adc_0_int_mask; - *scss_intmask &= ~BIT(8); -} diff --git a/drivers/adc/adc_sam_afec.c b/drivers/adc/adc_sam_afec.c index 6450857aebeaa..21d68e2de180d 100644 --- a/drivers/adc/adc_sam_afec.c +++ b/drivers/adc/adc_sam_afec.c @@ -1,11 +1,15 @@ /* * Copyright (c) 2017 comsuisse AG + * Copyright (c) 2018 Justin Watson * * SPDX-License-Identifier: Apache-2.0 */ /** @file * @brief Atmel SAM MCU family ADC (AFEC) driver. + * + * This is an implementation of the Zephyr ADC driver using the SAM Analog + * Front-End Controller (AFEC) peripheral. */ #include @@ -16,143 +20,254 @@ #include #include +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + #define SYS_LOG_DOMAIN "dev/adc_sam_afec" #define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL #include -#define ADC_CHANNELS 12 +#define NUM_CHANNELS 12 -struct adc_sam_dev_cfg { - Afec *regs; - const struct soc_gpio_pin *pin_trigger; - void (*irq_config)(void); - u8_t irq_id; - u8_t periph_id; -}; +#define CONF_ADC_PRESCALER ((SOC_ATMEL_SAM_MCK_FREQ_HZ / 15000000) - 1) + +typedef void (*cfg_func_t)(struct device *dev); + +struct adc_sam_data { + struct adc_context ctx; + struct device *dev; -struct channel_samples { + /* Pointer to the buffer in the sequence. */ u16_t *buffer; - u32_t length; -}; -struct adc_sam_dev_data { - struct k_sem sem_meas; - struct k_sem mutex_thread; - u16_t active_channels; - u16_t measured_channels; - u16_t active_chan_last; - struct channel_samples samples[ADC_CHANNELS]; + /* Pointer to the beginning of a sample. Consider the number of + * channels in the sequence: this buffer changes by that amount + * so all the channels would get repeated. + */ + u16_t *repeat_buffer; + + /* Bit mask of the channels to be sampled. */ + u32_t channels; + + /* Index of the channel being sampled. */ + u8_t channel_id; }; -#define CONF_ADC_PRESCALER ((SOC_ATMEL_SAM_MCK_FREQ_HZ / 15000000) - 1) +struct adc_sam_cfg { + Afec *regs; + cfg_func_t cfg_func; + u32_t periph_id; + struct soc_gpio_pin afec_trg_pin; +}; -#define DEV_NAME(dev) ((dev)->config->name) #define DEV_CFG(dev) \ - ((const struct adc_sam_dev_cfg *const)(dev)->config->config_info) + ((const struct adc_sam_cfg *const)(dev)->config->config_info) + #define DEV_DATA(dev) \ - ((struct adc_sam_dev_data *const)(dev)->driver_data) + ((struct adc_sam_data *)(dev)->driver_data) -static void adc_sam_isr(void *arg) +static int adc_sam_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) { - struct device *dev = (struct device *)arg; - const struct adc_sam_dev_cfg *const dev_cfg = DEV_CFG(dev); - struct adc_sam_dev_data *const dev_data = DEV_DATA(dev); - Afec *const afec = dev_cfg->regs; - struct channel_samples *samples = dev_data->samples; - u32_t isr_status; - u16_t value; - - isr_status = afec->AFEC_ISR; - for (int i = 0; i < ADC_CHANNELS; i++) { - if (isr_status & BIT(i)) { - /* Channel i has been sampled */ - afec->AFEC_CSELR = AFEC_CSELR_CSEL(i); - value = (u16_t)(afec->AFEC_CDR); - - if (samples[i].length > 0) { - /* Save sample */ - *samples[i].buffer = value; - samples[i].length--; - samples[i].buffer++; - - dev_data->measured_channels |= BIT(i); - } - if (samples[i].length == 0) { - afec->AFEC_CHDR |= BIT(i); - afec->AFEC_IDR |= BIT(i); - dev_data->active_channels &= ~BIT(i); - } - } + const struct adc_sam_cfg * const cfg = DEV_CFG(dev); + Afec *const afec = cfg->regs; + + u8_t channel_id = channel_cfg->channel_id; + + /* Clear the gain bits for the channel. */ + afec->AFEC_CGR &= ~(3 << channel_id * 2); + + switch (channel_cfg->gain) { + case ADC_GAIN_1: + /* A value of 0 in this register is a gain of 1. */ + break; + case ADC_GAIN_1_2: + afec->AFEC_CGR |= (1 << (channel_id * 2)); + break; + case ADC_GAIN_1_4: + afec->AFEC_CGR |= (2 << (channel_id * 2)); + break; + default: + SYS_LOG_ERR("Selected ADC gain is not valid"); + return -EINVAL; } - if (dev_data->active_chan_last == dev_data->measured_channels) { - if (!dev_data->active_channels) { - /* No more conversions needed */ - k_sem_give(&dev_data->sem_meas); - } else { - /* Start another conversion with remaining channels */ - dev_data->measured_channels = 0; - dev_data->active_chan_last = dev_data->active_channels; - afec->AFEC_CR = AFEC_CR_START; - } + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + SYS_LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; + } + + if (channel_cfg->reference != ADC_REF_EXTERNAL0) { + SYS_LOG_ERR("Selected reference is not valid"); + return -EINVAL; + } + + if (channel_cfg->differential) { + SYS_LOG_ERR("Differential input is not supported"); + return -EINVAL; } + + /* Set single ended channels to unsigned and differential channels + * to signed conversions. + */ + afec->AFEC_EMR &= ~(AFEC_EMR_SIGNMODE( + AFEC_EMR_SIGNMODE_SE_UNSG_DF_SIGN_Val)); + + return 0; } -static int adc_sam_read(struct device *dev, struct adc_seq_table *seq_tbl) +static void adc_sam_start_conversion(struct device *dev) { - const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev); - struct adc_sam_dev_data *const dev_data = DEV_DATA(dev); - Afec *const afec = dev_cfg->regs; - u8_t channel; - u32_t num_samples; + const struct adc_sam_cfg *const cfg = DEV_CFG(dev); + struct adc_sam_data *data = DEV_DATA(dev); + Afec *const afec = cfg->regs; - k_sem_take(&dev_data->mutex_thread, K_FOREVER); + data->channel_id = find_lsb_set(data->channels) - 1; - dev_data->active_channels = 0; + SYS_LOG_DBG("Starting channel %d", data->channel_id); - /* Enable chosen channels */ - for (int i = 0; i < seq_tbl->num_entries; i++) { - channel = seq_tbl->entries[i].channel_id; - if (channel >= ADC_CHANNELS) { - return -EINVAL; - } + /* Disable all channels. */ + afec->AFEC_CHDR = 0xfff; + afec->AFEC_IDR = 0xfff; - /* Check and set number of requested samples */ - num_samples = seq_tbl->entries[i].buffer_length / sizeof(u16_t); - if (!num_samples) { - return -EINVAL; - } - dev_data->samples[channel].length = num_samples; + /* Enable the ADC channel. This also enables/selects the channel pin as + * an input to the AFEC (50.5.1 SAM E70 datasheet). + */ + afec->AFEC_CHER = (1 << data->channel_id); - /* Set start of sample buffer */ - dev_data->samples[channel].buffer = - (u16_t *)seq_tbl->entries[i].buffer; + /* Enable the interrupt for the channel. */ + afec->AFEC_IER = (1 << data->channel_id); - /* Enable channel */ - dev_data->active_channels |= BIT(channel); - } + /* Start the conversions. */ + afec->AFEC_CR = AFEC_CR_START; +} + +/** + * This is only called once at the beginning of all the conversions, + * all channels as a group. + */ +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_sam_data *data = CONTAINER_OF(ctx, struct adc_sam_data, ctx); - /* Enable chosen channels and their interrupts */ - afec->AFEC_CHER = dev_data->active_channels; - afec->AFEC_IER = dev_data->active_channels; + data->channels = ctx->sequence->channels; - /* Start conversions */ - dev_data->measured_channels = 0; - dev_data->active_chan_last = dev_data->active_channels; - afec->AFEC_CR = AFEC_CR_START; - k_sem_take(&dev_data->sem_meas, K_FOREVER); + adc_sam_start_conversion(data->dev); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat_sampling) +{ + struct adc_sam_data *data = CONTAINER_OF(ctx, struct adc_sam_data, ctx); - k_sem_give(&dev_data->mutex_thread); + if (repeat_sampling) { + data->buffer = data->repeat_buffer; + } +} +static int check_buffer_size(const struct adc_sequence *sequence, + u8_t active_channels) +{ + size_t needed_buffer_size; + needed_buffer_size = active_channels * sizeof(u16_t); + if (sequence->options) { + needed_buffer_size *= (1 + sequence->options->extra_samplings); + } + if (sequence->buffer_size < needed_buffer_size) { + SYS_LOG_ERR("Provided buffer is too small (%u/%u)", + sequence->buffer_size, needed_buffer_size); + return -ENOMEM; + } return 0; } -static void adc_sam_configure(struct device *dev) +static int start_read(struct device *dev, const struct adc_sequence *sequence) { - const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev); - Afec *const afec = dev_cfg->regs; + struct adc_sam_data *data = DEV_DATA(dev); + int error = 0; + u32_t channels = sequence->channels; + + data->channels = 0; + + /* Signal an error if the channel selection is invalid (no channels or + * a non-existing one is selected). + */ + if (channels == 0 || + (channels & (~0UL << NUM_CHANNELS))) { + SYS_LOG_ERR("Invalid selection of channels"); + return -EINVAL; + } + + if (sequence->oversampling != 0) { + SYS_LOG_ERR("Oversampling is not supported"); + return -EINVAL; + } + + if (sequence->resolution != 12) { + /* TODO JKW: Support the Enhanced Resolution Mode 50.6.3 page + * 1544. + */ + SYS_LOG_ERR("ADC resolution value %d is not valid", + sequence->resolution); + return -EINVAL; + } + + if (sequence->options->interval_us != 0) { + SYS_LOG_ERR("Spaced intervals not supported"); + return -EINVAL; + } - /* Reset the AFEC */ + u8_t num_active_channels = 0; + u8_t channel = 0; + + while (channels > 0) { + if (channels & 1) { + ++num_active_channels; + } + channels >>= 1; + ++channel; + } + + error = check_buffer_size(sequence, num_active_channels); + if (error) { + return error; + } + + /* In the context you have a pointer to the adc_sam_data structure + * only. + */ + data->buffer = sequence->buffer; + data->repeat_buffer = sequence->buffer; + + /* At this point we allow the scheduler to do other things while + * we wait for the conversions to complete. This is provided by the + * adc_context functions. However, the caller of this function is + * blocked until the results are in. + */ + adc_context_start_read(&data->ctx, sequence); + + error = adc_context_wait_for_completion(&data->ctx); + adc_context_release(&data->ctx, error); + + return error; +} + +static int adc_sam_read(struct device *dev, + const struct adc_sequence *sequence) +{ + struct adc_sam_data *data = DEV_DATA(dev); + + adc_context_lock(&data->ctx, false, NULL); + return start_read(dev, sequence); +} + +static int adc_sam_init(struct device *dev) +{ + const struct adc_sam_cfg *const cfg = DEV_CFG(dev); + struct adc_sam_data *data = DEV_DATA(dev); + Afec *const afec = cfg->regs; + + /* Reset the AFEC. */ afec->AFEC_CR = AFEC_CR_SWRST; afec->AFEC_MR = AFEC_MR_TRGEN_DIS @@ -164,100 +279,127 @@ static void adc_sam_configure(struct device *dev) | AFEC_MR_ONE | AFEC_MR_USEQ_NUM_ORDER; - /* Set all channels CM voltage to Vrefp/2 (512) */ - for (int i = 0; i < ADC_CHANNELS; i++) { + /* Set all channels CM voltage to Vrefp/2 (512). */ + for (int i = 0; i < NUM_CHANNELS; i++) { afec->AFEC_CSELR = i; afec->AFEC_COCR = 512; } - /* Enable PGA and Current Bias */ + /* Enable PGA and Current Bias. */ afec->AFEC_ACR = AFEC_ACR_PGA0EN | AFEC_ACR_PGA1EN | AFEC_ACR_IBCTL(1); + + soc_pmc_peripheral_enable(cfg->periph_id); + + cfg->cfg_func(dev); + + data->dev = dev; + + adc_context_unlock_unconditionally(&data->ctx); + + return 0; } -static int adc_sam_initialize(struct device *dev) +#ifdef CONFIG_ADC_ASYNC +static int adc_sam_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) { - const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev); - struct adc_sam_dev_data *const dev_data = DEV_DATA(dev); + struct adc_sam_data *data = DEV_DATA(dev); - /* Initialize semaphores */ - k_sem_init(&dev_data->sem_meas, 0, 1); - k_sem_init(&dev_data->mutex_thread, 1, 1); + adc_context_lock(&data->ctx, true, async); + return start_read(dev, sequence); +} +#endif - /* Configure interrupts */ - dev_cfg->irq_config(); +static const struct adc_driver_api adc_sam_api = { + .channel_setup = adc_sam_channel_setup, + .read = adc_sam_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_sam_read_async, +#endif +}; - /* Enable module's clock */ - soc_pmc_peripheral_enable(dev_cfg->periph_id); +static void adc_sam_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct adc_sam_data *data = DEV_DATA(dev); + const struct adc_sam_cfg *const cfg = DEV_CFG(dev); + Afec *const afec = cfg->regs; + u16_t result; - /* Configure ADC */ - adc_sam_configure(dev); + afec->AFEC_CHDR |= BIT(data->channel_id); + afec->AFEC_IDR |= BIT(data->channel_id); - /* Enable module interrupt */ - irq_enable(dev_cfg->irq_id); + afec->AFEC_CSELR = AFEC_CSELR_CSEL(data->channel_id); + result = (u16_t)(afec->AFEC_CDR); - SYS_LOG_INF("Device %s initialized", DEV_NAME(dev)); + *data->buffer++ = result; + data->channels &= ~BIT(data->channel_id); - return 0; + if (data->channels) { + adc_sam_start_conversion(dev); + } else { + /* Called once all conversions have completed.*/ + adc_context_on_sampling_done(&data->ctx, dev); + } } -static const struct adc_driver_api adc_sam_driver_api = { - .read = adc_sam_read, +#ifdef CONFIG_ADC_0 +static void adc0_sam_cfg_func(struct device *dev); + +static const struct adc_sam_cfg adc0_sam_cfg = { + .regs = (Afec *)CONFIG_ADC_0_BASE_ADDRESS, + .cfg_func = adc0_sam_cfg_func, + .periph_id = CONFIG_ADC_0_PERIPHERAL_ID, + .afec_trg_pin = PIN_AFE0_ADTRG, }; -/* ADC_0 */ +static struct adc_sam_data adc0_sam_data = { + ADC_CONTEXT_INIT_TIMER(adc0_sam_data, ctx), + ADC_CONTEXT_INIT_LOCK(adc0_sam_data, ctx), + ADC_CONTEXT_INIT_SYNC(adc0_sam_data, ctx), +}; -#ifdef CONFIG_ADC_0 -static struct device DEVICE_NAME_GET(adc0_sam); +DEVICE_AND_API_INIT(adc0_sam, CONFIG_ADC_0_NAME, adc_sam_init, + &adc0_sam_data, &adc0_sam_cfg, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &adc_sam_api); -static void adc0_irq_config(void) +static void adc0_sam_cfg_func(struct device *dev) { - IRQ_CONNECT(AFEC0_IRQn, CONFIG_ADC_0_IRQ_PRI, adc_sam_isr, + IRQ_CONNECT(CONFIG_ADC_0_IRQ, CONFIG_ADC_0_IRQ_PRI, adc_sam_isr, DEVICE_GET(adc0_sam), 0); + irq_enable(CONFIG_ADC_0_IRQ); } -static const struct soc_gpio_pin pin_adc0 = PIN_AFE0_ADTRG; +#endif /* CONFIG_ADC_0 */ -static const struct adc_sam_dev_cfg adc0_sam_config = { - .regs = AFEC0, - .pin_trigger = &pin_adc0, - .periph_id = ID_AFEC0, - .irq_config = adc0_irq_config, - .irq_id = AFEC0_IRQn, -}; - -static struct adc_sam_dev_data adc0_sam_data; +#ifdef CONFIG_ADC_1 +static void adc1_sam_cfg_func(struct device *dev); -DEVICE_AND_API_INIT(adc0_sam, CONFIG_ADC_0_NAME, &adc_sam_initialize, - &adc0_sam_data, &adc0_sam_config, POST_KERNEL, - CONFIG_ADC_INIT_PRIORITY, &adc_sam_driver_api); -#endif +static const struct adc_sam_cfg adc1_sam_cfg = { + .regs = (Afec *)CONFIG_ADC_1_BASE_ADDRESS, + .cfg_func = adc1_sam_cfg_func, + .periph_id = CONFIG_ADC_1_PERIPHERAL_ID, + .afec_trg_pin = PIN_AFE1_ADTRG, +}; -/* ADC_1 */ +static struct adc_sam_data adc1_sam_data = { + ADC_CONTEXT_INIT_TIMER(adc1_sam_data, ctx), + ADC_CONTEXT_INIT_LOCK(adc1_sam_data, ctx), + ADC_CONTEXT_INIT_SYNC(adc1_sam_data, ctx), +}; -#ifdef CONFIG_ADC_1 -static struct device DEVICE_NAME_GET(adc1_sam); +DEVICE_AND_API_INIT(adc1_sam, CONFIG_ADC_1_NAME, adc_sam_init, + &adc1_sam_data, &adc1_sam_cfg, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &adc_sam_api); -static void adc1_irq_config(void) +static void adc1_sam_cfg_func(struct device *dev) { - IRQ_CONNECT(AFEC1_IRQn, CONFIG_ADC_1_IRQ_PRI, adc_sam_isr, + IRQ_CONNECT(CONFIG_ADC_1_IRQ, CONFIG_ADC_1_IRQ_PRI, adc_sam_isr, DEVICE_GET(adc1_sam), 0); + irq_enable(CONFIG_ADC_1_IRQ); } -static const struct soc_gpio_pin pin_adc1 = PIN_AFE1_ADTRG; - -static const struct adc_sam_dev_cfg adc1_sam_config = { - .regs = AFEC1, - .pin_trigger = &pin_adc1, - .periph_id = ID_AFEC1, - .irq_config = adc1_irq_config, - .irq_id = AFEC1_IRQn, -}; - -static struct adc_sam_dev_data adc1_sam_data; - -DEVICE_AND_API_INIT(adc1_sam, CONFIG_ADC_1_NAME, &adc_sam_initialize, - &adc1_sam_data, &adc1_sam_config, POST_KERNEL, - CONFIG_ADC_INIT_PRIORITY, &adc_sam_driver_api); -#endif +#endif /* CONFIG_ADC_1 */ diff --git a/drivers/grove/light_sensor.c b/drivers/grove/light_sensor.c index 1c176aaead716..3e39a0f0a6dad 100644 --- a/drivers/grove/light_sensor.c +++ b/drivers/grove/light_sensor.c @@ -16,16 +16,26 @@ struct gls_data { struct device *adc; - struct adc_seq_entry sample; - struct adc_seq_table adc_table; + struct adc_channel_cfg ch10_cfg; u8_t adc_buffer[4]; }; +static struct adc_sequence_options options = { + .interval_us = 12, + .extra_samplings = 0, +}; + +static struct adc_sequence adc_table = { + .options = &options, +}; + +#define ADC_RESOLUTION 8 + static int gls_sample_fetch(struct device *dev, enum sensor_channel chan) { struct gls_data *drv_data = dev->driver_data; - return adc_read(drv_data->adc, &drv_data->adc_table); + return adc_read(drv_data->adc, &adc_table); } static int gls_channel_get(struct device *dev, @@ -44,7 +54,7 @@ static int gls_channel_get(struct device *dev, /* * The formula for converting the analog value to lux is taken from * the UPM project: - * https://github.com/intel-iot-devkit/upm/blob/master/src/grove/grove.cxx#L161 + * https://github.com/intel-iot-devkit/upm/blob/master/src/grove/grove.cxx#L161 */ ldr_val = (1023.0 - analog_val) * 10.0 / analog_val; dval = 10000.0 / pow(ldr_val * 15.0, 4.0/3.0); @@ -71,15 +81,18 @@ static int gls_init(struct device *dev) return -EINVAL; } - drv_data->sample.sampling_delay = 12; - drv_data->sample.channel_id = CONFIG_GROVE_LIGHT_SENSOR_ADC_CHANNEL; - drv_data->sample.buffer = drv_data->adc_buffer; - drv_data->sample.buffer_length = 4; - - drv_data->adc_table.entries = &drv_data->sample; - drv_data->adc_table.num_entries = 1; - - adc_enable(drv_data->adc); + /*Change following parameters according to board if necessary*/ + drv_data->ch10_cfg.channel_id = CONFIG_GROVE_LIGHT_SENSOR_ADC_CHANNEL; + drv_data->ch10_cfg.differential = false; + drv_data->ch10_cfg.gain = ADC_GAIN_1, + drv_data->ch10_cfg.reference = ADC_REF_INTERNAL; + drv_data->ch10_cfg.acquisition_time = ADC_ACQ_TIME_DEFAULT; + adc_table.buffer = drv_data->adc_buffer; + adc_table.channels = BIT(CONFIG_GROVE_LIGHT_SENSOR_ADC_CHANNEL); + adc_table.resolution = ADC_RESOLUTION; + adc_table.buffer_size = 4; + + adc_channel_setup(drv_data->adc, &drv_data->ch10_cfg); dev->driver_api = &gls_api; diff --git a/drivers/grove/temperature_sensor.c b/drivers/grove/temperature_sensor.c index 919bd6a9872ec..574d8f20e577c 100644 --- a/drivers/grove/temperature_sensor.c +++ b/drivers/grove/temperature_sensor.c @@ -21,18 +21,28 @@ #define B_CONST 4250 #endif +#define ADC_RESOLUTION 10 + struct gts_data { struct device *adc; - struct adc_seq_entry sample; - struct adc_seq_table adc_table; + struct adc_channel_cfg ch10_cfg; u8_t adc_buffer[4]; }; +static struct adc_sequence_options options = { + .extra_samplings = 0, + .interval_us = 15, +}; + +static struct adc_sequence adc_table = { + .options = &options, +}; + static int gts_sample_fetch(struct device *dev, enum sensor_channel chan) { struct gts_data *drv_data = dev->driver_data; - return adc_read(drv_data->adc, &drv_data->adc_table); + return adc_read(drv_data->adc, &adc_table); } static int gts_channel_get(struct device *dev, @@ -78,16 +88,18 @@ static int gts_init(struct device *dev) return -EINVAL; } - drv_data->sample.sampling_delay = 12; - drv_data->sample.channel_id = - CONFIG_GROVE_TEMPERATURE_SENSOR_ADC_CHANNEL; - drv_data->sample.buffer = drv_data->adc_buffer; - drv_data->sample.buffer_length = 4; - - drv_data->adc_table.entries = &drv_data->sample; - drv_data->adc_table.num_entries = 1; - - adc_enable(drv_data->adc); + /*Change following parameters according to board if necessary*/ + drv_data->ch10_cfg.channel_id = CONFIG_GROVE_TEMPERATURE_SENSOR_ADC_CHANNEL; + drv_data->ch10_cfg.differential = false; + drv_data->ch10_cfg.gain = ADC_GAIN_1, + drv_data->ch10_cfg.reference = ADC_REF_INTERNAL; + drv_data->ch10_cfg.acquisition_time = ADC_ACQ_TIME_DEFAULT; + adc_table.buffer = drv_data->adc_buffer; + adc_table.resolution = ADC_RESOLUTION; + adc_table.buffer_size = 4; + adc_table.channels = BIT(CONFIG_GROVE_TEMPERATURE_SENSOR_ADC_CHANNEL); + + adc_channel_setup(drv_data->adc, &drv_data->ch10_cfg); dev->driver_api = >s_api; diff --git a/dts/arc/quark_se_c1000_ss.dtsi b/dts/arc/quark_se_c1000_ss.dtsi index a79b0f50416d8..2c74f9bc334b9 100644 --- a/dts/arc/quark_se_c1000_ss.dtsi +++ b/dts/arc/quark_se_c1000_ss.dtsi @@ -176,5 +176,18 @@ status = "disabled"; }; + + adc0: adc@80015000 { + compatible = "snps,dw-adc"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x80015000 0x05>; + interrupts = <19 0>, <18 0>; + interrupt-names = "normal", "error"; + interrupt-parent = <&core_intc>; + label = "ADC_0"; + + status = "disabled"; + }; }; }; diff --git a/dts/arm/nordic/nrf51822.dtsi b/dts/arm/nordic/nrf51822.dtsi index d662f821a3213..db98c40eafbbf 100644 --- a/dts/arm/nordic/nrf51822.dtsi +++ b/dts/arm/nordic/nrf51822.dtsi @@ -39,6 +39,14 @@ }; soc { + adc: adc@40007000 { + compatible = "nordic,nrf-adc"; + reg = <0x40007000 0x1000>; + interrupts = <7 1>; + status = "disabled"; + label = "ADC_0"; + }; + uart0: uart@40002000 { compatible = "nordic,nrf-uart"; reg = <0x40002000 0x1000>; diff --git a/dts/arm/nordic/nrf52810.dtsi b/dts/arm/nordic/nrf52810.dtsi index 708f64859e121..d358f587257b8 100644 --- a/dts/arm/nordic/nrf52810.dtsi +++ b/dts/arm/nordic/nrf52810.dtsi @@ -39,6 +39,14 @@ }; soc { + adc: adc@40007000 { + compatible = "nordic,nrf-saadc"; + reg = <0x40007000 0x1000>; + interrupts = <7 1>; + status = "disabled"; + label = "ADC_0"; + }; + uart0: uart@40002000 { compatible = "nordic,nrf-uarte"; reg = <0x40002000 0x1000>; diff --git a/dts/arm/nordic/nrf52832.dtsi b/dts/arm/nordic/nrf52832.dtsi index bd92c8f266243..a88896369a307 100644 --- a/dts/arm/nordic/nrf52832.dtsi +++ b/dts/arm/nordic/nrf52832.dtsi @@ -39,6 +39,14 @@ }; soc { + adc: adc@40007000 { + compatible = "nordic,nrf-saadc"; + reg = <0x40007000 0x1000>; + interrupts = <7 1>; + status = "disabled"; + label = "ADC_0"; + }; + uart0: uart@40002000 { /* uart can be either UART or UARTE, for the user to pick */ /* compatible = "nordic,nrf-uarte" or "nordic,nrf-uart"; */ diff --git a/dts/arm/nordic/nrf52840.dtsi b/dts/arm/nordic/nrf52840.dtsi index dd5d478abed16..de914dc297802 100644 --- a/dts/arm/nordic/nrf52840.dtsi +++ b/dts/arm/nordic/nrf52840.dtsi @@ -40,6 +40,14 @@ }; soc { + adc: adc@40007000 { + compatible = "nordic,nrf-saadc"; + reg = <0x40007000 0x1000>; + interrupts = <7 1>; + status = "disabled"; + label = "ADC_0"; + }; + uart0: uart@40002000 { /* uart can be either UART or UARTE, for the user to pick */ /* compatible = "nordic,nrf-uarte" or "nordic,nrf-uart"; */ diff --git a/dts/bindings/iio/adc/nordic,nrf-adc.yaml b/dts/bindings/iio/adc/nordic,nrf-adc.yaml new file mode 100644 index 0000000000000..2617c22a1a979 --- /dev/null +++ b/dts/bindings/iio/adc/nordic,nrf-adc.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2018, Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Nordic Semiconductor nRF Family ADC +id: nordic,nrf-adc +version: 0.1 + +description: > + This is a representation of the nRF ADC node + +inherits: + !include adc.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "nordic,nrf-adc" + + reg: + type: array + description: mmio register space + generation: define + category: required + + interrupts: + type: array + category: required + description: required interrupts + generation: define +... diff --git a/dts/bindings/iio/adc/nordic,nrf-saadc.yaml b/dts/bindings/iio/adc/nordic,nrf-saadc.yaml new file mode 100644 index 0000000000000..0c988d3717c66 --- /dev/null +++ b/dts/bindings/iio/adc/nordic,nrf-saadc.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2018, Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: Nordic Semiconductor nRF Family SAADC +id: nordic,nrf-saadc +version: 0.1 + +description: > + This is a representation of the nRF SAADC node + +inherits: + !include adc.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "nordic,nrf-saadc" + + reg: + type: array + description: mmio register space + generation: define + category: required + + interrupts: + type: array + category: required + description: required interrupts + generation: define +... diff --git a/dts/bindings/iio/adc/snps,dw-adc.yaml b/dts/bindings/iio/adc/snps,dw-adc.yaml new file mode 100644 index 0000000000000..ea34c5eeb6a03 --- /dev/null +++ b/dts/bindings/iio/adc/snps,dw-adc.yaml @@ -0,0 +1,35 @@ +# +# Copyright (c) 2018 Intel Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: DesignWare ADC +id: snps,dw-adc +version: 0.1 + +description: > + This is a representation of the DesignWare ADC node + +inherits: + !include adc.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "snps,dw-adc" + + reg: + type: array + description: mmio register space + generation: define + category: required + + interrupts: + type: array + category: required + description: required interrupts + generation: define +... diff --git a/ext/hal/nordic/Kconfig b/ext/hal/nordic/Kconfig index 81c6cd33235c4..ab80a1dc86ade 100644 --- a/ext/hal/nordic/Kconfig +++ b/ext/hal/nordic/Kconfig @@ -10,9 +10,15 @@ config HAS_NORDIC_DRIVERS config HAS_NRFX bool +config NRFX_ADC + bool + config NRFX_PWM bool +config NRFX_SAADC + bool + config NRFX_SPI bool diff --git a/ext/hal/nordic/nrfx_config_nrf51.h b/ext/hal/nordic/nrfx_config_nrf51.h index 18328cd5c6841..005b4116b5ed5 100644 --- a/ext/hal/nordic/nrfx_config_nrf51.h +++ b/ext/hal/nordic/nrfx_config_nrf51.h @@ -38,8 +38,8 @@ // NRFX_ADC_ENABLED - nrfx_adc - ADC peripheral driver //========================================================== -#ifndef NRFX_ADC_ENABLED -#define NRFX_ADC_ENABLED 0 +#ifdef CONFIG_NRFX_ADC +#define NRFX_ADC_ENABLED 1 #endif // NRFX_ADC_CONFIG_IRQ_PRIORITY - Interrupt priority diff --git a/ext/hal/nordic/nrfx_config_nrf52810.h b/ext/hal/nordic/nrfx_config_nrf52810.h index cb17db3e97625..f1b370c460653 100644 --- a/ext/hal/nordic/nrfx_config_nrf52810.h +++ b/ext/hal/nordic/nrfx_config_nrf52810.h @@ -1125,8 +1125,8 @@ // NRFX_SAADC_ENABLED - nrfx_saadc - SAADC peripheral driver //========================================================== -#ifndef NRFX_SAADC_ENABLED -#define NRFX_SAADC_ENABLED 0 +#ifdef CONFIG_NRFX_SAADC +#define NRFX_SAADC_ENABLED 1 #endif // NRFX_SAADC_CONFIG_RESOLUTION - Resolution diff --git a/ext/hal/nordic/nrfx_config_nrf52832.h b/ext/hal/nordic/nrfx_config_nrf52832.h index ab1d70c259839..e29b994075930 100644 --- a/ext/hal/nordic/nrfx_config_nrf52832.h +++ b/ext/hal/nordic/nrfx_config_nrf52832.h @@ -1500,8 +1500,8 @@ // NRFX_SAADC_ENABLED - nrfx_saadc - SAADC peripheral driver //========================================================== -#ifndef NRFX_SAADC_ENABLED -#define NRFX_SAADC_ENABLED 0 +#ifdef CONFIG_NRFX_SAADC +#define NRFX_SAADC_ENABLED 1 #endif // NRFX_SAADC_CONFIG_RESOLUTION - Resolution diff --git a/ext/hal/nordic/nrfx_config_nrf52840.h b/ext/hal/nordic/nrfx_config_nrf52840.h index 096b7403e0b27..675678a28f661 100644 --- a/ext/hal/nordic/nrfx_config_nrf52840.h +++ b/ext/hal/nordic/nrfx_config_nrf52840.h @@ -1607,8 +1607,8 @@ // NRFX_SAADC_ENABLED - nrfx_saadc - SAADC peripheral driver //========================================================== -#ifndef NRFX_SAADC_ENABLED -#define NRFX_SAADC_ENABLED 0 +#ifdef CONFIG_NRFX_SAADC +#define NRFX_SAADC_ENABLED 1 #endif // NRFX_SAADC_CONFIG_RESOLUTION - Resolution diff --git a/include/adc.h b/include/adc.h index f4dee8e401183..4da12f82182aa 100644 --- a/include/adc.h +++ b/include/adc.h @@ -4,6 +4,7 @@ */ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2015 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -12,7 +13,6 @@ #ifndef __INCLUDE_ADC_H__ #define __INCLUDE_ADC_H__ -#include #include #ifdef __cplusplus @@ -26,121 +26,345 @@ extern "C" { * @{ */ +/** @brief ADC channel gain factors. */ +enum adc_gain { + ADC_GAIN_1_6, /**< x 1/6. */ + ADC_GAIN_1_5, /**< x 1/5. */ + ADC_GAIN_1_4, /**< x 1/4. */ + ADC_GAIN_1_3, /**< x 1/3. */ + ADC_GAIN_1_2, /**< x 1/2. */ + ADC_GAIN_2_3, /**< x 2/3. */ + ADC_GAIN_1, /**< x 1. */ + ADC_GAIN_2, /**< x 2. */ + ADC_GAIN_3, /**< x 3. */ + ADC_GAIN_4, /**< x 4. */ + ADC_GAIN_8, /**< x 8. */ + ADC_GAIN_16, /**< x 16. */ + ADC_GAIN_32, /**< x 32. */ + ADC_GAIN_64, /**< x 64. */ +}; + +/** @brief ADC references. */ +enum adc_reference { + ADC_REF_VDD_1, /**< VDD. */ + ADC_REF_VDD_1_2, /**< VDD/2. */ + ADC_REF_VDD_1_3, /**< VDD/3. */ + ADC_REF_VDD_1_4, /**< VDD/4. */ + ADC_REF_INTERNAL, /**< Internal. */ + ADC_REF_EXTERNAL0, /**< External, input 0. */ + ADC_REF_EXTERNAL1, /**< External, input 1. */ +}; + +/** Acquisition time is expressed in microseconds. */ +#define ADC_ACQ_TIME_MICROSECONDS (1u) +/** Acquisition time is expressed in nanoseconds. */ +#define ADC_ACQ_TIME_NANOSECONDS (2u) +/** Acquisition time is expressed in ADC ticks. */ +#define ADC_ACQ_TIME_TICKS (3u) +/** Macro for composing the acquisition time value in given units. */ +#define ADC_ACQ_TIME(unit, value) (((unit) << 14) | ((value) & BIT_MASK(14))) +/** Value indicating that the default acquisition time should be used. */ +#define ADC_ACQ_TIME_DEFAULT 0 + +#define ADC_ACQ_TIME_UNIT(time) (((time) >> 14) & BIT_MASK(2)) +#define ADC_ACQ_TIME_VALUE(time) ((time) & BIT_MASK(14)) + /** - * @brief ADC driver Sequence entry - * - * This structure defines a sequence entry used - * to define a sample from a specific channel. + * @brief Structure for specifying the configuration of an ADC channel. */ -struct adc_seq_entry { - /** Clock ticks delay before sampling the ADC. */ - s32_t sampling_delay; +struct adc_channel_cfg { + /** Gain selection. */ + enum adc_gain gain; + + /** Reference selection. */ + enum adc_reference reference; + + /** + * Acquisition time. + * Use the ADC_ACQ_TIME macro to compose the value for this field or + * pass ADC_ACQ_TIME_DEFAULT to use the default setting for a given + * hardware (e.g. when the hardware does not allow to configure the + * acquisition time). + * Particular drivers do not necessarily support all the possible units. + * Value range is 0-16383 for a given unit. + */ + u16_t acquisition_time; + + /** + * Channel identifier. + * This value primarily identifies the channel within the ADC API - when + * a read request is done, the corresponding bit in the "channels" field + * of the "adc_sequence" structure must be set to include this channel + * in the sampling. + * For hardware that does not allow selection of analog inputs for given + * channels, but rather have dedicated ones, this value also selects the + * physical ADC input to be used in the sampling. Otherwise, when it is + * needed to explicitly select an analog input for the channel, or two + * inputs when the channel is a differential one, the selection is done + * in "input_positive" and "input_negative" fields. + * Particular drivers indicate which one of the above two cases they + * support by selecting or not a special hidden Kconfig option named + * ADC_CONFIGURABLE_INPUTS. If this option is not selected, the macro + * CONFIG_ADC_CONFIGURABLE_INPUTS is not defined and consequently the + * mentioned two fields are not present in this structure. + * While this API allows identifiers from range 0-31, particular drivers + * may support only a limited number of channel identifiers (dependent + * on the underlying hardware capabilities or configured via a dedicated + * Kconfig option). + */ + u8_t channel_id : 5; + + /** Channel type: single-ended or differential. */ + u8_t differential : 1; + +#ifdef CONFIG_ADC_CONFIGURABLE_INPUTS + /** + * Positive ADC input. + * This is a driver dependent value that identifies an ADC input to be + * associated with the channel. + */ + u8_t input_positive; + + /** + * Negative ADC input (used only for differential channels). + * This is a driver dependent value that identifies an ADC input to be + * associated with the channel. + */ + u8_t input_negative; +#endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */ +}; + - /** Buffer pointer where the sample is written.*/ - u8_t *buffer; +/* Forward declaration of the adc_sequence structure. */ +struct adc_sequence; - /** Length of the sampling buffer.*/ - u32_t buffer_length; +/** + * @brief Action to be performed after a sampling is done. + */ +enum adc_action { + /** The sequence should be continued normally. */ + ADC_ACTION_CONTINUE = 0, - /** Channel ID that should be sampled from the ADC */ - u8_t channel_id; + /** + * The sampling should be repeated. New samples or sample should be + * read from the ADC and written in the same place as the recent ones. + */ + ADC_ACTION_REPEAT, - u8_t stride[3]; + /** The sequence should be finished immediately. */ + ADC_ACTION_FINISH, }; /** - * @brief ADC driver Sequence table + * @brief Type definition of the optional callback function to be called after + * a requested sampling is done. * - * This structure defines a list of sequence entries - * used to execute a sequence of samplings. + * @param dev Pointer to the device structure for the driver + * instance. + * @param sequence Pointer to the sequence structure that triggered the + * sampling. + * @param sampling_index Index (0-65535) of the sampling done. + * + * @returns Action to be performed by the driver. See @ref adc_action. + */ +typedef enum adc_action (*adc_sequence_callback)( + struct device *dev, + const struct adc_sequence *sequence, + u16_t sampling_index); + +/** + * @brief Structure defining additional options for an ADC sampling sequence. + */ +struct adc_sequence_options { + /** + * Interval between consecutive samplings (in microseconds), 0 means + * sample as fast as possible, without involving any timer. + * The accuracy of this interval is dependent on the implementation of + * a given driver. The default routine that handles the intervals uses + * a kernel timer for this purpose, thus, it has the accuracy of the + * kernel's system clock. Particular drivers may use some dedicated + * hardware timers and achieve a better precision. + */ + u32_t interval_us; + + /** + * Callback function to be called after each sampling is done. + * Optional - set to NULL if it is not needed. + */ + adc_sequence_callback callback; + + /** + * Number of extra samplings to perform (the total number of samplings + * is 1 + extra_samplings). + */ + u16_t extra_samplings; +}; + +/** + * @brief Structure defining an ADC sampling sequence. */ -struct adc_seq_table { - /* Pointer to a sequence entry array. */ - struct adc_seq_entry *entries; +struct adc_sequence { + /** + * Pointer to a structure defining additional options for the sequence. + * If NULL, the sequence consists of a single sampling. + */ + const struct adc_sequence_options *options; + + /** + * Bit-mask indicating the channels to be included in each sampling + * of this sequence. + * All selected channels must be configured with adc_channel_setup() + * before they are used in a sequence. + */ + u32_t channels; + + /** + * Pointer to a buffer where the samples are to be written. Samples + * from subsequent samplings are written sequentially in the buffer. + * The number of samples written for each sampling is determined by + * the number of channels selected in the "channels" field. + * The buffer must be of an appropriate size, taking into account + * the number of selected channels and the ADC resolution used, + * as well as the number of samplings contained in the sequence. + */ + void *buffer; + + /** + * Specifies the actual size of the buffer pointed by the "buffer" + * field (in bytes). The driver must ensure that samples are not + * written beyond the limit and it must return an error if the buffer + * turns out to be not large enough to hold all the requested samples. + */ + size_t buffer_size; - /* Number of entries in the sequence entry array. */ - u8_t num_entries; - u8_t stride[3]; + /** + * ADC resolution. + * For single-ended channels the sample values are from range: + * 0 .. 2^resolution - 1, + * for differential ones: + * - 2^(resolution-1) .. 2^(resolution-1) - 1. + */ + u8_t resolution; + + /** + * Oversampling setting. + * Each sample is averaged from 2^oversampling conversion results. + * This feature may be unsupported by a given ADC hardware, or in + * a specific mode (e.g. when sampling multiple channels). + */ + u8_t oversampling; }; + +/** + * @brief Type definition of ADC API function for configuring a channel. + * See adc_channel_setup() for argument descriptions. + */ +typedef int (*adc_api_channel_setup)(struct device *dev, + const struct adc_channel_cfg *channel_cfg); + +/** + * @brief Type definition of ADC API function for setting a read request. + * See adc_read() for argument descriptions. + */ +typedef int (*adc_api_read)(struct device *dev, + const struct adc_sequence *sequence); + +#ifdef CONFIG_ADC_ASYNC +/** + * @brief Type definition of ADC API function for setting an asynchronous + * read request. + * See adc_read_async() for argument descriptions. + */ +typedef int (*adc_api_read_async)(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async); +#endif + /** * @brief ADC driver API * - * This structure holds all API function pointers. + * This is the mandatory API any ADC driver needs to expose. */ struct adc_driver_api { - /** Pointer to the enable routine. */ - void (*enable)(struct device *dev); - - /** Pointer to the disable routine. */ - void (*disable)(struct device *dev); - - /** Pointer to the read routine. */ - int (*read)(struct device *dev, struct adc_seq_table *seq_table); + adc_api_channel_setup channel_setup; + adc_api_read read; +#ifdef CONFIG_ADC_ASYNC + adc_api_read_async read_async; +#endif }; /** - * @brief Enable ADC hardware + * @brief Configure an ADC channel. * - * This routine enables the ADC hardware block for data sampling for the - * specified device. + * It is required to call this function and configure each channel before it is + * selected for a read request. * - * @param dev Pointer to the device structure for the driver instance. + * @param dev Pointer to the device structure for the driver instance. + * @param channel_cfg Channel configuration. * - * @return N/A + * @retval 0 On success. + * @retval -EINVAL If a parameter with an invalid value has been provided. */ -__syscall void adc_enable(struct device *dev); - -static inline void _impl_adc_enable(struct device *dev) +static inline int adc_channel_setup(struct device *dev, + const struct adc_channel_cfg *channel_cfg) { const struct adc_driver_api *api = dev->driver_api; - api->enable(dev); + return api->channel_setup(dev, channel_cfg); } /** - * @brief Disable ADC hardware - * - * This routine disables the ADC hardware block for data sampling for the - * specified device. + * @brief Set a read request. * - * @param dev Pointer to the device structure for the driver instance. + * @param dev Pointer to the device structure for the driver instance. + * @param sequence Structure specifying requested sequence of samplings. * - * @return N/A + * @retval 0 On success. + * @retval -EINVAL If a parameter with an invalid value has been provided. + * @retval -ENOMEM If the provided buffer is to small to hold the results + * of all requested samplings. + * @retval -ENOTSUP If the requested mode of operation is not supported. + * @retval -EIO If another sampling was triggered while the previous one + * was still in progress. This may occur only when samplings + * are done with intervals, and it indicates that the selected + * interval was too small. All requested samples are written + * in the buffer, but at least some of them were taken with + * an extra delay compared to what was scheduled. */ -__syscall void adc_disable(struct device *dev); - -static inline void _impl_adc_disable(struct device *dev) +static inline int adc_read(struct device *dev, + const struct adc_sequence *sequence) { const struct adc_driver_api *api = dev->driver_api; - api->disable(dev); + return api->read(dev, sequence); } +#ifdef CONFIG_ADC_ASYNC /** - * @brief Set a read request. + * @brief Set an asynchronous read request. * - * This routine sends a read or sampling request to the ADC hardware block. - * A sequence table describes the read request. - * The routine returns once the ADC completes the read sequence. - * The sample data can be retrieved from the memory buffers in - * the sequence table structure. + * @param dev Pointer to the device structure for the driver instance. + * @param sequence Structure specifying requested sequence of samplings. + * @param async Pointer to a valid and ready to be signaled struct + * k_poll_signal. (Note: if NULL this function will not notify + * the end of the transaction, and whether it went successfully + * or not). * - * @param dev Pointer to the device structure for the driver instance. - * @param seq_table Pointer to the structure representing the sequence table. + * @returns The same + * 0 on success, negative error code otherwise. The returned values + * are the * - * @retval 0 On success - * @retval else Otherwise. */ -__syscall int adc_read(struct device *dev, struct adc_seq_table *seq_table); - -static inline int _impl_adc_read(struct device *dev, - struct adc_seq_table *seq_table) +static inline int adc_read_async(struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) { const struct adc_driver_api *api = dev->driver_api; - return api->read(dev, seq_table); + return api->read_async(dev, sequence, async); } +#endif /* CONFIG_ADC_ASYNC */ /** * @} @@ -150,6 +374,4 @@ static inline int _impl_adc_read(struct device *dev, } #endif -#include - #endif /* __INCLUDE_ADC_H__ */ diff --git a/samples/sensor/grove_light/sample.yaml b/samples/sensor/grove_light/sample.yaml index f033adf0f4bc8..9b6c8ef6deeb0 100644 --- a/samples/sensor/grove_light/sample.yaml +++ b/samples/sensor/grove_light/sample.yaml @@ -2,8 +2,7 @@ sample: name: Grove Light Sensor tests: test: - platform_whitelist: arduino_101_sss quark_d2000_crb - arduino_due + platform_whitelist: arduino_101_sss arduino_due tags: drivers sensor grove light harness: grove depends_on: adc diff --git a/samples/sensor/grove_light/src/main.c b/samples/sensor/grove_light/src/main.c index 8541bf931e5da..e10b42b6f645b 100644 --- a/samples/sensor/grove_light/src/main.c +++ b/samples/sensor/grove_light/src/main.c @@ -14,20 +14,29 @@ void main(void) { - struct device *dev = device_get_binding("GROVE_LIGHT_SENSOR"); + struct device *dev = device_get_binding(CONFIG_GROVE_LIGHT_SENSOR_NAME); if (dev == NULL) { printk("device not found. aborting test.\n"); return; } while (1) { + int read; struct sensor_value lux; - sensor_sample_fetch(dev); - sensor_channel_get(dev, SENSOR_CHAN_LIGHT, &lux); + read = sensor_sample_fetch(dev); + if (read) { + printk("sample fetch error %d\n", read); + continue; + } - printf("lux: %f\n", sensor_value_to_double(&lux)); + sensor_channel_get(dev, SENSOR_CHAN_LIGHT, &lux); +#ifdef CONFIG_NEWLIB_LIBC_FLOAT_PRINTF + printk("lux: %d\n", sensor_value_to_double(&lux)); +#else + printk("lux: %d\n", lux.val1); +#endif k_sleep(SLEEP_TIME); } } diff --git a/samples/sensor/grove_temperature/sample.yaml b/samples/sensor/grove_temperature/sample.yaml index 792834275dbe5..5fe3e660c93d6 100644 --- a/samples/sensor/grove_temperature/sample.yaml +++ b/samples/sensor/grove_temperature/sample.yaml @@ -3,8 +3,7 @@ sample: tests: test: min_flash: 33 - platform_whitelist: arduino_101_sss quark_d2000_crb - arduino_due + platform_whitelist: arduino_101_sss arduino_due tags: drivers sensor grove temperature harness: grove depends_on: adc diff --git a/samples/sensor/grove_temperature/src/main.c b/samples/sensor/grove_temperature/src/main.c index 1f803e706e88a..0b8a52eee373e 100644 --- a/samples/sensor/grove_temperature/src/main.c +++ b/samples/sensor/grove_temperature/src/main.c @@ -19,7 +19,9 @@ void main(void) { - struct device *dev = device_get_binding("GROVE_TEMPERATURE_SENSOR"); + struct device *dev = device_get_binding(CONFIG_GROVE_TEMPERATURE_SENSOR_NAME); + struct sensor_value temp; + int read; if (dev == NULL) { printf("device not found. aborting test.\n"); @@ -41,9 +43,12 @@ void main(void) #endif while (1) { - struct sensor_value temp; - sensor_sample_fetch(dev); + read = sensor_sample_fetch(dev); + if (read) { + printk("sample fetch error\n"); + continue; + } sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp); #ifdef CONFIG_GROVE_LCD_RGB char row[16]; @@ -57,14 +62,23 @@ void main(void) /* display temperature on LCD */ glcd_cursor_pos_set(glcd, 0, 0); +#ifdef CONFIG_NEWLIB_LIBC_FLOAT_PRINTF sprintf(row, "T:%.2f%cC", sensor_value_to_double(&temp), 223 /* degree symbol */); +#else + sprintf(row, "T:%d%cC", temp.val1, + 223 /* degree symbol */); +#endif glcd_print(glcd, row, strlen(row)); #endif - printf("Temperature: %.2f C\n", sensor_value_to_double(&temp)); +#ifdef CONFIG_NEWLIB_LIBC_FLOAT_PRINTF + printf("Temperature: %.2f C\n", sensor_value_to_double(&temp)); +#else + printk("Temperature: %d\n", temp.val1); +#endif k_sleep(SLEEP_TIME); } } diff --git a/tests/drivers/adc/adc_api/CMakeLists.txt b/tests/drivers/adc/adc_api/CMakeLists.txt index 800ba5841af30..d94f55d415341 100644 --- a/tests/drivers/adc/adc_api/CMakeLists.txt +++ b/tests/drivers/adc/adc_api/CMakeLists.txt @@ -1,4 +1,12 @@ cmake_minimum_required(VERSION 3.8.2) +macro(set_conf_file) + if(EXISTS ${APPLICATION_SOURCE_DIR}/boards/${BOARD}.conf) + set(CONF_FILE "prj_base.conf ${APPLICATION_SOURCE_DIR}/boards/${BOARD}.conf") + else() + set(CONF_FILE "prj_base.conf") + endif() +endmacro() + include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(NONE) diff --git a/tests/drivers/adc/adc_api/README b/tests/drivers/adc/adc_api/README deleted file mode 100644 index 7bb7402999331..0000000000000 --- a/tests/drivers/adc/adc_api/README +++ /dev/null @@ -1,15 +0,0 @@ -This is the test app which test ADC on Quark SE processor. - -Default ADC mode is interrupt mode. -The case tests ADC working in different resolutions. - -The analog input pin and channel number mapping -for Quark Se Devboard. -A0 Channel 10 -A1 Channel 11 -A2 Channel 12 -A3 Channel 13 -A4 Channel 14 - -This test uses channel 10 to sample the voltage of VCC3.3. -So connect A0 to VCC3.3 and start the APP. diff --git a/tests/drivers/adc/adc_api/boards/nrf51_pca10028.conf b/tests/drivers/adc/adc_api/boards/nrf51_pca10028.conf new file mode 100644 index 0000000000000..55b034a04948a --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/nrf51_pca10028.conf @@ -0,0 +1,2 @@ +CONFIG_ADC_NRFX_ADC=y +CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT=3 diff --git a/tests/drivers/adc/adc_api/boards/nrf52840_pca10056.conf b/tests/drivers/adc/adc_api/boards/nrf52840_pca10056.conf new file mode 100644 index 0000000000000..4fa585362ecf4 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/nrf52840_pca10056.conf @@ -0,0 +1 @@ +CONFIG_ADC_NRFX_SAADC=y diff --git a/tests/drivers/adc/adc_api/boards/nrf52_pca10040.conf b/tests/drivers/adc/adc_api/boards/nrf52_pca10040.conf new file mode 100644 index 0000000000000..4fa585362ecf4 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/nrf52_pca10040.conf @@ -0,0 +1 @@ +CONFIG_ADC_NRFX_SAADC=y diff --git a/tests/drivers/adc/adc_api/boards/sam_e70_xplained.conf b/tests/drivers/adc/adc_api/boards/sam_e70_xplained.conf new file mode 100644 index 0000000000000..9cc28af15bc55 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/sam_e70_xplained.conf @@ -0,0 +1,3 @@ +CONFIG_ADC=y +CONFIG_ADC_SAM_AFEC=y +CONFIG_ADC_0=y diff --git a/tests/drivers/adc/adc_api/prj.conf b/tests/drivers/adc/adc_api/prj.conf deleted file mode 100644 index c286916189532..0000000000000 --- a/tests/drivers/adc/adc_api/prj.conf +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_ADC=y -CONFIG_ZTEST=y diff --git a/tests/drivers/adc/adc_api/prj_base.conf b/tests/drivers/adc/adc_api/prj_base.conf new file mode 100644 index 0000000000000..17f655ec2eddd --- /dev/null +++ b/tests/drivers/adc/adc_api/prj_base.conf @@ -0,0 +1,8 @@ +CONFIG_ZTEST=y + +CONFIG_ADC=y +CONFIG_ADC_ASYNC=y +CONFIG_ADC_0=y + +CONFIG_SYS_LOG=y +CONFIG_SYS_LOG_ADC_LEVEL=1 diff --git a/tests/drivers/adc/adc_api/src/main.c b/tests/drivers/adc/adc_api/src/main.c index fca46436cc220..21f50d17295af 100644 --- a/tests/drivers/adc/adc_api/src/main.c +++ b/tests/drivers/adc/adc_api/src/main.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -11,7 +12,11 @@ * @} */ -extern void test_adc_sample(void); +extern void test_adc_sample_one_channel(void); +extern void test_adc_sample_two_channels(void); +extern void test_adc_asynchronous_call(void); +extern void test_adc_sample_with_interval(void); +extern void test_adc_repeated_samplings(void); #include #include @@ -19,6 +24,10 @@ extern void test_adc_sample(void); void test_main(void) { ztest_test_suite(adc_basic_test, - ztest_unit_test(test_adc_sample)); + ztest_unit_test(test_adc_sample_one_channel), + ztest_unit_test(test_adc_sample_two_channels), + ztest_unit_test(test_adc_asynchronous_call), + ztest_unit_test(test_adc_sample_with_interval), + ztest_unit_test(test_adc_repeated_samplings)); ztest_run_test_suite(adc_basic_test); } diff --git a/tests/drivers/adc/adc_api/src/test_adc.c b/tests/drivers/adc/adc_api/src/test_adc.c index 84b75f07f1a98..cc88e3e7593ce 100644 --- a/tests/drivers/adc/adc_api/src/test_adc.c +++ b/tests/drivers/adc/adc_api/src/test_adc.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -8,19 +9,7 @@ * @addtogroup test_adc_basic_operations * @{ * @defgroup t_adc_basic_basic_operations test_adc_sample - * @brief TestPurpose: verify ADC works well with different resolutions - * and sample mode - * @details - * - Test Steps - * -# Connect A0 to VCC3.3. - * -# Prepare ADC sequence table. - * -# Bind ADC device. - * -# Enable ADC device. - * -# Call adc_read() to fetch ADC sample. - * -# Dump the sample results. - * - Expected Results - * -# ADC will return the sample result for VCC3.3. Different resolutions - * will all return almost the biggest value in each sample width. + * @brief TestPurpose: verify ADC driver handles different sampling scenarios * @} */ @@ -28,78 +17,444 @@ #include #include -#define BUFFER_SIZE 5 +#if defined(CONFIG_BOARD_NRF51_PCA10028) + +#include +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 10 +#define ADC_GAIN ADC_GAIN_1_3 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 0 +#define ADC_1ST_CHANNEL_INPUT NRF_ADC_CONFIG_INPUT_2 +#define ADC_2ND_CHANNEL_ID 2 +#define ADC_2ND_CHANNEL_INPUT NRF_ADC_CONFIG_INPUT_3 + +#elif defined(CONFIG_BOARD_NRF52_PCA10040) || \ + defined(CONFIG_BOARD_NRF52840_PCA10056) + +#include +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 10 +#define ADC_GAIN ADC_GAIN_1_6 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10) +#define ADC_1ST_CHANNEL_ID 0 +#define ADC_1ST_CHANNEL_INPUT NRF_SAADC_INPUT_AIN1 +#define ADC_2ND_CHANNEL_ID 2 +#define ADC_2ND_CHANNEL_INPUT NRF_SAADC_INPUT_AIN2 + +#elif defined(CONFIG_BOARD_FRDM_K64F) + +#define ADC_DEVICE_NAME CONFIG_ADC_1_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 14 +#define ADC_1ST_CHANNEL_INPUT 0 -#if defined(CONFIG_BOARD_FRDM_K64F) -#define ADC_DEV_NAME CONFIG_ADC_1_NAME -#define ADC_CHANNEL 14 #elif defined(CONFIG_BOARD_FRDM_KL25Z) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 12 + +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 12 +#define ADC_1ST_CHANNEL_INPUT 0 + #elif defined(CONFIG_BOARD_FRDM_KW41Z) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 3 + +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 3 +#define ADC_1ST_CHANNEL_INPUT 0 + #elif defined(CONFIG_BOARD_HEXIWEAR_K64) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 16 + +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 16 +#define ADC_1ST_CHANNEL_INPUT 0 + #elif defined(CONFIG_BOARD_HEXIWEAR_KW40Z) -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 1 + +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 1 +#define ADC_1ST_CHANNEL_INPUT 0 + +#elif defined(CONFIG_BOARD_SAM_E70_XPLAINED) + +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 12 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_EXTERNAL0 +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 0 +#define ADC_1ST_CHANNEL_INPUT 0 + +#elif defined(CONFIG_BOARD_QUARK_SE_C1000_DEVBOARD_SS) || \ + defined(CONFIG_BOARD_ARDUINO_101_SSS) +#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME +#define ADC_RESOLUTION 10 +#define ADC_GAIN ADC_GAIN_1 +#define ADC_REFERENCE ADC_REF_INTERNAL +#define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT +#define ADC_1ST_CHANNEL_ID 10 +#define ADC_2ND_CHANNEL_ID 11 + #else -#define ADC_DEV_NAME CONFIG_ADC_0_NAME -#define ADC_CHANNEL 10 +#error "Unsupported board." #endif -static u16_t seq_buffer[BUFFER_SIZE]; +#define BUFFER_SIZE 6 +static s16_t m_sample_buffer[BUFFER_SIZE]; -static struct adc_seq_entry entry = { - .sampling_delay = 30, - .channel_id = ADC_CHANNEL, - .buffer = (void *)seq_buffer, - .buffer_length = BUFFER_SIZE * sizeof(seq_buffer[0]) +static const struct adc_channel_cfg m_1st_channel_cfg = { + .gain = ADC_GAIN, + .reference = ADC_REFERENCE, + .acquisition_time = ADC_ACQUISITION_TIME, + .channel_id = ADC_1ST_CHANNEL_ID, +#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) + .input_positive = ADC_1ST_CHANNEL_INPUT, +#endif }; - -static struct adc_seq_table table = { - .entries = &entry, - .num_entries = 1, +#if defined(ADC_2ND_CHANNEL_ID) +static const struct adc_channel_cfg m_2nd_channel_cfg = { + .gain = ADC_GAIN, + .reference = ADC_REFERENCE, + .acquisition_time = ADC_ACQUISITION_TIME, + .channel_id = ADC_2ND_CHANNEL_ID, +#if defined(CONFIG_ADC_CONFIGURABLE_INPUTS) + .input_positive = ADC_2ND_CHANNEL_INPUT, +#endif }; +#endif /* defined(ADC_2ND_CHANNEL_ID) */ -static int test_task(void) +static struct device *init_adc(void) +{ + int ret; + struct device *adc_dev = device_get_binding(ADC_DEVICE_NAME); + + zassert_not_null(adc_dev, "Cannot get ADC device"); + + ret = adc_channel_setup(adc_dev, &m_1st_channel_cfg); + zassert_equal(ret, 0, + "Setting up of the first channel failed with code %d", ret); + +#if defined(ADC_2ND_CHANNEL_ID) + ret = adc_channel_setup(adc_dev, &m_2nd_channel_cfg); + zassert_equal(ret, 0, + "Setting up of the second channel failed with code %d", ret); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + + memset(m_sample_buffer, 0, sizeof(m_sample_buffer)); + + return adc_dev; +} + +static void check_samples(int expected_count) { int i; + + TC_PRINT("Samples read: "); + for (i = 0; i < BUFFER_SIZE; i++) { + s16_t sample_value = m_sample_buffer[i]; + + TC_PRINT("0x%04x ", sample_value); + if (i < expected_count) { + zassert_not_equal(0, sample_value, + "[%u] should be non-zero", i); + } else { + zassert_equal(0, sample_value, + "[%u] should be zero", i); + } + } + TC_PRINT("\n"); +} + +/******************************************************************************* + * test_adc_sample_one_channel + */ +static int test_task_one_channel(void) +{ int ret; - struct device *adc_dev = device_get_binding(ADC_DEV_NAME); + +#if defined(CONFIG_BOARD_QUARK_SE_C1000_DEVBOARD_SS) || \ + defined(CONFIG_BOARD_ARDUINO_101_SSS) + const struct adc_sequence_options options = { + .interval_us = 10, + .extra_samplings = 0, + }; +#endif + const struct adc_sequence sequence = { +#if defined(CONFIG_BOARD_QUARK_SE_C1000_DEVBOARD_SS) || \ + defined(CONFIG_BOARD_ARDUINO_101_SSS) + .options = &options, +#endif + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + struct device *adc_dev = init_adc(); if (!adc_dev) { - TC_PRINT("Cannot get ADC device\n"); return TC_FAIL; } - /* 1. Verify adc_enable() */ - adc_enable(adc_dev); + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); - k_sleep(500); + check_samples(1); - /* 2. Verify adc_read() */ - ret = adc_read(adc_dev, &table); - if (ret != 0) { - TC_PRINT("Failed to fetch sample data from ADC controller\n"); + return TC_PASS; +} +void test_adc_sample_one_channel(void) +{ + zassert_true(test_task_one_channel() == TC_PASS, NULL); +} + +/******************************************************************************* + * test_adc_sample_two_channels + */ +#if defined(ADC_2ND_CHANNEL_ID) +static int test_task_two_channels(void) +{ + int ret; + +#if defined(CONFIG_BOARD_QUARK_SE_C1000_DEVBOARD_SS) || \ + defined(CONFIG_BOARD_ARDUINO_101_SSS) + const struct adc_sequence_options options = { + .interval_us = 30, + .extra_samplings = 0, + }; +#endif + const struct adc_sequence sequence = { +#if defined(CONFIG_BOARD_QUARK_SE_C1000_DEVBOARD_SS) || \ + defined(CONFIG_BOARD_ARDUINO_101_SSS) + .options = &options, +#endif + .channels = BIT(ADC_1ST_CHANNEL_ID) | + BIT(ADC_2ND_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + struct device *adc_dev = init_adc(); + + if (!adc_dev) { return TC_FAIL; } - TC_PRINT("Channel %d ADC Sample: ", ADC_CHANNEL); - for (i = 0; i < BUFFER_SIZE; i++) { - TC_PRINT("%d ", seq_buffer[i]); + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + check_samples(2); + + return TC_PASS; +} +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + +void test_adc_sample_two_channels(void) +{ +#if defined(ADC_2ND_CHANNEL_ID) + zassert_true(test_task_two_channels() == TC_PASS, NULL); +#else + ztest_test_skip(); +#endif /* defined(ADC_2ND_CHANNEL_ID) */ +} + + +/******************************************************************************* + * test_adc_asynchronous_call + */ +#if defined(CONFIG_ADC_ASYNC) +static int test_task_asynchronous_call(void) +{ + int ret; + const struct adc_sequence_options options = { + .extra_samplings = 4, + .interval_us = 10, + }; + const struct adc_sequence sequence = { + .options = &options, + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + struct k_poll_signal async_sig = K_POLL_SIGNAL_INITIALIZER(async_sig); + struct k_poll_event async_evt = + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &async_sig); + + struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; } - TC_PRINT("\n"); - /* 3. Verify adc_disable() */ - adc_disable(adc_dev); + ret = adc_read_async(adc_dev, &sequence, &async_sig); + zassert_equal(ret, 0, "adc_read_async() failed with code %d", ret); + + ret = k_poll(&async_evt, 1, K_MSEC(1000)); + zassert_equal(ret, 0, "async signal not received as expected"); + + check_samples(1 + options.extra_samplings); + + return TC_PASS; +} +#endif /* defined(CONFIG_ADC_ASYNC) */ +void test_adc_asynchronous_call(void) +{ +#if defined(CONFIG_ADC_ASYNC) + zassert_true(test_task_asynchronous_call() == TC_PASS, NULL); +#else + ztest_test_skip(); +#endif /* defined(CONFIG_ADC_ASYNC) */ +} + + +/******************************************************************************* + * test_adc_sample_with_interval + */ +static enum adc_action sample_with_interval_callback( + struct device *dev, + const struct adc_sequence *sequence, + u16_t sampling_index) +{ + TC_PRINT("%s: sampling %d\n", __func__, sampling_index); + return ADC_ACTION_CONTINUE; +} +static int test_task_with_interval(void) +{ + int ret; + const struct adc_sequence_options options = { + .interval_us = 100 * 1000UL, + .callback = sample_with_interval_callback, + .extra_samplings = 4, + }; + const struct adc_sequence sequence = { + .options = &options, + .channels = BIT(ADC_1ST_CHANNEL_ID), + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + check_samples(1 + options.extra_samplings); return TC_PASS; } +void test_adc_sample_with_interval(void) +{ + zassert_true(test_task_with_interval() == TC_PASS, NULL); +} + + +/******************************************************************************* + * test_adc_repeated_samplings + */ +static u8_t m_samplings_done; +static enum adc_action repeated_samplings_callback( + struct device *dev, + const struct adc_sequence *sequence, + u16_t sampling_index) +{ + ++m_samplings_done; + TC_PRINT("%s: done %d\n", __func__, m_samplings_done); + if (m_samplings_done == 1) { + #if defined(ADC_2ND_CHANNEL_ID) + check_samples(2); + #else + check_samples(1); + #endif /* defined(ADC_2ND_CHANNEL_ID) */ + + /* After first sampling continue normally. */ + return ADC_ACTION_CONTINUE; + } else { + #if defined(ADC_2ND_CHANNEL_ID) + check_samples(4); + #else + check_samples(2); + #endif /* defined(ADC_2ND_CHANNEL_ID) */ -void test_adc_sample(void) + /* + * The second sampling is repeated 9 times (the samples are + * written in the same place), then the sequence is finished + * prematurely. + */ + if (m_samplings_done < 10) { + return ADC_ACTION_REPEAT; + } else { + return ADC_ACTION_FINISH; + } + } +} +static int test_task_repeated_samplings(void) +{ + int ret; + const struct adc_sequence_options options = { + .callback = repeated_samplings_callback, + /* + * This specifies that 3 samplings are planned. However, + * the callback function above is constructed in such way + * that the first sampling is done normally, the second one + * is repeated 9 times, and then the sequence is finished. + * Hence, the third sampling will not take place. + */ + .extra_samplings = 2, + .interval_us = 15, + }; + const struct adc_sequence sequence = { + .options = &options, +#if defined(ADC_2ND_CHANNEL_ID) + .channels = BIT(ADC_1ST_CHANNEL_ID) | + BIT(ADC_2ND_CHANNEL_ID), +#else + .channels = BIT(ADC_1ST_CHANNEL_ID), +#endif /* defined(ADC_2ND_CHANNEL_ID) */ + .buffer = m_sample_buffer, + .buffer_size = sizeof(m_sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + struct device *adc_dev = init_adc(); + + if (!adc_dev) { + return TC_FAIL; + } + + ret = adc_read(adc_dev, &sequence); + zassert_equal(ret, 0, "adc_read() failed with code %d", ret); + + return TC_PASS; +} +void test_adc_repeated_samplings(void) { - zassert_true(test_task() == TC_PASS, NULL); + zassert_true(test_task_repeated_samplings() == TC_PASS, NULL); } diff --git a/tests/drivers/adc/adc_api/testcase.yaml b/tests/drivers/adc/adc_api/testcase.yaml index 35eed63ca447a..7f5dc25c9eba4 100644 --- a/tests/drivers/adc/adc_api/testcase.yaml +++ b/tests/drivers/adc/adc_api/testcase.yaml @@ -3,23 +3,3 @@ common: tests: peripheral.adc: depends_on: adc - peripheral.adc.poll_mode: - extra_configs: - - CONFIG_ADC_QMSI_POLL=y - platform_whitelist: quark_se_c1000_ss_devboard - peripheral.adc.resolution_10: - extra_configs: - - CONFIG_ADC_QMSI_SAMPLE_WIDTH=10 - platform_whitelist: quark_se_c1000_ss_devboard - peripheral.adc.resolution_12: - extra_configs: - - CONFIG_ADC_QMSI_SAMPLE_WIDTH=12 - platform_whitelist: quark_se_c1000_ss_devboard - peripheral.adc.resolution_6: - extra_configs: - - CONFIG_ADC_QMSI_SAMPLE_WIDTH=6 - platform_whitelist: quark_se_c1000_ss_devboard - peripheral.adc.resolution_8: - extra_configs: - - CONFIG_ADC_QMSI_SAMPLE_WIDTH=8 - platform_whitelist: quark_se_c1000_ss_devboard diff --git a/tests/drivers/adc/adc_simple/CMakeLists.txt b/tests/drivers/adc/adc_simple/CMakeLists.txt deleted file mode 100644 index 800ba5841af30..0000000000000 --- a/tests/drivers/adc/adc_simple/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -cmake_minimum_required(VERSION 3.8.2) -include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) -project(NONE) - -FILE(GLOB app_sources src/*.c) -target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/adc/adc_simple/prj.conf b/tests/drivers/adc/adc_simple/prj.conf deleted file mode 100644 index 44074b400a942..0000000000000 --- a/tests/drivers/adc/adc_simple/prj.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_ZTEST=y -CONFIG_ADC=y -CONFIG_SYS_LOG_ADC_LEVEL=4 diff --git a/tests/drivers/adc/adc_simple/src/main.c b/tests/drivers/adc/adc_simple/src/main.c deleted file mode 100644 index 7a50d5893ed86..0000000000000 --- a/tests/drivers/adc/adc_simple/src/main.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2015 Intel Corporation. - * - * SPDX-License-Identifier: Apache-2.0 - */ -/* - * Copyright (c) 2016 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include -#include -#include -#include - -/* in millisecond */ -#define SLEEPTIME 2000 - -#if defined(CONFIG_BOARD_FRDM_K64F) -#define ADC_DEVICE_NAME CONFIG_ADC_1_NAME -#define CHANNEL 14 -#elif defined(CONFIG_BOARD_FRDM_KL25Z) -#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME -#define CHANNEL 12 -#elif defined(CONFIG_BOARD_FRDM_KW41Z) -#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME -#define CHANNEL 3 -#elif defined(CONFIG_BOARD_HEXIWEAR_K64) -#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME -#define CHANNEL 16 -#elif defined(CONFIG_BOARD_HEXIWEAR_KW40Z) -#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME -#define CHANNEL 1 -#else -#define ADC_DEVICE_NAME CONFIG_ADC_0_NAME -#define CHANNEL 10 -#endif - -/* - * The analog input pin and channel number mapping - * for Arduino 101 board. - * A0 Channel 10 - * A1 Channel 11 - * A2 Channel 12 - * A3 Channel 13 - * A4 Channel 14 - */ - -#define BUFFER_SIZE 10 - -static u32_t seq_buffer[2][BUFFER_SIZE]; - -static struct adc_seq_entry sample = { - .sampling_delay = 30, - .channel_id = CHANNEL, - .buffer_length = BUFFER_SIZE * sizeof(seq_buffer[0][0]) -}; - -static struct adc_seq_table table = { - .entries = &sample, - .num_entries = 1, -}; - -static void _print_sample_in_hex(const u32_t *buf, u32_t length) -{ - const u32_t *top; - - printk("Buffer content:\n"); - for (top = buf + length; buf < top; buf++) - printk("0x%x ", *buf); - printk("\n"); -} - -static long _abs(long x) -{ - return x < 0 ? -x : x; -} - - -static void test_adc(void) -{ - int result = TC_FAIL; - struct device *adc; - unsigned int loops = 10; - unsigned int bufi0 = ~0, bufi; - - adc = device_get_binding(ADC_DEVICE_NAME); - zassert_not_null(adc, "Cannot get adc controller"); - - adc_enable(adc); - while (loops--) { - bufi = loops & 0x1; - /* .buffer should be void * ... */ - sample.buffer = (void *) seq_buffer[bufi]; - result = adc_read(adc, &table); - zassert_equal(result, 0, "Sampling could not proceed, " - "an error occurred"); - printk("loop %u: sampling done to buffer #%u\n", loops, bufi); - _print_sample_in_hex(seq_buffer[bufi], BUFFER_SIZE); - if (bufi0 != ~0) { - unsigned int cnt; - long delta; - - for (cnt = 0; cnt < BUFFER_SIZE; cnt++) { - delta = _abs((long)seq_buffer[bufi][cnt] - - seq_buffer[bufi0][cnt]); - printk("loop %u delta %u = %ld\n", - loops, cnt, delta); - } - } - k_sleep(SLEEPTIME); - bufi0 = bufi; - } - adc_disable(adc); -} - - -void test_main(void) -{ - ztest_test_suite(_adc_test, - ztest_unit_test(test_adc)); - ztest_run_test_suite(_adc_test); -} - diff --git a/tests/drivers/adc/adc_simple/testcase.yaml b/tests/drivers/adc/adc_simple/testcase.yaml deleted file mode 100644 index c9325da5ee312..0000000000000 --- a/tests/drivers/adc/adc_simple/testcase.yaml +++ /dev/null @@ -1,4 +0,0 @@ -tests: - peripheral.adc: - depends_on: adc - tags: driver adc