/* * 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 #include #include #include #include #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 */ regs->en &= ~(1 << index); /* Disable power in comparator */ 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 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 */ regs->pwr |= (1 << index); /* Enable comparator */ 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; }