/* * Copyright (c) 2020 Nuvoton Technology Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_npcx_adc #include #include #include #include #include #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" #include LOG_MODULE_REGISTER(adc_npcx, CONFIG_ADC_LOG_LEVEL); /* ADC speed/delay values during initialization */ #define ADC_REGULAR_DLY_VAL 0x03 #define ADC_REGULAR_ADCCNF2_VAL 0x8B07 #define ADC_REGULAR_GENDLY_VAL 0x0100 #define ADC_REGULAR_MEAST_VAL 0x0001 /* ADC channel number */ #define NPCX_ADC_CH_COUNT 10 /* ADC targeted operating frequency (2MHz) */ #define NPCX_ADC_CLK 2000000 /* ADC internal reference voltage (Unit:mV) */ #define NPCX_ADC_VREF_VOL 2816 /* ADC conversion mode */ #define NPCX_ADC_CHN_CONVERSION_MODE 0 #define NPCX_ADC_SCAN_CONVERSION_MODE 1 /* Device config */ struct adc_npcx_config { /* adc controller base address */ uintptr_t base; /* clock configuration */ struct npcx_clk_cfg clk_cfg; /* pinmux configuration */ const struct npcx_alt *alts_list; }; /* Driver data */ struct adc_npcx_data { /* Input clock for ADC converter */ uint32_t input_clk; /* mutex of ADC channels */ struct adc_context ctx; /* * Bit-mask indicating the channels to be included in each sampling * of this sequence. */ uint16_t channels; /* ADC Device pointer used in api functions */ const struct device *adc_dev; uint16_t *buffer; uint16_t *repeat_buffer; /* end pointer of buffer to ensure enough space for storing ADC data. */ uint16_t *buf_end; }; /* Driver convenience defines */ #define DRV_CONFIG(dev) ((const struct adc_npcx_config *)(dev)->config) #define DRV_DATA(dev) ((struct adc_npcx_data *)(dev)->data) #define HAL_INSTANCE(dev) (struct adc_reg *)(DRV_CONFIG(dev)->base) /* ADC local functions */ static void adc_npcx_isr(void *arg) { struct adc_npcx_data *const data = DRV_DATA((const struct device *)arg); struct adc_reg *const inst = HAL_INSTANCE((const struct device *)arg); uint16_t status = inst->ADCSTS; uint16_t result, channel; /* Clear status pending bits first */ inst->ADCSTS = status; LOG_DBG("%s: status is %04X\n", __func__, status); /* Is end of conversion cycle event? ie. Scan conversion is done. */ if (IS_BIT_SET(status, NPCX_ADCSTS_EOCCEV)) { /* Stop conversion for scan conversion mode */ inst->ADCCNF |= BIT(NPCX_ADCCNF_STOP); /* Get result for each ADC selected channel */ while (data->channels) { channel = find_lsb_set(data->channels) - 1; result = GET_FIELD(inst->CHNDAT[channel], NPCX_CHNDAT_CHDAT_FIELD); /* * Save ADC result and adc_npcx_validate_buffer_size() * already ensures that the buffer has enough space for * storing result. */ if (data->buffer < data->buf_end) { *data->buffer++ = result; } data->channels &= ~BIT(channel); } /* Turn off ADC and inform sampling is done */ inst->ADCCNF &= ~(BIT(NPCX_ADCCNF_ADCEN)); adc_context_on_sampling_done(&data->ctx, data->adc_dev); } } /* * Validate the buffer size with adc channels mask. If it is lower than what * we need return -ENOSPC. */ static int adc_npcx_validate_buffer_size(const struct device *dev, const struct adc_sequence *sequence) { uint8_t channels = 0; uint32_t mask; size_t needed; for (mask = BIT(NPCX_ADC_CH_COUNT - 1); mask != 0; mask >>= 1) { if (mask & sequence->channels) { channels++; } } needed = channels * sizeof(uint16_t); if (sequence->options) { needed *= (1 + sequence->options->extra_samplings); } if (sequence->buffer_size < needed) { return -ENOSPC; } return 0; } static void adc_npcx_start_scan(const struct device *dev) { struct adc_npcx_data *const data = DRV_DATA(dev); struct adc_reg *const inst = HAL_INSTANCE(dev); /* Turn on ADC first */ inst->ADCCNF |= BIT(NPCX_ADCCNF_ADCEN); /* Update selected channels in scan mode by channels mask */ inst->ADCCS = data->channels; /* Select 'Scan' Conversion mode. */ SET_FIELD(inst->ADCCNF, NPCX_ADCCNF_ADCMD_FIELD, NPCX_ADC_SCAN_CONVERSION_MODE); /* Select 'One-Shot' Repetitive mode */ inst->ADCCNF |= BIT(NPCX_ADCCNF_INTECEN); /* Start conversion */ inst->ADCCNF |= BIT(NPCX_ADCCNF_START); LOG_DBG("Start ADC scan conversion and ADCCNF,ADCCS are (%04X,%04X)\n", inst->ADCCNF, inst->ADCCS); } static int adc_npcx_start_read(const struct device *dev, const struct adc_sequence *sequence) { struct adc_npcx_data *const data = DRV_DATA(dev); int error = 0; if (!sequence->channels || (sequence->channels & ~BIT_MASK(NPCX_ADC_CH_COUNT))) { LOG_ERR("Invalid ADC channels"); return -EINVAL; } /* Fixed 10 bit resolution of npcx ADC */ if (sequence->resolution != 10) { LOG_ERR("Unfixed 10 bit ADC resolution"); return -ENOTSUP; } error = adc_npcx_validate_buffer_size(dev, sequence); if (error) { LOG_ERR("ADC buffer size too small"); return error; } /* Save ADC sequence sampling buffer and its end pointer address */ data->buffer = sequence->buffer; data->buf_end = data->buffer + sequence->buffer_size / sizeof(uint16_t); /* Start ADC conversion */ adc_context_start_read(&data->ctx, sequence); error = adc_context_wait_for_completion(&data->ctx); return error; } /* ADC api functions */ static void adc_context_start_sampling(struct adc_context *ctx) { struct adc_npcx_data *const data = CONTAINER_OF(ctx, struct adc_npcx_data, ctx); data->repeat_buffer = data->buffer; data->channels = ctx->sequence.channels; /* Start ADC scan conversion */ adc_npcx_start_scan(data->adc_dev); } static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) { struct adc_npcx_data *const data = CONTAINER_OF(ctx, struct adc_npcx_data, ctx); if (repeat_sampling) { data->buffer = data->repeat_buffer; } } static int adc_npcx_channel_setup(const struct device *dev, const struct adc_channel_cfg *channel_cfg) { const struct adc_npcx_config *const config = DRV_CONFIG(dev); uint8_t channel_id = channel_cfg->channel_id; if (channel_id >= NPCX_ADC_CH_COUNT) { LOG_ERR("Invalid channel %d", channel_id); return -EINVAL; } if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { LOG_ERR("Unsupported channel acquisition time"); return -ENOTSUP; } if (channel_cfg->differential) { LOG_ERR("Differential channels are not supported"); return -ENOTSUP; } if (channel_cfg->gain != ADC_GAIN_1) { LOG_ERR("Unsupported channel gain %d", channel_cfg->gain); return -ENOTSUP; } if (channel_cfg->reference != ADC_REF_INTERNAL) { LOG_ERR("Unsupported channel reference"); return -ENOTSUP; } /* Configure pin-mux for ADC channel */ npcx_pinctrl_mux_configure(config->alts_list + channel_cfg->channel_id, 1, 1); LOG_DBG("ADC channel %d, alts(%d,%d)", channel_cfg->channel_id, config->alts_list[channel_cfg->channel_id].group, config->alts_list[channel_cfg->channel_id].bit); return 0; } static int adc_npcx_read(const struct device *dev, const struct adc_sequence *sequence) { struct adc_npcx_data *const data = DRV_DATA(dev); int error; adc_context_lock(&data->ctx, false, NULL); error = adc_npcx_start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #if defined(CONFIG_ADC_ASYNC) static int adc_npcx_read_async(const struct device *dev, const struct adc_sequence *sequence, struct k_poll_signal *async) { struct adc_npcx_data *const data = DRV_DATA(dev); int error; adc_context_lock(&data->ctx, true, async); error = adc_npcx_start_read(dev, sequence); adc_context_release(&data->ctx, error); return error; } #endif /* CONFIG_ADC_ASYNC */ /* ADC driver registration */ static const struct adc_driver_api adc_npcx_driver_api = { .channel_setup = adc_npcx_channel_setup, .read = adc_npcx_read, #if defined(CONFIG_ADC_ASYNC) .read_async = adc_npcx_read_async, #endif .ref_internal = NPCX_ADC_VREF_VOL, }; static int adc_npcx_init(const struct device *dev); static const struct npcx_alt adc_alts[] = NPCX_DT_ALT_ITEMS_LIST(0); static const struct adc_npcx_config adc_npcx_cfg_0 = { .base = DT_INST_REG_ADDR(0), .clk_cfg = NPCX_DT_CLK_CFG_ITEM(0), .alts_list = adc_alts, }; static struct adc_npcx_data adc_npcx_data_0 = { ADC_CONTEXT_INIT_TIMER(adc_npcx_data_0, ctx), ADC_CONTEXT_INIT_LOCK(adc_npcx_data_0, ctx), ADC_CONTEXT_INIT_SYNC(adc_npcx_data_0, ctx), }; DEVICE_DT_INST_DEFINE(0, adc_npcx_init, device_pm_control_nop, &adc_npcx_data_0, &adc_npcx_cfg_0, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &adc_npcx_driver_api); static int adc_npcx_init(const struct device *dev) { const struct adc_npcx_config *const config = DRV_CONFIG(dev); struct adc_npcx_data *const data = DRV_DATA(dev); struct adc_reg *const inst = HAL_INSTANCE(dev); const struct device *const clk_dev = device_get_binding(NPCX_CLK_CTRL_NAME); int prescaler = 0, ret; /* Save ADC device in data */ data->adc_dev = dev; /* Turn on device clock first and get source clock freq. */ ret = clock_control_on(clk_dev, (clock_control_subsys_t *) &config->clk_cfg); if (ret < 0) { LOG_ERR("Turn on ADC clock fail %d", ret); return ret; } ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t *) &config->clk_cfg, &data->input_clk); if (ret < 0) { LOG_ERR("Get ADC clock rate error %d", ret); return ret; } /* Configure the ADC clock */ prescaler = ceiling_fraction(data->input_clk, NPCX_ADC_CLK); if (prescaler > 0x40) prescaler = 0x40; /* Set Core Clock Division Factor in order to obtain the ADC clock */ SET_FIELD(inst->ATCTL, NPCX_ATCTL_SCLKDIV_FIELD, prescaler - 1); /* Set regular ADC delay */ SET_FIELD(inst->ATCTL, NPCX_ATCTL_DLY_FIELD, ADC_REGULAR_DLY_VAL); /* Set ADC speed sequentially */ inst->ADCCNF2 = ADC_REGULAR_ADCCNF2_VAL; inst->GENDLY = ADC_REGULAR_GENDLY_VAL; inst->MEAST = ADC_REGULAR_MEAST_VAL; /* Configure ADC interrupt and enable it */ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), adc_npcx_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQN(0)); /* Initialize mutex of ADC channels */ adc_context_unlock_unconditionally(&data->ctx); return 0; } BUILD_ASSERT(ARRAY_SIZE(adc_alts) == NPCX_ADC_CH_COUNT, "The number of ADC channels and pin-mux configurations don't match!");