blob: eb50e4a4388c3aa306bfd27ce884f36fb2801939 [file] [log] [blame]
/*
* Copyright (c) 2016 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 <board.h>
#include <device.h>
#include <init.h>
#include <aio_comparator.h>
#include "qm_comparator.h"
#define INT_COMPARATORS_MASK 0x7FFFF
#define AIO_QMSI_CMP_COUNT (19)
struct aio_qmsi_cmp_cb {
aio_cmp_cb cb;
void *param;
};
struct aio_qmsi_cmp_dev_data_t {
/** Number of total comparators */
uint8_t num_cmp;
/** Callback for each comparator */
struct aio_qmsi_cmp_cb cb[AIO_QMSI_CMP_COUNT];
};
/* Shadow configuration to keep track of changes */
static qm_ac_config_t config;
static int aio_cmp_config(struct device *dev);
static int aio_qmsi_cmp_disable(struct device *dev, uint8_t index)
{
if (index >= AIO_QMSI_CMP_COUNT) {
return -EINVAL;
}
/* Disable interrupt to host */
QM_SCSS_INT->int_comparators_host_mask |= (1 << index);
/* Disable comparator according to index */
config.int_en &= ~(1 << index);
config.power &= ~(1 << index);
if (qm_ac_set_config(&config) != 0) {
return -EINVAL;
}
return 0;
}
static int aio_qmsi_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 aio_qmsi_cmp_dev_data_t *dev_data =
(struct aio_qmsi_cmp_dev_data_t *)dev->driver_data;
if (index >= AIO_QMSI_CMP_COUNT) {
return -EINVAL;
}
aio_qmsi_cmp_disable(dev, index);
dev_data->cb[index].cb = cb;
dev_data->cb[index].param = param;
if (refsel == AIO_CMP_REF_A) {
config.reference &= ~(1 << index);
} else {
config.reference |= (1 << index);
}
if (polarity == AIO_CMP_POL_RISE) {
config.polarity &= ~(1 << index);
} else {
config.polarity |= (1 << index);
}
/* The driver will not use QMSI callback mechanism */
config.callback = NULL;
/* Enable comparator */
config.int_en |= (1 << index);
config.power |= (1 << index);
if (qm_ac_set_config(&config) != 0) {
return -EINVAL;
}
/* Enable Interrupts to host for an specific comparator */
QM_SCSS_INT->int_comparators_host_mask &= ~(1 << index);
return 0;
}
static struct aio_cmp_driver_api aio_cmp_funcs = {
.disable = aio_qmsi_cmp_disable,
.configure = aio_qmsi_cmp_configure,
};
int aio_qmsi_cmp_init(struct device *dev)
{
uint8_t i;
struct aio_qmsi_cmp_dev_data_t *dev_data =
(struct aio_qmsi_cmp_dev_data_t *)dev->driver_data;
aio_cmp_config(dev);
/* Disable all comparator interrupts */
QM_SCSS_INT->int_comparators_host_mask |= INT_COMPARATORS_MASK;
/* Clear status and dissble all comparators */
QM_SCSS_CMP->cmp_stat_clr |= INT_COMPARATORS_MASK;
QM_SCSS_CMP->cmp_pwr &= ~INT_COMPARATORS_MASK;
QM_SCSS_CMP->cmp_en &= ~INT_COMPARATORS_MASK;
/* Don't use the QMSI callback */
config.callback = NULL;
/* Get Initial configuration from HW */
config.reference = QM_SCSS_CMP->cmp_ref_sel;
config.polarity = QM_SCSS_CMP->cmp_ref_pol;
config.power = QM_SCSS_CMP->cmp_pwr;
config.int_en = QM_SCSS_CMP->cmp_en;
/* 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(QM_IRQ_AC);
return 0;
}
void aio_qmsi_cmp_isr(struct device *dev)
{
uint8_t i;
struct aio_qmsi_cmp_dev_data_t *dev_data =
(struct aio_qmsi_cmp_dev_data_t *)dev->driver_data;
uint32_t int_status = QM_SCSS_CMP->cmp_stat_clr;
for (i = 0; i < dev_data->num_cmp; i++) {
if (int_status & (1 << i)) {
aio_qmsi_cmp_disable(dev, i);
if (dev_data->cb[i].cb != NULL) {
dev_data->cb[i].cb(dev_data->cb[i].param);
}
}
}
/* Clear all pending interrupts */
QM_SCSS_CMP->cmp_stat_clr = int_status;
}
struct aio_qmsi_cmp_dev_data_t aio_qmsi_cmp_dev_data = {
.num_cmp = AIO_QMSI_CMP_COUNT,
};
DEVICE_AND_API_INIT(aio_qmsi_cmp, CONFIG_AIO_COMPARATOR_0_NAME,
&aio_qmsi_cmp_init, &aio_qmsi_cmp_dev_data, NULL,
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
(void *)&aio_cmp_funcs);
static int aio_cmp_config(struct device *dev)
{
ARG_UNUSED(dev);
IRQ_CONNECT(QM_IRQ_AC, CONFIG_AIO_COMPARATOR_0_IRQ_PRI,
aio_qmsi_cmp_isr, DEVICE_GET(aio_qmsi_cmp), 0);
return 0;
}