diff --git a/arch/x86/core/driver_static_irq_stubs.S b/arch/x86/core/driver_static_irq_stubs.S index d896bbb5770..3d3905b4cb4 100644 --- a/arch/x86/core/driver_static_irq_stubs.S +++ b/arch/x86/core/driver_static_irq_stubs.S @@ -79,6 +79,16 @@ by x86 platforms. #endif /* CONFIG_GPIO_DW_1 */ #endif /* CONFIG_GPIO_DW */ +#if defined(CONFIG_SHARED_IRQ) +#if CONFIG_SHARED_IRQ_0 + ioapic_mkstub shared_irq_0 shared_irq_isr_0 +#endif /* CONFIG_SHIRQ_0 */ + +#if CONFIG_SHARED_IRQ_1 + ioapic_mkstub shared_irq_1 shared_irq_isr_1 +#endif /* CONFIG_SHARED_IRQ_1 */ +#endif /* CONFIG_SHARED_IRQ */ + #if defined(CONFIG_SPI_INTEL) #if defined(CONFIG_SPI_INTEL_PORT_0) ioapic_mkstub spi_intel_irq_port_0 spi_intel_isr_0 diff --git a/drivers/Kconfig b/drivers/Kconfig index d7ac7e1ac84..14dbc385f2c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -51,6 +51,8 @@ source "drivers/pci/Kconfig" source "drivers/gpio/Kconfig" +source "drivers/shared_irq/Kconfig" + source "drivers/spi/Kconfig" source "drivers/i2c/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index f037141270f..a6fd003517c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -10,6 +10,7 @@ obj-y += interrupt_controller/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_BLUETOOTH) += bluetooth/ obj-$(CONFIG_UART_SIMPLE) += simple/ +obj-$(CONFIG_SHARED_IRQ) += shared_irq/ obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_GPIO) += gpio/ obj-$(CONFIG_I2C) += i2c/ diff --git a/drivers/shared_irq/Kconfig b/drivers/shared_irq/Kconfig new file mode 100644 index 00000000000..37f3c4702f2 --- /dev/null +++ b/drivers/shared_irq/Kconfig @@ -0,0 +1,67 @@ +config SHARED_IRQ + bool + prompt "Shared interrupt driver" + default n + help + Include shared interrupt support in system. Shared interrupt + support is NOT required in most systems. If in doubt answer no. + +config SHARED_IRQ_NUM_CLIENTS + int + depends on SHARED_IRQ + prompt "The number of clients per instance" + default 5 + help + Configures the maximum nuber of clients allowed per shared + instance of the shared interrupt driver. To conserve RAM set + this value to the lowest practical value. + +config SHARED_IRQ_0 + bool "Shared interrupt instance 0" + depends on SHARED_IRQ + help + Provide an instance of the shared interrupt driver when system + configuration requires that multiple devices share an interrupt. + +config SHARED_IRQ_0_NAME + string "Select a name for the device" + depends on SHARED_IRQ_0 + default "SHARED_IRQ0" + +config SHARED_IRQ_0_IRQ + int "instance 0 interrupt" + depends on SHARED_IRQ_0 + help + System interrupt number to be handled by this instance of the driver. + +config SHARED_IRQ_0_PRI + int "instance 0 interrupt priority" + default 0 + depends on SHARED_IRQ_0 + help + Prioity of shared interrupt handler for the configured interrupt + +config SHARED_IRQ_1 + bool "Shared interrupt instance 1" + select SHARED_IRQ + help + Provide an instance of the shared interrupt driver when system + configuration requires that multiple devices share an interrupt. + +config SHARED_IRQ_1_NAME + string "Select a name for the device" + depends on SHARED_IRQ_1 + default "SHARED_IRQ1" + +config SHARED_IRQ_1_IRQ + int "instance 1 interrupt" + depends on SHARED_IRQ_1 + help + System interrupt number to be handled by this instance of the driver. + +config SHARED_IRQ_1_PRI + int "instance 1 interrupt priority" + default 0 + depends on SHARED_IRQ_0 + help + Prioity of shared interrupt handler for the configured interrupt diff --git a/drivers/shared_irq/Makefile b/drivers/shared_irq/Makefile new file mode 100644 index 00000000000..7cef7add9ab --- /dev/null +++ b/drivers/shared_irq/Makefile @@ -0,0 +1,3 @@ +ccflags-y +=-I$(srctree)/drivers + +obj-$(CONFIG_SHARED_IRQ) = shared_irq.o diff --git a/drivers/shared_irq/shared_irq.c b/drivers/shared_irq/shared_irq.c new file mode 100644 index 00000000000..d8ee0008864 --- /dev/null +++ b/drivers/shared_irq/shared_irq.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2015 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Register a device ISR + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_func Pointer to the ISR function for the device. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static int isr_register(struct device *dev, isr_t isr_func, + struct device *isr_dev) +{ + struct shared_irq_runtime *clients = dev->driver_data; + struct shared_irq_config *config = dev->config->config_info; + uint32_t i; + + for (i = 0; i < config->client_count; i++) { + if (!clients->client[i].isr_dev) { + clients->client[i].isr_dev = isr_dev; + clients->client[i].isr_func = isr_func; + return DEV_OK; + } + } + return DEV_FAIL; +} + +/** + * @brief Enable ISR for device + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static inline int enable(struct device *dev, struct device *isr_dev) +{ + struct shared_irq_runtime *clients = dev->driver_data; + struct shared_irq_config *config = dev->config->config_info; + uint32_t i; + + for (i = 0; i < config->client_count; i++) { + if (clients->client[i].isr_dev == isr_dev) { + clients->client[i].enabled = 1; + irq_enable(config->irq_num); + return DEV_OK; + } + } + return DEV_FAIL; +} + +static int last_enabled_isr(struct shared_irq_runtime *clients, int count) +{ + uint32_t i; + + for (i = 0; i < count; i++) { + if (clients->client[i].enabled) { + return 0; + } + } + return 1; +} +/** + * @brief Disable ISR for device + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static inline int disable(struct device *dev, struct device *isr_dev) +{ + struct shared_irq_runtime *clients = dev->driver_data; + struct shared_irq_config *config = dev->config->config_info; + uint32_t i; + + for (i = 0; i < config->client_count; i++) { + if (clients->client[i].isr_dev == isr_dev) { + clients->client[i].enabled = 0; + if (last_enabled_isr(clients, config->client_count)) { + irq_disable(config->irq_num); + } + return DEV_OK; + } + } + return DEV_FAIL; +} + +void shared_irq_isr(struct device *dev) +{ + struct shared_irq_runtime *clients = dev->driver_data; + struct shared_irq_config *config = dev->config->config_info; + uint32_t i; + + for (i = 0; i < config->client_count; i++) { + if (clients->client[i].isr_dev) { + clients->client[i].isr_func(clients->client[i].isr_dev); + } + } +} + +static struct shared_irq_driver_api api_funcs = { + .isr_register = isr_register, + .enable = enable, + .disable = disable, +}; + + +int shared_irq_initialize(struct device *dev) +{ + struct shared_irq_config *config = dev->config->config_info; + + dev->driver_api = &api_funcs; + config->config(dev); + + return 0; +} + +#if CONFIG_SHARED_IRQ_0 +void shared_irq_config_0_irq(struct device *port); + +struct shared_irq_config shared_irq_config_0 = { + .irq_num = CONFIG_SHARED_IRQ_0_IRQ, + .client_count = CONFIG_SHARED_IRQ_NUM_CLIENTS, + .config = shared_irq_config_0_irq +}; + +struct shared_irq_runtime shared_irq_0_runtime; + +DECLARE_DEVICE_INIT_CONFIG(shared_irq_0, CONFIG_SHARED_IRQ_0_NAME, + shared_irq_initialize, &shared_irq_config_0); +pre_kernel_early_init(shared_irq_0, &shared_irq_0_runtime); + +IRQ_CONNECT_STATIC(shared_irq_0, CONFIG_SHARED_IRQ_0_IRQ, + CONFIG_SHARED_IRQ_0_PRI, shared_irq_isr_0, 0); + +void shared_irq_config_0_irq(struct device *port) +{ + struct shared_irq_config *config = port->config->config_info; + + IRQ_CONFIG(shared_irq_0, config->irq_num); +} + +void shared_irq_isr_0(void *unused) +{ + shared_irq_isr(&__initconfig_shared_irq_01); +} + +#endif /* CONFIG_SHARED_IRQ_0 */ + +#if CONFIG_SHARED_IRQ_1 +void shared_irq_config_1_irq(struct device *port); + +struct shared_irq_config shared_irq_config_1 = { + .irq_num = CONFIG_SHARED_IRQ_1_IRQ, + .client_count = CONFIG_SHARED_IRQ_NUM_CLIENTS, + .config = shared_irq_config_1_irq +}; + +struct shared_irq_runtime shared_irq_1_runtime; + +DECLARE_DEVICE_INIT_CONFIG(shared_irq_1, CONFIG_SHARED_IRQ_1_NAME, + shared_irq_initialize, &shared_irq_config_1); +pre_kernel_early_init(shared_irq_1, &shared_irq_1_runtime); + +IRQ_CONNECT_STATIC(shared_irq_1, CONFIG_SHARED_IRQ_1_IRQ, + CONFIG_SHARED_IRQ_1_PRI, shared_irq_isr_1, 0); + +void shared_irq_config_1_irq(struct device *port) +{ + struct shared_irq_config *config = port->config->config_info; + + IRQ_CONFIG(shared_irq_1, config->irq_num); +} + +void shared_irq_isr_1(void *unused) +{ + shared_irq_isr(&__initconfig_shared_irq_11); +} + +#endif /* CONFIG_SHARED_IRQ_1 */ + diff --git a/include/shared_irq.h b/include/shared_irq.h new file mode 100644 index 00000000000..491ced7eea4 --- /dev/null +++ b/include/shared_irq.h @@ -0,0 +1,114 @@ +/* shared_irq - Shared interrupt driver */ + +/* + * Copyright (c) 2015 Intel corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3) Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SHIRQ_H_ +#define _SHIRQ_H_ + +#include + +typedef int (*isr_t)(struct device *dev); + +/* driver API definition */ +typedef int (*shared_irq_register_t)(struct device *dev, + isr_t isr_func, + struct device *isr_dev); +typedef int (*shared_irq_enable_t)(struct device *dev, struct device *isr_dev); +typedef int (*shared_irq_disable_t)(struct device *dev, struct device *isr_dev); + +struct shared_irq_driver_api { + shared_irq_register_t isr_register; + shared_irq_enable_t enable; + shared_irq_disable_t disable; +}; + +extern int shared_irq_initialize(struct device *port); + +typedef void (*shared_irq_config_irq_t)(struct device *port); + +struct shared_irq_config { + uint32_t irq_num; + shared_irq_config_irq_t config; + uint32_t client_count; +}; + +struct shared_irq_client { + struct device *isr_dev; + isr_t isr_func; + uint32_t enabled; +}; + +struct shared_irq_runtime { + struct shared_irq_client client[CONFIG_SHARED_IRQ_NUM_CLIENTS]; +}; + +/** + * @brief Register a device ISR + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_func Pointer to the ISR function for the device. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static inline int shared_irq_isr_register(struct device *dev, isr_t isr_func, + struct device *isr_dev) +{ + struct shared_irq_driver_api *api; + + api = (struct shared_irq_driver_api *) dev->driver_api; + return api->isr_register(dev, isr_func, isr_dev); +} + +/** + * @brief Enable ISR for device + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static inline int shared_irq_enable(struct device *dev, struct device *isr_dev) +{ + struct shared_irq_driver_api *api; + + api = (struct shared_irq_driver_api *) dev->driver_api; + return api->enable(dev, isr_dev); +} + +/** + * @brief Disable ISR for device + * @param dev Pointer to device structure for SHARED_IRQ driver instance. + * @param isr_dev Pointer to the device that will service the interrupt. + */ +static inline int shared_irq_disable(struct device *dev, struct device *isr_dev) +{ + struct shared_irq_driver_api *api; + + api = (struct shared_irq_driver_api *) dev->driver_api; + return api->disable(dev, isr_dev); +} + +#endif /* _SHARED_IRQ_H_ */