206 lines
5.0 KiB
C
206 lines
5.0 KiB
C
/*
|
|
* 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 <kernel.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)
|
|
#if (QM_LAKEMONT)
|
|
#define CMP_INTR_ROUTER QM_INTERRUPT_ROUTER->comparator_0_host_int_mask
|
|
#else
|
|
#define CMP_INTR_ROUTER QM_INTERRUPT_ROUTER->comparator_0_ss_int_mask
|
|
#endif
|
|
|
|
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 current core */
|
|
CMP_INTR_ROUTER |= (1 << index);
|
|
|
|
/* Disable comparator according to index */
|
|
config.int_en &= ~(1 << index);
|
|
config.power &= ~(1 << index);
|
|
config.reference &= ~(1 << index);
|
|
config.polarity &= ~(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 current core for an specific comparator */
|
|
CMP_INTR_ROUTER &= ~(1 << index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t aio_cmp_qmsi_get_pending_int(struct device *dev)
|
|
{
|
|
return QM_SCSS_CMP->cmp_stat_clr;
|
|
}
|
|
|
|
static const struct aio_cmp_driver_api aio_cmp_funcs = {
|
|
.disable = aio_qmsi_cmp_disable,
|
|
.configure = aio_qmsi_cmp_configure,
|
|
.get_pending_int = aio_cmp_qmsi_get_pending_int,
|
|
};
|
|
|
|
static 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 */
|
|
CMP_INTR_ROUTER |= 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(IRQ_GET_NUMBER(QM_IRQ_COMPARATOR_0_INT));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aio_qmsi_cmp_isr(void *data)
|
|
{
|
|
uint8_t i;
|
|
struct device *dev = data;
|
|
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;
|
|
}
|
|
|
|
static 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,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
(void *)&aio_cmp_funcs);
|
|
|
|
static int aio_cmp_config(struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
IRQ_CONNECT(IRQ_GET_NUMBER(QM_IRQ_COMPARATOR_0_INT),
|
|
CONFIG_AIO_COMPARATOR_0_IRQ_PRI, aio_qmsi_cmp_isr,
|
|
DEVICE_GET(aio_qmsi_cmp), 0);
|
|
|
|
return 0;
|
|
}
|