| /* |
| * Copyright (c) 2016 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #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 */ |
| u8_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, u8_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.cmp_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, u8_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.cmp_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 u32_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) |
| { |
| u8_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.cmp_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) |
| { |
| u8_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; |
| |
| u32_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; |
| } |