root/drivers/gpu/drm/imagination/pvr_fw.h
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */

#ifndef PVR_FW_H
#define PVR_FW_H

#include "pvr_fw_info.h"
#include "pvr_fw_trace.h"
#include "pvr_gem.h"

#include <drm/drm_mm.h>

#include <linux/types.h>

/* Forward declarations from "pvr_device.h". */
struct pvr_device;
struct pvr_file;

/* Forward declaration from "pvr_vm.h". */
struct pvr_vm_context;

#define ROGUE_FWIF_FWCCB_NUMCMDS_LOG2 5

#define ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT 7

/**
 * struct pvr_fw_object - container for firmware memory allocations
 */
struct pvr_fw_object {
        /** @ref_count: FW object reference counter. */
        struct kref ref_count;

        /** @gem: GEM object backing the FW object. */
        struct pvr_gem_object *gem;

        /**
         * @fw_mm_node: Node representing mapping in FW address space. @pvr_obj->lock must
         *              be held when writing.
         */
        struct drm_mm_node fw_mm_node;

        /**
         * @fw_addr_offset: Virtual address offset of firmware mapping. Only
         *                  valid if @flags has %PVR_GEM_OBJECT_FLAGS_FW_MAPPED
         *                  set.
         */
        u32 fw_addr_offset;

        /**
         * @init: Initialisation callback. Will be called on object creation and FW hard reset.
         *        Object will have been zeroed before this is called.
         */
        void (*init)(void *cpu_ptr, void *priv);

        /** @init_priv: Private data for initialisation callback. */
        void *init_priv;

        /** @node: Node for firmware object list. */
        struct list_head node;
};

/**
 * struct pvr_fw_defs - FW processor function table and static definitions
 */
struct pvr_fw_defs {
        /**
         * @init:
         *
         * FW processor specific initialisation.
         * @pvr_dev: Target PowerVR device.
         *
         * This function must call pvr_fw_heap_calculate() to initialise the firmware heap for this
         * FW processor.
         *
         * This function is mandatory.
         *
         * Returns:
         *  * 0 on success, or
         *  * Any appropriate error on failure.
         */
        int (*init)(struct pvr_device *pvr_dev);

        /**
         * @fini:
         *
         * FW processor specific finalisation.
         * @pvr_dev: Target PowerVR device.
         *
         * This function is optional.
         */
        void (*fini)(struct pvr_device *pvr_dev);

        /**
         * @fw_process:
         *
         * Load and process firmware image.
         * @pvr_dev: Target PowerVR device.
         * @fw: Pointer to firmware image.
         * @fw_code_ptr: Pointer to firmware code section.
         * @fw_data_ptr: Pointer to firmware data section.
         * @fw_core_code_ptr: Pointer to firmware core code section. May be %NULL.
         * @fw_core_data_ptr: Pointer to firmware core data section. May be %NULL.
         * @core_code_alloc_size: Total allocation size of core code section.
         *
         * This function is mandatory.
         *
         * Returns:
         *  * 0 on success, or
         *  * Any appropriate error on failure.
         */
        int (*fw_process)(struct pvr_device *pvr_dev, const u8 *fw,
                          u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr,
                          u8 *fw_core_data_ptr, u32 core_code_alloc_size);

