|  | /* | 
|  | * Copyright (c) 2016 Intel Corporation. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <kernel.h> | 
|  | #include <soc.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 = 0U; 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 = 0U; 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; | 
|  | } |