11bb92983SJerome Forissier /* SPDX-License-Identifier: BSD-2-Clause */ 27315b7b4SJens Wiklander /* 3e9f46c74SJens Wiklander * Copyright (c) 2016-2019, Linaro Limited 47315b7b4SJens Wiklander */ 57315b7b4SJens Wiklander #ifndef __KERNEL_INTERRUPT_H 67315b7b4SJens Wiklander #define __KERNEL_INTERRUPT_H 77315b7b4SJens Wiklander 8702fe5a7SClément Léger #include <dt-bindings/interrupt-controller/irq.h> 9f932e355SEtienne Carriere #include <mm/core_memprot.h> 107315b7b4SJens Wiklander #include <sys/queue.h> 11f932e355SEtienne Carriere #include <tee_api_types.h> 12f932e355SEtienne Carriere #include <types_ext.h> 131c832d7cSdavidwang #include <util.h> 147315b7b4SJens Wiklander 151c832d7cSdavidwang #define ITRF_TRIGGER_LEVEL BIT(0) 161c832d7cSdavidwang #define ITRF_SHARED BIT(1) 177315b7b4SJens Wiklander 18f932e355SEtienne Carriere struct itr_handler; 19f932e355SEtienne Carriere 20a009881dSEtienne Carriere /* 21a009881dSEtienne Carriere * struct itr_chip - Interrupt controller 22a009881dSEtienne Carriere * 23a009881dSEtienne Carriere * @ops Operation callback functions 24f932e355SEtienne Carriere * @name Controller name, for debug purpose 25f932e355SEtienne Carriere * @handlers Registered handlers list head 26a009881dSEtienne Carriere * @dt_get_irq Device tree node parsing function 27a009881dSEtienne Carriere */ 287315b7b4SJens Wiklander struct itr_chip { 297315b7b4SJens Wiklander const struct itr_ops *ops; 30f932e355SEtienne Carriere const char *name; 31f932e355SEtienne Carriere SLIST_HEAD(, itr_handler) handlers; 32702fe5a7SClément Léger /* 33702fe5a7SClément Léger * dt_get_irq - parse a device tree interrupt property 34702fe5a7SClément Léger * 35702fe5a7SClément Léger * @properties raw interrupt property from device tree 36702fe5a7SClément Léger * @count number of elements in @properties 37702fe5a7SClément Léger * @type If not NULL, output interrupt type (IRQ_TYPE_* defines) 38702fe5a7SClément Léger * or IRQ_TYPE_NONE if unknown 39702fe5a7SClément Léger * @prio If not NULL, output interrupt priority value or 0 if unknown 40702fe5a7SClément Léger */ 41702fe5a7SClément Léger int (*dt_get_irq)(const uint32_t *properties, int count, uint32_t *type, 42702fe5a7SClément Léger uint32_t *prio); 437315b7b4SJens Wiklander }; 447315b7b4SJens Wiklander 45a009881dSEtienne Carriere /* 46a009881dSEtienne Carriere * struct itr_ops - Interrupt controller operations 47a009881dSEtienne Carriere * @add Register and configure an interrupt 48a009881dSEtienne Carriere * @enable Enable an interrupt 49a009881dSEtienne Carriere * @disable Disable an interrupt 50f932e355SEtienne Carriere * @mask Mask an interrupt, may be called from an interrupt context 51f932e355SEtienne Carriere * @unmask Unmask an interrupt, may be called from an interrupt context 52a009881dSEtienne Carriere * @raise_pi Raise per-cpu interrupt or NULL if not applicable 53a009881dSEtienne Carriere * @raise_sgi Raise a SGI or NULL if not applicable to that controller 54a009881dSEtienne Carriere * @set_affinity Set interrupt/cpu affinity or NULL if not applicable 55f932e355SEtienne Carriere * 56f932e355SEtienne Carriere * Handlers @enable, @disable, @mask, @unmask and @add are mandated. Handlers 57f932e355SEtienne Carriere * @mask and @unmask have unpaged memory contrainsts. See itr_chip_is_valid(). 58a009881dSEtienne Carriere */ 597315b7b4SJens Wiklander struct itr_ops { 60702fe5a7SClément Léger void (*add)(struct itr_chip *chip, size_t it, uint32_t type, 61702fe5a7SClément Léger uint32_t prio); 627315b7b4SJens Wiklander void (*enable)(struct itr_chip *chip, size_t it); 637315b7b4SJens Wiklander void (*disable)(struct itr_chip *chip, size_t it); 64f932e355SEtienne Carriere void (*mask)(struct itr_chip *chip, size_t it); 65f932e355SEtienne Carriere void (*unmask)(struct itr_chip *chip, size_t it); 6626ed70ecSGuanchao Liang void (*raise_pi)(struct itr_chip *chip, size_t it); 6726ed70ecSGuanchao Liang void (*raise_sgi)(struct itr_chip *chip, size_t it, 6826ed70ecSGuanchao Liang uint8_t cpu_mask); 6926ed70ecSGuanchao Liang void (*set_affinity)(struct itr_chip *chip, size_t it, 7026ed70ecSGuanchao Liang uint8_t cpu_mask); 717315b7b4SJens Wiklander }; 727315b7b4SJens Wiklander 73a009881dSEtienne Carriere /* Interrupt handler return value */ 747315b7b4SJens Wiklander enum itr_return { 757315b7b4SJens Wiklander ITRR_NONE, 767315b7b4SJens Wiklander ITRR_HANDLED, 777315b7b4SJens Wiklander }; 787315b7b4SJens Wiklander 79a009881dSEtienne Carriere /* Interrupt handler signature */ 80acc5dd21SLudovic Barre typedef enum itr_return (*itr_handler_t)(struct itr_handler *h); 81acc5dd21SLudovic Barre 82a009881dSEtienne Carriere /* 83a009881dSEtienne Carriere * struct itr_handler - Interrupt handler reference 84a009881dSEtienne Carriere * @it Interrupt number 85f932e355SEtienne Carriere * @flags Property bit flags (ITRF_*) or 0 86a009881dSEtienne Carriere * @data Private data for that interrupt handler 87f932e355SEtienne Carriere * @chip Interrupt controller chip device 88a009881dSEtienne Carriere * @link Reference in controller handler list 89a009881dSEtienne Carriere */ 907315b7b4SJens Wiklander struct itr_handler { 917315b7b4SJens Wiklander size_t it; 927315b7b4SJens Wiklander uint32_t flags; 93acc5dd21SLudovic Barre itr_handler_t handler; 947315b7b4SJens Wiklander void *data; 95f932e355SEtienne Carriere struct itr_chip *chip; 967315b7b4SJens Wiklander SLIST_ENTRY(itr_handler) link; 977315b7b4SJens Wiklander }; 987315b7b4SJens Wiklander 99f932e355SEtienne Carriere #define ITR_HANDLER(_chip, _itr_num, _flags, _fn, _priv) \ 100f932e355SEtienne Carriere ((struct itr_handler){ \ 101f932e355SEtienne Carriere .chip = (_chip), .it = (_itr_num), .flags = (_flags), \ 102f932e355SEtienne Carriere .handler = (_fn), .data = (_priv), \ 103f932e355SEtienne Carriere }) 104f932e355SEtienne Carriere 105f932e355SEtienne Carriere /* 106f932e355SEtienne Carriere * Return true only if interrupt chip provides required handlers 107f932e355SEtienne Carriere * @chip: Interrupt controller reference 108f932e355SEtienne Carriere */ 109f932e355SEtienne Carriere static inline bool itr_chip_is_valid(struct itr_chip *chip) 110f932e355SEtienne Carriere { 111f932e355SEtienne Carriere return chip && is_unpaged(chip) && chip->ops && 112f932e355SEtienne Carriere is_unpaged((void *)chip->ops) && 113f932e355SEtienne Carriere chip->ops->mask && is_unpaged(chip->ops->mask) && 114f932e355SEtienne Carriere chip->ops->unmask && is_unpaged(chip->ops->unmask) && 115f932e355SEtienne Carriere chip->ops->enable && chip->ops->disable && 116f932e355SEtienne Carriere chip->ops->add; 117f932e355SEtienne Carriere } 118f932e355SEtienne Carriere 119f932e355SEtienne Carriere /* 120f932e355SEtienne Carriere * Initialise an interrupt controller handle 121f932e355SEtienne Carriere * @chip Interrupt controller 122f932e355SEtienne Carriere */ 123f932e355SEtienne Carriere TEE_Result itr_chip_init(struct itr_chip *chip); 124f932e355SEtienne Carriere 12501980f3fSEtienne Carriere /* 126a009881dSEtienne Carriere * Initialise main interrupt controller driver 127a009881dSEtienne Carriere * @data Main controller main data reference to register 12801980f3fSEtienne Carriere */ 12901980f3fSEtienne Carriere void interrupt_main_init(struct itr_chip *data); 13001980f3fSEtienne Carriere 131e050e0a7SEtienne Carriere /* Retrieve main interrupt controller reference */ 132e050e0a7SEtienne Carriere struct itr_chip *interrupt_get_main_chip(void); 133e050e0a7SEtienne Carriere 13467729d8dSLudovic Barre #ifdef CFG_DT 13567729d8dSLudovic Barre /* 136702fe5a7SClément Léger * Get the DT interrupt property at @node. In the DT an interrupt property can 137702fe5a7SClément Léger * specify additional information which can be retrieved with @type and @prio. 13867729d8dSLudovic Barre * 13967729d8dSLudovic Barre * @fdt reference to the Device Tree 140702fe5a7SClément Léger * @node is the node offset to read the interrupt property from 141702fe5a7SClément Léger * @type interrupt type (IRQ_TYPE_* defines) if specified by interrupt property 142702fe5a7SClément Léger * or IRQ_TYPE_NONE if not. Can be NULL if not needed 143702fe5a7SClément Léger * @prio interrupt priority if specified by interrupt property or 0 if not. Can 144702fe5a7SClément Léger * be NULL if not needed 14567729d8dSLudovic Barre * 14667729d8dSLudovic Barre * Returns the interrupt number if value >= 0 14767729d8dSLudovic Barre * otherwise DT_INFO_INVALID_INTERRUPT 14867729d8dSLudovic Barre */ 149702fe5a7SClément Léger int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type, 150702fe5a7SClément Léger uint32_t *prio); 151702fe5a7SClément Léger 152702fe5a7SClément Léger /* 153702fe5a7SClément Léger * Get the DT interrupt property at @node 154702fe5a7SClément Léger */ 155702fe5a7SClément Léger static inline int dt_get_irq(const void *fdt, int node) 156702fe5a7SClément Léger { 157702fe5a7SClément Léger return dt_get_irq_type_prio(fdt, node, NULL, NULL); 158702fe5a7SClément Léger } 15967729d8dSLudovic Barre #endif 16067729d8dSLudovic Barre 161702fe5a7SClément Léger struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler, 162702fe5a7SClément Léger uint32_t flags, void *data, 163702fe5a7SClément Léger uint32_t type, uint32_t prio); 164acc5dd21SLudovic Barre void itr_free(struct itr_handler *hdl); 165702fe5a7SClément Léger void itr_add_type_prio(struct itr_handler *handler, uint32_t type, 166702fe5a7SClément Léger uint32_t prio); 16726ed70ecSGuanchao Liang void itr_enable(size_t it); 16826ed70ecSGuanchao Liang void itr_disable(size_t it); 16926ed70ecSGuanchao Liang /* raise the Peripheral Interrupt corresponding to the interrupt ID */ 17026ed70ecSGuanchao Liang void itr_raise_pi(size_t it); 17126ed70ecSGuanchao Liang /* 17226ed70ecSGuanchao Liang * raise the Software Generated Interrupt corresponding to the interrupt ID, 17326ed70ecSGuanchao Liang * the cpu_mask represents which cpu interface to forward. 17426ed70ecSGuanchao Liang */ 17526ed70ecSGuanchao Liang void itr_raise_sgi(size_t it, uint8_t cpu_mask); 17626ed70ecSGuanchao Liang /* 17726ed70ecSGuanchao Liang * let corresponding interrupt forward to the cpu interface 17826ed70ecSGuanchao Liang * according to the cpu_mask. 17926ed70ecSGuanchao Liang */ 18026ed70ecSGuanchao Liang void itr_set_affinity(size_t it, uint8_t cpu_mask); 1817315b7b4SJens Wiklander 182e9f46c74SJens Wiklander /* 183e9f46c74SJens Wiklander * __weak overridable function which is called when a secure interrupt is 184e9f46c74SJens Wiklander * received. The default function calls panic() immediately, platforms which 185e9f46c74SJens Wiklander * expects to receive secure interrupts should override this function. 186e9f46c74SJens Wiklander */ 187358bf47cSEtienne Carriere void interrupt_main_handler(void); 188e9f46c74SJens Wiklander 189702fe5a7SClément Léger static inline void itr_add(struct itr_handler *handler) 190702fe5a7SClément Léger { 191702fe5a7SClément Léger itr_add_type_prio(handler, IRQ_TYPE_NONE, 0); 192702fe5a7SClément Léger } 193702fe5a7SClément Léger 194702fe5a7SClément Léger static inline struct itr_handler *itr_alloc_add(size_t it, 195702fe5a7SClément Léger itr_handler_t handler, 196702fe5a7SClément Léger uint32_t flags, void *data) 197702fe5a7SClément Léger { 198702fe5a7SClément Léger return itr_alloc_add_type_prio(it, handler, flags, data, IRQ_TYPE_NONE, 199702fe5a7SClément Léger 0); 200702fe5a7SClément Léger } 201702fe5a7SClément Léger 202f932e355SEtienne Carriere /* 203f932e355SEtienne Carriere * Interrupt controller chip API functions 204f932e355SEtienne Carriere */ 205f932e355SEtienne Carriere 206f932e355SEtienne Carriere /* 207f932e355SEtienne Carriere * interrupt_call_handlers() - Call registered handlers for an interrupt 208f932e355SEtienne Carriere * @chip Interrupt controller 209f932e355SEtienne Carriere * @itr_num Interrupt number 210f932e355SEtienne Carriere * 211f932e355SEtienne Carriere * This function is called from an interrupt context by a primary interrupt 212f932e355SEtienne Carriere * handler. This function calls the handlers registered for that interrupt. 213f932e355SEtienne Carriere * If interrupt is not handled, it is masked. 214f932e355SEtienne Carriere */ 215f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num); 216f932e355SEtienne Carriere 217f932e355SEtienne Carriere /* 218f932e355SEtienne Carriere * interrupt_mask() - Mask an interrupt 219f932e355SEtienne Carriere * @chip Interrupt controller 220f932e355SEtienne Carriere * @itr_num Interrupt number 221f932e355SEtienne Carriere * 222f932e355SEtienne Carriere * This function may be called in interrupt context 223f932e355SEtienne Carriere */ 224f932e355SEtienne Carriere static inline void interrupt_mask(struct itr_chip *chip, size_t itr_num) 225f932e355SEtienne Carriere { 226f932e355SEtienne Carriere chip->ops->mask(chip, itr_num); 227f932e355SEtienne Carriere } 228f932e355SEtienne Carriere 229f932e355SEtienne Carriere /* 230f932e355SEtienne Carriere * interrupt_unmask() - Unmask an interrupt 231f932e355SEtienne Carriere * @chip Interrupt controller 232f932e355SEtienne Carriere * @itr_num Interrupt number 233f932e355SEtienne Carriere * 234f932e355SEtienne Carriere * This function may be called in interrupt context 235f932e355SEtienne Carriere */ 236f932e355SEtienne Carriere static inline void interrupt_unmask(struct itr_chip *chip, size_t itr_num) 237f932e355SEtienne Carriere { 238f932e355SEtienne Carriere chip->ops->unmask(chip, itr_num); 239f932e355SEtienne Carriere } 240f932e355SEtienne Carriere 241f932e355SEtienne Carriere /* 242f932e355SEtienne Carriere * interrupt_enable() - Enable an interrupt 243f932e355SEtienne Carriere * @chip Interrupt controller 244f932e355SEtienne Carriere * @itr_num Interrupt number 245f932e355SEtienne Carriere */ 246f932e355SEtienne Carriere static inline void interrupt_enable(struct itr_chip *chip, size_t itr_num) 247f932e355SEtienne Carriere { 248f932e355SEtienne Carriere chip->ops->enable(chip, itr_num); 249f932e355SEtienne Carriere } 250f932e355SEtienne Carriere 251f932e355SEtienne Carriere /* 252f932e355SEtienne Carriere * interrupt_disable() - Disable an interrupt 253f932e355SEtienne Carriere * @chip Interrupt controller 254f932e355SEtienne Carriere * @itr_num Interrupt number 255f932e355SEtienne Carriere */ 256f932e355SEtienne Carriere static inline void interrupt_disable(struct itr_chip *chip, size_t itr_num) 257f932e355SEtienne Carriere { 258f932e355SEtienne Carriere chip->ops->disable(chip, itr_num); 259f932e355SEtienne Carriere } 260f932e355SEtienne Carriere 261f932e355SEtienne Carriere /* 262f932e355SEtienne Carriere * interrupt_configure() - Configure an interrupt in an interrupt controller 263f932e355SEtienne Carriere * @chip Interrupt controller 264f932e355SEtienne Carriere * @itr_num Interrupt number 265f932e355SEtienne Carriere * @type Interrupt trigger type (IRQ_TYPE_* defines) or IRQ_TYPE_NONE 266f932e355SEtienne Carriere * @prio Interrupt priority or 0 267f932e355SEtienne Carriere * 268f932e355SEtienne Carriere * Interrupt consumers that get their interrupt from the DT do not need to 269f932e355SEtienne Carriere * call interrupt_configure() since the interrupt configuration has already 270f932e355SEtienne Carriere * been done by interrupt controller based on the DT bidings. 271f932e355SEtienne Carriere */ 272f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 273f932e355SEtienne Carriere uint32_t type, uint32_t prio); 274f932e355SEtienne Carriere 275f932e355SEtienne Carriere /* 276f932e355SEtienne Carriere * interrupt_add_and_configure_handler() - Register and configure a handler 277f932e355SEtienne Carriere * @hdl Interrupt handler to register 278f932e355SEtienne Carriere * @type Interrupt trigger type (IRQ_TYPE_* defines) or IRQ_TYPE_NONE 279f932e355SEtienne Carriere * @prio Interrupt priority or 0 280f932e355SEtienne Carriere */ 281f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 282f932e355SEtienne Carriere uint32_t type, uint32_t prio); 283f932e355SEtienne Carriere 284f932e355SEtienne Carriere /* 285f932e355SEtienne Carriere * interrupt_add_handler() - Register an interrupt handler 286f932e355SEtienne Carriere * @hdl Interrupt handler to register 287f932e355SEtienne Carriere * 288f932e355SEtienne Carriere * This helper function assumes interrupt type is set to IRQ_TYPE_NONE 289f932e355SEtienne Carriere * and interrupt priority to 0. 290f932e355SEtienne Carriere */ 291f932e355SEtienne Carriere static inline TEE_Result interrupt_add_handler(struct itr_handler *hdl) 292f932e355SEtienne Carriere { 293f932e355SEtienne Carriere return interrupt_add_configure_handler(hdl, IRQ_TYPE_NONE, 0); 294f932e355SEtienne Carriere } 295f932e355SEtienne Carriere 296f932e355SEtienne Carriere /* 297f932e355SEtienne Carriere * interrupt_add_handler_with_chip() - Register an interrupt handler providing 298f932e355SEtienne Carriere * the interrupt chip reference in specific argument @chip. 299f932e355SEtienne Carriere * @chip Interrupt controller 300f932e355SEtienne Carriere * @h Interrupt handler to register 301f932e355SEtienne Carriere */ 302f932e355SEtienne Carriere static inline TEE_Result interrupt_add_handler_with_chip(struct itr_chip *chip, 303f932e355SEtienne Carriere struct itr_handler *h) 304f932e355SEtienne Carriere { 305f932e355SEtienne Carriere h->chip = chip; 306f932e355SEtienne Carriere return interrupt_add_handler(h); 307f932e355SEtienne Carriere } 308f932e355SEtienne Carriere 309f932e355SEtienne Carriere /* 310f932e355SEtienne Carriere * interrupt_remove_handler() - Remove a registered interrupt handler 311f932e355SEtienne Carriere * @hdl Interrupt handler to remove 312f932e355SEtienne Carriere * 313f932e355SEtienne Carriere * This function is the counterpart of interrupt_add_handler(). 314f932e355SEtienne Carriere * This function may panic on non-NULL invalid @hdl reference. 315f932e355SEtienne Carriere */ 316f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl); 317f932e355SEtienne Carriere 318f932e355SEtienne Carriere /* 319*1b5c7ca4SEtienne Carriere * interrupt_alloc_add_conf_handler() - Allocate, configure, register a handler 320*1b5c7ca4SEtienne Carriere * @chip Interrupt controller 321*1b5c7ca4SEtienne Carriere * @itr_num Interrupt number 322*1b5c7ca4SEtienne Carriere * @handler Interrupt handler to register 323*1b5c7ca4SEtienne Carriere * @flags Bitmask flag ITRF_* 324*1b5c7ca4SEtienne Carriere * @data Private data reference passed to @handler 325*1b5c7ca4SEtienne Carriere * @type Interrupt trigger type (IRQ_TYPE_* defines) or IRQ_TYPE_NONE 326*1b5c7ca4SEtienne Carriere * @prio Interrupt priority or 0 327*1b5c7ca4SEtienne Carriere * @out_hdl NULL or output pointer to allocated struct itr_handler 328*1b5c7ca4SEtienne Carriere */ 329*1b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 330*1b5c7ca4SEtienne Carriere size_t it_num, 331*1b5c7ca4SEtienne Carriere itr_handler_t handler, 332*1b5c7ca4SEtienne Carriere uint32_t flags, void *data, 333*1b5c7ca4SEtienne Carriere uint32_t type, uint32_t prio, 334*1b5c7ca4SEtienne Carriere struct itr_handler **out_hdl); 335*1b5c7ca4SEtienne Carriere 336*1b5c7ca4SEtienne Carriere /* 337f932e355SEtienne Carriere * interrupt_alloc_add_handler() - Allocate and register an interrupt handler 338f932e355SEtienne Carriere * @chip Interrupt controller 339f932e355SEtienne Carriere * @itr_num Interrupt number 340f932e355SEtienne Carriere * @handler Interrupt handler to register 341f932e355SEtienne Carriere * @flags Bitmask flag ITRF_* 342f932e355SEtienne Carriere * @data Private data reference passed to @handler 343f932e355SEtienne Carriere * @out_hdl NULL or output pointer to allocated struct itr_handler 344f932e355SEtienne Carriere */ 345*1b5c7ca4SEtienne Carriere static inline TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, 346*1b5c7ca4SEtienne Carriere size_t it_num, 347*1b5c7ca4SEtienne Carriere itr_handler_t handler, 348*1b5c7ca4SEtienne Carriere uint32_t flags, 349f932e355SEtienne Carriere void *data, 350*1b5c7ca4SEtienne Carriere struct itr_handler **hdl) 351*1b5c7ca4SEtienne Carriere { 352*1b5c7ca4SEtienne Carriere return interrupt_alloc_add_conf_handler(chip, it_num, handler, flags, 353*1b5c7ca4SEtienne Carriere data, IRQ_TYPE_NONE, 0, hdl); 354*1b5c7ca4SEtienne Carriere } 355f932e355SEtienne Carriere 356f932e355SEtienne Carriere /* 357f932e355SEtienne Carriere * interrupt_remove_free_handler() - Remove/free a registered interrupt handler 358f932e355SEtienne Carriere * @hdl Interrupt handler to remove and free 359f932e355SEtienne Carriere * 360*1b5c7ca4SEtienne Carriere * This function is the counterpart of interrupt_alloc_add_handler() 361*1b5c7ca4SEtienne Carriere * and interrupt_alloc_add_conf_handler(). 362f932e355SEtienne Carriere * This function may panic on non-NULL invalid @hdl reference. 363f932e355SEtienne Carriere */ 364f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl); 3657315b7b4SJens Wiklander #endif /*__KERNEL_INTERRUPT_H*/ 366