You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.9 KiB
215 lines
5.9 KiB
/* |
|
* 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 <nanokernel.h> |
|
#include <device.h> |
|
#include <shared_irq.h> |
|
#include <init.h> |
|
#include <board.h> |
|
#include <sys_io.h> |
|
|
|
#ifdef CONFIG_IOAPIC |
|
#include <drivers/ioapic.h> |
|
#endif |
|
|
|
/** |
|
* @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(); |
|
|
|
return 0; |
|
} |
|
|
|
#if CONFIG_SHARED_IRQ_0 |
|
void shared_irq_config_0_irq(void); |
|
|
|
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; |
|
|
|
DEVICE_INIT(shared_irq_0, CONFIG_SHARED_IRQ_0_NAME, shared_irq_initialize, |
|
&shared_irq_0_runtime, &shared_irq_config_0, |
|
SECONDARY, CONFIG_SHARED_IRQ_INIT_PRIORITY); |
|
|
|
#if defined(CONFIG_IOAPIC) |
|
#if defined(CONFIG_SHARED_IRQ_0) |
|
#if defined(CONFIG_SHARED_IRQ_0_FALLING_EDGE) |
|
#define SHARED_IRQ_0_FLAGS (IOAPIC_EDGE | IOAPIC_LOW) |
|
#elif defined(CONFIG_SHARED_IRQ_0_RISING_EDGE) |
|
#define SHARED_IRQ_0_FLAGS (IOAPIC_EDGE | IOAPIC_HIGH) |
|
#elif defined(CONFIG_SHARED_IRQ_0_LEVEL_HIGH) |
|
#define SHARED_IRQ_0_FLAGS (IOAPIC_LEVEL | IOAPIC_HIGH) |
|
#elif defined(CONFIG_SHARED_IRQ_0_LEVEL_LOW) |
|
#define SHARED_IRQ_0_FLAGS (IOAPIC_LEVEL | IOAPIC_LOW) |
|
#endif |
|
#endif /* CONFIG_SHARED_IRQ_0 */ |
|
#else |
|
#define SHARED_IRQ_0_FLAGS 0 |
|
#endif /* CONFIG_IOAPIC */ |
|
|
|
void shared_irq_config_0_irq(void) |
|
{ |
|
irq_connect(CONFIG_SHARED_IRQ_0_IRQ, CONFIG_SHARED_IRQ_0_PRI, |
|
shared_irq_isr, DEVICE_GET(shared_irq_0), |
|
SHARED_IRQ_0_FLAGS); |
|
} |
|
|
|
#endif /* CONFIG_SHARED_IRQ_0 */ |
|
|
|
#if CONFIG_SHARED_IRQ_1 |
|
void shared_irq_config_1_irq(void); |
|
|
|
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; |
|
|
|
DEVICE_INIT(shared_irq_1, CONFIG_SHARED_IRQ_1_NAME, shared_irq_initialize, |
|
&shared_irq_1_runtime, &shared_irq_config_1, |
|
SECONDARY, CONFIG_SHARED_IRQ_INIT_PRIORITY); |
|
|
|
#if defined(CONFIG_IOAPIC) |
|
#if defined(CONFIG_SHARED_IRQ_1) |
|
#if defined(CONFIG_SHARED_IRQ_1_FALLING_EDGE) |
|
#define SHARED_IRQ_1_FLAGS (IOAPIC_EDGE | IOAPIC_LOW) |
|
#elif defined(CONFIG_SHARED_IRQ_1_RISING_EDGE) |
|
#define SHARED_IRQ_1_FLAGS (IOAPIC_EDGE | IOAPIC_HIGH) |
|
#elif defined(CONFIG_SHARED_IRQ_1_LEVEL_HIGH) |
|
#define SHARED_IRQ_1_FLAGS (IOAPIC_LEVEL | IOAPIC_HIGH) |
|
#elif defined(CONFIG_SHARED_IRQ_1_LEVEL_LOW) |
|
#define SHARED_IRQ_1_FLAGS (IOAPIC_LEVEL | IOAPIC_LOW) |
|
#endif |
|
#endif /* CONFIG_SHARED_IRQ_1 */ |
|
#else |
|
#define SHARED_IRQ_1_FLAGS 0 |
|
#endif /* CONFIG_IOAPIC */ |
|
|
|
void shared_irq_config_1_irq(void) |
|
{ |
|
irq_connect(CONFIG_SHARED_IRQ_1_IRQ, CONFIG_SHARED_IRQ_1_PRI, |
|
shared_irq_isr, DEVICE_GET(shared_irq_1), |
|
SHARED_IRQ_1_FLAGS); |
|
} |
|
|
|
#endif /* CONFIG_SHARED_IRQ_1 */ |
|
|
|
|