blob: 13c49fcc27a117e0371fb6b25264dc29631b85a5 [file] [log] [blame]
/*
* 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);
}