| /* |
| * 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; |
| } |