        /**
         * @vm_map:
         *
         * Map FW object into FW processor address space.
         * @pvr_dev: Target PowerVR device.
         * @fw_obj: FW object to map.
         *
         * This function is mandatory.
         *
         * Returns:
         *  * 0 on success, or
         *  * Any appropriate error on failure.
         */
        int (*vm_map)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);

        /**
         * @vm_unmap:
         *
         * Unmap FW object from FW processor address space.
         * @pvr_dev: Target PowerVR device.
         * @fw_obj: FW object to map.
         *
         * This function is mandatory.
         */
        void (*vm_unmap)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);

        /**
         * @get_fw_addr_with_offset:
         *
         * Called to get address of object in firmware address space, with offset.
         * @fw_obj: Pointer to object.
         * @offset: Desired offset from start of object.
         *
         * This function is mandatory.
         *
         * Returns:
         *  * Address in firmware address space.
         */
        u32 (*get_fw_addr_with_offset)(struct pvr_fw_object *fw_obj, u32 offset);

        /**
         * @wrapper_init:
         *
         * Called to initialise FW wrapper.
         * @pvr_dev: Target PowerVR device.
         *
         * This function is mandatory.
         *
         * Returns:
         *  * 0 on success.
         *  * Any appropriate error on failure.
         */
        int (*wrapper_init)(struct pvr_device *pvr_dev);

        /**
         * @irq_pending: Check interrupt status register for pending interrupts.
         *
         * @pvr_dev: Target PowerVR device.
         *
         * This function is mandatory.
         */
        bool (*irq_pending)(struct pvr_device *pvr_dev);

        /**
         * @irq_clear: Clear pending interrupts.
         *
         * @pvr_dev: Target PowerVR device.
         *
         * This function is mandatory.
         */
        void (*irq_clear)(struct pvr_device *pvr_dev);

        /**
         * @has_fixed_data_addr: Specify whether the firmware fixed data must be loaded at the
         *                       address given by the firmware layout table.
         *
         * This value is mandatory.
         */
        bool has_fixed_data_addr;
};

/**
 * struct pvr_fw_mem - FW memory allocations
 */
struct pvr_fw_mem {
        /** @code_obj: Object representing firmware code. */
        struct pvr_fw_object *code_obj;

        /** @data_obj: Object representing firmware data. */
        struct pvr_fw_object *data_obj;

        /**
         * @core_code_obj: Object representing firmware core code. May be
         *                 %NULL if firmware does not contain this section.
         */
        struct pvr_fw_object *core_code_obj;

        /**
         * @core_data_obj: Object representing firmware core data. May be
         *                 %NULL if firmware does not contain this section.
         */
        struct pvr_fw_object *core_data_obj;

        /** @code: Driver-side copy of firmware code. */
        u8 *code;

        /** @data: Driver-side copy of firmware data. */
        u8 *data;

        /**
         * @core_code: Driver-side copy of firmware core code. May be %NULL if firmware does not
         *             contain this section.
         */
        u8 *core_code;

        /**
         * @core_data: Driver-side copy of firmware core data. May be %NULL if firmware does not
         *             contain this section.
         */
        u8 *core_data;

        /** @code_alloc_size: Allocation size of firmware code section. */
        u32 code_alloc_size;

        /** @data_alloc_size: Allocation size of firmware data section. */
        u32 data_alloc_size;

        /** @core_code_alloc_size: Allocation size of firmware core code section. */
        u32 core_code_alloc_size;

        /** @core_data_alloc_size: Allocation size of firmware core data section. */
        u32 core_data_alloc_size;

        /**
         * @fwif_connection_ctl_obj: Object representing FWIF connection control
         *                           structure.
         */
        struct pvr_fw_object *fwif_connection_ctl_obj;

        /** @osinit_obj: Object representing FW OSINIT structure. */
        struct pvr_fw_object *osinit_obj;

        /** @sysinit_obj: Object representing FW SYSINIT structure. */
        struct pvr_fw_object *sysinit_obj;

        /** @osdata_obj: Object representing FW OSDATA structure. */
        struct pvr_fw_object *osdata_obj;

        /** @hwrinfobuf_obj: Object representing FW hwrinfobuf structure. */
        struct pvr_fw_object *hwrinfobuf_obj;

        /** @sysdata_obj: Object representing FW SYSDATA structure. */
        struct pvr_fw_object *sysdata_obj;

        /** @power_sync_obj: Object representing power sync state. */
        struct pvr_fw_object *power_sync_obj;

        /** @fault_page_obj: Object representing FW fault page. */
        struct pvr_fw_object *fault_page_obj;

        /** @gpu_util_fwcb_obj: Object representing FW GPU utilisation control structure. */
        struct pvr_fw_object *gpu_util_fwcb_obj;

        /** @runtime_cfg_obj: Object representing FW runtime config structure. */
        struct pvr_fw_object *runtime_cfg_obj;

