Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 Intel Corporation. |
| 3 | * |
David B. Kinder | ac74d8b | 2017-01-18 17:01:01 -0800 | [diff] [blame] | 4 | * SPDX-License-Identifier: Apache-2.0 |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 5 | */ |
| 6 | |
Kumar Gala | 67d42d6 | 2020-03-25 12:05:44 -0500 | [diff] [blame] | 7 | #define DT_DRV_COMPAT shared_irq |
| 8 | |
Andre Guedes | 7c956d2 | 2016-03-09 14:38:02 -0300 | [diff] [blame] | 9 | #include <errno.h> |
| 10 | |
Flavio Santes | b04cdcd | 2016-12-04 14:59:37 -0600 | [diff] [blame] | 11 | #include <kernel.h> |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 12 | #include <device.h> |
| 13 | #include <shared_irq.h> |
| 14 | #include <init.h> |
Anas Nashif | ef281c4 | 2019-06-25 12:26:23 -0400 | [diff] [blame] | 15 | #include <sys/sys_io.h> |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 16 | |
Daniel Leung | 8effb4f | 2015-12-10 09:52:56 -0800 | [diff] [blame] | 17 | #ifdef CONFIG_IOAPIC |
Anas Nashif | 43a4933 | 2019-06-21 12:54:15 -0400 | [diff] [blame] | 18 | #include <drivers/interrupt_controller/ioapic.h> |
Daniel Leung | 8effb4f | 2015-12-10 09:52:56 -0800 | [diff] [blame] | 19 | #endif |
| 20 | |
Henrik Brix Andersen | e8ffafa | 2021-04-10 22:18:15 +0200 | [diff] [blame] | 21 | typedef void (*shared_irq_config_irq_t)(void); |
| 22 | |
| 23 | struct shared_irq_config { |
| 24 | uint32_t irq_num; |
| 25 | shared_irq_config_irq_t config; |
| 26 | uint32_t client_count; |
| 27 | }; |
| 28 | |
| 29 | struct shared_irq_client { |
| 30 | const struct device *isr_dev; |
| 31 | isr_t isr_func; |
| 32 | uint32_t enabled; |
| 33 | }; |
| 34 | |
| 35 | struct shared_irq_runtime { |
Thomas Stranger | dedd444 | 2021-03-28 18:07:56 +0200 | [diff] [blame] | 36 | struct shared_irq_client *const client; |
Henrik Brix Andersen | e8ffafa | 2021-04-10 22:18:15 +0200 | [diff] [blame] | 37 | }; |
| 38 | |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 39 | /** |
| 40 | * @brief Register a device ISR |
| 41 | * @param dev Pointer to device structure for SHARED_IRQ driver instance. |
| 42 | * @param isr_func Pointer to the ISR function for the device. |
| 43 | * @param isr_dev Pointer to the device that will service the interrupt. |
| 44 | */ |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 45 | static int isr_register(const struct device *dev, isr_t isr_func, |
| 46 | const struct device *isr_dev) |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 47 | { |
Tomasz Bursztyka | 98d9b01 | 2020-05-28 21:23:02 +0200 | [diff] [blame] | 48 | struct shared_irq_runtime *clients = dev->data; |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 49 | const struct shared_irq_config *config = dev->config; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 50 | uint32_t i; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 51 | |
Patrik Flykt | 8ff96b5 | 2018-11-29 11:12:22 -0800 | [diff] [blame] | 52 | for (i = 0U; i < config->client_count; i++) { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 53 | if (!clients->client[i].isr_dev) { |
| 54 | clients->client[i].isr_dev = isr_dev; |
| 55 | clients->client[i].isr_func = isr_func; |
Andre Guedes | 024cfe7 | 2016-03-09 14:01:20 -0300 | [diff] [blame] | 56 | return 0; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 57 | } |
| 58 | } |
Andre Guedes | 7c956d2 | 2016-03-09 14:38:02 -0300 | [diff] [blame] | 59 | return -EIO; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | /** |
| 63 | * @brief Enable ISR for device |
| 64 | * @param dev Pointer to device structure for SHARED_IRQ driver instance. |
| 65 | * @param isr_dev Pointer to the device that will service the interrupt. |
| 66 | */ |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 67 | static inline int enable(const struct device *dev, |
| 68 | const struct device *isr_dev) |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 69 | { |
Tomasz Bursztyka | 98d9b01 | 2020-05-28 21:23:02 +0200 | [diff] [blame] | 70 | struct shared_irq_runtime *clients = dev->data; |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 71 | const struct shared_irq_config *config = dev->config; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 72 | uint32_t i; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 73 | |
Patrik Flykt | 8ff96b5 | 2018-11-29 11:12:22 -0800 | [diff] [blame] | 74 | for (i = 0U; i < config->client_count; i++) { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 75 | if (clients->client[i].isr_dev == isr_dev) { |
Patrik Flykt | 24d7143 | 2019-03-26 19:57:45 -0600 | [diff] [blame] | 76 | clients->client[i].enabled = 1U; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 77 | irq_enable(config->irq_num); |
Andre Guedes | 024cfe7 | 2016-03-09 14:01:20 -0300 | [diff] [blame] | 78 | return 0; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 79 | } |
| 80 | } |
Andre Guedes | 7c956d2 | 2016-03-09 14:38:02 -0300 | [diff] [blame] | 81 | return -EIO; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | static int last_enabled_isr(struct shared_irq_runtime *clients, int count) |
| 85 | { |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 86 | uint32_t i; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 87 | |
Patrik Flykt | 8ff96b5 | 2018-11-29 11:12:22 -0800 | [diff] [blame] | 88 | for (i = 0U; i < count; i++) { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 89 | if (clients->client[i].enabled) { |
| 90 | return 0; |
| 91 | } |
| 92 | } |
| 93 | return 1; |
| 94 | } |
| 95 | /** |
| 96 | * @brief Disable ISR for device |
| 97 | * @param dev Pointer to device structure for SHARED_IRQ driver instance. |
| 98 | * @param isr_dev Pointer to the device that will service the interrupt. |
| 99 | */ |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 100 | static inline int disable(const struct device *dev, |
| 101 | const struct device *isr_dev) |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 102 | { |
Tomasz Bursztyka | 98d9b01 | 2020-05-28 21:23:02 +0200 | [diff] [blame] | 103 | struct shared_irq_runtime *clients = dev->data; |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 104 | const struct shared_irq_config *config = dev->config; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 105 | uint32_t i; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 106 | |
Patrik Flykt | 8ff96b5 | 2018-11-29 11:12:22 -0800 | [diff] [blame] | 107 | for (i = 0U; i < config->client_count; i++) { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 108 | if (clients->client[i].isr_dev == isr_dev) { |
Patrik Flykt | 24d7143 | 2019-03-26 19:57:45 -0600 | [diff] [blame] | 109 | clients->client[i].enabled = 0U; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 110 | if (last_enabled_isr(clients, config->client_count)) { |
| 111 | irq_disable(config->irq_num); |
| 112 | } |
Andre Guedes | 024cfe7 | 2016-03-09 14:01:20 -0300 | [diff] [blame] | 113 | return 0; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 114 | } |
| 115 | } |
Andre Guedes | 7c956d2 | 2016-03-09 14:38:02 -0300 | [diff] [blame] | 116 | return -EIO; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 117 | } |
| 118 | |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 119 | void shared_irq_isr(const struct device *dev) |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 120 | { |
Tomasz Bursztyka | 98d9b01 | 2020-05-28 21:23:02 +0200 | [diff] [blame] | 121 | struct shared_irq_runtime *clients = dev->data; |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 122 | const struct shared_irq_config *config = dev->config; |
Kumar Gala | a1b77fd | 2020-05-27 11:26:57 -0500 | [diff] [blame] | 123 | uint32_t i; |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 124 | |
Patrik Flykt | 8ff96b5 | 2018-11-29 11:12:22 -0800 | [diff] [blame] | 125 | for (i = 0U; i < config->client_count; i++) { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 126 | if (clients->client[i].isr_dev) { |
| 127 | clients->client[i].isr_func(clients->client[i].isr_dev); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
Marcus Shawcroft | 0bac787 | 2016-10-24 08:39:02 +0100 | [diff] [blame] | 132 | static const struct shared_irq_driver_api api_funcs = { |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 133 | .isr_register = isr_register, |
| 134 | .enable = enable, |
| 135 | .disable = disable, |
| 136 | }; |
| 137 | |
| 138 | |
Tomasz Bursztyka | e18fcbb | 2020-04-30 20:33:38 +0200 | [diff] [blame] | 139 | int shared_irq_initialize(const struct device *dev) |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 140 | { |
Tomasz Bursztyka | af6140c | 2020-05-28 20:44:16 +0200 | [diff] [blame] | 141 | const struct shared_irq_config *config = dev->config; |
Andrew Boie | d9cfbd5 | 2016-01-08 00:46:14 -0800 | [diff] [blame] | 142 | config->config(); |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 143 | return 0; |
| 144 | } |
| 145 | |
Thomas Stranger | a0bea08 | 2021-03-28 17:27:36 +0200 | [diff] [blame] | 146 | /* |
| 147 | * INST_SUPPORTS_DEP_ORDS_CNT: Counts the number of "elements" in |
| 148 | * DT_SUPPORTS_DEP_ORDS(n). There is a comma after each ordinal(inc. the last) |
| 149 | * Hence FOR_EACH adds "+1" once too often which has to be subtracted in the end. |
| 150 | */ |
| 151 | #define F1(x) 1 |
| 152 | #define INST_SUPPORTS_DEP_ORDS_CNT(n) \ |
| 153 | (FOR_EACH(F1, (+), DT_INST_SUPPORTS_DEP_ORDS(n)) - 1) |
| 154 | |
Thomas Stranger | 2398203 | 2021-03-28 17:04:43 +0200 | [diff] [blame] | 155 | #define SHARED_IRQ_CONFIG_FUNC(n) \ |
| 156 | void shared_irq_config_func_##n(void) \ |
| 157 | { \ |
| 158 | IRQ_CONNECT(DT_INST_IRQN(n), \ |
| 159 | DT_INST_IRQ(n, priority), \ |
| 160 | shared_irq_isr, \ |
| 161 | DEVICE_DT_INST_GET(n), \ |
Thomas Stranger | 8f559a3 | 2021-03-28 18:13:37 +0200 | [diff] [blame] | 162 | COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, sense), \ |
| 163 | (DT_INST_IRQ(n, sense)), \ |
| 164 | (0))); \ |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 165 | } |
| 166 | |
Thomas Stranger | 2398203 | 2021-03-28 17:04:43 +0200 | [diff] [blame] | 167 | #define SHARED_IRQ_INIT(n) \ |
| 168 | SHARED_IRQ_CONFIG_FUNC(n) \ |
Thomas Stranger | dedd444 | 2021-03-28 18:07:56 +0200 | [diff] [blame] | 169 | struct shared_irq_client clients_##n[INST_SUPPORTS_DEP_ORDS_CNT(n)]; \ |
| 170 | struct shared_irq_runtime shared_irq_data_##n = { \ |
| 171 | .client = clients_##n \ |
| 172 | }; \ |
Thomas Stranger | 2398203 | 2021-03-28 17:04:43 +0200 | [diff] [blame] | 173 | \ |
| 174 | const struct shared_irq_config shared_irq_config_##n = { \ |
| 175 | .irq_num = DT_INST_IRQN(n), \ |
Thomas Stranger | a0bea08 | 2021-03-28 17:27:36 +0200 | [diff] [blame] | 176 | .client_count = INST_SUPPORTS_DEP_ORDS_CNT(n), \ |
Thomas Stranger | 2398203 | 2021-03-28 17:04:43 +0200 | [diff] [blame] | 177 | .config = shared_irq_config_func_##n \ |
| 178 | }; \ |
| 179 | DEVICE_DT_INST_DEFINE(n, shared_irq_initialize, \ |
| 180 | NULL, \ |
| 181 | &shared_irq_data_##n, \ |
| 182 | &shared_irq_config_##n, POST_KERNEL, \ |
| 183 | CONFIG_SHARED_IRQ_INIT_PRIORITY, \ |
| 184 | &api_funcs); |
Dirk Brandewie | 002c53b | 2015-09-29 08:42:22 -0700 | [diff] [blame] | 185 | |
Thomas Stranger | 2398203 | 2021-03-28 17:04:43 +0200 | [diff] [blame] | 186 | DT_INST_FOREACH_STATUS_OKAY(SHARED_IRQ_INIT) |