/* adc_dw.c - Designware ADC driver */
/*
* Copyright (c) 2015 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <init.h>
#include <kernel.h>
#include <string.h>
#include <stdlib.h>
#include <board.h>
#include <adc.h>
#include <arch/cpu.h>
#include "adc_dw.h"
#define ADC_CLOCK_GATE (1 << 31)
#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 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 INPUT_MODE_POS 5
#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_DELAY_EVEN_POS 5
#define SEQ_MUX_ODD_POS 16
#define SEQ_DELAY_ODD_POS 21
#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);
#ifdef CONFIG_ADC_DW_CALIBRATION
static void calibration_command(u8_t command)
{
u32_t state;
u32_t reg_value;
state = irq_lock();
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value |= (command & THREE_BITS_SET) << 17;
reg_value |= 0x10000;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
irq_unlock(state);
/*Poll waiting for command*/
do {
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
} while ((reg_value & 0x10) == 0);
/*Clear Calibration Request*/
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value &= ~(0x10000);
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;
u32_t state;
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
if (((reg_value & 0xE) >> 1) != ADC_NORMAL_WITH_CALIB) {
state = irq_lock();
/*Request Normal With Calibration Mode*/
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= ADC_NORMAL_WITH_CALIB;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
irq_unlock(state);
/*Poll waiting for normal mode*/
do {
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
} while ((reg_value & 0x1) == 0);
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);
}
}
#else
static void adc_goto_normal_mode(struct device *dev)
{
u32_t reg_value;
u32_t state;
ARG_UNUSED(dev);
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR);
if (((reg_value & 0xE) >> 1) == ADC_NORMAL_WO_CALIB) {
state = irq_lock();
/*Request Power Down*/
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= ADC_POWER_DOWN;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
irq_unlock(state);
do {
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
} while ((reg_value & 0x1) == 0);
}
/*Request Normal With Calibration Mode*/
state = irq_lock();
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= ADC_NORMAL_WO_CALIB;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
irq_unlock(state);
/*Poll waiting for normal mode*/
do {
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
} while ((reg_value & 0x1) == 0);
}
#endif
static void adc_goto_deep_power_down(void)
{
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);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= 0 | ADC_CLOCK_GATE;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
irq_unlock(state);
do {
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_SLV0);
} while ((reg_value & 0x1) == 0);
}
}
static void adc_dw_enable(struct device *dev)
{
u32_t reg_value;
struct adc_info *info = dev->driver_data;
const struct adc_config *config = dev->config->config_info;
u32_t adc_base = config->reg_base;
/*Go to Normal Mode*/
sys_out32(ADC_INT_DSB|ENABLE_ADC, adc_base + ADC_CTRL);
adc_goto_normal_mode(dev);
/*Clock Gate*/
reg_value = sys_in32(PERIPH_ADDR_BASE_CREG_MST0);
reg_value &= ~(ADC_CLOCK_GATE);
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0);
sys_out32(ENABLE_ADC, adc_base + ADC_CTRL);
info->state = ADC_STATE_IDLE;
}
static void adc_dw_disable(struct device *dev)
{
u32_t saved;
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);
saved = irq_lock();
sys_out32(sys_in32(adc_base + ADC_SET)|ADC_FLUSH_RX, adc_base + ADC_SET);
irq_unlock(saved);
info->state = ADC_STATE_DISABLED;
}
static int adc_dw_read_request(struct device *dev, struct adc_seq_table *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;
if (info->state != ADC_STATE_IDLE) {
return 1;
}
saved = irq_lock();
info->seq_size = seq_tbl->num_entries;
ctrl = sys_in32(adc_base + ADC_CTRL);
ctrl |= ADC_SEQ_PTR_RST;
sys_out32(ctrl, adc_base + ADC_CTRL);
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 ((seq_tbl->num_entries % 2) != 0) {
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);
}
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);
if (info->state == ADC_STATE_ERROR) {
info->state = ADC_STATE_IDLE;
return -EIO;
}
return 0;
}
static int adc_dw_read(struct device *dev, struct adc_seq_table *seq_tbl)
{
struct adc_info *info = dev->driver_data;
#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;
}
#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;
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;
sys_out32(ADC_INT_DSB | ADC_CLK_ENABLE, adc_base + ADC_CTRL);
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->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);
sys_out32(tmp_val|val, adc_base + ADC_SET);
sys_out32(config->clock_ratio & ADC_CLK_RATIO_MASK,
adc_base + ADC_DIVSEQSTAT);
sys_out32(ADC_INT_ENABLE & ~(ADC_CLK_ENABLE),
adc_base + ADC_CTRL);
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);
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;
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);
}
/*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);
k_sem_give(&info->device_sync_sem);
return;
}
/*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;
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);
}
#ifdef CONFIG_ADC_DW
struct adc_info adc_info_dev = {
.state = ADC_STATE_IDLE,
#ifdef CONFIG_ADC_DW_CALIBRATION
.calibration_value = ADC_NONE_CALIBRATION,
#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,
#ifdef CONFIG_ADC_DW_SERIAL
.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,
#endif
#ifdef CONFIG_ADC_DW_RISING_EDGE
.capture_mode = 0,
#elif CONFIG_ADC_DW_FALLING_EDGE
.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,
};
DEVICE_AND_API_INIT(adc_dw, CONFIG_ADC_0_NAME, &adc_dw_init,
&adc_info_dev, &adc_config_dev,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&api_funcs);
static void adc_config_irq(void)
{
IRQ_CONNECT(IRQ_ADC_IRQ, CONFIG_ADC_0_IRQ_PRI, adc_dw_rx_isr,
DEVICE_GET(adc_dw), 0);
irq_enable(IRQ_ADC_IRQ);
IRQ_CONNECT(IRQ_ADC_ERR, CONFIG_ADC_0_IRQ_PRI,
adc_dw_err_isr, DEVICE_GET(adc_dw), 0);
irq_enable(IRQ_ADC_ERR);
}
#endif