1 /***************************************************************************//**
2 * \file cyhal_irq_impl.c
3 *
4 * \brief
5 * Provides internal utility functions for working with interrupts on CAT1/CAT2.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
10 * an affiliate of Cypress Semiconductor Corporation
11 *
12 * SPDX-License-Identifier: Apache-2.0
13 *
14 * Licensed under the Apache License, Version 2.0 (the "License");
15 * you may not use this file except in compliance with the License.
16 * You may obtain a copy of the License at
17 *
18 * http://www.apache.org/licenses/LICENSE-2.0
19 *
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
25 *******************************************************************************/
26
27 #include "cyhal_irq_impl.h"
28
29 /**
30 * \addtogroup group_hal_impl_irq IRQ Muxing (Interrupt muxing)
31 * \ingroup group_hal_impl
32 * \{
33 * There are two situations where system interrupts do not correlate 1:1 to CPU interrupts.
34 * ("System interrupt" refers to a signal on a peripheral that can request an interrupt.
35 * "CPU interrupt" refers to an IRQ input line on the cpu). Each has a different potential
36 * impact on application behavior.
37 * - When running on the CM0+ on PSoC 6 S1 devices, there are 32 CPU interrupts available.
38 * Each CPU interrupt can be associatedd with exactly one system interrupt. This means that
39 * if the application attempts to initialize more than 32 HAL driver instances which require
40 * unique interrupt handlers, the initialization will fail because there are no CPU interrupt
41 * slots remaining.
42 * - When running on the CM0+ on all other CAT1 devices, or when running on the CM7 on CAT3 devices,
43 * there are 8 CPU interrupts. Any system interrupt can be assigned to any CPU interrupt. In the event
44 * that more than one system interrupt fires at the same time for a given CPU interrupt, the interrupts
45 * are serviced in ascending numerical order (see the device datasheet for numeric IRQ values). This
46 * means that the above error case where all CPU interrupts have been consumed does not apply. The HAL
47 * automatically divides the system interrupts across the CPU interrupts.
48 * However, it is only possible to assign one priority per CPU interrupt, even though the HAL APIs expose
49 * the interrupt priority per system interrupt. The CAT1 HAL handles this situation by tracking the requested
50 * priority for each system interrupt, then setting the priority for each CPU interrupt as the lowest
51 * (i.e. most important) value requested across all of its associated system interrupts.
52 * \}
53 */
54
55 #if _CYHAL_IRQ_LEGACY_M0
56 #include "cyhal_hwmgr.h"
57
58 static const uint8_t _CYHAL_IRQ_COUNT_M0 = 32; /* Fixed in the IP definition */
59 #elif _CYHAL_IRQ_MUXING
60 #if defined(COMPONENT_CAT1A)
61 #include "cyhal_hwmgr.h" // TODO: This is a temporary workaround to assign 1:1 CPU to system mapping.
62 #endif
63
64 static uint8_t _cyhal_system_irq_priority[((_CYHAL_IRQ_PRIO_BITS * _CYHAL_IRQ_COUNT) + 7) / 8]; /* Round up to nearest byte */
65
_cyhal_system_irq_lookup_priority(cy_en_intr_t system_irq)66 uint8_t _cyhal_system_irq_lookup_priority(cy_en_intr_t system_irq)
67 {
68 uint16_t start_bit = ((uint16_t)system_irq) * _CYHAL_IRQ_PRIO_BITS;
69 uint8_t result = 0u;
70 for(uint16_t bit = start_bit; bit < start_bit + _CYHAL_IRQ_PRIO_BITS; ++bit)
71 {
72 uint16_t byte = bit / 8u;
73 uint8_t bit_in_byte = bit % 8u;
74 uint8_t offset = bit - start_bit;
75 uint8_t bit_value = ((_cyhal_system_irq_priority[byte] >> bit_in_byte) & 1u);
76 result |= (bit_value << offset);
77 }
78
79 return result;
80 }
81
_cyhal_system_irq_store_priority(cy_en_intr_t system_irq,uint8_t priority)82 void _cyhal_system_irq_store_priority(cy_en_intr_t system_irq, uint8_t priority)
83 {
84 uint16_t start_bit = ((uint16_t)system_irq) * _CYHAL_IRQ_PRIO_BITS;
85 for(uint16_t bit = start_bit; bit < start_bit + _CYHAL_IRQ_PRIO_BITS; ++bit)
86 {
87 uint16_t byte = bit / 8u;
88 uint8_t bit_in_byte = bit % 8u;
89 uint8_t offset = bit - start_bit;
90 uint8_t bit_value = priority & (1u << offset);
91 if(0u != bit_value)
92 {
93 _cyhal_system_irq_priority[byte] |= (1u << bit_in_byte);
94 }
95 else
96 {
97 _cyhal_system_irq_priority[byte] &= ~(1u << bit_in_byte);
98 }
99 }
100 }
101
_cyhal_system_irq_lowest_priority(IRQn_Type cpu_irq)102 uint8_t _cyhal_system_irq_lowest_priority(IRQn_Type cpu_irq)
103 {
104 uint8_t lowest_priority = 0xFF;
105 for(uint32_t i = 0; i < _CYHAL_IRQ_COUNT; ++i)
106 {
107 /* No reverse mapping from CPU interrupt to system interrupt, we have to look
108 * through all of the system interrupts to see which ones correspond to this one */
109 if(cpu_irq == Cy_SysInt_GetNvicConnection((cy_en_intr_t)i))
110 {
111 uint8_t priority = _cyhal_system_irq_lookup_priority((cy_en_intr_t)i);
112 if(priority < lowest_priority)
113 {
114 lowest_priority = priority;
115 }
116 }
117 }
118
119 return lowest_priority;
120 }
121
122 #endif
123
124 #if (_CYHAL_IRQ_MUXING) && defined(COMPONENT_CAT1A) && (!_CYHAL_IRQ_LEGACY_M0) && !defined(CY_DEVICE_TVIIBE)
125 uint8_t _cpu_irq_tracker = 0u; // TODO: This is a temporary workaround to assign 1:1 CPU to system mapping.
126 #endif
127
_cyhal_irq_register(_cyhal_system_irq_t system_intr,uint8_t intr_priority,cy_israddress irq_handler)128 cy_rslt_t _cyhal_irq_register(_cyhal_system_irq_t system_intr, uint8_t intr_priority, cy_israddress irq_handler)
129 {
130 #if _CYHAL_IRQ_MUXING
131 #if _CYHAL_IRQ_LEGACY_M0
132 /* Find a free CM0 slot */
133 IRQn_Type chosen_irq = unconnected_IRQn;
134 bool deepsleep_irq = (uint16_t)system_intr < CPUSS_DPSLP_IRQ_NR;
135 uint8_t max_cm0_irq = deepsleep_irq ? CPUSS_CM0_DPSLP_IRQ_NR : _CYHAL_IRQ_COUNT_M0;
136 for(int i = 0; i < max_cm0_irq; ++i)
137 {
138 IRQn_Type irqn = (IRQn_Type)i;
139 if(disconnected_IRQn == Cy_SysInt_GetInterruptSource(irqn))
140 {
141 chosen_irq = irqn;
142 break;
143 }
144 }
145 if(unconnected_IRQn == chosen_irq)
146 {
147 /* This is a highly device specific situation. "None free" is the closest matching generic error */
148 return CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
149 }
150 cy_stc_sysint_t intr_cfg = { chosen_irq, system_intr, intr_priority };
151 #else /* CM0+ on CPUSSv2, or CM4/CM7 on CPUSSv2 with SYSTEM_IRQ_PRESENT */
152 /* Any system interrupt can go to any CPU interrupt. Map the system interrupts evenly across the CPU interrupts. Cluster
153 * adjacent system interrupts together to make it more likely that interrupts from the same FFB type, which are reasonably
154 * likely to want similar priorities. */
155 #if defined(CY_IP_M4CPUSS)
156 const uint8_t NUM_CPU_INTERRUPTS = 8u; /* Value fixed in the IP */
157 #else /* M7CPUSS */
158 #if (CY_CPU_CORTEX_M0P)
159 /* There are 8 general purpose interrupts on the CM0+, but per comments in Cy_SysInt_Init, the first two
160 * CPU interrupts are reserved for ROM */
161 const uint8_t NUM_CPU_INTERRUPTS = 6u;
162 #else
163 const uint8_t NUM_CPU_INTERRUPTS = CPUSS_CM7_INT_NR;
164 #endif
165 #endif
166 // TODO: This is a temporary workaround to assign 1:1 CPU to system mapping.
167 // When the PDL allows more than one mapping, remove this logic.
168 #if defined(COMPONENT_CAT1A) && !defined(CY_DEVICE_TVIIBE)
169 CY_UNUSED_PARAMETER(system_intr);
170 cy_rslt_t status = CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
171 uint8_t cpu_irq;
172 for (int idx = 0; idx < NUM_CPU_INTERRUPTS; idx++)
173 {
174 if ((_cpu_irq_tracker & (1 << idx)) == 0)
175 {
176 cpu_irq = idx;
177 _cpu_irq_tracker |= (1 << idx);
178 status = CY_RSLT_SUCCESS;
179 break;
180 }
181 }
182 if (status != CY_RSLT_SUCCESS)
183 {
184 return status;
185 }
186 uint32_t intr_src = (uint32_t)system_intr;
187 intr_src |= cpu_irq << CY_SYSINT_INTRSRC_MUXIRQ_SHIFT;
188 #if !(CY_CPU_CORTEX_M0P)
189 _cyhal_system_irq_store_priority(system_intr, intr_priority);
190 /* We can avoid a "lowest priority" search here, because the currently set value is the lowest except
191 * possibly for the one we're just now registering */
192 uint8_t existing_priority = NVIC_GetPriority((IRQn_Type)cpu_irq);
193 uint8_t lowest_priority = (existing_priority < intr_priority) ? existing_priority : intr_priority;
194 #endif /* !(CY_CPU_CORTEX_M0P) */
195 #else
196 /* We want to always round up to ensure that even the highest numbered system IRQ still falls into
197 * the top indexed CPU IRQ */
198 const uint8_t SYSTEM_IRQ_PER_CPU_IRQ = (_CYHAL_IRQ_COUNT + (NUM_CPU_INTERRUPTS - 1)) / NUM_CPU_INTERRUPTS;
199 uint8_t cpu_irq = ((uint32_t)system_intr) / SYSTEM_IRQ_PER_CPU_IRQ;
200 #endif
201 #if defined(CY_IP_M7CPUSS) || (defined(CY_IP_M4CPUSS) && (CY_IP_M4CPUSS_VERSION == 2u) && (CPUSS_SYSTEM_IRQ_PRESENT))
202 #if (CY_CPU_CORTEX_M0P)
203 cpu_irq += 2u; /* Handle offset from interrupts reserved for ROM */
204 #endif
205 /* For CM7, the upper 20 bits of the intrSrc field are the CPU interrupt number */
206 uint32_t intr_src = (uint32_t)system_intr;
207 intr_src |= cpu_irq << CY_SYSINT_INTRSRC_MUXIRQ_SHIFT;
208 _cyhal_system_irq_store_priority(system_intr, intr_priority);
209 /* Find the lowest priority between all of the configured interrupts
210 and the interrupt that is currently being registered */
211 uint8_t existing_priority = _cyhal_system_irq_lowest_priority((IRQn_Type)cpu_irq);
212 uint8_t lowest_priority = (existing_priority < intr_priority) ? existing_priority : intr_priority;
213 #if (CY_CPU_CORTEX_M0P) && defined(CY_IP_M4CPUSS)
214 CY_UNUSED_PARAMETER(lowest_priority);
215 cy_stc_sysint_t intr_cfg = { (IRQn_Type)cpu_irq, system_intr, intr_priority };
216 #elif defined(CY_IP_M7CPUSS)
217 cy_stc_sysint_t intr_cfg = { intr_src, lowest_priority };
218 #else
219 cy_stc_sysint_t intr_cfg = { (IRQn_Type)intr_src, lowest_priority };
220 #endif
221 #else /* M4CPUSS and CY_IP_M4CPUSS_VERSION is 1 */
222 #if (CY_CPU_CORTEX_M0P)
223 cy_stc_sysint_t intr_cfg = { (IRQn_Type)cpu_irq, system_intr, intr_priority };
224 #else
225 cy_stc_sysint_t intr_cfg = { (IRQn_Type)intr_src, lowest_priority };
226 #endif
227 #endif
228 #endif
229 #else
230 cy_stc_sysint_t intr_cfg = { system_intr, intr_priority };
231 #endif
232 return Cy_SysInt_Init(&intr_cfg, irq_handler);
233 }
234
235 #if _CYHAL_IRQ_LEGACY_M0
_cyhal_irq_find_cm0(cy_en_intr_t system_irq)236 IRQn_Type _cyhal_irq_find_cm0(cy_en_intr_t system_irq)
237 {
238 /* Need to look through all of the enabled CPU interrupts and see if any of them
239 * is connected to this IRQn */
240 for(int i = 0; i < _CYHAL_IRQ_COUNT_M0; ++i)
241 {
242 IRQn_Type irqn = (IRQn_Type)i;
243 if(system_irq == Cy_SysInt_GetInterruptSource(irqn))
244 {
245 return irqn;
246 }
247 }
248
249 return unconnected_IRQn;
250 }
251 #endif
252