/* SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright(c) 2018 Intel Corporation. All rights reserved.
 *
 * Author: Janusz Jankowski <janusz.jankowski@linux.intel.com>
 */

#ifndef __SOF_DRIVERS_INTERRUPT_H__
#define __SOF_DRIVERS_INTERRUPT_H__

#include <platform/drivers/interrupt.h>

#if !defined(__ASSEMBLER__) && !defined(LINKER)
#include <arch/drivers/interrupt.h>
#include <sof/lib/cpu.h>
#include <sof/list.h>
#include <rtos/sof.h>
#include <rtos/spinlock.h>
#include <sof/trace/trace.h>
#include <user/trace.h>
#include <stdbool.h>
#include <stdint.h>

/**
 * \brief child IRQ descriptor for cascading IRQ controllers.
 */
struct irq_child {
	int enable_count[CONFIG_CORE_COUNT];	/**< IRQ enable counter */
	struct list_item list;			/**< head for IRQ descriptors,
						  * sharing this interrupt
						  */
};

/**
 * \brief interrupt client descriptor
 */
struct irq_desc {
	int irq;			/**< virtual IRQ number */
	void (*handler)(void *arg);	/**< interrupt handler function */
	void *handler_arg;		/**< interrupt handler argument */
	uint32_t cpu_mask;		/**< a mask of CPUs on which this
					  * interrupt is enabled
					  */
	struct list_item irq_list;	/**< to link to other irq_desc */
};

/**
 * \brief cascading IRQ controller operations.
 */
struct irq_cascade_ops {
	void (*mask)(struct irq_desc *desc, uint32_t irq,
		     unsigned int cpu);				/**< mask */
	void (*unmask)(struct irq_desc *desc, uint32_t irq,
		       unsigned int cpu);			/**< unmask */
};

/**
 * \brief cascading interrupt controller descriptor.
 */
struct irq_cascade_desc {
	const char *name;				/**< name of the
							  * controller
							  */
	int irq_base;					/**< first virtual IRQ
							  * number, assigned to
							  * this controller
							  */
	const struct irq_cascade_ops *ops;		/**< cascading interrupt
							  * controller driver
							  * operations
							  */
	struct irq_desc desc;				/**< the interrupt, that
							  * this controller is
							  * generating
							  */
	struct irq_cascade_desc *next;			/**< link to the global
							  * list of interrupt
							  * controllers
							  */
	bool global_mask;				/**< the controller
							  * cannot mask input
							  * interrupts per core
							  */
	struct k_spinlock lock;				/**< protect child
							  * lists, enable and
							  * child counters
							  */
	int enable_count[CONFIG_CORE_COUNT];		/**< enabled child
							  * interrupt counter
							  */
	unsigned int num_children[CONFIG_CORE_COUNT];	/**< number of children
							  */
	struct irq_child child[PLATFORM_IRQ_CHILDREN];	/**< array of child
							  * lists - one per
							  * multiplexed IRQ
							  */
};

/* A descriptor for cascading interrupt controller template */
struct irq_cascade_tmpl {
	const char *name;
	const struct irq_cascade_ops *ops;
	int irq;
	void (*handler)(void *arg);
	bool global_mask;
};

/**
 * \brief Cascading interrupt controller root.
 */
struct cascade_root {
	struct k_spinlock lock;		/**< locking mechanism */
	struct irq_cascade_desc *list;	/**< list of child cascade irqs */
	int last_irq;			/**< last registered cascade irq */
};

static inline struct cascade_root *cascade_root_get(void)
{
	return sof_get()->cascade_root;
}

/* For i.MX, while building SOF with Zephyr use the interrupt_*
 * functions from second level interrupt handling and IRQ_STEER.
 */
#if defined(__ZEPHYR__) && (defined(CONFIG_IMX) || defined(CONFIG_AMD))
int mux_interrupt_get_irq(unsigned int irq, const char *cascade);
int mux_interrupt_register(uint32_t irq, void(*handler)(void *arg), void *arg);
void mux_interrupt_unregister(uint32_t irq, const void *arg);
uint32_t mux_interrupt_enable(uint32_t irq, void *arg);
uint32_t mux_interrupt_disable(uint32_t irq, void *arg);
#endif

int interrupt_register(uint32_t irq, void(*handler)(void *arg), void *arg);
void interrupt_unregister(uint32_t irq, const void *arg);
uint32_t interrupt_enable(uint32_t irq, void *arg);
uint32_t interrupt_disable(uint32_t irq, void *arg);

/* Zephyr compat */
#if !defined(__ZEPHYR__)
#define arch_irq_lock() arch_interrupt_disable_mask(0xffffffff)
#endif

void platform_interrupt_init(void);

void platform_interrupt_set(uint32_t irq);
void platform_interrupt_clear(uint32_t irq, uint32_t mask);
uint32_t platform_interrupt_get_enabled(void);
void interrupt_mask(uint32_t irq, unsigned int cpu);
void interrupt_unmask(uint32_t irq, unsigned int cpu);

/*
 * On platforms, supporting cascading interrupts cascaded interrupt numbers
 * are greater than or equal to PLATFORM_IRQ_HW_NUM
 */
#define interrupt_is_dsp_direct(irq) (!PLATFORM_IRQ_CHILDREN || \
					irq < PLATFORM_IRQ_HW_NUM)

void interrupt_init(struct sof *sof);
int interrupt_cascade_register(const struct irq_cascade_tmpl *tmpl);
struct irq_cascade_desc *interrupt_get_parent(uint32_t irq);
int interrupt_get_irq(unsigned int irq, const char *cascade);

static inline void interrupt_set(int irq)
{
	platform_interrupt_set(irq);
}

static inline void interrupt_clear_mask(int irq, uint32_t mask)
{
	platform_interrupt_clear(irq, mask);
}

static inline void interrupt_clear(int irq)
{
	interrupt_clear_mask(irq, 1);
}

static inline uint32_t interrupt_global_disable(void)
{
	return arch_interrupt_global_disable();
}

static inline void interrupt_global_enable(uint32_t flags)
{
	arch_interrupt_global_enable(flags);
}

#if CONFIG_LIBRARY

/* temporary fix to remove build warning for testbench that will need shortly
 * realigned when Zephyr native APIs are used.
 */
static inline void __irq_local_disable(unsigned long flags) {}
static inline void __irq_local_enable(unsigned long flags) {}

/* disables all IRQ sources on current core - NO effect on library */
#define irq_local_disable(flags)		\
	do {					\
		flags = 0;			\
		__irq_local_disable(flags);	\
	} while (0)

/* re-enables IRQ sources on current core - NO effect on library*/
#define irq_local_enable(flags) \
	__irq_local_enable(flags)

#else
/* disables all IRQ sources on current core */
#define irq_local_disable(flags) \
	(flags = interrupt_global_disable())

/* re-enables IRQ sources on current core */
#define irq_local_enable(flags) \
	interrupt_global_enable(flags)
#endif
#endif
#endif /* __SOF_DRIVERS_INTERRUPT_H__ */
