From 5bcdfbe255bece0c427bc358a593257732d98a8a Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 5 Oct 2015 18:41:18 +0300 Subject: [PATCH] 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 --- drivers/adc/Kconfig | 48 +++++ drivers/adc/Makefile | 1 + drivers/adc/adc-ti-adc108s102.c | 306 ++++++++++++++++++++++++++++++++ drivers/adc/adc-ti-adc108s102.h | 74 ++++++++ 4 files changed, 429 insertions(+) create mode 100644 drivers/adc/adc-ti-adc108s102.c create mode 100644 drivers/adc/adc-ti-adc108s102.h diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 6fd3a74908f..9ebe66c8ad4 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -46,3 +46,51 @@ config ADC_DEBUG default n help 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. diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile index e69de29bb2d..6b080275c52 100644 --- a/drivers/adc/Makefile +++ b/drivers/adc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ADC_TI_ADC108S102) += adc-ti-adc108s102.o diff --git a/drivers/adc/adc-ti-adc108s102.c b/drivers/adc/adc-ti-adc108s102.c new file mode 100644 index 00000000000..1010d298ae2 --- /dev/null +++ b/drivers/adc/adc-ti-adc108s102.c @@ -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 +#include +#include +#include + +#include + +#ifndef CONFIG_ADC_DEBUG +#define DBG(...) { ; } +#else +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#define DBG printf +#else +#include +#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 */ diff --git a/drivers/adc/adc-ti-adc108s102.h b/drivers/adc/adc-ti-adc108s102.h new file mode 100644 index 00000000000..d89e2136854 --- /dev/null +++ b/drivers/adc/adc-ti-adc108s102.h @@ -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 +#include + +#include + +/* 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__ */