| /* |
| * Copyright (c) 2015-2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Intel Quark SE C1000 Sensor Subsystem ADC Driver |
| * |
| * This is the driver for the ADC block in the Intel Quark SE C1000 |
| * Sensor Subsystem. |
| */ |
| |
| #include <errno.h> |
| |
| #include <init.h> |
| #include <kernel.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <soc.h> |
| #include <adc.h> |
| #include <arch/cpu.h> |
| #include <misc/util.h> |
| |
| #define ADC_CONTEXT_USES_KERNEL_TIMER |
| #include "adc_context.h" |
| #include "adc_intel_quark_se_c1000_ss.h" |
| |
| #define LOG_LEVEL CONFIG_ADC_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(adc_intel_quark_se_c1000_ss); |
| |
| /* MST */ |
| #define ADC_CLOCK_GATE BIT(31) |
| #define ADC_CAL_REQ BIT(16) |
| #define ADC_DEEP_POWER_DOWN 0x01 |
| #define ADC_POWER_DOWN 0x01 |
| #define ADC_STANDBY 0x02 |
| #define ADC_NORMAL_WITH_CALIB 0x03 |
| #define ADC_NORMAL_WO_CALIB 0x04 |
| #define ADC_MODE_MASK 0x07 |
| #define ADC_DELAY_MASK (0xFFF8) |
| #define ADC_DELAY_POS 3 |
| #define ADC_DELAY_32MHZ (160 << ADC_DELAY_POS) |
| |
| /* SLV0 */ |
| #define ADC_CAL_ACK BIT(4) |
| #define ADC_PWR_MODE_STS BIT(3) |
| |
| #define ONE_BIT_SET 0x1 |
| #define THREE_BITS_SET 0x7 |
| #define FIVE_BITS_SET 0x1f |
| #define SIX_BITS_SET 0x3f |
| #define SEVEN_BITS_SET 0xef |
| #define ELEVEN_BITS_SET 0x7ff |
| |
| #define CAPTURE_MODE_POS 6 |
| #define OUTPUT_MODE_POS 7 |
| #define SERIAL_DELAY_POS 8 |
| #define SEQUENCE_MODE_POS 13 |
| #define SEQ_ENTRIES_POS 16 |
| #define THRESHOLD_POS 24 |
| |
| #define SEQ_MUX_EVEN_POS 0 |
| #define SEQ_DELAY_EVEN_POS 5 |
| #define SEQ_MUX_ODD_POS 16 |
| #define SEQ_DELAY_ODD_POS 21 |
| |
| #define ADC_NONE_CALIBRATION (0x80) |
| |
| #ifdef CONFIG_SOC_QUARK_SE_C1000_SS |
| #define int_unmask(__mask) \ |
| sys_write32(sys_read32((__mask)) & ENABLE_SSS_INTERRUPTS, (__mask)) |
| #else |
| #define int_unmask(...) { ; } |
| #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_INTEL_QUARK_SE_C1000_SS_CALIBRATION |
| .calibration_value = ADC_NONE_CALIBRATION, |
| #endif |
| }; |
| |
| static inline void wait_slv0_bit_set(u32_t bit_mask) |
| { |
| u32_t reg_value; |
| |
| do { |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); |
| } while ((reg_value & bit_mask) == 0U); |
| } |
| |
| static void set_power_mode_inner(u32_t mode) |
| { |
| u32_t reg_value; |
| u32_t state; |
| |
| state = irq_lock(); |
| |
| /* Request Power Down first before transitioning */ |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); |
| |
| reg_value &= ~(ADC_MODE_MASK); |
| reg_value |= mode; |
| |
| sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0); |
| |
| irq_unlock(state); |
| |
| /* Wait for power mode to be set */ |
| wait_slv0_bit_set(ADC_PWR_MODE_STS); |
| } |
| |
| static void set_power_mode(u32_t mode) |
| { |
| u32_t reg_value; |
| |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); |
| |
| /* no need to set if power mode is the same as requested */ |
| if ((reg_value & ADC_MODE_MASK) == mode) { |
| return; |
| } |
| |
| /* Request Power Down first before transitioning */ |
| set_power_mode_inner(ADC_POWER_DOWN); |
| |
| /* Then set to the desired mode */ |
| set_power_mode_inner(mode); |
| } |
| |
| /* |
| * A dummy conversion is needed after coming out of deep |
| * power down, or else the first conversion would not be |
| * correct. |
| */ |
| static void dummy_conversion(struct device *dev) |
| { |
| const struct adc_config *config = dev->config->config_info; |
| u32_t adc_base = config->reg_base; |
| u32_t reg_value; |
| |
| /* Flush FIFO */ |
| reg_value = sys_in32(adc_base + ADC_SET); |
| reg_value |= ADC_SET_FLUSH_RX; |
| sys_out32(reg_value, adc_base + ADC_SET); |
| |
| /* Reset sequence table */ |
| reg_value = sys_in32(adc_base + ADC_CTRL); |
| reg_value |= ADC_CTRL_SEQ_TABLE_RST; |
| sys_out32(reg_value, adc_base + ADC_CTRL); |
| |
| /* Setup sequence table for dummy */ |
| sys_out32((10 << 5), adc_base + ADC_SEQ); |
| |
| /* |
| * Set number of entries in sequencer (n-1) |
| * and threshold to generate interrupt. |
| */ |
| reg_value = sys_in32(adc_base + ADC_SET); |
| reg_value &= ~(ADC_SET_SEQ_ENTRIES_MASK | ADC_SET_THRESHOLD_MASK); |
| sys_out32(reg_value, adc_base + ADC_SET); |
| |
| /* |
| * Reset sequence pointer, |
| * Clear and mask interrupts, |
| * Enable ADC and start sequencer. |
| */ |
| reg_value = sys_in32(adc_base + ADC_CTRL); |
| reg_value |= ADC_CTRL_SEQ_PTR_RST | ADC_CTRL_INT_CLR_ALL | |
| ADC_CTRL_INT_MASK_ALL | ADC_CTRL_ENABLE | |
| ADC_CTRL_SEQ_START; |
| sys_out32(reg_value, adc_base + ADC_CTRL); |
| |
| /* Wait for data available */ |
| do { |
| reg_value = sys_in32(adc_base + ADC_INTSTAT); |
| } while ((reg_value & ADC_INTSTAT_DATA_A) == 0U); |
| |
| /* Flush FIFO */ |
| reg_value = sys_in32(adc_base + ADC_SET); |
| reg_value |= ADC_SET_FLUSH_RX; |
| sys_out32(reg_value, adc_base + ADC_SET); |
| |
| /* Clear data available interrupt and disable ADC */ |
| reg_value = sys_in32(adc_base + ADC_CTRL); |
| reg_value |= ADC_CTRL_CLR_DATA_A; |
| reg_value &= ~ADC_CTRL_ENABLE; |
| sys_out32(reg_value, adc_base + ADC_CTRL); |
| } |
| |
| #ifdef CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_CALIBRATION |
| static void calibration_command(u8_t command) |
| { |
| u32_t state; |
| u32_t reg_value; |
| |
| /* Set Calibration Request */ |
| state = irq_lock(); |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); |
| reg_value |= (command & THREE_BITS_SET) << 17; |
| reg_value |= ADC_CAL_REQ; |
| sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0); |
| irq_unlock(state); |
| |
| /* Waiting for calibration ack */ |
| wait_slv0_bit_set(ADC_CAL_ACK); |
| |
| /* Clear Calibration Request once done */ |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); |
| reg_value &= ~ADC_CAL_REQ; |
| sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0); |
| } |
| |
| static void adc_goto_normal_mode(struct device *dev) |
| { |
| struct adc_info *info = dev->driver_data; |
| u8_t calibration_value; |
| u32_t reg_value; |
| |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); |
| |
| if ((reg_value & ADC_MODE_MASK) != ADC_NORMAL_WITH_CALIB) { |
| |
| /* Request Normal With Calibration Mode */ |
| set_power_mode(ADC_NORMAL_WITH_CALIB); |
| |
| /* Poll waiting for normal mode with calibration */ |
| wait_slv0_bit_set(ADC_PWR_MODE_STS); |
| |
| if (info->calibration_value == ADC_NONE_CALIBRATION) { |
| /* Reset Calibration */ |
| calibration_command(ADC_CMD_RESET_CALIBRATION); |
| /* Request Calibration */ |
| calibration_command(ADC_CMD_START_CALIBRATION); |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0); |
| calibration_value = (reg_value >> 5) & SEVEN_BITS_SET; |
| info->calibration_value = calibration_value; |
| } |
| |
| /* Load Calibration */ |
| reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); |
| reg_value |= (info->calibration_value << 20); |
| sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0); |
| calibration_command(ADC_CMD_LOAD_CALIBRATION); |
| } |
| |
| dummy_conversion(dev); |
| } |
| |
| #else |
| static void adc_goto_normal_mode(struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| /* Request Normal Without Calibration Mode */ |
| set_power_mode(ADC_NORMAL_WO_CALIB); |
| |
| dummy_conversion(dev); |
| } |
| #endif |
| |
| static int set_resolution(struct device *dev, |
| const struct adc_sequence *sequence) |
| { |
| u32_t tmp_val; |
| const struct adc_config *config = dev->config->config_info; |
| u32_t adc_base = config->reg_base; |
| |
| 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; |
| } |
| |
| sys_out32(tmp_val, adc_base + ADC_SET); |
| |
| return 0; |
| } |
| |
| /* Implementation of the ADC driver API function: adc_channel_setup. */ |
| static int adc_quark_se_ss_channel_setup( |
| struct device *dev, |
| const struct adc_channel_cfg *channel_cfg |
| ) |
| { |
| u8_t channel_id = channel_cfg->channel_id; |
| struct adc_info *info = dev->driver_data; |
| |
| if (channel_id >= DW_CHANNEL_COUNT) { |
| LOG_ERR("Invalid channel id"); |
| return -EINVAL; |
| } |
| |
| if (channel_cfg->gain != ADC_GAIN_1) { |
| LOG_ERR("Invalid channel gain"); |
| return -EINVAL; |
| } |
| |
| if (channel_cfg->reference != ADC_REF_INTERNAL) { |
| LOG_ERR("Invalid channel reference"); |
| return -EINVAL; |
| } |
| |
| if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { |
| LOG_ERR("Invalid channel acquisition time"); |
| return -EINVAL; |
| } |
| |
| if (info->state != ADC_STATE_IDLE) { |
| LOG_ERR("ADC is busy or in error state"); |
| return -EAGAIN; |
| } |
| |
| info->active_channels |= 1 << channel_id; |
| return 0; |
| } |
| |
| static int adc_quark_se_ss_read_request(struct device *dev, |
| const struct adc_sequence *seq_tbl) |
| { |
| struct adc_info *info = dev->driver_data; |
| int error = 0; |
| u32_t utmp, num_channels, interval = 0U; |
| |
| 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; |
| } |
| |
| /* |
| * Make sure the requested interval is longer than the time |
| * needed to do one conversion. |
| */ |
| if (seq_tbl->options && |
| (seq_tbl->options->interval_us > 0)) { |
| /* |
| * System clock is 32MHz, which means 1us == 32 cycles |
| * if divider is 1. |
| */ |
| interval = seq_tbl->options->interval_us * 32U / |
| CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_CLOCK_RATIO; |
| |
| if (interval < (seq_tbl->resolution + 2)) { |
| return -EINVAL; |
| } |
| } |
| |
| info->entries = seq_tbl; |
| info->buffer = (u16_t *)seq_tbl->buffer; |
| |
| /* check if buffer has enough size */ |
| utmp = info->channels; |
| num_channels = 0U; |
| while (utmp) { |
| if (utmp & BIT(0)) { |
| num_channels++; |
| } |
| utmp >>= 1; |
| } |
| utmp = num_channels * sizeof(u16_t); |
| |
| if (seq_tbl->options) { |
| utmp *= (1 + seq_tbl->options->extra_samplings); |
| } |
| |
| if (utmp > seq_tbl->buffer_size) { |
| return -ENOMEM; |
| } |
| |
| info->state = ADC_STATE_SAMPLING; |
| |
| adc_context_start_read(&info->ctx, seq_tbl); |
| error = adc_context_wait_for_completion(&info->ctx); |
| |
| if (info->state == ADC_STATE_ERROR) { |
| info->state = ADC_STATE_IDLE; |
| return -EIO; |
| } |
| |
| return error; |
| } |
| |
| static int adc_quark_se_ss_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_quark_se_ss_read_request(dev, seq_tbl); |
| adc_context_release(&info->ctx, ret); |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_ADC_ASYNC |
| /* Implementation of the ADC driver API function: adc_read_async. */ |
| static int adc_quark_se_ss_read_async(struct device *dev, |
| const struct adc_sequence *sequence, |
| struct k_poll_signal *async) |
| { |
| struct adc_info *info = dev->driver_data; |
| int ret; |
| |
| adc_context_lock(&info->ctx, true, async); |
| ret = adc_quark_se_ss_read_request(dev, sequence); |
| adc_context_release(&info->ctx, ret); |
| |
| return ret; |
| } |
| #endif |
| |
| static void adc_quark_se_ss_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, sample_window; |
| |
| info->channel_id = find_lsb_set(info->channels) - 1; |
| |
| /* Flush FIFO */ |
| tmp_val = sys_in32(adc_base + ADC_SET); |
| tmp_val |= ADC_SET_FLUSH_RX; |
| sys_out32(tmp_val, adc_base + ADC_SET); |
| |
| /* Reset sequence table */ |
| ctrl = sys_in32(adc_base + ADC_CTRL); |
| ctrl |= ADC_CTRL_SEQ_TABLE_RST; |
| sys_out32(ctrl, adc_base + ADC_CTRL); |
| |
| /* |
| * Hardware requires min (resolution + 2) cycles, |
| * or will emit SEQERROR. |
| */ |
| sample_window = entry->resolution + 2; |
| tmp_val = (sample_window & ELEVEN_BITS_SET) << SEQ_DELAY_EVEN_POS; |
| tmp_val |= (info->channel_id & FIVE_BITS_SET); |
| |
| sys_out32(tmp_val, adc_base + ADC_SEQ); |
| |
| /* |
| * Clear number of entries in sequencer and threshold to generate |
| * interrupt, since only 1 conversion is needed and fields are |
| * zero-based. |
| */ |
| tmp_val = sys_in32(adc_base + ADC_SET); |
| tmp_val &= ~(ADC_SET_SEQ_ENTRIES_MASK | ADC_SET_THRESHOLD_MASK); |
| sys_out32(tmp_val, adc_base + ADC_SET); |
| |
| /* |
| * Reset sequence pointer, |
| * Clear and unmask interrupts, |
| * Enable ADC and start sequencer. |
| */ |
| ctrl = sys_in32(adc_base + ADC_CTRL); |
| ctrl &= ~ADC_CTRL_INT_MASK_ALL; |
| ctrl |= ADC_CTRL_SEQ_PTR_RST | ADC_CTRL_INT_CLR_ALL | |
| ADC_CTRL_ENABLE | ADC_CTRL_SEQ_START; |
| sys_out32(ctrl, adc_base + ADC_CTRL); |
| } |
| |
| static void adc_context_start_sampling(struct adc_context *ctx) |
| { |
| struct adc_info *info = CONTAINER_OF(ctx, struct adc_info, ctx); |
| |
| info->channels = ctx->sequence.channels; |
| |
| adc_quark_se_ss_start_conversion(info->dev); |
| } |
| |
| static void adc_context_update_buffer_pointer(struct adc_context *ctx, |
| bool repeat) |
| { |
| struct adc_info *info = CONTAINER_OF(ctx, struct adc_info, ctx); |
| const struct adc_sequence *entry = &ctx->sequence; |
| |
| if (repeat) { |
| info->buffer = (u16_t *)entry->buffer; |
| } |
| } |
| |
| int adc_quark_se_ss_init(struct device *dev) |
| { |
| u32_t val; |
| const struct adc_config *config = dev->config->config_info; |
| u32_t adc_base = config->reg_base; |
| struct adc_info *info = dev->driver_data; |
| |
| /* Disable clock gating */ |
| val = sys_in32(PERIPH_ADDR_BASE_CREG_MST0); |
| val &= ~(ADC_CLOCK_GATE); |
| sys_out32(val, PERIPH_ADDR_BASE_CREG_MST0); |
| |
| /* Mask all interrupts and enable clock */ |
| val = ADC_CTRL_INT_MASK_ALL | ADC_CTRL_CLK_ENABLE; |
| sys_out32(val, adc_base + ADC_CTRL); |
| |
| /* Configure common properties */ |
| 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 &= ~(ADC_SET_INPUT_MODE_MASK); |
| sys_out32(val, adc_base + ADC_SET); |
| |
| /* Set the clock ratio */ |
| sys_out32(config->clock_ratio & ADC_DIVSEQSTAT_CLK_RATIO_MASK, |
| adc_base + ADC_DIVSEQSTAT); |
| |
| config->config_func(); |
| |
| int_unmask(config->reg_irq_mask); |
| int_unmask(config->reg_err_mask); |
| |
| info->dev = dev; |
| |
| adc_goto_normal_mode(dev); |
| |
| info->state = ADC_STATE_IDLE; |
| |
| adc_context_unlock_unconditionally(&info->ctx); |
| return 0; |
| } |
| |
| static void adc_quark_se_ss_rx_isr(void *arg) |
| { |
| struct device *dev = (struct device *)arg; |
| struct adc_info *info = dev->driver_data; |
| const struct adc_config *config = dev->config->config_info; |
| struct adc_sequence *seq = &info->ctx.sequence; |
| u32_t adc_base = config->reg_base; |
| u32_t reg_val; |
| |
| /* Pop data from FIFO and put it into buffer */ |
| reg_val = sys_in32(adc_base + ADC_SET); |
| reg_val |= ADC_SET_POP_RX; |
| sys_out32(reg_val, adc_base + ADC_SET); |
| |
| /* Sample is always 12-bit, so need to shift */ |
| *info->buffer++ = sys_in32(adc_base + ADC_SAMPLE) >> |
| (12 - seq->resolution); |
| |
| /* Clear data available register */ |
| reg_val = sys_in32(adc_base + ADC_CTRL); |
| reg_val |= ADC_CTRL_CLR_DATA_A; |
| sys_out32(reg_val, adc_base + ADC_CTRL); |
| |
| /* Stop sequencer and mask all interrupts */ |
| reg_val = sys_in32(adc_base + ADC_CTRL); |
| reg_val &= ~ADC_CTRL_SEQ_START; |
| reg_val |= ADC_CTRL_INT_MASK_ALL; |
| sys_out32(reg_val, adc_base + ADC_CTRL); |
| |
| /* Disable ADC */ |
| reg_val = sys_in32(adc_base + ADC_CTRL); |
| reg_val &= ~ADC_CTRL_ENABLE; |
| sys_out32(reg_val, adc_base + ADC_CTRL); |
| |
| info->state = ADC_STATE_IDLE; |
| info->channels &= ~BIT(info->channel_id); |
| |
| if (info->channels) { |
| adc_quark_se_ss_start_conversion(dev); |
| } else { |
| adc_context_on_sampling_done(&info->ctx, dev); |
| } |
| } |
| |
| static void adc_quark_se_ss_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; |
| u32_t adc_base = config->reg_base; |
| u32_t reg_val = sys_in32(adc_base + ADC_SET); |
| |
| /* |
| * Stop sequencer, mask/clear all interrupts, |
| * and disable ADC. |
| */ |
| reg_val = sys_in32(adc_base + ADC_CTRL); |
| reg_val &= ~(ADC_CTRL_SEQ_START | ADC_CTRL_ENABLE); |
| reg_val |= ADC_CTRL_INT_MASK_ALL; |
| reg_val |= ADC_CTRL_INT_CLR_ALL; |
| sys_out32(reg_val, adc_base + ADC_CTRL); |
| |
| info->state = ADC_STATE_ERROR; |
| adc_context_on_sampling_done(&info->ctx, dev); |
| } |
| |
| static const struct adc_driver_api api_funcs = { |
| .channel_setup = adc_quark_se_ss_channel_setup, |
| .read = adc_quark_se_ss_read, |
| #ifdef CONFIG_ADC_ASYNC |
| .read_async = adc_quark_se_ss_read_async, |
| #endif |
| }; |
| |
| const static struct adc_config adc_config_dev = { |
| .reg_base = DT_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_INTEL_QUARK_SE_C1000_SS_SERIAL |
| .out_mode = 0, |
| #elif CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_PARALLEL |
| .out_mode = 1, |
| #endif |
| .seq_mode = 0, |
| |
| #ifdef CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_RISING_EDGE |
| .capture_mode = 0, |
| #elif CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_FALLING_EDGE |
| .capture_mode = 1, |
| #endif |
| .clock_ratio = CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_CLOCK_RATIO, |
| .serial_dly = CONFIG_ADC_INTEL_QUARK_SE_C1000_SS_SERIAL_DELAY, |
| .config_func = adc_config_irq, |
| }; |
| |
| DEVICE_AND_API_INIT(adc_quark_se_ss, DT_ADC_0_NAME, &adc_quark_se_ss_init, |
| &adc_info_dev, &adc_config_dev, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, |
| &api_funcs); |
| |
| static void adc_config_irq(void) |
| { |
| IRQ_CONNECT(DT_ADC_0_IRQ, DT_ADC_0_IRQ_PRI, adc_quark_se_ss_rx_isr, |
| DEVICE_GET(adc_quark_se_ss), 0); |
| irq_enable(DT_ADC_0_IRQ); |
| |
| IRQ_CONNECT(DT_ADC_IRQ_ERR, DT_ADC_0_IRQ_PRI, |
| adc_quark_se_ss_err_isr, DEVICE_GET(adc_quark_se_ss), 0); |
| irq_enable(DT_ADC_IRQ_ERR); |
| } |