| /* |
| * Copyright (c) 2015 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 <stdio.h> |
| |
| #include <nanokernel.h> |
| #include <device.h> |
| #include <init.h> |
| #include <aio_comparator.h> |
| |
| #include "aio_dw_comparator.h" |
| |
| #define INT_COMPARATORS_MASK 0x7FFFF |
| |
| static int dw_aio_cmp_config(struct device *dev); |
| |
| static int dw_aio_cmp_disable(struct device *dev, uint8_t index) |
| { |
| struct dw_aio_cmp_dev_cfg_t *config = |
| (struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info; |
| struct dw_aio_cmp_t *regs = |
| (struct dw_aio_cmp_t *)config->base_address; |
| |
| if (index >= AIO_DW_CMP_COUNT) { |
| return DEV_INVALID_CONF; |
| } |
| |
| /* Disable interrupt to host */ |
| SCSS_INTERRUPT->int_comparators_host_mask |= (1 << index); |
| |
| /* Disable comparator <index> */ |
| regs->en &= ~(1 << index); |
| /* Disable power in comparator <index> */ |
| regs->pwr &= ~(1 << index); |
| |
| return DEV_OK; |
| } |
| |
| static int dw_aio_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 dw_aio_cmp_dev_cfg_t *config = |
| (struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info; |
| struct dw_aio_cmp_dev_data_t *dev_data = |
| (struct dw_aio_cmp_dev_data_t *)dev->driver_data; |
| struct dw_aio_cmp_t *regs = |
| (struct dw_aio_cmp_t *)config->base_address; |
| uint32_t reg_val; |
| |
| /* index out of range */ |
| if (index >= AIO_DW_CMP_COUNT) { |
| return DEV_INVALID_CONF; |
| } |
| |
| /* make sure reference makes sense */ |
| if ((refsel != AIO_CMP_REF_A) && (refsel != AIO_CMP_REF_B)) |
| return DEV_INVALID_CONF; |
| |
| /* make sure polarity makes sense */ |
| if ((polarity != AIO_CMP_POL_RISE) && (polarity != AIO_CMP_POL_FALL)) |
| return DEV_INVALID_CONF; |
| |
| dev_data->cb[index].cb = cb; |
| dev_data->cb[index].param = param; |
| |
| /* Disable interrupt to host */ |
| SCSS_INTERRUPT->int_comparators_host_mask |= (1 << index); |
| |
| /* Disable comparator <index> before config */ |
| regs->en &= ~(1 << index); |
| regs->pwr &= ~(1 << index); |
| |
| /** |
| * Setup reference voltage source |
| * REF_A: bit ==> 0, REF_B: bit ==> 1 |
| */ |
| reg_val = regs->ref_sel; |
| if (refsel == AIO_CMP_REF_A) |
| reg_val &= ~(1 << index); |
| else |
| reg_val |= (1 << index); |
| regs->ref_sel = reg_val; |
| |
| /** |
| * Setup reference polarity |
| * RISING: bit ==> 0, FALLING: bit ==> 1 |
| */ |
| reg_val = regs->ref_pol; |
| if (polarity == AIO_CMP_POL_RISE) |
| reg_val &= ~(1 << index); |
| else |
| reg_val |= (1 << index); |
| regs->ref_pol = reg_val; |
| |
| /* Enable power of comparator <index> */ |
| regs->pwr |= (1 << index); |
| |
| /* Enable comparator <index> */ |
| regs->en |= (1 << index); |
| |
| /* Enable interrupt to host */ |
| SCSS_INTERRUPT->int_comparators_host_mask &= ~(1 << index); |
| |
| return DEV_OK; |
| } |
| |
| void dw_aio_cmp_isr(struct device *dev) |
| { |
| struct dw_aio_cmp_dev_cfg_t *config = |
| (struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info; |
| struct dw_aio_cmp_dev_data_t *dev_data = |
| (struct dw_aio_cmp_dev_data_t *)dev->driver_data; |
| struct dw_aio_cmp_t *regs = |
| (struct dw_aio_cmp_t *)config->base_address; |
| int i; |
| int reg_stat_clr; |
| |
| reg_stat_clr = regs->stat_clr; |
| for (i = 0; i < dev_data->num_cmp; i++) { |
| if (reg_stat_clr & (1 << i)) { |
| |
| dw_aio_cmp_disable(dev, i); |
| |
| if (dev_data->cb[i].cb != NULL) { |
| dev_data->cb[i].cb(dev_data->cb[i].param); |
| } |
| } |
| } |
| |
| /* Clear interrupt status by writing 1s */ |
| regs->stat_clr = reg_stat_clr; |
| } |
| |
| static struct aio_cmp_driver_api dw_aio_cmp_funcs = { |
| .disable = dw_aio_cmp_disable, |
| .configure = dw_aio_cmp_configure, |
| }; |
| |
| int dw_aio_cmp_init(struct device *dev) |
| { |
| struct dw_aio_cmp_dev_cfg_t *config = |
| (struct dw_aio_cmp_dev_cfg_t *)dev->config->config_info; |
| struct dw_aio_cmp_dev_data_t *dev_data = |
| (struct dw_aio_cmp_dev_data_t *)dev->driver_data; |
| struct dw_aio_cmp_t *regs = |
| (struct dw_aio_cmp_t *)config->base_address; |
| int i; |
| |
| if ((config->base_address == 0) || (config->interrupt_num == 0)) |
| return DEV_INVALID_CONF; |
| |
| if (config->config_func) { |
| i = config->config_func(dev); |
| if (i != DEV_OK) |
| return i; |
| } |
| |
| dev->driver_api = &dw_aio_cmp_funcs; |
| |
| /* Clear host interrupt mask */ |
| SCSS_INTERRUPT->int_comparators_host_mask |= INT_COMPARATORS_MASK; |
| |
| /* Clear comparator interrupt status */ |
| regs->stat_clr |= INT_COMPARATORS_MASK; |
| |
| /* Disable all comparators */ |
| regs->en &= ~INT_COMPARATORS_MASK; |
| |
| /* Power down all comparators */ |
| regs->pwr &= ~INT_COMPARATORS_MASK; |
| |
| /* 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(config->interrupt_num); |
| |
| return DEV_OK; |
| } |
| |
| struct dw_aio_cmp_dev_cfg_t dw_aio_cmp_dev_config = { |
| .base_address = CONFIG_AIO_DW_COMPARATOR_BASE_ADDR, |
| .interrupt_num = INT_AIO_CMP_IRQ, |
| .config_func = dw_aio_cmp_config, |
| }; |
| |
| struct dw_aio_cmp_dev_data_t dw_aio_cmp_dev_data = { |
| .num_cmp = AIO_DW_CMP_COUNT, |
| }; |
| |
| DEVICE_INIT(dw_aio_cmp, CONFIG_AIO_DW_COMPARATOR_DEV_NAME, &dw_aio_cmp_init, |
| &dw_aio_cmp_dev_data, &dw_aio_cmp_dev_config, |
| SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |
| |
| static int dw_aio_cmp_config(struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| IRQ_CONNECT(INT_AIO_CMP_IRQ, CONFIG_AIO_DW_COMPARATOR_IRQ_PRI, |
| dw_aio_cmp_isr, |
| DEVICE_GET(dw_aio_cmp), 0); |
| return DEV_OK; |
| } |