/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-08-24     GuEe-GUI     first version
 */

#ifndef __PIC_H__
#define __PIC_H__

#include <rthw.h>

#include <bitmap.h>
#include <drivers/ofw.h>
#include <drivers/core/dm.h>

struct rt_pci_msi_desc;
struct rt_pci_msi_msg;

struct rt_pic_ops;
struct rt_pic_irq;

struct rt_pic
{
    /*
     * Other IC is not implemented with PIC but rt_device/object, we need to
     * identify with this object:
     *
     *  struct rt_ic_XYZ_device
     *  {
     *      struct rt_device parent;
     *      struct rt_pic pic;
     *      ...
     *  };
     */
    struct rt_object parent;

    rt_list_t list;

    struct rt_pic_ops *ops;

    void *priv_data;
    void *user_data;

    int irq_start;
    rt_size_t irq_nr;
    struct rt_pic_irq *pirqs;
};

struct rt_pic_ops
{
    const char  *name;

    rt_err_t    (*irq_init)(struct rt_pic *pic);
    rt_err_t    (*irq_finit)(struct rt_pic *pic);

    void        (*irq_enable)(struct rt_pic_irq *pirq);
    void        (*irq_disable)(struct rt_pic_irq *pirq);
    void        (*irq_ack)(struct rt_pic_irq *pirq);
    void        (*irq_mask)(struct rt_pic_irq *pirq);
    void        (*irq_unmask)(struct rt_pic_irq *pirq);
    void        (*irq_eoi)(struct rt_pic_irq *pirq);

    rt_err_t    (*irq_set_priority)(struct rt_pic_irq *pirq, rt_uint32_t priority);
    rt_err_t    (*irq_set_affinity)(struct rt_pic_irq *pirq, rt_bitmap_t *affinity);
    rt_err_t    (*irq_set_triger_mode)(struct rt_pic_irq *pirq, rt_uint32_t mode);

    void        (*irq_send_ipi)(struct rt_pic_irq *pirq, rt_bitmap_t *cpumask);

    void        (*irq_compose_msi_msg)(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg);
    void        (*irq_write_msi_msg)(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg);
    int         (*irq_alloc_msi)(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc);
    void        (*irq_free_msi)(struct rt_pic *pic, int irq);

    int         (*irq_map)(struct rt_pic *pic, int hwirq, rt_uint32_t mode);
    rt_err_t    (*irq_parse)(struct rt_pic *pic, struct rt_ofw_cell_args *args, struct rt_pic_irq *out_pirq);

#define RT_PIC_F_IRQ_ROUTING    RT_BIT(0)   /* Routing ISR when cascade */
    rt_ubase_t  flags;
};

struct rt_pic_isr
{
    rt_list_t list;

#define RT_IRQ_F_NONE 0
    int flags;
    struct rt_irq_desc action;
};

struct rt_pic_irq
{
    int irq;
    int hwirq;

#define RT_IRQ_MODE_NONE            0
#define RT_IRQ_MODE_EDGE_RISING     1
#define RT_IRQ_MODE_EDGE_FALLING    2
#define RT_IRQ_MODE_EDGE_BOTH       (RT_IRQ_MODE_EDGE_FALLING | RT_IRQ_MODE_EDGE_RISING)
#define RT_IRQ_MODE_LEVEL_HIGH      4
#define RT_IRQ_MODE_LEVEL_LOW       8
#define RT_IRQ_MODE_LEVEL_MASK      (RT_IRQ_MODE_LEVEL_LOW | RT_IRQ_MODE_LEVEL_HIGH)
#define RT_IRQ_MODE_MASK            0xf
    rt_uint32_t mode;

    rt_uint32_t priority;
    RT_BITMAP_DECLARE(affinity, RT_CPUS_NR);

    rt_list_t list;
    rt_list_t children_nodes;

    struct rt_pci_msi_desc *msi_desc;

    struct rt_pic_isr isr;

    struct rt_spinlock rw_lock;

    struct rt_pic *pic;
    struct rt_pic_irq *parent;
};

void rt_pic_default_name(struct rt_pic *pic);
struct rt_pic *rt_pic_dynamic_cast(void *ptr);

rt_err_t rt_pic_linear_irq(struct rt_pic *pic, rt_size_t irq_nr);

int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq);
int rt_pic_config_irq(struct rt_pic *pic, int irq_index, int hwirq);

rt_inline struct rt_pic_irq *rt_pic_find_irq(struct rt_pic *pic, int irq_index)
{
    /* This is a quickly interface */
    RT_ASSERT(pic != RT_NULL);
    RT_ASSERT(pic->pirqs != RT_NULL);
    RT_ASSERT(irq_index < pic->irq_nr);

    return &pic->pirqs[irq_index];
}

struct rt_pic_irq *rt_pic_find_ipi(struct rt_pic *pic, int ipi_index);
struct rt_pic_irq *rt_pic_find_pirq(struct rt_pic *pic, int irq);

rt_err_t rt_pic_cascade(struct rt_pic_irq *pirq, int parent_irq);
rt_err_t rt_pic_uncascade(struct rt_pic_irq *pirq);

rt_err_t rt_pic_attach_irq(int irq, rt_isr_handler_t handler, void *uid, const char *name, int flags);
rt_err_t rt_pic_detach_irq(int irq, void *uid);

rt_err_t rt_pic_add_traps(rt_bool_t (*handler)(void *), void *data);
rt_err_t rt_pic_do_traps(void);
rt_err_t rt_pic_handle_isr(struct rt_pic_irq *pirq);

/* User-implemented extensions */
rt_err_t rt_pic_user_extends(struct rt_pic *pic);

rt_err_t rt_pic_irq_init(void);
rt_err_t rt_pic_irq_finit(void);
void rt_pic_irq_enable(int irq);
void rt_pic_irq_disable(int irq);
void rt_pic_irq_ack(int irq);
void rt_pic_irq_mask(int irq);
void rt_pic_irq_unmask(int irq);
void rt_pic_irq_eoi(int irq);
rt_err_t rt_pic_irq_set_priority(int irq, rt_uint32_t priority);
rt_uint32_t rt_pic_irq_get_priority(int irq);
rt_err_t rt_pic_irq_set_affinity(int irq, rt_bitmap_t *affinity);
rt_err_t rt_pic_irq_get_affinity(int irq, rt_bitmap_t *out_affinity);
rt_err_t rt_pic_irq_set_triger_mode(int irq, rt_uint32_t mode);
rt_uint32_t rt_pic_irq_get_triger_mode(int irq);
void rt_pic_irq_send_ipi(int irq, rt_bitmap_t *cpumask);

void rt_pic_irq_parent_enable(struct rt_pic_irq *pirq);
void rt_pic_irq_parent_disable(struct rt_pic_irq *pirq);
void rt_pic_irq_parent_ack(struct rt_pic_irq *pirq);
void rt_pic_irq_parent_mask(struct rt_pic_irq *pirq);
void rt_pic_irq_parent_unmask(struct rt_pic_irq *pirq);
void rt_pic_irq_parent_eoi(struct rt_pic_irq *pirq);
rt_err_t rt_pic_irq_parent_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority);
rt_err_t rt_pic_irq_parent_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity);
rt_err_t rt_pic_irq_parent_set_triger_mode(struct rt_pic_irq *pirq, rt_uint32_t mode);

#define RT_PIC_OFW_DECLARE(name, ids, handler)   RT_OFW_STUB_EXPORT(name, ids, pic, handler)

rt_err_t rt_pic_init(void);

#endif /* __PIC_H__ */