        /** @mmucache_sync_obj: Object used as the sync parameter in an MMU cache operation. */
        struct pvr_fw_object *mmucache_sync_obj;
};

struct pvr_fw_device {
        /** @firmware: Handle to the firmware loaded into the device. */
        const struct firmware *firmware;

        /** @header: Pointer to firmware header. */
        const struct pvr_fw_info_header *header;

        /** @layout_entries: Pointer to firmware layout. */
        const struct pvr_fw_layout_entry *layout_entries;

        /** @mem: Structure containing objects representing firmware memory allocations. */
        struct pvr_fw_mem mem;

        /** @booted: %true if the firmware has been booted, %false otherwise. */
        bool booted;

        /**
         * @processor_type: FW processor type for this device. Must be one of
         *                  %PVR_FW_PROCESSOR_TYPE_*.
         */
        u16 processor_type;

        /** @funcs: Function table for the FW processor used by this device. */
        const struct pvr_fw_defs *defs;

        /** @processor_data: Pointer to data specific to FW processor. */
        union {
                /** @mips_data: Pointer to MIPS-specific data. */
                struct pvr_fw_mips_data *mips_data;
        } processor_data;

        /** @fw_heap_info: Firmware heap information. */
        struct {
                /** @gpu_addr: Base address of firmware heap in GPU address space. */
                u64 gpu_addr;

                /** @size: Size of main area of heap. */
                u32 size;

                /** @offset_mask: Mask for offsets within FW heap. */
                u32 offset_mask;

                /** @raw_size: Raw size of heap, including reserved areas. */
                u32 raw_size;

                /** @log2_size: Log2 of raw size of heap. */
                u32 log2_size;

                /** @config_offset: Offset of config area within heap. */
                u32 config_offset;

                /** @reserved_size: Size of reserved area in heap. */
                u32 reserved_size;
        } fw_heap_info;

        /** @fw_mm: Firmware address space allocator. */
        struct drm_mm fw_mm;

        /** @fw_mm_lock: Lock protecting access to &fw_mm. */
        spinlock_t fw_mm_lock;

        /** @fw_mm_base: Base address of address space managed by @fw_mm. */
        u64 fw_mm_base;

        /**
         * @fwif_connection_ctl: Pointer to CPU mapping of FWIF connection
         *                       control structure.
         */
        struct rogue_fwif_connection_ctl *fwif_connection_ctl;

        /** @fwif_sysinit: Pointer to CPU mapping of FW SYSINIT structure. */
        struct rogue_fwif_sysinit *fwif_sysinit;

        /** @fwif_sysdata: Pointer to CPU mapping of FW SYSDATA structure. */
        struct rogue_fwif_sysdata *fwif_sysdata;

        /** @fwif_osinit: Pointer to CPU mapping of FW OSINIT structure. */
        struct rogue_fwif_osinit *fwif_osinit;

        /** @fwif_osdata: Pointer to CPU mapping of FW OSDATA structure. */
        struct rogue_fwif_osdata *fwif_osdata;

        /** @power_sync: Pointer to CPU mapping of power sync state. */
        u32 *power_sync;

        /** @hwrinfobuf: Pointer to CPU mapping of FW HWR info buffer. */
        struct rogue_fwif_hwrinfobuf *hwrinfobuf;

        /** @fw_trace: Device firmware trace buffer state. */
        struct pvr_fw_trace fw_trace;

        /** @fw_objs: Structure tracking FW objects. */
        struct {
                /** @list: Head of FW object list. */
                struct list_head list;

                /** @lock: Lock protecting access to FW object list. */
                struct mutex lock;
        } fw_objs;
};

enum pvr_fw_processor_type {
        PVR_FW_PROCESSOR_TYPE_META = 0,
        PVR_FW_PROCESSOR_TYPE_MIPS,
        PVR_FW_PROCESSOR_TYPE_RISCV,
        PVR_FW_PROCESSOR_TYPE_COUNT,
};

