adc: Adding driver for TI's adc108s102 chip
It's an SPI driven chip and proposes up to 8 channels for input conversion. It's found on Galileo board. Change-Id: I916367e9be0849812c6a509082a501730a01ce60 Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
parent
935eedeedf
commit
5bcdfbe255
4 changed files with 429 additions and 0 deletions
|
@ -46,3 +46,51 @@ config ADC_DEBUG
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable debug output for ADC drivers
|
Enable debug output for ADC drivers
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102
|
||||||
|
bool "TI adc108s102 chip driver"
|
||||||
|
depends on ADC
|
||||||
|
select SPI
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable support for TI's ADC chip adc108s102 driver.
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0
|
||||||
|
bool "First instance of TI's adc108s102 chip"
|
||||||
|
depends on ADC_TI_ADC108S102
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enalbe first instance of adc108s102 driver.
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0_DRV_NAME
|
||||||
|
string "Driver's name"
|
||||||
|
depends on ADC_TI_ADC108S102_0
|
||||||
|
default "adc108s102_0"
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0_SPI_PORT_NAME
|
||||||
|
string "Master SPI port name"
|
||||||
|
depends on ADC_TI_ADC108S102_0
|
||||||
|
default ""
|
||||||
|
help
|
||||||
|
Master SPI port name through which adc108s102 chip is accessed.
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0_SPI_CONFIGURATION
|
||||||
|
hex "Master SPI port configuration"
|
||||||
|
depends on ADC_TI_ADC108S102_0
|
||||||
|
default 0x0
|
||||||
|
help
|
||||||
|
Master SPI port configuration flags used to access adc108s102 chip.
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0_SPI_MAX_FREQ
|
||||||
|
int "Master SPI port max frequency"
|
||||||
|
depends on ADC_TI_ADC108S102_0
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Master SPI port maximum frequency used to access adc108s102 chip.
|
||||||
|
|
||||||
|
config ADC_TI_ADC108S102_0_SPI_SLAVE
|
||||||
|
int "SPI slave slot"
|
||||||
|
depends on ADC_TI_ADC108S102_0
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
adc108s102 chip's SPI slave number on master SPI port.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
obj-$(CONFIG_ADC_TI_ADC108S102) += adc-ti-adc108s102.o
|
306
drivers/adc/adc-ti-adc108s102.c
Normal file
306
drivers/adc/adc-ti-adc108s102.c
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
/* adc-ti-adc108s102.c - TI's ADC 108s102 driver implementation */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Intel Corporation
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1) Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3) Neither the name of Intel Corporation nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nanokernel.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <init.h>
|
||||||
|
|
||||||
|
#include <adc-ti-adc108s102.h>
|
||||||
|
|
||||||
|
#ifndef CONFIG_ADC_DEBUG
|
||||||
|
#define DBG(...) { ; }
|
||||||
|
#else
|
||||||
|
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||||
|
#include <stdio.h>
|
||||||
|
#define DBG printf
|
||||||
|
#else
|
||||||
|
#include <misc/printk.h>
|
||||||
|
#define DBG printk
|
||||||
|
#endif /* CONFIG_STDOUT_CONSOLE */
|
||||||
|
#endif /* CONFIG_ADC_DEBUG */
|
||||||
|
|
||||||
|
static void _ti_adc108s102_sampling(int data, int unused)
|
||||||
|
{
|
||||||
|
struct device *dev = INT_TO_POINTER(data);
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
|
||||||
|
ARG_UNUSED(unused);
|
||||||
|
|
||||||
|
DBG("Sampling!\n");
|
||||||
|
|
||||||
|
/* SPI deals with uint8_t buffers so multiplying by 2 the length */
|
||||||
|
spi_transceive(adc->spi,
|
||||||
|
(uint8_t *) adc->cmd_buffer,
|
||||||
|
adc->cmd_buf_len*2,
|
||||||
|
(uint8_t *) adc->sampling_buffer,
|
||||||
|
adc->sampling_buf_len*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _ti_adc108s102_completed(struct device *dev,
|
||||||
|
enum adc_callback_type cb_type)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
|
||||||
|
adc->seq_table = NULL;
|
||||||
|
|
||||||
|
adc->cb(dev, cb_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _ti_adc108s102_handle_result(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
struct adc_seq_table *seq_table = adc->seq_table;
|
||||||
|
struct ti_adc108s102_chan *chan;
|
||||||
|
struct adc_seq_entry *entry;
|
||||||
|
uint32_t s_i, i;
|
||||||
|
|
||||||
|
DBG("_ti_adc108s102_handle_result()");
|
||||||
|
|
||||||
|
for (i = 0, s_i = 1; i < seq_table->num_entries; i++, s_i++) {
|
||||||
|
entry = &seq_table->entries[i];
|
||||||
|
chan = &adc->chans[entry->channel_id];
|
||||||
|
|
||||||
|
if (entry->buffer_length - chan->buf_idx == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*((uint16_t *)entry->buffer+chan->buf_idx) =
|
||||||
|
sys_be16_to_cpu(adc->sampling_buffer[s_i]);
|
||||||
|
|
||||||
|
chan->buf_idx += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t _ti_adc108s102_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
struct adc_seq_table *seq_table = adc->seq_table;
|
||||||
|
struct ti_adc108s102_chan *chan;
|
||||||
|
int32_t sampling_delay = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
adc->cmd_buf_len = 0;
|
||||||
|
adc->sampling_buf_len = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < seq_table->num_entries; i++) {
|
||||||
|
struct adc_seq_entry *entry = &seq_table->entries[i];
|
||||||
|
|
||||||
|
/* No more space in the buffer? */
|
||||||
|
chan = &adc->chans[entry->channel_id];
|
||||||
|
if (entry->buffer_length - chan->buf_idx == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->cmd_buffer[adc->cmd_buf_len] =
|
||||||
|
ADC108S102_CHANNEL_CMD(entry->channel_id);
|
||||||
|
|
||||||
|
adc->cmd_buf_len++;
|
||||||
|
adc->sampling_buf_len++;
|
||||||
|
|
||||||
|
sampling_delay = entry->sampling_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adc->cmd_buf_len == 0) {
|
||||||
|
/* We are done */
|
||||||
|
_ti_adc108s102_completed(dev, ADC_CB_DONE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dummy cmd byte */
|
||||||
|
adc->cmd_buffer[adc->cmd_buf_len] = 0;
|
||||||
|
adc->cmd_buf_len++;
|
||||||
|
|
||||||
|
/* 64bits timestamp */
|
||||||
|
adc->sampling_buf_len += 4;
|
||||||
|
|
||||||
|
DBG("ADC108S102 is prepared...");
|
||||||
|
|
||||||
|
return sampling_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _ti_adc108s102_run_with_delay(struct device *dev,
|
||||||
|
int32_t delay)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
|
||||||
|
fiber_delayed_start(adc->sampling_stack,
|
||||||
|
ADC108S102_SAMPLING_STACK_SIZE,
|
||||||
|
_ti_adc108s102_sampling,
|
||||||
|
POINTER_TO_INT(dev), 0,
|
||||||
|
0, 0, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _ti_adc108s102_spi_cb(struct device *spi_dev,
|
||||||
|
enum spi_cb_type cb_type,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct device *dev = user_data;
|
||||||
|
int32_t delay;
|
||||||
|
|
||||||
|
DBG("_ti_adc108s102_spi_cb(%d)\n", cb_type);
|
||||||
|
|
||||||
|
switch (cb_type) {
|
||||||
|
case SPI_CB_WRITE: /* fall through */
|
||||||
|
case SPI_CB_READ: /* fall through */
|
||||||
|
case SPI_CB_TRANSCEIVE:
|
||||||
|
_ti_adc108s102_handle_result(dev);
|
||||||
|
delay = _ti_adc108s102_prepare(dev);
|
||||||
|
|
||||||
|
_ti_adc108s102_run_with_delay(dev, delay);
|
||||||
|
break;
|
||||||
|
case SPI_CB_ERROR: /* fall through */
|
||||||
|
default:
|
||||||
|
_ti_adc108s102_completed(dev, ADC_CB_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ti_adc108s102_enable(struct device *dev)
|
||||||
|
{
|
||||||
|
/* There is nothing to be done. If there is no sampling going on,
|
||||||
|
* the chip will put itself on power-saving mode (that is because
|
||||||
|
* SPI will release CS) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ti_adc108s102_disable(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Same issue as with ti_adc108s102_enable() */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ti_adc108s102_set_callback(struct device *dev, adc_callback_t cb)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
|
||||||
|
adc->cb = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int _verify_entries(struct adc_seq_table *seq_table)
|
||||||
|
{
|
||||||
|
struct adc_seq_entry *entry;
|
||||||
|
uint32_t chans_set = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < seq_table->num_entries; i++) {
|
||||||
|
entry = &seq_table->entries[i];
|
||||||
|
|
||||||
|
if (entry->sampling_delay <= 0 ||
|
||||||
|
entry->channel_id > ADC108S102_CHANNELS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry->buffer_length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chans_set++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chans_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ti_adc108s102_read(struct device *dev,
|
||||||
|
struct adc_seq_table *seq_table)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_config *config = dev->config->config_info;
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
struct spi_config spi_conf;
|
||||||
|
|
||||||
|
spi_conf.config = config->spi_config_flags;
|
||||||
|
spi_conf.max_sys_freq = config->spi_freq;
|
||||||
|
spi_conf.callback = _ti_adc108s102_spi_cb;
|
||||||
|
|
||||||
|
if (spi_configure(adc->spi, &spi_conf, dev)) {
|
||||||
|
return DEV_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_slave_select(adc->spi, config->spi_slave)) {
|
||||||
|
return DEV_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resetting all internal channel data */
|
||||||
|
memset(adc->chans, 0, ADC108S102_CHANNELS_SIZE);
|
||||||
|
|
||||||
|
if (_verify_entries(seq_table) == 0) {
|
||||||
|
return DEV_INVALID_CONF;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc->seq_table = seq_table;
|
||||||
|
|
||||||
|
/* Requesting sampling right away */
|
||||||
|
_ti_adc108s102_prepare(dev);
|
||||||
|
_ti_adc108s102_sampling(POINTER_TO_INT(dev), 0);
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct adc_driver_api ti_adc108s102_api = {
|
||||||
|
.enable = ti_adc108s102_enable,
|
||||||
|
.disable = ti_adc108s102_disable,
|
||||||
|
.set_callback = ti_adc108s102_set_callback,
|
||||||
|
.read = ti_adc108s102_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ti_adc108s102_init(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ti_adc108s102_config *config = dev->config->config_info;
|
||||||
|
struct ti_adc108s102_data *adc = dev->driver_data;
|
||||||
|
|
||||||
|
adc->spi = device_get_binding((char *)config->spi_port);
|
||||||
|
if (!adc->spi) {
|
||||||
|
return DEV_NOT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG("ADC108s102 initialized\n");
|
||||||
|
|
||||||
|
dev->driver_api = &ti_adc108s102_api;
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ADC_TI_ADC108S102_0
|
||||||
|
|
||||||
|
struct ti_adc108s102_data adc108s102_0_data;
|
||||||
|
|
||||||
|
struct ti_adc108s102_config adc108s102_0_config = {
|
||||||
|
.spi_port = CONFIG_ADC_TI_ADC108S102_0_SPI_PORT_NAME,
|
||||||
|
.spi_config_flags = CONFIG_ADC_TI_ADC108S102_0_SPI_CONFIGURATION,
|
||||||
|
.spi_freq = CONFIG_ADC_TI_ADC108S102_0_SPI_MAX_FREQ,
|
||||||
|
.spi_slave = CONFIG_ADC_TI_ADC108S102_0_SPI_SLAVE,
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DEVICE_INIT_CONFIG(adc108s102_0, CONFIG_ADC_TI_ADC108S102_0_DRV_NAME,
|
||||||
|
ti_adc108s102_init, &adc108s102_0_config);
|
||||||
|
|
||||||
|
nano_early_init(adc108s102_0, &adc108s102_0_data);
|
||||||
|
|
||||||
|
#endif /* CONFIG_ADC_TI_ADC108S102_0 */
|
74
drivers/adc/adc-ti-adc108s102.h
Normal file
74
drivers/adc/adc-ti-adc108s102.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/* adc-ti-adc108s102.h - Private */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Intel Corporation
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1) Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3) Neither the name of Intel Corporation nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __ADC108S102_PRIV_H__
|
||||||
|
#define __ADC108S102_PRIV_H__
|
||||||
|
|
||||||
|
#include <spi.h>
|
||||||
|
#include <adc.h>
|
||||||
|
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
/* 8 chans maximum + 1 dummy, 16 bits per-chans -> 18 bytes */
|
||||||
|
#define ADC108S102_CMD_BUFFER_SIZE 9
|
||||||
|
/* 17 maximum dummy bytes + 16 result bytes maximum + 4 timestamp bytes */
|
||||||
|
#define ADC108S102_SAMPLING_BUFFER_SIZE 13
|
||||||
|
#define ADC108S102_SAMPLING_STACK_SIZE 128
|
||||||
|
#define ADC108S102_CHANNELS 8
|
||||||
|
#define ADC108S102_CHANNELS_SIZE \
|
||||||
|
(ADC108S102_CHANNELS * sizeof(uint32_t))
|
||||||
|
#define ADC108S102_CHANNEL_CMD(_channel_) \
|
||||||
|
sys_cpu_to_be16((_channel_ << 8) << 3)
|
||||||
|
|
||||||
|
struct ti_adc108s102_config {
|
||||||
|
const char *spi_port;
|
||||||
|
uint32_t spi_config_flags;
|
||||||
|
uint32_t spi_freq;
|
||||||
|
uint32_t spi_slave;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ti_adc108s102_chan {
|
||||||
|
uint32_t buf_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ti_adc108s102_data {
|
||||||
|
char __stack sampling_stack[ADC108S102_SAMPLING_STACK_SIZE];
|
||||||
|
uint16_t cmd_buffer[ADC108S102_CMD_BUFFER_SIZE];
|
||||||
|
uint8_t cmd_buf_len;
|
||||||
|
uint16_t sampling_buffer[ADC108S102_SAMPLING_BUFFER_SIZE];
|
||||||
|
uint8_t sampling_buf_len;
|
||||||
|
struct device *spi;
|
||||||
|
struct ti_adc108s102_chan chans[ADC108S102_CHANNELS];
|
||||||
|
struct adc_seq_table *seq_table;
|
||||||
|
adc_callback_t cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __ADC108S102_PRIV_H__ */
|
Loading…
Add table
Add a link
Reference in a new issue