| /* adc-ti-adc108s102.c - TI's ADC 108s102 driver implementation */ |
| |
| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| |
| #include <kernel.h> |
| #include <misc/util.h> |
| #define SYS_LOG_NO_NEWLINE |
| #define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL |
| #include <logging/sys_log.h> |
| #include <string.h> |
| #include <init.h> |
| |
| #include "adc_ti_adc108s102.h" |
| |
| static inline int _ti_adc108s102_sampling(struct device *dev) |
| { |
| struct ti_adc108s102_data *adc = dev->driver_data; |
| |
| SYS_LOG_DBG("Sampling!\n"); |
| |
| /* SPI deals with u8_t buffers so multiplying by 2 the length */ |
| return spi_transceive(adc->spi, adc->cmd_buffer, |
| adc->cmd_buf_len * 2, |
| adc->sampling_buffer, |
| adc->sampling_buf_len * 2); |
| } |
| |
| 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; |
| u32_t s_i, i; |
| |
| SYS_LOG_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; |
| } |
| |
| *((u16_t *)(entry->buffer+chan->buf_idx)) = |
| ADC108S102_RESULT(adc->sampling_buffer[s_i]); |
| |
| chan->buf_idx += 2; |
| } |
| } |
| |
| static inline s32_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; |
| s32_t sampling_delay = 0; |
| u32_t i; |
| |
| adc->cmd_buf_len = 0; |
| adc->sampling_buf_len = 1; /* Counting the dummy byte */ |
| |
| 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; |
| } |
| |
| SYS_LOG_DBG("Requesting channel %d\n", entry->channel_id); |
| 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) { |
| return ADC108S102_DONE; |
| } |
| |
| /* dummy cmd byte */ |
| adc->cmd_buffer[adc->cmd_buf_len] = 0; |
| adc->cmd_buf_len++; |
| |
| SYS_LOG_DBG("ADC108S102 is prepared..."); |
| |
| return sampling_delay; |
| } |
| |
| static void ti_adc108s102_enable(struct device *dev) |
| { |
| ARG_UNUSED(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) |
| { |
| ARG_UNUSED(dev); |
| |
| /* Same issue as with ti_adc108s102_enable() */ |
| } |
| |
| static inline int _verify_entries(struct adc_seq_table *seq_table) |
| { |
| struct adc_seq_entry *entry; |
| u32_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) |
| { |
| const struct ti_adc108s102_config *config = dev->config->config_info; |
| struct ti_adc108s102_data *adc = dev->driver_data; |
| struct spi_config spi_conf; |
| int ret = 0; |
| s32_t delay; |
| |
| spi_conf.config = config->spi_config_flags; |
| spi_conf.max_sys_freq = config->spi_freq; |
| |
| if (spi_configure(adc->spi, &spi_conf)) { |
| return -EIO; |
| } |
| |
| if (spi_slave_select(adc->spi, config->spi_slave)) { |
| return -EIO; |
| } |
| |
| /* Resetting all internal channel data */ |
| memset(adc->chans, 0, ADC108S102_CHANNELS_SIZE); |
| |
| if (_verify_entries(seq_table) == 0) { |
| return -EINVAL; |
| } |
| |
| adc->seq_table = seq_table; |
| |
| /* Sampling */ |
| while (1) { |
| delay = _ti_adc108s102_prepare(dev); |
| if (delay == ADC108S102_DONE) { |
| break; |
| } |
| |
| /* convert to milliseconds */ |
| delay = (s32_t)((MSEC_PER_SEC * (u64_t)delay) / |
| sys_clock_ticks_per_sec); |
| |
| k_sleep(delay); |
| |
| ret = _ti_adc108s102_sampling(dev); |
| if (ret != 0) { |
| break; |
| } |
| |
| _ti_adc108s102_handle_result(dev); |
| } |
| |
| return ret; |
| } |
| |
| static const struct adc_driver_api ti_adc108s102_api = { |
| .enable = ti_adc108s102_enable, |
| .disable = ti_adc108s102_disable, |
| .read = ti_adc108s102_read, |
| }; |
| |
| static int ti_adc108s102_init(struct device *dev) |
| { |
| const 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 -EPERM; |
| } |
| |
| SYS_LOG_DBG("ADC108s102 initialized\n"); |
| |
| dev->driver_api = &ti_adc108s102_api; |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_ADC_TI_ADC108S102 |
| |
| static struct ti_adc108s102_data adc108s102_data; |
| |
| static const struct ti_adc108s102_config adc108s102_config = { |
| .spi_port = CONFIG_ADC_TI_ADC108S102_SPI_PORT_NAME, |
| .spi_config_flags = CONFIG_ADC_TI_ADC108S102_SPI_CONFIGURATION, |
| .spi_freq = CONFIG_ADC_TI_ADC108S102_SPI_MAX_FREQ, |
| .spi_slave = CONFIG_ADC_TI_ADC108S102_SPI_SLAVE, |
| }; |
| |
| DEVICE_INIT(adc108s102, CONFIG_ADC_0_NAME, |
| ti_adc108s102_init, |
| &adc108s102_data, &adc108s102_config, |
| POST_KERNEL, CONFIG_ADC_INIT_PRIORITY); |
| |
| #endif /* CONFIG_ADC_TI_ADC108S102 */ |