197 lines
4.6 KiB
C
197 lines
4.6 KiB
C
/*
|
|
* Copyright (c) 2015 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT shared_irq
|
|
|
|
#include <errno.h>
|
|
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <shared_irq.h>
|
|
#include <init.h>
|
|
#include <sys/sys_io.h>
|
|
|
|
#ifdef CONFIG_IOAPIC
|
|
#include <drivers/interrupt_controller/ioapic.h>
|
|
#endif
|
|
|
|
typedef void (*shared_irq_config_irq_t)(void);
|
|
|
|
struct shared_irq_config {
|
|
uint32_t irq_num;
|
|
shared_irq_config_irq_t config;
|
|
uint32_t client_count;
|
|
};
|
|
|
|
struct shared_irq_client {
|
|
const 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 int isr_register(const struct device *dev, isr_t isr_func,
|
|
const struct device *isr_dev)
|
|
{
|
|
struct shared_irq_runtime *clients = dev->data;
|
|
const struct shared_irq_config *config = dev->config;
|
|
uint32_t i;
|
|
|
|
for (i = 0U; 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 0;
|
|
}
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
/**
|
|
* @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(const struct device *dev,
|
|
const struct device *isr_dev)
|
|
{
|
|
struct shared_irq_runtime *clients = dev->data;
|
|
const struct shared_irq_config *config = dev->config;
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < config->client_count; i++) {
|
|
if (clients->client[i].isr_dev == isr_dev) {
|
|
clients->client[i].enabled = 1U;
|
|
irq_enable(config->irq_num);
|
|
return 0;
|
|
}
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
static int last_enabled_isr(struct shared_irq_runtime *clients, int count)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0U; 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(const struct device *dev,
|
|
const struct device *isr_dev)
|
|
{
|
|
struct shared_irq_runtime *clients = dev->data;
|
|
const struct shared_irq_config *config = dev->config;
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < config->client_count; i++) {
|
|
if (clients->client[i].isr_dev == isr_dev) {
|
|
clients->client[i].enabled = 0U;
|
|
if (last_enabled_isr(clients, config->client_count)) {
|
|
irq_disable(config->irq_num);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
void shared_irq_isr(const struct device *dev)
|
|
{
|
|
struct shared_irq_runtime *clients = dev->data;
|
|
const struct shared_irq_config *config = dev->config;
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < config->client_count; i++) {
|
|
if (clients->client[i].isr_dev) {
|
|
clients->client[i].isr_func(clients->client[i].isr_dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const struct shared_irq_driver_api api_funcs = {
|
|
.isr_register = isr_register,
|
|
.enable = enable,
|
|
.disable = disable,
|
|
};
|
|
|
|
|
|
int shared_irq_initialize(const struct device *dev)
|
|
{
|
|
const struct shared_irq_config *config = dev->config;
|
|
config->config();
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_SHARED_IRQ_0
|
|
void shared_irq_config_0_irq(void);
|
|
|
|
const struct shared_irq_config shared_irq_config_0 = {
|
|
.irq_num = DT_INST_IRQN(0),
|
|
.client_count = CONFIG_SHARED_IRQ_NUM_CLIENTS,
|
|
.config = shared_irq_config_0_irq
|
|
};
|
|
|
|
struct shared_irq_runtime shared_irq_0_runtime;
|
|
|
|
DEVICE_DT_INST_DEFINE(0, shared_irq_initialize,
|
|
device_pm_control_nop, &shared_irq_0_runtime,
|
|
&shared_irq_config_0, POST_KERNEL,
|
|
CONFIG_SHARED_IRQ_INIT_PRIORITY, &api_funcs);
|
|
|
|
void shared_irq_config_0_irq(void)
|
|
{
|
|
IRQ_CONNECT(DT_INST_IRQN(0),
|
|
DT_INST_IRQ(0, priority),
|
|
shared_irq_isr, DEVICE_DT_INST_GET(0),
|
|
DT_INST_IRQ(0, sense));
|
|
}
|
|
|
|
#endif /* CONFIG_SHARED_IRQ_0 */
|
|
|
|
#if CONFIG_SHARED_IRQ_1
|
|
void shared_irq_config_1_irq(void);
|
|
|
|
const struct shared_irq_config shared_irq_config_1 = {
|
|
.irq_num = DT_INST_IRQN(1),
|
|
.client_count = CONFIG_SHARED_IRQ_NUM_CLIENTS,
|
|
.config = shared_irq_config_1_irq
|
|
};
|
|
|
|
struct shared_irq_runtime shared_irq_1_runtime;
|
|
|
|
DEVICE_DT_INST_DEFINE(1, shared_irq_initialize,
|
|
device_pm_control_nop, &shared_irq_1_runtime,
|
|
&shared_irq_config_1, POST_KERNEL,
|
|
CONFIG_SHARED_IRQ_INIT_PRIORITY, &api_funcs);
|
|
|
|
void shared_irq_config_1_irq(void)
|
|
{
|
|
IRQ_CONNECT(DT_INST_IRQN(1),
|
|
DT_INST_IRQ(1, priority),
|
|
shared_irq_isr, DEVICE_DT_INST_GET(1),
|
|
DT_INST_IRQ(1, sense));
|
|
}
|
|
|
|
#endif /* CONFIG_SHARED_IRQ_1 */
|