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)
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 defined(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)
169 cy_rslt_t status = CYHAL_HWMGR_RSLT_ERR_NONE_FREE;
170 uint8_t cpu_irq;
171 for (int idx = 0; idx < NUM_CPU_INTERRUPTS; idx++)
172 {
173 if ((_cpu_irq_tracker & (1 << idx)) == 0)
174 {
175 cpu_irq = idx;
176 _cpu_irq_tracker |= (1 << idx);
177 status = CY_RSLT_SUCCESS;
178 break;
179 }
180 }
181 if (status != CY_RSLT_SUCCESS)
182 {
183 return status;
184 }
185 #else
186 const uint8_t SYSTEM_IRQ_PER_CPU_IRQ = (_CYHAL_IRQ_COUNT + (NUM_CPU_INTERRUPTS / 2)) / NUM_CPU_INTERRUPTS;
187 uint8_t cpu_irq = ((uint32_t)system_intr) / SYSTEM_IRQ_PER_CPU_IRQ;
188 #endif
189 #if defined (CY_IP_M7CPUSS)
190 #if defined(CY_CPU_CORTEX_M0P)
191 cpu_irq += 2u; /* Handle offset from interrupts reserved for ROM */
192 #endif
193 /* For CM7, the upper 16 bits of the intrSrc field are the CPU interrupt number */
194 uint32_t intr_src = (uint32_t)system_intr;
195 intr_src |= cpu_irq << 16;
196 _cyhal_system_irq_store_priority(system_intr, intr_priority);
197 /* We can avoid a "lowest priority" search here, because the currently set value is the lowest except
198 * possibly for the one we're just now registering */
199 uint8_t existing_priority = NVIC_GetPriority((IRQn_Type)cpu_irq);
200 uint8_t lowest_priority = (existing_priority < intr_priority) ? existing_priority : intr_priority;
201 cy_stc_sysint_t intr_cfg = { intr_src, lowest_priority };
202 #else /* M4CPUSS */
203 cy_stc_sysint_t intr_cfg = { (IRQn_Type)cpu_irq, system_intr, intr_priority };
204 #endif
205 #endif
206 #else
207 cy_stc_sysint_t intr_cfg = { system_intr, intr_priority };
208 #endif
209 return Cy_SysInt_Init(&intr_cfg, irq_handler);
210 }
211
212 #if _CYHAL_IRQ_LEGACY_M0
_cyhal_irq_find_cm0(cy_en_intr_t system_irq)213 IRQn_Type _cyhal_irq_find_cm0(cy_en_intr_t system_irq)
214 {
215 /* Need to look through all of the enabled CPU interrupts and see if any of them
216 * is connected to this IRQn */
217 for(int i = 0; i < _CYHAL_IRQ_COUNT_M0; ++i)
218 {
219 IRQn_Type irqn = (IRQn_Type)i;
220 if(system_irq == Cy_SysInt_GetInterruptSource(irqn))
221 {
222 return irqn;
223 }
224 }
225
226 return unconnected_IRQn;
227 }
228 #endif
229