zephyr/drivers/aio/aio_comparator_qmsi.c
Baohong Liu da17225d63 drivers: aio: remove aio disabling before invoking callback
Remove aio disabling code before calling callback. If aio is
disabled before callback is invoked, calling pending
interrupt check API in the callback will get negative result.
If user wants to disable the aio, it can do this in callback
instead of this being done blindly before callback.

Jira: ZEP-1437

Change-Id: I42ebe4584af6396ac2360152f9b0e2c389c44145
Signed-off-by: Baohong Liu <baohong.liu@intel.com>
2017-01-07 14:14:29 +00:00

205 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)) {
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;
}