zephyr/drivers/adc/adc_dw.c

346 lines
8.9 KiB
C

/* adc_dw.c - Designware ADC driver */
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <init.h>
#include <nanokernel.h>
#include <string.h>
#include <stdlib.h>
#include <board.h>
#include <adc.h>
#include <arch/cpu.h>
#include "adc_dw.h"
#include "adc_ss_dw.h"
#define ADC_CLOCK_GATE (1 << 31)
#define ADC_STANDBY 0x02
#define ADC_NORMAL_WO_CALIB 0x04
#define ADC_MODE_MASK 0x07
#define ONE_BIT_SET 0x1
#define FIVE_BITS_SET 0x1f
#define SIX_BITS_SET 0x3f
#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
#define ADC_INT_PRIORITY 0
#ifdef CONFIG_PLATFORM_QUARK_SE_ARC
#define int_unmask(__mask) \
sys_write32(sys_read32((__mask)) & ENABLE_SSS_INTERRUPTS, (__mask))
#else
#define int_unmask(...) { ; }
#endif
static void adc_config_0_irq(struct device *dev);
static void adc_goto_normal_mode_wo_calibration(void)
{
uint32_t reg_value;
uint32_t state;
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR);
if ((reg_value & ADC_MODE_MASK) != ADC_NORMAL_WO_CALIB) {
state = irq_lock();
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= ADC_STANDBY | ADC_CLOCK_GATE;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
irq_unlock(state);
do {
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR) & 0x8;
} while (reg_value == 0);
state = irq_lock();
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= ADC_NORMAL_WO_CALIB | ADC_CLOCK_GATE;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
irq_unlock(state);
do {
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR) & 0x8;
} while (reg_value == 0);
}
}
static void adc_goto_deep_power_down(void)
{
uint32_t reg_value;
uint32_t state;
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0+SLV_OBSR);
if ((reg_value & ADC_MODE_MASK) != 0) {
state = irq_lock();
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
reg_value &= ~(ADC_MODE_MASK);
reg_value |= 0 | ADC_CLOCK_GATE;
sys_out32(reg_value, PERIPH_ADDR_BASE_CREG_MST0 + MST_CTL);
irq_unlock(state);
do {
reg_value = sys_in32(
PERIPH_ADDR_BASE_CREG_SLV0 + SLV_OBSR) & 0x8;
} while (reg_value == 0);
}
}
static void adc_dw_enable(struct device *dev)
{
struct adc_info *info = dev->driver_data;
struct adc_config *config = dev->config->config_info;
uint32_t adc_base = config->reg_base;
adc_goto_normal_mode_wo_calibration();
sys_out32(ENABLE_ADC, adc_base + ADC_CTRL);
info->state = ADC_STATE_IDLE;
}
static void adc_dw_disable(struct device *dev)
{
uint32_t saved;
struct adc_info *info = dev->driver_data;
struct adc_config *config = dev->config->config_info;
uint32_t adc_base = config->reg_base;
adc_goto_deep_power_down();
sys_out32(ADC_INT_DSB|ADC_SEQ_PTR_RST, adc_base + ADC_CTRL);
saved = irq_lock();
info->cb = NULL;
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(struct device *dev, struct adc_seq_table *seq_tbl)
{
uint32_t i;
uint32_t ctrl;
uint32_t tmp_val;
uint32_t num_iters;
uint32_t saved;
struct adc_seq_entry *entry;
struct adc_info *info = dev->driver_data;
struct adc_config *config = dev->config->config_info;
uint32_t adc_base = config->reg_base;
if (info->state != ADC_STATE_IDLE) {
return 1;
}
for (i = 0, entry = seq_tbl->entries; i < seq_tbl->num_entries; i++) {
if (entry[i].buffer_length % 4 != 0) {
return 1;
}
}
saved = irq_lock();
ctrl = sys_in32(adc_base + ADC_CTRL);
ctrl |= ADC_SEQ_PTR_RST;
sys_out32(ctrl, adc_base + ADC_CTRL);
info->seq_size = seq_tbl->num_entries;
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 |= ((info->seq_size - 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);
if (info->state == ADC_STATE_IDLE) {
int num_entries = seq_tbl->num_entries;
memset(info->rx_buf, (int)NULL, BUFS_NUM);
for (i = 0; i < num_entries; i++) {
info->rx_buf[i] = (uint32_t *)entry[i].buffer;
info->rx_len[i] = entry[i].buffer_length/4;
}
info->index = i-1;
info->state = ADC_STATE_SAMPLING;
sys_out32(START_ADC_SEQ, adc_base + ADC_CTRL);
} else if (config->seq_mode == IO_ADC_SEQ_MODE_REPETITIVE) {
uint32_t idx = info->index;
if (info->rx_buf[idx] == NULL) {
info->rx_buf[idx] = (uint32_t *)entry[idx].buffer;
info->rx_len[idx] = entry[idx].buffer_length/4;
}
}
return 0;
}
static void dw_set_user_callback(struct device *dev, adc_callback_t cb)
{
struct adc_info *info = dev->driver_data;
info->cb = cb;
}
static struct adc_driver_api api_funcs = {
.enable = adc_dw_enable,
.disable = adc_dw_disable,
.read = adc_dw_read,
.set_callback = dw_set_user_callback
};
int adc_dw_init(struct device *dev)
{
uint32_t tmp_val;
uint32_t val;
struct adc_config *config = dev->config->config_info;
uint32_t adc_base = config->reg_base;
dev->driver_api = &api_funcs;
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 |= ((config->in_mode & ONE_BIT_SET) << 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(dev);
int_unmask(config->reg_irq_mask);
int_unmask(config->reg_err_mask);
return 0;
}
#ifdef CONFIG_ADC_DW_0
struct adc_info adc_info_dev_0 = {
.seq_size = 1,
.state = ADC_STATE_IDLE
};
struct adc_config adc_config_dev_0 = {
.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,
.rx_vector = IO_ADC0_INT_IRQ,
.err_vector = IO_ADC0_INT_ERR,
.fifo_tld = IO_ADC0_FS/2,
.in_mode = CONFIG_ADC_DW_INPUT_MODE,
.out_mode = CONFIG_ADC_DW_OUTPUT_MODE,
.capture_mode = CONFIG_ADC_DW_CAPTURE_MODE,
.seq_mode = CONFIG_ADC_DW_SEQ_MODE,
.sample_width = CONFIG_ADC_DW_WIDTH,
.clock_ratio = CONFIG_ADC_DW_CLOCK_RATIO,
.serial_dly = CONFIG_ADC_DW_SERIAL_DELAY,
.config_func = adc_config_0_irq,
};
DECLARE_DEVICE_INIT_CONFIG(adc_dw_0, /* config name*/
CONFIG_ADC_DW_NAME_0, /* driver name*/
&adc_dw_init, /* init function*/
&adc_config_dev_0); /* config options*/
pre_kernel_late_init(adc_dw_0, &adc_info_dev_0);
struct device *adc_dw_isr_0_device = SYS_GET_DEVICE(adc_dw_0);
IRQ_CONNECT_STATIC(adc_dw_0,
IO_ADC0_INT_IRQ,
ADC_INT_PRIORITY,
adc_dw_rx_isr,
0);
IRQ_CONNECT_STATIC(adc_dw_0,
IO_ADC0_INT_IRQ,
ADC_INT_ERR,
adc_dw_err_isr,
0);
static void adc_config_0_irq(struct device *dev)
{
struct adc_config *config = dev->config->config_info;
IRQ_CONFIG(adc_dw_rx_isr, config->rx_vector, 0);
irq_enable(config->rx_vector);
IRQ_CONFIG(adc_dw_err_isr, config->err_vector, 0);
irq_enable(config->err_vector);
}
#endif