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