zephyr/drivers/adc/adc_qmsi_ss.c
Leandro Pereira 732424f065 drivers, net: Clean up semaphore initialization
Change the common "init with 0" + "give" idiom to "init with 1".  This
won't change the behavior or performance, but should decrease the size
ever so slightly.

This change has been performed mechanically with the following
Coccinelle script:

    @@
    expression SEM;
    expression LIMIT;
    expression TIMEOUT;
    @@

    - k_sem_init(SEM, 0, LIMIT);
    - k_sem_give(SEM);
    + k_sem_init(SEM, 1, LIMIT);

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
2017-07-27 15:23:07 -04:00

346 lines
7.8 KiB
C

/* adc_qmsi.c - QMSI ADC Sensor Subsystem driver */
/*
* Copyright (c) 2016 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 <atomic.h>
#include "qm_ss_isr.h"
#include "qm_ss_adc.h"
#include "ss_clk.h"
enum {
ADC_STATE_IDLE,
ADC_STATE_BUSY,
ADC_STATE_ERROR
};
struct adc_info {
atomic_t state;
struct k_sem device_sync_sem;
struct k_sem sem;
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
u32_t device_power_state;
qm_ss_adc_context_t adc_ctx;
#endif
};
static void adc_config_irq(void);
static qm_ss_adc_config_t cfg;
#if (CONFIG_ADC_QMSI_INTERRUPT)
static void complete_callback(void *data, int error, qm_ss_adc_status_t status,
qm_ss_adc_cb_source_t source)
{
ARG_UNUSED(status);
ARG_UNUSED(source);
struct device *dev = data;
struct adc_info *info = dev->driver_data;
if (info) {
if (error) {
info->state = ADC_STATE_ERROR;
}
k_sem_give(&info->device_sync_sem);
}
}
#endif
static void adc_lock(struct adc_info *data)
{
k_sem_take(&data->sem, K_FOREVER);
data->state = ADC_STATE_BUSY;
}
static void adc_unlock(struct adc_info *data)
{
k_sem_give(&data->sem);
data->state = ADC_STATE_IDLE;
}
#if (CONFIG_ADC_QMSI_CALIBRATION)
static void adc_qmsi_ss_enable(struct device *dev)
{
struct adc_info *info = dev->driver_data;
adc_lock(info);
qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_NORM_CAL);
qm_ss_adc_calibrate(QM_SS_ADC_0);
adc_unlock(info);
}
#else
static void adc_qmsi_ss_enable(struct device *dev)
{
struct adc_info *info = dev->driver_data;
adc_lock(info);
qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_NORM_NO_CAL);
adc_unlock(info);
}
#endif /* CONFIG_ADC_QMSI_CALIBRATION */
static void adc_qmsi_ss_disable(struct device *dev)
{
struct adc_info *info = dev->driver_data;
adc_lock(info);
/* Go to deep sleep */
qm_ss_adc_set_mode(QM_SS_ADC_0, QM_SS_ADC_MODE_DEEP_PWR_DOWN);
adc_unlock(info);
}
#if (CONFIG_ADC_QMSI_POLL)
static int adc_qmsi_ss_read(struct device *dev, struct adc_seq_table *seq_tbl)
{
int i, ret = 0;
qm_ss_adc_xfer_t xfer;
qm_ss_adc_status_t status;
struct adc_info *info = dev->driver_data;
for (i = 0; i < seq_tbl->num_entries; i++) {
xfer.ch = (qm_ss_adc_channel_t *)&seq_tbl->entries[i].channel_id;
/* Just one channel at the time using the Zephyr sequence table
*/
xfer.ch_len = 1;
xfer.samples = (qm_ss_adc_sample_t *)seq_tbl->entries[i].buffer;
/* buffer length (bytes) the number of samples, the QMSI Driver
* does not allow more than QM_ADC_FIFO_LEN samples at the time
* in polling mode, if that happens, the qm_adc_convert api will
* return with an error
*/
xfer.samples_len =
(seq_tbl->entries[i].buffer_length)/sizeof(qm_ss_adc_sample_t);
xfer.callback = NULL;
xfer.callback_data = NULL;
cfg.window = seq_tbl->entries[i].sampling_delay;
adc_lock(info);
if (qm_ss_adc_set_config(QM_SS_ADC_0, &cfg) != 0) {
ret = -EINVAL;
adc_unlock(info);
break;
}
/* Run the conversion, here the function will poll for the
* samples. The function will constantly read the status
* register to check if the number of samples required has been
* captured
*/
if (qm_ss_adc_convert(QM_SS_ADC_0, &xfer, &status) != 0) {
ret = -EIO;
adc_unlock(info);
break;
}
/* Successful Analog to Digital conversion */
adc_unlock(info);
}
return ret;
}
#else
static int adc_qmsi_ss_read(struct device *dev, struct adc_seq_table *seq_tbl)
{
int i, ret = 0;
qm_ss_adc_xfer_t xfer;
struct adc_info *info = dev->driver_data;
for (i = 0; i < seq_tbl->num_entries; i++) {
xfer.ch = (qm_ss_adc_channel_t *)&seq_tbl->entries[i].channel_id;
/* Just one channel at the time using the Zephyr sequence table */
xfer.ch_len = 1;
xfer.samples =
(qm_ss_adc_sample_t *)seq_tbl->entries[i].buffer;
xfer.samples_len =
(seq_tbl->entries[i].buffer_length)/sizeof(qm_ss_adc_sample_t);
xfer.callback = complete_callback;
xfer.callback_data = dev;
cfg.window = seq_tbl->entries[i].sampling_delay;
adc_lock(info);
if (qm_ss_adc_set_config(QM_SS_ADC_0, &cfg) != 0) {
ret = -EINVAL;
adc_unlock(info);
break;
}
/* This is the interrupt driven API, will generate and interrupt and
* call the complete_callback function once the samples have been
* obtained
*/
if (qm_ss_adc_irq_convert(QM_SS_ADC_0, &xfer) != 0) {
ret = -EIO;
adc_unlock(info);
break;
}
/* Wait for the interrupt to finish */
k_sem_take(&info->device_sync_sem, K_FOREVER);
if (info->state == ADC_STATE_ERROR) {
ret = -EIO;
adc_unlock(info);
break;
}
/* Successful Analog to Digital conversion */
adc_unlock(info);
}
return ret;
}
#endif /* CONFIG_ADC_QMSI_POLL */
static void adc_qmsi_ss_rx_isr(void *arg)
{
ARG_UNUSED(arg);
qm_ss_adc_0_isr(NULL);
}
static void adc_qmsi_ss_err_isr(void *arg)
{
ARG_UNUSED(arg);
qm_ss_adc_0_error_isr(NULL);
}
static const struct adc_driver_api api_funcs = {
.enable = adc_qmsi_ss_enable,
.disable = adc_qmsi_ss_disable,
.read = adc_qmsi_ss_read,
};
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static void adc_qmsi_ss_set_power_state(struct device *dev,
u32_t power_state)
{
struct adc_info *context = dev->driver_data;
context->device_power_state = power_state;
}
static u32_t adc_qmsi_ss_get_power_state(struct device *dev)
{
struct adc_info *context = dev->driver_data;
return context->device_power_state;
}
static int adc_qmsi_ss_suspend_device(struct device *dev)
{
struct adc_info *context = dev->driver_data;
qm_ss_adc_save_context(QM_SS_ADC_0, &context->adc_ctx);
adc_qmsi_ss_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
return 0;
}
static int adc_qmsi_ss_resume_device_from_suspend(struct device *dev)
{
struct adc_info *context = dev->driver_data;
qm_ss_adc_restore_context(QM_SS_ADC_0, &context->adc_ctx);
adc_qmsi_ss_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
static int adc_qmsi_ss_device_ctrl(struct device *dev, u32_t ctrl_command,
void *context)
{
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
return adc_qmsi_ss_suspend_device(dev);
} else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
return adc_qmsi_ss_resume_device_from_suspend(dev);
}
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
*((u32_t *)context) = adc_qmsi_ss_get_power_state(dev);
}
return 0;
}
#else
#define adc_qmsi_ss_set_power_state(...)
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
static int adc_qmsi_ss_init(struct device *dev)
{
struct adc_info *info = dev->driver_data;
/* Set up config */
/* Clock cycles between the start of each sample */
cfg.window = CONFIG_ADC_QMSI_SERIAL_DELAY;
cfg.resolution = CONFIG_ADC_QMSI_SAMPLE_WIDTH;
qm_ss_adc_set_config(QM_SS_ADC_0, &cfg);
ss_clk_adc_enable();
ss_clk_adc_set_div(CONFIG_ADC_QMSI_CLOCK_RATIO);
k_sem_init(&info->device_sync_sem, 0, UINT_MAX);
k_sem_init(&info->sem, 1, UINT_MAX);
info->state = ADC_STATE_IDLE;
adc_config_irq();
adc_qmsi_ss_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
return 0;
}
static struct adc_info adc_info_dev;
DEVICE_DEFINE(adc_qmsi_ss, CONFIG_ADC_0_NAME, &adc_qmsi_ss_init,
adc_qmsi_ss_device_ctrl, &adc_info_dev, NULL, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &api_funcs);
static void adc_config_irq(void)
{
u32_t *scss_intmask;
IRQ_CONNECT(IRQ_ADC_IRQ, CONFIG_ADC_0_IRQ_PRI,
adc_qmsi_ss_rx_isr, DEVICE_GET(adc_qmsi_ss), 0);
irq_enable(IRQ_ADC_IRQ);
IRQ_CONNECT(IRQ_ADC_ERR, CONFIG_ADC_0_IRQ_PRI,
adc_qmsi_ss_err_isr, DEVICE_GET(adc_qmsi_ss), 0);
irq_enable(IRQ_ADC_ERR);
scss_intmask =
(u32_t *)&QM_INTERRUPT_ROUTER->ss_adc_0_error_int_mask;
*scss_intmask &= ~BIT(8);
scss_intmask = (u32_t *)&QM_INTERRUPT_ROUTER->ss_adc_0_int_mask;
*scss_intmask &= ~BIT(8);
}