Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /* * Copyright 2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/sw_isr_table.h> #include <zephyr/spinlock.h> /* an interrupt line can be considered shared only if there's * at least 2 clients using it. As such, enforce the fact that * the maximum number of allowed clients should be at least 2. */ BUILD_ASSERT(CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS >= 2, "maximum number of clients should be at least 2"); void z_shared_isr(const void *data) { size_t i; const struct z_shared_isr_table_entry *entry; const struct z_shared_isr_client *client; entry = data; for (i = 0; i < entry->client_num; i++) { client = &entry->clients[i]; if (client->isr) { client->isr(client->arg); } } } #ifdef CONFIG_DYNAMIC_INTERRUPTS static struct k_spinlock lock; void z_isr_install(unsigned int irq, void (*routine)(const void *), const void *param) { struct z_shared_isr_table_entry *shared_entry; struct _isr_table_entry *entry; struct z_shared_isr_client *client; unsigned int table_idx; int i; k_spinlock_key_t key; table_idx = z_get_sw_isr_table_idx(irq); /* check for out of bounds table index */ if (table_idx >= CONFIG_NUM_IRQS) { return; } shared_entry = &z_shared_sw_isr_table[table_idx]; entry = &_sw_isr_table[table_idx]; key = k_spin_lock(&lock); /* have we reached the client limit? */ __ASSERT(shared_entry->client_num < CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS, "reached maximum number of clients"); if (entry->isr == z_irq_spurious) { /* this is the first time a ISR/arg pair is registered * for INTID => no need to share it. */ entry->isr = routine; entry->arg = param; k_spin_unlock(&lock, key); return; } else if (entry->isr != z_shared_isr) { /* INTID is being used by another ISR/arg pair. * Push back the ISR/arg pair registered in _sw_isr_table * to the list of clients and hijack the pair stored in * _sw_isr_table with our own z_shared_isr/shared_entry pair. */ shared_entry->clients[shared_entry->client_num].isr = entry->isr; shared_entry->clients[shared_entry->client_num].arg = entry->arg; shared_entry->client_num++; entry->isr = z_shared_isr; entry->arg = shared_entry; } /* don't register the same ISR/arg pair multiple times */ for (i = 0; i < shared_entry->client_num; i++) { client = &shared_entry->clients[i]; __ASSERT(client->isr != routine && client->arg != param, "trying to register duplicate ISR/arg pair"); } shared_entry->clients[shared_entry->client_num].isr = routine; shared_entry->clients[shared_entry->client_num].arg = param; shared_entry->client_num++; k_spin_unlock(&lock, key); } static void swap_client_data(struct z_shared_isr_client *a, struct z_shared_isr_client *b) { struct z_shared_isr_client tmp; tmp.arg = a->arg; tmp.isr = a->isr; a->arg = b->arg; a->isr = b->isr; b->arg = tmp.arg; b->isr = tmp.isr; } static void shared_irq_remove_client(struct z_shared_isr_table_entry *shared_entry, int client_idx, unsigned int table_idx) { int i; shared_entry->clients[client_idx].isr = NULL; shared_entry->clients[client_idx].arg = NULL; /* push back the removed client to the end of the client list */ for (i = client_idx; i <= (int)shared_entry->client_num - 2; i++) { swap_client_data(&shared_entry->clients[i], &shared_entry->clients[i + 1]); } shared_entry->client_num--; /* "unshare" interrupt if there will be a single client left */ if (shared_entry->client_num == 1) { _sw_isr_table[table_idx].isr = shared_entry->clients[0].isr; _sw_isr_table[table_idx].arg = shared_entry->clients[0].arg; shared_entry->clients[0].isr = NULL; shared_entry->clients[0].arg = NULL; shared_entry->client_num--; } } int __weak arch_irq_disconnect_dynamic(unsigned int irq, unsigned int priority, void (*routine)(const void *parameter), const void *parameter, uint32_t flags) { ARG_UNUSED(priority); ARG_UNUSED(flags); return z_isr_uninstall(irq, routine, parameter); } int z_isr_uninstall(unsigned int irq, void (*routine)(const void *), const void *parameter) { struct z_shared_isr_table_entry *shared_entry; struct _isr_table_entry *entry; struct z_shared_isr_client *client; unsigned int table_idx; size_t i; k_spinlock_key_t key; table_idx = z_get_sw_isr_table_idx(irq); /* check for out of bounds table index */ if (table_idx >= CONFIG_NUM_IRQS) { return -EINVAL; } shared_entry = &z_shared_sw_isr_table[table_idx]; entry = &_sw_isr_table[table_idx]; key = k_spin_lock(&lock); /* note: it's important that we remove the ISR/arg pair even if * the IRQ line is not being shared because z_isr_install() will * not overwrite it unless the _sw_isr_table entry for the given * IRQ line contains the default pair which is z_irq_spurious/NULL. */ if (!shared_entry->client_num) { if (entry->isr == routine && entry->arg == parameter) { entry->isr = z_irq_spurious; entry->arg = NULL; } goto out_unlock; } for (i = 0; i < shared_entry->client_num; i++) { client = &shared_entry->clients[i]; if (client->isr == routine && client->arg == parameter) { /* note: this is the only match we're going to get */ shared_irq_remove_client(shared_entry, i, table_idx); goto out_unlock; } } out_unlock: k_spin_unlock(&lock, key); return 0; } #endif /* CONFIG_DYNAMIC_INTERRUPTS */ |