extern const struct pvr_fw_defs pvr_fw_defs_meta;
extern const struct pvr_fw_defs pvr_fw_defs_mips;
extern const struct pvr_fw_defs pvr_fw_defs_riscv;

int pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev);
int pvr_fw_init(struct pvr_device *pvr_dev);
void pvr_fw_fini(struct pvr_device *pvr_dev);

int pvr_wait_for_fw_boot(struct pvr_device *pvr_dev);

int
pvr_fw_hard_reset(struct pvr_device *pvr_dev);

void pvr_fw_mts_schedule(struct pvr_device *pvr_dev, u32 val);

void
pvr_fw_heap_info_init(struct pvr_device *pvr_dev, u32 log2_size, u32 reserved_size);

const struct pvr_fw_layout_entry *
pvr_fw_find_layout_entry(struct pvr_device *pvr_dev, enum pvr_fw_section_id id);
int
pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw_code_ptr,
                        void *fw_data_ptr, void *fw_core_code_ptr, void *fw_core_data_ptr,
                        void **host_addr_out);

int
pvr_fw_structure_cleanup(struct pvr_device *pvr_dev, u32 type, struct pvr_fw_object *fw_obj,
                         u32 offset);

int pvr_fw_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags,
                         void (*init)(void *cpu_ptr, void *priv), void *init_priv,
                         struct pvr_fw_object **pvr_obj_out);

void *pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64 flags,
                                   void (*init)(void *cpu_ptr, void *priv),
                                   void *init_priv, struct pvr_fw_object **pvr_obj_out);

void *
pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, u32 dev_offset, size_t size,
                                    u64 flags, void (*init)(void *cpu_ptr, void *priv),
                                    void *init_priv, struct pvr_fw_object **pvr_obj_out);

static __always_inline void *
pvr_fw_object_vmap(struct pvr_fw_object *fw_obj)
{
        return pvr_gem_object_vmap(fw_obj->gem);
}

static __always_inline void
pvr_fw_object_vunmap(struct pvr_fw_object *fw_obj)
{
        pvr_gem_object_vunmap(fw_obj->gem);
}

void pvr_fw_object_destroy(struct pvr_fw_object *fw_obj);

static __always_inline void
pvr_fw_object_unmap_and_destroy(struct pvr_fw_object *fw_obj)
{
        pvr_fw_object_vunmap(fw_obj);
        pvr_fw_object_destroy(fw_obj);
}

/**
 * pvr_fw_object_get_dma_addr() - Get DMA address for given offset in firmware
 * object.
 * @fw_obj: Pointer to object to lookup address in.
 * @offset: Offset within object to lookup address at.
 * @dma_addr_out: Pointer to location to store DMA address.
 *
 * Returns:
 *  * 0 on success, or
 *  * -%EINVAL if object is not currently backed, or if @offset is out of valid
 *    range for this object.
 */
static __always_inline int
pvr_fw_object_get_dma_addr(struct pvr_fw_object *fw_obj, u32 offset, dma_addr_t *dma_addr_out)
{
        return pvr_gem_get_dma_addr(fw_obj->gem, offset, dma_addr_out);
}

void pvr_fw_object_get_fw_addr_offset(struct pvr_fw_object *fw_obj, u32 offset, u32 *fw_addr_out);

static __always_inline void
pvr_fw_object_get_fw_addr(struct pvr_fw_object *fw_obj, u32 *fw_addr_out)
{
        pvr_fw_object_get_fw_addr_offset(fw_obj, 0, fw_addr_out);
}

u64
pvr_fw_obj_get_gpu_addr(struct pvr_fw_object *fw_obj);

static __always_inline size_t
pvr_fw_obj_get_object_size(struct pvr_fw_object *fw_obj)
{
        return pvr_gem_object_size(fw_obj->gem);
}

/* Util functions defined in pvr_fw_util.c. These are intended for use in pvr_fw_<arch>.c files. */
int
pvr_fw_process_elf_command_stream(struct pvr_device *pvr_dev, const u8 *fw, u8 *fw_code_ptr,
                                  u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr);

#endif /* PVR_FW_H */