222 lines
5.5 KiB
C
222 lines
5.5 KiB
C
/*
|
|
* 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 <errno.h>
|
|
#include <stdio.h>
|
|
|
|
#include <nanokernel.h>
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <aio_comparator.h>
|
|
|
|
#include "aio_dw_comparator.h"
|
|
|
|
#define INT_COMPARATORS_MASK 0x7FFFF
|
|
|
|
static int dw_aio_cmp_config(struct device *dev);
|
|
|
|
static int dw_aio_cmp_disable(struct device *dev, uint8_t index)
|
|
{
|
|
struct dw_aio_cmp_dev_cfg_t *config =
|
|
(struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info;
|
|
struct dw_aio_cmp_t *regs =
|
|
(struct dw_aio_cmp_t *)config->base_address;
|
|
|
|
if (index >= AIO_DW_CMP_COUNT) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Disable interrupt to host */
|
|
SCSS_INTERRUPT->int_comparators_host_mask |= (1 << index);
|
|
|
|
/* Disable comparator <index> */
|
|
regs->en &= ~(1 << index);
|
|
/* Disable power in comparator <index> */
|
|
regs->pwr &= ~(1 << index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw_aio_cmp_configure(struct device *dev, uint8_t index,
|
|
enum aio_cmp_polarity polarity,
|
|
enum aio_cmp_ref refsel,
|
|
aio_cmp_cb cb, void *param)
|
|
{
|
|
struct dw_aio_cmp_dev_cfg_t *config =
|
|
(struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info;
|
|
struct dw_aio_cmp_dev_data_t *dev_data =
|
|
(struct dw_aio_cmp_dev_data_t *)dev->driver_data;
|
|
struct dw_aio_cmp_t *regs =
|
|
(struct dw_aio_cmp_t *)config->base_address;
|
|
uint32_t reg_val;
|
|
|
|
/* index out of range */
|
|
if (index >= AIO_DW_CMP_COUNT) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* make sure reference makes sense */
|
|
if ((refsel != AIO_CMP_REF_A) && (refsel != AIO_CMP_REF_B))
|
|
return -EINVAL;
|
|
|
|
/* make sure polarity makes sense */
|
|
if ((polarity != AIO_CMP_POL_RISE) && (polarity != AIO_CMP_POL_FALL))
|
|
return -EINVAL;
|
|
|
|
dev_data->cb[index].cb = cb;
|
|
dev_data->cb[index].param = param;
|
|
|
|
/* Disable interrupt to host */
|
|
SCSS_INTERRUPT->int_comparators_host_mask |= (1 << index);
|
|
|
|
/* Disable comparator <index> before config */
|
|
regs->en &= ~(1 << index);
|
|
regs->pwr &= ~(1 << index);
|
|
|
|
/**
|
|
* Setup reference voltage source
|
|
* REF_A: bit ==> 0, REF_B: bit ==> 1
|
|
*/
|
|
reg_val = regs->ref_sel;
|
|
if (refsel == AIO_CMP_REF_A)
|
|
reg_val &= ~(1 << index);
|
|
else
|
|
reg_val |= (1 << index);
|
|
regs->ref_sel = reg_val;
|
|
|
|
/**
|
|
* Setup reference polarity
|
|
* RISING: bit ==> 0, FALLING: bit ==> 1
|
|
*/
|
|
reg_val = regs->ref_pol;
|
|
if (polarity == AIO_CMP_POL_RISE)
|
|
reg_val &= ~(1 << index);
|
|
else
|
|
reg_val |= (1 << index);
|
|
regs->ref_pol = reg_val;
|
|
|
|
/* Enable power of comparator <index> */
|
|
regs->pwr |= (1 << index);
|
|
|
|
/* Enable comparator <index> */
|
|
regs->en |= (1 << index);
|
|
|
|
/* Enable interrupt to host */
|
|
SCSS_INTERRUPT->int_comparators_host_mask &= ~(1 << index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dw_aio_cmp_isr(struct device *dev)
|
|
{
|
|
struct dw_aio_cmp_dev_cfg_t *config =
|
|
(struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info;
|
|
struct dw_aio_cmp_dev_data_t *dev_data =
|
|
(struct dw_aio_cmp_dev_data_t *)dev->driver_data;
|
|
struct dw_aio_cmp_t *regs =
|
|
(struct dw_aio_cmp_t *)config->base_address;
|
|
int i;
|
|
int reg_stat_clr;
|
|
|
|
reg_stat_clr = regs->stat_clr;
|
|
for (i = 0; i < dev_data->num_cmp; i++) {
|
|
if (reg_stat_clr & (1 << i)) {
|
|
|
|
dw_aio_cmp_disable(dev, i);
|
|
|
|
if (dev_data->cb[i].cb != NULL) {
|
|
dev_data->cb[i].cb(dev_data->cb[i].param);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear interrupt status by writing 1s */
|
|
regs->stat_clr = reg_stat_clr;
|
|
}
|
|
|
|
static struct aio_cmp_driver_api dw_aio_cmp_funcs = {
|
|
.disable = dw_aio_cmp_disable,
|
|
.configure = dw_aio_cmp_configure,
|
|
};
|
|
|
|
int dw_aio_cmp_init(struct device *dev)
|
|
{
|
|
struct dw_aio_cmp_dev_cfg_t *config =
|
|
(struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info;
|
|
struct dw_aio_cmp_dev_data_t *dev_data =
|
|
(struct dw_aio_cmp_dev_data_t *)dev->driver_data;
|
|
struct dw_aio_cmp_t *regs =
|
|
(struct dw_aio_cmp_t *)config->base_address;
|
|
int i;
|
|
|
|
if ((config->base_address == 0) || (config->interrupt_num == 0))
|
|
return -EINVAL;
|
|
|
|
if (config->config_func) {
|
|
i = config->config_func(dev);
|
|
if (i != 0)
|
|
return i;
|
|
}
|
|
|
|
dev->driver_api = &dw_aio_cmp_funcs;
|
|
|
|
/* Clear host interrupt mask */
|
|
SCSS_INTERRUPT->int_comparators_host_mask |= INT_COMPARATORS_MASK;
|
|
|
|
/* Clear comparator interrupt status */
|
|
regs->stat_clr |= INT_COMPARATORS_MASK;
|
|
|
|
/* Disable all comparators */
|
|
regs->en &= ~INT_COMPARATORS_MASK;
|
|
|
|
/* Power down all comparators */
|
|
regs->pwr &= ~INT_COMPARATORS_MASK;
|
|
|
|
/* Clear callback pointers */
|
|
for (i = 0; i < dev_data->num_cmp; i++) {
|
|
dev_data->cb[i].cb = NULL;
|
|
dev_data->cb[i].param = NULL;
|
|
}
|
|
|
|
irq_enable(config->interrupt_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct dw_aio_cmp_dev_cfg_t dw_aio_cmp_dev_config = {
|
|
.base_address = AIO_DW_COMPARATOR_BASE_ADDR,
|
|
.interrupt_num = INT_AIO_CMP_IRQ,
|
|
.config_func = dw_aio_cmp_config,
|
|
};
|
|
|
|
struct dw_aio_cmp_dev_data_t dw_aio_cmp_dev_data = {
|
|
.num_cmp = AIO_DW_CMP_COUNT,
|
|
};
|
|
|
|
DEVICE_INIT(dw_aio_cmp, CONFIG_AIO_DW_COMPARATOR_DEV_NAME, &dw_aio_cmp_init,
|
|
&dw_aio_cmp_dev_data, &dw_aio_cmp_dev_config,
|
|
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
|
|
|
static int dw_aio_cmp_config(struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
IRQ_CONNECT(INT_AIO_CMP_IRQ, CONFIG_AIO_DW_COMPARATOR_IRQ_PRI,
|
|
dw_aio_cmp_isr,
|
|
DEVICE_GET(dw_aio_cmp), 0);
|
|
return 0;
|
|
}
|