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> 9*f932e355SEtienne Carriere #include <mm/core_memprot.h> 107315b7b4SJens Wiklander #include <sys/queue.h> 11*f932e355SEtienne Carriere #include <tee_api_types.h> 12*f932e355SEtienne 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 18*f932e355SEtienne Carriere struct itr_handler; 19*f932e355SEtienne Carriere 20a009881dSEtienne Carriere /* 21a009881dSEtienne Carriere * struct itr_chip - Interrupt controller 22a009881dSEtienne Carriere * 23a009881dSEtienne Carriere * @ops Operation callback functions 24*f932e355SEtienne Carriere * @name Controller name, for debug purpose 25*f932e355SEtienne 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; 30*f932e355SEtienne Carriere const char *name; 31*f932e355SEtienne 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 50*f932e355SEtienne Carriere * @mask Mask an interrupt, may be called from an interrupt context 51*f932e355SEtienne 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 55*f932e355SEtienne Carriere * 56*f932e355SEtienne Carriere * Handlers @enable, @disable, @mask, @unmask and @add are mandated. Handlers 57*f932e355SEtienne 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); 64*f932e355SEtienne Carriere void (*mask)(struct itr_chip *chip, size_t it); 65*f932e355SEtienne 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 85*f932e355SEtienne Carriere * @flags Property bit flags (ITRF_*) or 0 86a009881dSEtienne Carriere * @data Private data for that interrupt handler 87*f932e355SEtienne 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; 95*f932e355SEtienne Carriere struct itr_chip *chip; 967315b7b4SJens Wiklander SLIST_ENTRY(itr_handler) link; 977315b7b4SJens Wiklander }; 987315b7b4SJens Wiklander 99*f932e355SEtienne Carriere #define ITR_HANDLER(_chip, _itr_num, _flags, _fn, _priv) \ 100*f932e355SEtienne Carriere ((struct itr_handler){ \ 101*f932e355SEtienne Carriere .chip = (_chip), .it = (_itr_num), .flags = (_flags), \ 102*f932e355SEtienne Carriere .handler = (_fn), .data = (_priv), \ 103*f932e355SEtienne Carriere }) 104*f932e355SEtienne Carriere 105*f932e355SEtienne Carriere /* 106*f932e355SEtienne Carriere * Return true only if interrupt chip provides required handlers 107*f932e355SEtienne Carriere * @chip: Interrupt controller reference 108*f932e355SEtienne Carriere */ 109*f932e355SEtienne Carriere static inline bool itr_chip_is_valid(struct itr_chip *chip) 110*f932e355SEtienne Carriere { 111*f932e355SEtienne Carriere return chip && is_unpaged(chip) && chip->ops && 112*f932e355SEtienne Carriere is_unpaged((void *)chip->ops) && 113*f932e355SEtienne Carriere chip->ops->mask && is_unpaged(chip->ops->mask) && 114*f932e355SEtienne Carriere chip->ops->unmask && is_unpaged(chip->ops->unmask) && 115*f932e355SEtienne Carriere chip->ops->enable && chip->ops->disable && 116*f932e355SEtienne Carriere chip->ops->add; 117*f932e355SEtienne Carriere } 118*f932e355SEtienne Carriere 119*f932e355SEtienne Carriere /* 120*f932e355SEtienne Carriere * Initialise an interrupt controller handle 121*f932e355SEtienne Carriere * @chip Interrupt controller 122*f932e355SEtienne Carriere */ 123*f932e355SEtienne Carriere TEE_Result itr_chip_init(struct itr_chip *chip); 124*f932e355SEtienne 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 131a009881dSEtienne Carriere /* 132a009881dSEtienne Carriere * Call handlers registered for that interrupt in core interrupt controller 133a009881dSEtienne Carriere * @it Interrupt line number 134a009881dSEtienne Carriere */ 1357315b7b4SJens Wiklander void itr_handle(size_t it); 1367315b7b4SJens Wiklander 137e050e0a7SEtienne Carriere /* Retrieve main interrupt controller reference */ 138e050e0a7SEtienne Carriere struct itr_chip *interrupt_get_main_chip(void); 139e050e0a7SEtienne Carriere 14067729d8dSLudovic Barre #ifdef CFG_DT 14167729d8dSLudovic Barre /* 142702fe5a7SClément Léger * Get the DT interrupt property at @node. In the DT an interrupt property can 143702fe5a7SClément Léger * specify additional information which can be retrieved with @type and @prio. 14467729d8dSLudovic Barre * 14567729d8dSLudovic Barre * @fdt reference to the Device Tree 146702fe5a7SClément Léger * @node is the node offset to read the interrupt property from 147702fe5a7SClément Léger * @type interrupt type (IRQ_TYPE_* defines) if specified by interrupt property 148702fe5a7SClément Léger * or IRQ_TYPE_NONE if not. Can be NULL if not needed 149702fe5a7SClément Léger * @prio interrupt priority if specified by interrupt property or 0 if not. Can 150702fe5a7SClément Léger * be NULL if not needed 15167729d8dSLudovic Barre * 15267729d8dSLudovic Barre * Returns the interrupt number if value >= 0 15367729d8dSLudovic Barre * otherwise DT_INFO_INVALID_INTERRUPT 15467729d8dSLudovic Barre */ 155702fe5a7SClément Léger int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type, 156702fe5a7SClément Léger uint32_t *prio); 157702fe5a7SClément Léger 158702fe5a7SClément Léger /* 159702fe5a7SClément Léger * Get the DT interrupt property at @node 160702fe5a7SClément Léger */ 161702fe5a7SClément Léger static inline int dt_get_irq(const void *fdt, int node) 162702fe5a7SClément Léger { 163702fe5a7SClément Léger return dt_get_irq_type_prio(fdt, node, NULL, NULL); 164702fe5a7SClément Léger } 16567729d8dSLudovic Barre #endif 16667729d8dSLudovic Barre 167702fe5a7SClément Léger struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler, 168702fe5a7SClément Léger uint32_t flags, void *data, 169702fe5a7SClément Léger uint32_t type, uint32_t prio); 170acc5dd21SLudovic Barre void itr_free(struct itr_handler *hdl); 171702fe5a7SClément Léger void itr_add_type_prio(struct itr_handler *handler, uint32_t type, 172702fe5a7SClément Léger uint32_t prio); 17326ed70ecSGuanchao Liang void itr_enable(size_t it); 17426ed70ecSGuanchao Liang void itr_disable(size_t it); 17526ed70ecSGuanchao Liang /* raise the Peripheral Interrupt corresponding to the interrupt ID */ 17626ed70ecSGuanchao Liang void itr_raise_pi(size_t it); 17726ed70ecSGuanchao Liang /* 17826ed70ecSGuanchao Liang * raise the Software Generated Interrupt corresponding to the interrupt ID, 17926ed70ecSGuanchao Liang * the cpu_mask represents which cpu interface to forward. 18026ed70ecSGuanchao Liang */ 18126ed70ecSGuanchao Liang void itr_raise_sgi(size_t it, uint8_t cpu_mask); 18226ed70ecSGuanchao Liang /* 18326ed70ecSGuanchao Liang * let corresponding interrupt forward to the cpu interface 18426ed70ecSGuanchao Liang * according to the cpu_mask. 18526ed70ecSGuanchao Liang */ 18626ed70ecSGuanchao Liang void itr_set_affinity(size_t it, uint8_t cpu_mask); 1877315b7b4SJens Wiklander 188e9f46c74SJens Wiklander /* 189e9f46c74SJens Wiklander * __weak overridable function which is called when a secure interrupt is 190e9f46c74SJens Wiklander * received. The default function calls panic() immediately, platforms which 191e9f46c74SJens Wiklander * expects to receive secure interrupts should override this function. 192e9f46c74SJens Wiklander */ 193358bf47cSEtienne Carriere void interrupt_main_handler(void); 194e9f46c74SJens Wiklander 195702fe5a7SClément Léger static inline void itr_add(struct itr_handler *handler) 196702fe5a7SClément Léger { 197702fe5a7SClément Léger itr_add_type_prio(handler, IRQ_TYPE_NONE, 0); 198702fe5a7SClément Léger } 199702fe5a7SClément Léger 200702fe5a7SClément Léger static inline struct itr_handler *itr_alloc_add(size_t it, 201702fe5a7SClément Léger itr_handler_t handler, 202702fe5a7SClément Léger uint32_t flags, void *data) 203702fe5a7SClément Léger { 204702fe5a7SClément Léger return itr_alloc_add_type_prio(it, handler, flags, data, IRQ_TYPE_NONE, 205702fe5a7SClément Léger 0); 206702fe5a7SClément Léger } 207702fe5a7SClément Léger 208*f932e355SEtienne Carriere /* 209*f932e355SEtienne Carriere * Interrupt controller chip API functions 210*f932e355SEtienne Carriere */ 211*f932e355SEtienne Carriere 212*f932e355SEtienne Carriere /* 213*f932e355SEtienne Carriere * interrupt_call_handlers() - Call registered handlers for an interrupt 214*f932e355SEtienne Carriere * @chip Interrupt controller 215*f932e355SEtienne Carriere * @itr_num Interrupt number 216*f932e355SEtienne Carriere * 217*f932e355SEtienne Carriere * This function is called from an interrupt context by a primary interrupt 218*f932e355SEtienne Carriere * handler. This function calls the handlers registered for that interrupt. 219*f932e355SEtienne Carriere * If interrupt is not handled, it is masked. 220*f932e355SEtienne Carriere */ 221*f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num); 222*f932e355SEtienne Carriere 223*f932e355SEtienne Carriere /* 224*f932e355SEtienne Carriere * interrupt_mask() - Mask an interrupt 225*f932e355SEtienne Carriere * @chip Interrupt controller 226*f932e355SEtienne Carriere * @itr_num Interrupt number 227*f932e355SEtienne Carriere * 228*f932e355SEtienne Carriere * This function may be called in interrupt context 229*f932e355SEtienne Carriere */ 230*f932e355SEtienne Carriere static inline void interrupt_mask(struct itr_chip *chip, size_t itr_num) 231*f932e355SEtienne Carriere { 232*f932e355SEtienne Carriere chip->ops->mask(chip, itr_num); 233*f932e355SEtienne Carriere } 234*f932e355SEtienne Carriere 235*f932e355SEtienne Carriere /* 236*f932e355SEtienne Carriere * interrupt_unmask() - Unmask an interrupt 237*f932e355SEtienne Carriere * @chip Interrupt controller 238*f932e355SEtienne Carriere * @itr_num Interrupt number 239*f932e355SEtienne Carriere * 240*f932e355SEtienne Carriere * This function may be called in interrupt context 241*f932e355SEtienne Carriere */ 242*f932e355SEtienne Carriere static inline void interrupt_unmask(struct itr_chip *chip, size_t itr_num) 243*f932e355SEtienne Carriere { 244*f932e355SEtienne Carriere chip->ops->unmask(chip, itr_num); 245*f932e355SEtienne Carriere } 246*f932e355SEtienne Carriere 247*f932e355SEtienne Carriere /* 248*f932e355SEtienne Carriere * interrupt_enable() - Enable an interrupt 249*f932e355SEtienne Carriere * @chip Interrupt controller 250*f932e355SEtienne Carriere * @itr_num Interrupt number 251*f932e355SEtienne Carriere */ 252*f932e355SEtienne Carriere static inline void interrupt_enable(struct itr_chip *chip, size_t itr_num) 253*f932e355SEtienne Carriere { 254*f932e355SEtienne Carriere chip->ops->enable(chip, itr_num); 255*f932e355SEtienne Carriere } 256*f932e355SEtienne Carriere 257*f932e355SEtienne Carriere /* 258*f932e355SEtienne Carriere * interrupt_disable() - Disable an interrupt 259*f932e355SEtienne Carriere * @chip Interrupt controller 260*f932e355SEtienne Carriere * @itr_num Interrupt number 261*f932e355SEtienne Carriere */ 262*f932e355SEtienne Carriere static inline void interrupt_disable(struct itr_chip *chip, size_t itr_num) 263*f932e355SEtienne Carriere { 264*f932e355SEtienne Carriere chip->ops->disable(chip, itr_num); 265*f932e355SEtienne Carriere } 266*f932e355SEtienne Carriere 267*f932e355SEtienne Carriere /* 268*f932e355SEtienne Carriere * interrupt_configure() - Configure an interrupt in an interrupt controller 269*f932e355SEtienne Carriere * @chip Interrupt controller 270*f932e355SEtienne Carriere * @itr_num Interrupt number 271*f932e355SEtienne Carriere * @type Interrupt trigger type (IRQ_TYPE_* defines) or IRQ_TYPE_NONE 272*f932e355SEtienne Carriere * @prio Interrupt priority or 0 273*f932e355SEtienne Carriere * 274*f932e355SEtienne Carriere * Interrupt consumers that get their interrupt from the DT do not need to 275*f932e355SEtienne Carriere * call interrupt_configure() since the interrupt configuration has already 276*f932e355SEtienne Carriere * been done by interrupt controller based on the DT bidings. 277*f932e355SEtienne Carriere */ 278*f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 279*f932e355SEtienne Carriere uint32_t type, uint32_t prio); 280*f932e355SEtienne Carriere 281*f932e355SEtienne Carriere /* 282*f932e355SEtienne Carriere * interrupt_add_and_configure_handler() - Register and configure a handler 283*f932e355SEtienne Carriere * @hdl Interrupt handler to register 284*f932e355SEtienne Carriere * @type Interrupt trigger type (IRQ_TYPE_* defines) or IRQ_TYPE_NONE 285*f932e355SEtienne Carriere * @prio Interrupt priority or 0 286*f932e355SEtienne Carriere */ 287*f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 288*f932e355SEtienne Carriere uint32_t type, uint32_t prio); 289*f932e355SEtienne Carriere 290*f932e355SEtienne Carriere /* 291*f932e355SEtienne Carriere * interrupt_add_handler() - Register an interrupt handler 292*f932e355SEtienne Carriere * @hdl Interrupt handler to register 293*f932e355SEtienne Carriere * 294*f932e355SEtienne Carriere * This helper function assumes interrupt type is set to IRQ_TYPE_NONE 295*f932e355SEtienne Carriere * and interrupt priority to 0. 296*f932e355SEtienne Carriere */ 297*f932e355SEtienne Carriere static inline TEE_Result interrupt_add_handler(struct itr_handler *hdl) 298*f932e355SEtienne Carriere { 299*f932e355SEtienne Carriere return interrupt_add_configure_handler(hdl, IRQ_TYPE_NONE, 0); 300*f932e355SEtienne Carriere } 301*f932e355SEtienne Carriere 302*f932e355SEtienne Carriere /* 303*f932e355SEtienne Carriere * interrupt_add_handler_with_chip() - Register an interrupt handler providing 304*f932e355SEtienne Carriere * the interrupt chip reference in specific argument @chip. 305*f932e355SEtienne Carriere * @chip Interrupt controller 306*f932e355SEtienne Carriere * @h Interrupt handler to register 307*f932e355SEtienne Carriere */ 308*f932e355SEtienne Carriere static inline TEE_Result interrupt_add_handler_with_chip(struct itr_chip *chip, 309*f932e355SEtienne Carriere struct itr_handler *h) 310*f932e355SEtienne Carriere { 311*f932e355SEtienne Carriere h->chip = chip; 312*f932e355SEtienne Carriere return interrupt_add_handler(h); 313*f932e355SEtienne Carriere } 314*f932e355SEtienne Carriere 315*f932e355SEtienne Carriere /* 316*f932e355SEtienne Carriere * interrupt_remove_handler() - Remove a registered interrupt handler 317*f932e355SEtienne Carriere * @hdl Interrupt handler to remove 318*f932e355SEtienne Carriere * 319*f932e355SEtienne Carriere * This function is the counterpart of interrupt_add_handler(). 320*f932e355SEtienne Carriere * This function may panic on non-NULL invalid @hdl reference. 321*f932e355SEtienne Carriere */ 322*f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl); 323*f932e355SEtienne Carriere 324*f932e355SEtienne Carriere /* 325*f932e355SEtienne Carriere * interrupt_alloc_add_handler() - Allocate and register an interrupt handler 326*f932e355SEtienne Carriere * @chip Interrupt controller 327*f932e355SEtienne Carriere * @itr_num Interrupt number 328*f932e355SEtienne Carriere * @handler Interrupt handler to register 329*f932e355SEtienne Carriere * @flags Bitmask flag ITRF_* 330*f932e355SEtienne Carriere * @data Private data reference passed to @handler 331*f932e355SEtienne Carriere * @out_hdl NULL or output pointer to allocated struct itr_handler 332*f932e355SEtienne Carriere */ 333*f932e355SEtienne Carriere TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, size_t it_num, 334*f932e355SEtienne Carriere itr_handler_t handler, uint32_t flags, 335*f932e355SEtienne Carriere void *data, 336*f932e355SEtienne Carriere struct itr_handler **out_hdl); 337*f932e355SEtienne Carriere 338*f932e355SEtienne Carriere /* 339*f932e355SEtienne Carriere * interrupt_remove_free_handler() - Remove/free a registered interrupt handler 340*f932e355SEtienne Carriere * @hdl Interrupt handler to remove and free 341*f932e355SEtienne Carriere * 342*f932e355SEtienne Carriere * This function is the counterpart of interrupt_alloc_add_handler(). 343*f932e355SEtienne Carriere * This function may panic on non-NULL invalid @hdl reference. 344*f932e355SEtienne Carriere */ 345*f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl); 3467315b7b4SJens Wiklander #endif /*__KERNEL_INTERRUPT_H*/ 347