mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
drm/imagination: Implement firmware infrastructure and META FW support
The infrastructure includes parsing of the firmware image, initialising
FW-side structures, handling the kernel and firmware command
ringbuffers and starting & stopping the firmware processor.
This patch also adds the necessary support code for the META firmware
processor.
Changes since v8:
- Fix documentation for pvr_fwccb_process()
- Corrected license identifiers
Changes since v6:
- Add a minimum retry count to pvr_kccb_reserve_slot_sync()
Changes since v5:
- Add workaround for BRN 71242
- Attempt to recover GPU on MMU flush command failure
Changes since v4:
- Remove use of drm_gem_shmem_get_pages()
- Remove interrupt resource name
Changes since v3:
- Hard reset FW processor on watchdog timeout
- Switch to threaded IRQ
- Rework FW object creation/initialisation to aid hard reset
- Added MODULE_FIRMWARE()
- Use drm_dev_{enter,exit}
Signed-off-by: Sarah Walker <sarah.walker@imgtec.com>
Signed-off-by: Donald Robson <donald.robson@imgtec.com>
Link: https://lore.kernel.org/r/bb52a8dc84f296b37dc6668dfe8fbaf2ba551139.1700668843.git.donald.robson@imgtec.com
Signed-off-by: Maxime Ripard <mripard@kernel.org>
This commit is contained in:
@@ -4,10 +4,14 @@
|
||||
subdir-ccflags-y := -I$(srctree)/$(src)
|
||||
|
||||
powervr-y := \
|
||||
pvr_ccb.o \
|
||||
pvr_device.o \
|
||||
pvr_device_info.o \
|
||||
pvr_drv.o \
|
||||
pvr_fw.o \
|
||||
pvr_fw_meta.o \
|
||||
pvr_fw_startstop.o \
|
||||
pvr_fw_trace.o \
|
||||
pvr_gem.o \
|
||||
pvr_mmu.o \
|
||||
pvr_power.o \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#ifndef PVR_CCB_H
|
||||
#define PVR_CCB_H
|
||||
|
||||
#include "pvr_rogue_fwif.h"
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Forward declaration from pvr_device.h. */
|
||||
struct pvr_device;
|
||||
|
||||
/* Forward declaration from pvr_gem.h. */
|
||||
struct pvr_fw_object;
|
||||
|
||||
struct pvr_ccb {
|
||||
/** @ctrl_obj: FW object representing CCB control structure. */
|
||||
struct pvr_fw_object *ctrl_obj;
|
||||
/** @ccb_obj: FW object representing CCB. */
|
||||
struct pvr_fw_object *ccb_obj;
|
||||
|
||||
/** @ctrl_fw_addr: FW virtual address of CCB control structure. */
|
||||
u32 ctrl_fw_addr;
|
||||
/** @ccb_fw_addr: FW virtual address of CCB. */
|
||||
u32 ccb_fw_addr;
|
||||
|
||||
/** @num_cmds: Number of commands in this CCB. */
|
||||
u32 num_cmds;
|
||||
|
||||
/** @cmd_size: Size of each command in this CCB, in bytes. */
|
||||
u32 cmd_size;
|
||||
|
||||
/** @lock: Mutex protecting @ctrl and @ccb. */
|
||||
struct mutex lock;
|
||||
/**
|
||||
* @ctrl: Kernel mapping of CCB control structure. @lock must be held
|
||||
* when accessing.
|
||||
*/
|
||||
struct rogue_fwif_ccb_ctl *ctrl;
|
||||
/** @ccb: Kernel mapping of CCB. @lock must be held when accessing. */
|
||||
void *ccb;
|
||||
};
|
||||
|
||||
int pvr_kccb_init(struct pvr_device *pvr_dev);
|
||||
void pvr_kccb_fini(struct pvr_device *pvr_dev);
|
||||
int pvr_fwccb_init(struct pvr_device *pvr_dev);
|
||||
void pvr_ccb_fini(struct pvr_ccb *ccb);
|
||||
|
||||
void pvr_fwccb_process(struct pvr_device *pvr_dev);
|
||||
|
||||
struct dma_fence *pvr_kccb_fence_alloc(void);
|
||||
void pvr_kccb_fence_put(struct dma_fence *fence);
|
||||
struct dma_fence *
|
||||
pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f);
|
||||
void pvr_kccb_release_slot(struct pvr_device *pvr_dev);
|
||||
int pvr_kccb_send_cmd(struct pvr_device *pvr_dev,
|
||||
struct rogue_fwif_kccb_cmd *cmd, u32 *kccb_slot);
|
||||
int pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev,
|
||||
struct rogue_fwif_kccb_cmd *cmd,
|
||||
u32 *kccb_slot);
|
||||
void pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev,
|
||||
struct rogue_fwif_kccb_cmd *cmd,
|
||||
u32 *kccb_slot);
|
||||
int pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr, u32 timeout,
|
||||
u32 *rtn_out);
|
||||
bool pvr_kccb_is_idle(struct pvr_device *pvr_dev);
|
||||
void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev);
|
||||
|
||||
#endif /* PVR_CCB_H */
|
||||
@@ -114,6 +114,87 @@ static int pvr_device_clk_init(struct pvr_device *pvr_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data)
|
||||
{
|
||||
struct pvr_device *pvr_dev = data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
/* We are in the threaded handler, we can keep dequeuing events until we
|
||||
* don't see any. This should allow us to reduce the number of interrupts
|
||||
* when the GPU is receiving a massive amount of short jobs.
|
||||
*/
|
||||
while (pvr_fw_irq_pending(pvr_dev)) {
|
||||
pvr_fw_irq_clear(pvr_dev);
|
||||
|
||||
if (pvr_dev->fw_dev.booted) {
|
||||
pvr_fwccb_process(pvr_dev);
|
||||
pvr_kccb_wake_up_waiters(pvr_dev);
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(from_pvr_device(pvr_dev)->dev);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Unmask FW irqs before returning, so new interrupts can be received. */
|
||||
pvr_fw_irq_enable(pvr_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t pvr_device_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct pvr_device *pvr_dev = data;
|
||||
|
||||
if (!pvr_fw_irq_pending(pvr_dev))
|
||||
return IRQ_NONE; /* Spurious IRQ - ignore. */
|
||||
|
||||
/* Mask the FW interrupts before waking up the thread. Will be unmasked
|
||||
* when the thread handler is done processing events.
|
||||
*/
|
||||
pvr_fw_irq_disable(pvr_dev);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_device_irq_init() - Initialise IRQ required by a PowerVR device
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
*
|
||||
* Returns:
|
||||
* * 0 on success,
|
||||
* * Any error returned by platform_get_irq_byname(), or
|
||||
* * Any error returned by request_irq().
|
||||
*/
|
||||
static int
|
||||
pvr_device_irq_init(struct pvr_device *pvr_dev)
|
||||
{
|
||||
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
|
||||
struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
|
||||
|
||||
init_waitqueue_head(&pvr_dev->kccb.rtn_q);
|
||||
|
||||
pvr_dev->irq = platform_get_irq(plat_dev, 0);
|
||||
if (pvr_dev->irq < 0)
|
||||
return pvr_dev->irq;
|
||||
|
||||
/* Clear any pending events before requesting the IRQ line. */
|
||||
pvr_fw_irq_clear(pvr_dev);
|
||||
pvr_fw_irq_enable(pvr_dev);
|
||||
|
||||
return request_threaded_irq(pvr_dev->irq, pvr_device_irq_handler,
|
||||
pvr_device_irq_thread_handler,
|
||||
IRQF_SHARED, "gpu", pvr_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_device_irq_fini() - Deinitialise IRQ required by a PowerVR device
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
*/
|
||||
static void
|
||||
pvr_device_irq_fini(struct pvr_device *pvr_dev)
|
||||
{
|
||||
free_irq(pvr_dev->irq, pvr_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_build_firmware_filename() - Construct a PowerVR firmware filename
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
@@ -324,7 +405,19 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev)
|
||||
return PTR_ERR(pvr_dev->kernel_vm_ctx);
|
||||
}
|
||||
|
||||
err = pvr_fw_init(pvr_dev);
|
||||
if (err)
|
||||
goto err_vm_ctx_put;
|
||||
|
||||
return 0;
|
||||
|
||||
err_vm_ctx_put:
|
||||
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
|
||||
pvr_vm_context_put(pvr_dev->kernel_vm_ctx);
|
||||
pvr_dev->kernel_vm_ctx = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,6 +427,8 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev)
|
||||
static void
|
||||
pvr_device_gpu_fini(struct pvr_device *pvr_dev)
|
||||
{
|
||||
pvr_fw_fini(pvr_dev);
|
||||
|
||||
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
|
||||
WARN_ON(!pvr_vm_context_put(pvr_dev->kernel_vm_ctx));
|
||||
pvr_dev->kernel_vm_ctx = NULL;
|
||||
@@ -386,10 +481,17 @@ pvr_device_init(struct pvr_device *pvr_dev)
|
||||
if (err)
|
||||
goto err_pm_runtime_put;
|
||||
|
||||
err = pvr_device_irq_init(pvr_dev);
|
||||
if (err)
|
||||
goto err_device_gpu_fini;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_device_gpu_fini:
|
||||
pvr_device_gpu_fini(pvr_dev);
|
||||
|
||||
err_pm_runtime_put:
|
||||
pm_runtime_put_sync_suspend(dev);
|
||||
|
||||
@@ -407,6 +509,7 @@ pvr_device_fini(struct pvr_device *pvr_dev)
|
||||
* Deinitialization stages are performed in reverse order compared to
|
||||
* the initialization stages in pvr_device_init().
|
||||
*/
|
||||
pvr_device_irq_fini(pvr_dev);
|
||||
pvr_device_gpu_fini(pvr_dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#ifndef PVR_DEVICE_H
|
||||
#define PVR_DEVICE_H
|
||||
|
||||
#include "pvr_ccb.h"
|
||||
#include "pvr_device_info.h"
|
||||
#include "pvr_fw.h"
|
||||
|
||||
@@ -123,6 +124,12 @@ struct pvr_device {
|
||||
*/
|
||||
struct clk *mem_clk;
|
||||
|
||||
/** @irq: IRQ number. */
|
||||
int irq;
|
||||
|
||||
/** @fwccb: Firmware CCB. */
|
||||
struct pvr_ccb fwccb;
|
||||
|
||||
/**
|
||||
* @kernel_vm_ctx: Virtual memory context used for kernel mappings.
|
||||
*
|
||||
@@ -153,6 +160,49 @@ struct pvr_device {
|
||||
u32 kccb_stall_count;
|
||||
} watchdog;
|
||||
|
||||
struct {
|
||||
/** @ccb: Kernel CCB. */
|
||||
struct pvr_ccb ccb;
|
||||
|
||||
/** @rtn_q: Waitqueue for KCCB command return waiters. */
|
||||
wait_queue_head_t rtn_q;
|
||||
|
||||
/** @rtn_obj: Object representing KCCB return slots. */
|
||||
struct pvr_fw_object *rtn_obj;
|
||||
|
||||
/**
|
||||
* @rtn: Pointer to CPU mapping of KCCB return slots. Must be accessed by
|
||||
* READ_ONCE()/WRITE_ONCE().
|
||||
*/
|
||||
u32 *rtn;
|
||||
|
||||
/** @slot_count: Total number of KCCB slots available. */
|
||||
u32 slot_count;
|
||||
|
||||
/** @reserved_count: Number of KCCB slots reserved for future use. */
|
||||
u32 reserved_count;
|
||||
|
||||
/**
|
||||
* @waiters: List of KCCB slot waiters.
|
||||
*/
|
||||
struct list_head waiters;
|
||||
|
||||
/** @fence_ctx: KCCB fence context. */
|
||||
struct {
|
||||
/** @id: KCCB fence context ID allocated with dma_fence_context_alloc(). */
|
||||
u64 id;
|
||||
|
||||
/** @seqno: Sequence number incremented each time a fence is created. */
|
||||
atomic_t seqno;
|
||||
|
||||
/**
|
||||
* @lock: Lock used to synchronize access to fences allocated by this
|
||||
* context.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
} fence_ctx;
|
||||
} kccb;
|
||||
|
||||
/**
|
||||
* @lost: %true if the device has been lost.
|
||||
*
|
||||
@@ -161,6 +211,16 @@ struct pvr_device {
|
||||
*/
|
||||
bool lost;
|
||||
|
||||
/**
|
||||
* @reset_sem: Reset semaphore.
|
||||
*
|
||||
* GPU reset code will lock this for writing. Any code that submits commands to the firmware
|
||||
* that isn't in an IRQ handler or on the scheduler workqueue must lock this for reading.
|
||||
* Once this has been successfully locked, &pvr_dev->lost _must_ be checked, and -%EIO must
|
||||
* be returned if it is set.
|
||||
*/
|
||||
struct rw_semaphore reset_sem;
|
||||
|
||||
/** @sched_wq: Workqueue for schedulers. */
|
||||
struct workqueue_struct *sched_wq;
|
||||
};
|
||||
|
||||
@@ -1331,3 +1331,4 @@ MODULE_AUTHOR("Imagination Technologies Ltd.");
|
||||
MODULE_DESCRIPTION(PVR_DRIVER_DESC);
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
MODULE_FIRMWARE("powervr/rogue_33.15.11.3_v1.fw");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,10 @@
|
||||
#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>
|
||||
|
||||
@@ -12,6 +16,289 @@
|
||||
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);
|
||||
|
||||
/**
|
||||
* @has_fixed_data_addr:
|
||||
*
|
||||
* Called to check if firmware fixed data must be loaded at the address given by the
|
||||
* firmware layout table.
|
||||
*
|
||||
* This function is mandatory.
|
||||
*
|
||||
* Returns:
|
||||
* * %true if firmware fixed data must be loaded at the address given by the firmware
|
||||
* layout table.
|
||||
* * %false otherwise.
|
||||
*/
|
||||
bool (*has_fixed_data_addr)(void);
|
||||
|
||||
/**
|
||||
* @irq: FW Interrupt information.
|
||||
*
|
||||
* Those are processor dependent, and should be initialized by the
|
||||
* processor backend in pvr_fw_funcs::init().
|
||||
*/
|
||||
struct {
|
||||
/** @enable_reg: FW interrupt enable register. */
|
||||
u32 enable_reg;
|
||||
|
||||
/** @status_reg: FW interrupt status register. */
|
||||
u32 status_reg;
|
||||
|
||||
/**
|
||||
* @clear_reg: FW interrupt clear register.
|
||||
*
|
||||
* If @status_reg == @clear_reg, we clear by write a bit to zero,
|
||||
* otherwise we clear by writing a bit to one.
|
||||
*/
|
||||
u32 clear_reg;
|
||||
|
||||
/** @event_mask: Bitmask of events to listen for. */
|
||||
u32 event_mask;
|
||||
|
||||
/** @clear_mask: Value to write to the clear_reg in order to clear FW IRQs. */
|
||||
u32 clear_mask;
|
||||
} irq;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -22,13 +309,200 @@ struct pvr_fw_device {
|
||||
/** @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;
|
||||
};
|
||||
|
||||
#define pvr_fw_irq_read_reg(pvr_dev, name) \
|
||||
pvr_cr_read32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg)
|
||||
|
||||
#define pvr_fw_irq_write_reg(pvr_dev, name, value) \
|
||||
pvr_cr_write32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg, value)
|
||||
|
||||
#define pvr_fw_irq_pending(pvr_dev) \
|
||||
(pvr_fw_irq_read_reg(pvr_dev, status) & (pvr_dev)->fw_dev.defs->irq.event_mask)
|
||||
|
||||
#define pvr_fw_irq_clear(pvr_dev) \
|
||||
pvr_fw_irq_write_reg(pvr_dev, clear, (pvr_dev)->fw_dev.defs->irq.clear_mask)
|
||||
|
||||
#define pvr_fw_irq_enable(pvr_dev) \
|
||||
pvr_fw_irq_write_reg(pvr_dev, enable, (pvr_dev)->fw_dev.defs->irq.event_mask)
|
||||
|
||||
#define pvr_fw_irq_disable(pvr_dev) \
|
||||
pvr_fw_irq_write_reg(pvr_dev, enable, 0)
|
||||
|
||||
extern const struct pvr_fw_defs pvr_fw_defs_meta;
|
||||
extern const struct pvr_fw_defs pvr_fw_defs_mips;
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
#endif /* PVR_FW_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#ifndef PVR_FW_META_H
|
||||
#define PVR_FW_META_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Forward declaration from pvr_device.h */
|
||||
struct pvr_device;
|
||||
|
||||
int pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out);
|
||||
|
||||
#endif /* PVR_FW_META_H */
|
||||
@@ -0,0 +1,306 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#include "pvr_device.h"
|
||||
#include "pvr_fw.h"
|
||||
#include "pvr_fw_meta.h"
|
||||
#include "pvr_fw_startstop.h"
|
||||
#include "pvr_rogue_cr_defs.h"
|
||||
#include "pvr_rogue_meta.h"
|
||||
#include "pvr_vm.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define POLL_TIMEOUT_USEC 1000000
|
||||
|
||||
static void
|
||||
rogue_axi_ace_list_init(struct pvr_device *pvr_dev)
|
||||
{
|
||||
/* Setup AXI-ACE config. Set everything to outer cache. */
|
||||
u64 reg_val =
|
||||
(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_SHIFT) |
|
||||
(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_SHIFT) |
|
||||
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_SHIFT);
|
||||
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_AXI_ACE_LITE_CONFIGURATION, reg_val);
|
||||
}
|
||||
|
||||
static void
|
||||
rogue_bif_init(struct pvr_device *pvr_dev)
|
||||
{
|
||||
dma_addr_t pc_dma_addr;
|
||||
u64 pc_addr;
|
||||
|
||||
/* Acquire the address of the Kernel Page Catalogue. */
|
||||
pc_dma_addr = pvr_vm_get_page_table_root_addr(pvr_dev->kernel_vm_ctx);
|
||||
|
||||
/* Write the kernel catalogue base. */
|
||||
pc_addr = ((((u64)pc_dma_addr >> ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSHIFT)
|
||||
<< ROGUE_CR_BIF_CAT_BASE0_ADDR_SHIFT) &
|
||||
~ROGUE_CR_BIF_CAT_BASE0_ADDR_CLRMSK);
|
||||
|
||||
pvr_cr_write64(pvr_dev, BIF_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV),
|
||||
pc_addr);
|
||||
}
|
||||
|
||||
static int
|
||||
rogue_slc_init(struct pvr_device *pvr_dev)
|
||||
{
|
||||
u16 slc_cache_line_size_bits;
|
||||
u32 reg_val;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* SLC Misc control.
|
||||
*
|
||||
* Note: This is a 64bit register and we set only the lower 32bits
|
||||
* leaving the top 32bits (ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS)
|
||||
* unchanged from the HW default.
|
||||
*/
|
||||
reg_val = (pvr_cr_read32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC) &
|
||||
ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_EN) |
|
||||
ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH1;
|
||||
|
||||
err = PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits, &slc_cache_line_size_bits);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Bypass burst combiner if SLC line size is smaller than 1024 bits. */
|
||||
if (slc_cache_line_size_bits < 1024)
|
||||
reg_val |= ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_EN;
|
||||
|
||||
if (PVR_HAS_QUIRK(pvr_dev, 71242) && !PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support))
|
||||
reg_val |= ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_EN;
|
||||
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC, reg_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_fw_start() - Start FW processor and boot firmware
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
*
|
||||
* Returns:
|
||||
* * 0 on success, or
|
||||
* * Any error returned by rogue_slc_init().
|
||||
*/
|
||||
int
|
||||
pvr_fw_start(struct pvr_device *pvr_dev)
|
||||
{
|
||||
bool has_reset2 = PVR_HAS_FEATURE(pvr_dev, xe_tpu2);
|
||||
u64 soft_reset_mask;
|
||||
int err;
|
||||
|
||||
if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
|
||||
soft_reset_mask = ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL;
|
||||
else
|
||||
soft_reset_mask = ROGUE_CR_SOFT_RESET_MASKFULL;
|
||||
|
||||
if (PVR_HAS_FEATURE(pvr_dev, sys_bus_secure_reset)) {
|
||||
/*
|
||||
* Disable the default sys_bus_secure protection to perform
|
||||
* minimal setup.
|
||||
*/
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE, 0);
|
||||
(void)pvr_cr_read32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE); /* Fence write */
|
||||
}
|
||||
|
||||
/* Set Rogue in soft-reset. */
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
|
||||
if (has_reset2)
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, ROGUE_CR_SOFT_RESET2_MASKFULL);
|
||||
|
||||
/* Read soft-reset to fence previous write in order to clear the SOCIF pipeline. */
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
|
||||
if (has_reset2)
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
|
||||
|
||||
/* Take Rascal and Dust out of reset. */
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
|
||||
soft_reset_mask ^ ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN);
|
||||
if (has_reset2)
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
|
||||
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
|
||||
if (has_reset2)
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
|
||||
|
||||
/* Take everything out of reset but the FW processor. */
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_GARTEN_EN);
|
||||
if (has_reset2)
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
|
||||
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
|
||||
if (has_reset2)
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
|
||||
|
||||
err = rogue_slc_init(pvr_dev);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
|
||||
/* Initialise Firmware wrapper. */
|
||||
pvr_dev->fw_dev.defs->wrapper_init(pvr_dev);
|
||||
|
||||
/* We must init the AXI-ACE interface before first BIF transaction. */
|
||||
rogue_axi_ace_list_init(pvr_dev);
|
||||
|
||||
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
|
||||
/* Initialise BIF. */
|
||||
rogue_bif_init(pvr_dev);
|
||||
}
|
||||
|
||||
/* Need to wait for at least 16 cycles before taking the FW processor out of reset ... */
|
||||
udelay(3);
|
||||
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, 0x0);
|
||||
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
|
||||
|
||||
/* ... and afterwards. */
|
||||
udelay(3);
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset:
|
||||
/* Put everything back into soft-reset. */
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_fw_stop() - Stop FW processor
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
*
|
||||
* Returns:
|
||||
* * 0 on success, or
|
||||
* * Any error returned by pvr_cr_poll_reg32().
|
||||
*/
|
||||
int
|
||||
pvr_fw_stop(struct pvr_device *pvr_dev)
|
||||
{
|
||||
const u32 sidekick_idle_mask = ROGUE_CR_SIDEKICK_IDLE_MASKFULL &
|
||||
~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN |
|
||||
ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN |
|
||||
ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN);
|
||||
bool skip_garten_idle = false;
|
||||
u32 reg_value;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
|
||||
* For cores with the LAYOUT_MARS feature, SIDEKICK would have been
|
||||
* powered down by the FW.
|
||||
*/
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
|
||||
sidekick_idle_mask, POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Unset MTS DM association with threads. */
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC,
|
||||
ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_MASKFULL &
|
||||
ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC,
|
||||
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL &
|
||||
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC,
|
||||
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL &
|
||||
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
|
||||
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC,
|
||||
ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_MASKFULL &
|
||||
ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
|
||||
|
||||
/* Extra Idle checks. */
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_STATUS_MMU, 0,
|
||||
ROGUE_CR_BIF_STATUS_MMU_MASKFULL,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_STATUS_MMU, 0,
|
||||
ROGUE_CR_BIFPM_STATUS_MMU_MASKFULL,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!PVR_HAS_FEATURE(pvr_dev, xt_top_infrastructure)) {
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_READS_EXT_STATUS, 0,
|
||||
ROGUE_CR_BIF_READS_EXT_STATUS_MASKFULL,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_READS_EXT_STATUS, 0,
|
||||
ROGUE_CR_BIFPM_READS_EXT_STATUS_MASKFULL,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pvr_cr_poll_reg64(pvr_dev, ROGUE_CR_SLC_STATUS1, 0,
|
||||
ROGUE_CR_SLC_STATUS1_MASKFULL,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Wait for SLC to signal IDLE.
|
||||
* For cores with the LAYOUT_MARS feature, SLC would have been powered
|
||||
* down by the FW.
|
||||
*/
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE,
|
||||
ROGUE_CR_SLC_IDLE_MASKFULL,
|
||||
ROGUE_CR_SLC_IDLE_MASKFULL, POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
|
||||
* For cores with the LAYOUT_MARS feature, SIDEKICK would have been powered
|
||||
* down by the FW.
|
||||
*/
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
|
||||
sidekick_idle_mask, POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META) {
|
||||
err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, ®_value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Wait for Sidekick/Jones to signal IDLE including the Garten
|
||||
* Wrapper if there is no debugger attached (TxVECINT_BHALT =
|
||||
* 0x0).
|
||||
*/
|
||||
if (reg_value)
|
||||
skip_garten_idle = true;
|
||||
}
|
||||
|
||||
if (!skip_garten_idle) {
|
||||
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE,
|
||||
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
|
||||
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
|
||||
POLL_TIMEOUT_USEC);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
|
||||
ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL);
|
||||
else
|
||||
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_MASKFULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#ifndef PVR_FW_STARTSTOP_H
|
||||
#define PVR_FW_STARTSTOP_H
|
||||
|
||||
/* Forward declaration from pvr_device.h. */
|
||||
struct pvr_device;
|
||||
|
||||
int pvr_fw_start(struct pvr_device *pvr_dev);
|
||||
int pvr_fw_stop(struct pvr_device *pvr_dev);
|
||||
|
||||
#endif /* PVR_FW_STARTSTOP_H */
|
||||
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#include "pvr_device.h"
|
||||
#include "pvr_gem.h"
|
||||
#include "pvr_rogue_fwif.h"
|
||||
#include "pvr_fw_trace.h"
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static void
|
||||
tracebuf_ctrl_init(void *cpu_ptr, void *priv)
|
||||
{
|
||||
struct rogue_fwif_tracebuf *tracebuf_ctrl = cpu_ptr;
|
||||
struct pvr_fw_trace *fw_trace = priv;
|
||||
u32 thread_nr;
|
||||
|
||||
tracebuf_ctrl->tracebuf_size_in_dwords = ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
|
||||
tracebuf_ctrl->tracebuf_flags = 0;
|
||||
|
||||
if (fw_trace->group_mask)
|
||||
tracebuf_ctrl->log_type = fw_trace->group_mask | ROGUE_FWIF_LOG_TYPE_TRACE;
|
||||
else
|
||||
tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
|
||||
|
||||
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
|
||||
struct rogue_fwif_tracebuf_space *tracebuf_space =
|
||||
&tracebuf_ctrl->tracebuf[thread_nr];
|
||||
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
|
||||
|
||||
pvr_fw_object_get_fw_addr(trace_buffer->buf_obj,
|
||||
&tracebuf_space->trace_buffer_fw_addr);
|
||||
|
||||
tracebuf_space->trace_buffer = trace_buffer->buf;
|
||||
tracebuf_space->trace_pointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int pvr_fw_trace_init(struct pvr_device *pvr_dev)
|
||||
{
|
||||
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
|
||||
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
|
||||
u32 thread_nr;
|
||||
int err;
|
||||
|
||||
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
|
||||
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
|
||||
|
||||
trace_buffer->buf =
|
||||
pvr_fw_object_create_and_map(pvr_dev,
|
||||
ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS *
|
||||
sizeof(*trace_buffer->buf),
|
||||
PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
|
||||
PVR_BO_FW_NO_CLEAR_ON_RESET,
|
||||
NULL, NULL, &trace_buffer->buf_obj);
|
||||
if (IS_ERR(trace_buffer->buf)) {
|
||||
drm_err(drm_dev, "Unable to allocate trace buffer\n");
|
||||
err = PTR_ERR(trace_buffer->buf);
|
||||
trace_buffer->buf = NULL;
|
||||
goto err_free_buf;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Provide control of group mask. */
|
||||
fw_trace->group_mask = 0;
|
||||
|
||||
fw_trace->tracebuf_ctrl =
|
||||
pvr_fw_object_create_and_map(pvr_dev,
|
||||
sizeof(*fw_trace->tracebuf_ctrl),
|
||||
PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
|
||||
PVR_BO_FW_NO_CLEAR_ON_RESET,
|
||||
tracebuf_ctrl_init, fw_trace,
|
||||
&fw_trace->tracebuf_ctrl_obj);
|
||||
if (IS_ERR(fw_trace->tracebuf_ctrl)) {
|
||||
drm_err(drm_dev, "Unable to allocate trace buffer control structure\n");
|
||||
err = PTR_ERR(fw_trace->tracebuf_ctrl);
|
||||
goto err_free_buf;
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(fw_trace->tracebuf_ctrl->tracebuf) !=
|
||||
ARRAY_SIZE(fw_trace->buffers));
|
||||
|
||||
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
|
||||
struct rogue_fwif_tracebuf_space *tracebuf_space =
|
||||
&fw_trace->tracebuf_ctrl->tracebuf[thread_nr];
|
||||
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
|
||||
|
||||
trace_buffer->tracebuf_space = tracebuf_space;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_buf:
|
||||
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
|
||||
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
|
||||
|
||||
if (trace_buffer->buf)
|
||||
pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void pvr_fw_trace_fini(struct pvr_device *pvr_dev)
|
||||
{
|
||||
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
|
||||
u32 thread_nr;
|
||||
|
||||
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
|
||||
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
|
||||
|
||||
pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
|
||||
}
|
||||
pvr_fw_object_unmap_and_destroy(fw_trace->tracebuf_ctrl_obj);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||
|
||||
#ifndef PVR_FW_TRACE_H
|
||||
#define PVR_FW_TRACE_H
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "pvr_rogue_fwif.h"
|
||||
|
||||
/* Forward declaration from pvr_device.h. */
|
||||
struct pvr_device;
|
||||
|
||||
/* Forward declaration from pvr_gem.h. */
|
||||
struct pvr_fw_object;
|
||||
|
||||
/* Forward declarations from pvr_rogue_fwif.h */
|
||||
struct rogue_fwif_tracebuf;
|
||||
struct rogue_fwif_tracebuf_space;
|
||||
|
||||
/**
|
||||
* struct pvr_fw_trace_buffer - Structure representing a trace buffer
|
||||
*/
|
||||
struct pvr_fw_trace_buffer {
|
||||
/** @buf_obj: FW buffer object representing trace buffer. */
|
||||
struct pvr_fw_object *buf_obj;
|
||||
|
||||
/** @buf: Pointer to CPU mapping of trace buffer. */
|
||||
u32 *buf;
|
||||
|
||||
/**
|
||||
* @tracebuf_space: Pointer to FW tracebuf_space structure for this
|
||||
* trace buffer.
|
||||
*/
|
||||
struct rogue_fwif_tracebuf_space *tracebuf_space;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pvr_fw_trace - Device firmware trace data
|
||||
*/
|
||||
struct pvr_fw_trace {
|
||||
/**
|
||||
* @tracebuf_ctrl_obj: Object representing FW trace buffer control
|
||||
* structure.
|
||||
*/
|
||||
struct pvr_fw_object *tracebuf_ctrl_obj;
|
||||
|
||||
/**
|
||||
* @tracebuf_ctrl: Pointer to CPU mapping of FW trace buffer control
|
||||
* structure.
|
||||
*/
|
||||
struct rogue_fwif_tracebuf *tracebuf_ctrl;
|
||||
|
||||
/**
|
||||
* @buffers: Array representing the actual trace buffers owned by this
|
||||
* device.
|
||||
*/
|
||||
struct pvr_fw_trace_buffer buffers[ROGUE_FW_THREAD_MAX];
|
||||
|
||||
/** @group_mask: Mask of enabled trace groups. */
|
||||
u32 group_mask;
|
||||
};
|
||||
|
||||
int pvr_fw_trace_init(struct pvr_device *pvr_dev);
|
||||
void pvr_fw_trace_fini(struct pvr_device *pvr_dev);
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
/* Forward declaration from <linux/dcache.h>. */
|
||||
struct dentry;
|
||||
|
||||
void pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask,
|
||||
u32 new_mask);
|
||||
|
||||
void pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir);
|
||||
#endif /* defined(CONFIG_DEBUG_FS) */
|
||||
|
||||
#endif /* PVR_FW_TRACE_H */
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
#include "pvr_mmu.h"
|
||||
|
||||
#include "pvr_ccb.h"
|
||||
#include "pvr_device.h"
|
||||
#include "pvr_fw.h"
|
||||
#include "pvr_gem.h"
|
||||
#include "pvr_power.h"
|
||||
#include "pvr_rogue_fwif.h"
|
||||
#include "pvr_rogue_mmu_defs.h"
|
||||
|
||||
@@ -95,7 +97,7 @@ static void pvr_mmu_set_flush_flags(struct pvr_device *pvr_dev, u32 flags)
|
||||
*/
|
||||
void pvr_mmu_flush_request_all(struct pvr_device *pvr_dev)
|
||||
{
|
||||
/* TODO: implement */
|
||||
pvr_mmu_set_flush_flags(pvr_dev, PVR_MMU_SYNC_LEVEL_2_FLAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,8 +122,70 @@ void pvr_mmu_flush_request_all(struct pvr_device *pvr_dev)
|
||||
*/
|
||||
int pvr_mmu_flush_exec(struct pvr_device *pvr_dev, bool wait)
|
||||
{
|
||||
/* TODO: implement */
|
||||
return -ENODEV;
|
||||
struct rogue_fwif_kccb_cmd cmd_mmu_cache = {};
|
||||
struct rogue_fwif_mmucachedata *cmd_mmu_cache_data =
|
||||
&cmd_mmu_cache.cmd_data.mmu_cache_data;
|
||||
int err = 0;
|
||||
u32 slot;
|
||||
int idx;
|
||||
|
||||
if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx))
|
||||
return -EIO;
|
||||
|
||||
/* Can't flush MMU if the firmware hasn't booted yet. */
|
||||
if (!pvr_dev->fw_dev.booted)
|
||||
goto err_drm_dev_exit;
|
||||
|
||||
cmd_mmu_cache_data->cache_flags =
|
||||
atomic_xchg(&pvr_dev->mmu_flush_cache_flags, 0);
|
||||
|
||||
if (!cmd_mmu_cache_data->cache_flags)
|
||||
goto err_drm_dev_exit;
|
||||
|
||||
cmd_mmu_cache.cmd_type = ROGUE_FWIF_KCCB_CMD_MMUCACHE;
|
||||
|
||||
pvr_fw_object_get_fw_addr(pvr_dev->fw_dev.mem.mmucache_sync_obj,
|
||||
&cmd_mmu_cache_data->mmu_cache_sync_fw_addr);
|
||||
cmd_mmu_cache_data->mmu_cache_sync_update_value = 0;
|
||||
|
||||
err = pvr_kccb_send_cmd(pvr_dev, &cmd_mmu_cache, &slot);
|
||||
if (err)
|
||||
goto err_reset_and_retry;
|
||||
|
||||
err = pvr_kccb_wait_for_completion(pvr_dev, slot, HZ, NULL);
|
||||
if (err)
|
||||
goto err_reset_and_retry;
|
||||
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return 0;
|
||||
|
||||
err_reset_and_retry:
|
||||
/*
|
||||
* Flush command failure is most likely the result of a firmware lockup. Hard
|
||||
* reset the GPU and retry.
|
||||
*/
|
||||
err = pvr_power_reset(pvr_dev, true);
|
||||
if (err)
|
||||
goto err_drm_dev_exit; /* Device is lost. */
|
||||
|
||||
/* Retry sending flush request. */
|
||||
err = pvr_kccb_send_cmd(pvr_dev, &cmd_mmu_cache, &slot);
|
||||
if (err) {
|
||||
pvr_device_lost(pvr_dev);
|
||||
goto err_drm_dev_exit;
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
err = pvr_kccb_wait_for_completion(pvr_dev, slot, HZ, NULL);
|
||||
if (err)
|
||||
pvr_device_lost(pvr_dev);
|
||||
}
|
||||
|
||||
err_drm_dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pvr_device.h"
|
||||
#include "pvr_fw.h"
|
||||
#include "pvr_fw_startstop.h"
|
||||
#include "pvr_power.h"
|
||||
#include "pvr_rogue_fwif.h"
|
||||
|
||||
@@ -21,11 +22,38 @@
|
||||
|
||||
#define WATCHDOG_TIME_MS (500)
|
||||
|
||||
/**
|
||||
* pvr_device_lost() - Mark GPU device as lost
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
*
|
||||
* This will cause the DRM device to be unplugged.
|
||||
*/
|
||||
void
|
||||
pvr_device_lost(struct pvr_device *pvr_dev)
|
||||
{
|
||||
if (!pvr_dev->lost) {
|
||||
pvr_dev->lost = true;
|
||||
drm_dev_unplug(from_pvr_device(pvr_dev));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
pvr_power_send_command(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *pow_cmd)
|
||||
{
|
||||
/* TODO: implement */
|
||||
return -ENODEV;
|
||||
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
|
||||
u32 slot_nr;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
WRITE_ONCE(*fw_dev->power_sync, 0);
|
||||
|
||||
err = pvr_kccb_send_cmd_powered(pvr_dev, pow_cmd, &slot_nr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Wait for FW to acknowledge. */
|
||||
return readl_poll_timeout(pvr_dev->fw_dev.power_sync, value, value != 0, 100,
|
||||
POWER_SYNC_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -71,8 +99,7 @@ pvr_power_fw_disable(struct pvr_device *pvr_dev, bool hard_reset)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* TODO: stop firmware */
|
||||
return -ENODEV;
|
||||
return pvr_fw_stop(pvr_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -80,11 +107,17 @@ pvr_power_fw_enable(struct pvr_device *pvr_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* TODO: start firmware */
|
||||
err = -ENODEV;
|
||||
err = pvr_fw_start(pvr_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pvr_wait_for_fw_boot(pvr_dev);
|
||||
if (err) {
|
||||
drm_err(from_pvr_device(pvr_dev), "Firmware failed to boot\n");
|
||||
pvr_fw_stop(pvr_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work,
|
||||
msecs_to_jiffies(WATCHDOG_TIME_MS));
|
||||
|
||||
@@ -94,14 +127,39 @@ pvr_power_fw_enable(struct pvr_device *pvr_dev)
|
||||
bool
|
||||
pvr_power_is_idle(struct pvr_device *pvr_dev)
|
||||
{
|
||||
/* TODO: implement */
|
||||
return true;
|
||||
/*
|
||||
* FW power state can be out of date if a KCCB command has been submitted but the FW hasn't
|
||||
* started processing it yet. So also check the KCCB status.
|
||||
*/
|
||||
enum rogue_fwif_pow_state pow_state = READ_ONCE(pvr_dev->fw_dev.fwif_sysdata->pow_state);
|
||||
bool kccb_idle = pvr_kccb_is_idle(pvr_dev);
|
||||
|
||||
return (pow_state == ROGUE_FWIF_POW_IDLE) && kccb_idle;
|
||||
}
|
||||
|
||||
static bool
|
||||
pvr_watchdog_kccb_stalled(struct pvr_device *pvr_dev)
|
||||
{
|
||||
/* TODO: implement */
|
||||
/* Check KCCB commands are progressing. */
|
||||
u32 kccb_cmds_executed = pvr_dev->fw_dev.fwif_osdata->kccb_cmds_executed;
|
||||
bool kccb_is_idle = pvr_kccb_is_idle(pvr_dev);
|
||||
|
||||
if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed && !kccb_is_idle) {
|
||||
pvr_dev->watchdog.kccb_stall_count++;
|
||||
|
||||
/*
|
||||
* If we have commands pending with no progress for 2 consecutive polls then
|
||||
* consider KCCB command processing stalled.
|
||||
*/
|
||||
if (pvr_dev->watchdog.kccb_stall_count == 2) {
|
||||
pvr_dev->watchdog.kccb_stall_count = 0;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
pvr_dev->watchdog.old_kccb_cmds_executed = kccb_cmds_executed;
|
||||
pvr_dev->watchdog.kccb_stall_count = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,6 +176,9 @@ pvr_watchdog_worker(struct work_struct *work)
|
||||
if (pm_runtime_get_if_in_use(from_pvr_device(pvr_dev)->dev) <= 0)
|
||||
goto out_requeue;
|
||||
|
||||
if (!pvr_dev->fw_dev.booted)
|
||||
goto out_pm_runtime_put;
|
||||
|
||||
stalled = pvr_watchdog_kccb_stalled(pvr_dev);
|
||||
|
||||
if (stalled) {
|
||||
@@ -127,6 +188,7 @@ pvr_watchdog_worker(struct work_struct *work)
|
||||
/* Device may be lost at this point. */
|
||||
}
|
||||
|
||||
out_pm_runtime_put:
|
||||
pm_runtime_put(from_pvr_device(pvr_dev)->dev);
|
||||
|
||||
out_requeue:
|
||||
@@ -158,18 +220,26 @@ pvr_power_device_suspend(struct device *dev)
|
||||
struct platform_device *plat_dev = to_platform_device(dev);
|
||||
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
|
||||
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
|
||||
int err = 0;
|
||||
int idx;
|
||||
|
||||
if (!drm_dev_enter(drm_dev, &idx))
|
||||
return -EIO;
|
||||
|
||||
if (pvr_dev->fw_dev.booted) {
|
||||
err = pvr_power_fw_disable(pvr_dev, false);
|
||||
if (err)
|
||||
goto err_drm_dev_exit;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(pvr_dev->mem_clk);
|
||||
clk_disable_unprepare(pvr_dev->sys_clk);
|
||||
clk_disable_unprepare(pvr_dev->core_clk);
|
||||
|
||||
err_drm_dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -196,10 +266,19 @@ pvr_power_device_resume(struct device *dev)
|
||||
if (err)
|
||||
goto err_sys_clk_disable;
|
||||
|
||||
if (pvr_dev->fw_dev.booted) {
|
||||
err = pvr_power_fw_enable(pvr_dev);
|
||||
if (err)
|
||||
goto err_mem_clk_disable;
|
||||
}
|
||||
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return 0;
|
||||
|
||||
err_mem_clk_disable:
|
||||
clk_disable_unprepare(pvr_dev->mem_clk);
|
||||
|
||||
err_sys_clk_disable:
|
||||
clk_disable_unprepare(pvr_dev->sys_clk);
|
||||
|
||||
@@ -239,7 +318,6 @@ pvr_power_device_idle(struct device *dev)
|
||||
int
|
||||
pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)
|
||||
{
|
||||
/* TODO: Implement hard reset. */
|
||||
int err;
|
||||
|
||||
/*
|
||||
@@ -248,13 +326,69 @@ pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)
|
||||
*/
|
||||
WARN_ON(pvr_power_get(pvr_dev));
|
||||
|
||||
err = pvr_power_fw_disable(pvr_dev, false);
|
||||
if (err)
|
||||
goto err_power_put;
|
||||
down_write(&pvr_dev->reset_sem);
|
||||
|
||||
err = pvr_power_fw_enable(pvr_dev);
|
||||
if (pvr_dev->lost) {
|
||||
err = -EIO;
|
||||
goto err_up_write;
|
||||
}
|
||||
|
||||
/* Disable IRQs for the duration of the reset. */
|
||||
disable_irq(pvr_dev->irq);
|
||||
|
||||
do {
|
||||
err = pvr_power_fw_disable(pvr_dev, hard_reset);
|
||||
if (!err) {
|
||||
if (hard_reset) {
|
||||
pvr_dev->fw_dev.booted = false;
|
||||
WARN_ON(pm_runtime_force_suspend(from_pvr_device(pvr_dev)->dev));
|
||||
|
||||
err = pvr_fw_hard_reset(pvr_dev);
|
||||
if (err)
|
||||
goto err_device_lost;
|
||||
|
||||
err = pm_runtime_force_resume(from_pvr_device(pvr_dev)->dev);
|
||||
pvr_dev->fw_dev.booted = true;
|
||||
if (err)
|
||||
goto err_device_lost;
|
||||
} else {
|
||||
/* Clear the FW faulted flags. */
|
||||
pvr_dev->fw_dev.fwif_sysdata->hwr_state_flags &=
|
||||
~(ROGUE_FWIF_HWR_FW_FAULT |
|
||||
ROGUE_FWIF_HWR_RESTART_REQUESTED);
|
||||
}
|
||||
|
||||
pvr_fw_irq_clear(pvr_dev);
|
||||
|
||||
err = pvr_power_fw_enable(pvr_dev);
|
||||
}
|
||||
|
||||
if (err && hard_reset)
|
||||
goto err_device_lost;
|
||||
|
||||
if (err && !hard_reset) {
|
||||
drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset");
|
||||
hard_reset = true;
|
||||
}
|
||||
} while (err);
|
||||
|
||||
enable_irq(pvr_dev->irq);
|
||||
|
||||
up_write(&pvr_dev->reset_sem);
|
||||
|
||||
pvr_power_put(pvr_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_device_lost:
|
||||
drm_err(from_pvr_device(pvr_dev), "GPU device lost");
|
||||
pvr_device_lost(pvr_dev);
|
||||
|
||||
/* Leave IRQs disabled if the device is lost. */
|
||||
|
||||
err_up_write:
|
||||
up_write(&pvr_dev->reset_sem);
|
||||
|
||||
err_power_put:
|
||||
pvr_power_put(pvr_dev);
|
||||
|
||||
return err;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
int pvr_watchdog_init(struct pvr_device *pvr_dev);
|
||||
void pvr_watchdog_fini(struct pvr_device *pvr_dev);
|
||||
|
||||
void pvr_device_lost(struct pvr_device *pvr_dev);
|
||||
|
||||
bool pvr_power_is_idle(struct pvr_device *pvr_dev);
|
||||
|
||||
int pvr_power_device_suspend(struct device *dev);
|
||||
|
||||
@@ -540,6 +540,16 @@ static const struct drm_gpuvm_ops pvr_vm_gpuva_ops = {
|
||||
.sm_step_unmap = pvr_vm_gpuva_unmap,
|
||||
};
|
||||
|
||||
static void
|
||||
fw_mem_context_init(void *cpu_ptr, void *priv)
|
||||
{
|
||||
struct rogue_fwif_fwmemcontext *fw_mem_ctx = cpu_ptr;
|
||||
struct pvr_vm_context *vm_ctx = priv;
|
||||
|
||||
fw_mem_ctx->pc_dev_paddr = pvr_vm_get_page_table_root_addr(vm_ctx);
|
||||
fw_mem_ctx->page_cat_base_reg_set = ROGUE_FW_BIF_INVALID_PCSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* pvr_vm_create_context() - Create a new VM context.
|
||||
* @pvr_dev: Target PowerVR device.
|
||||
@@ -602,13 +612,19 @@ pvr_vm_create_context(struct pvr_device *pvr_dev, bool is_userspace_context)
|
||||
}
|
||||
|
||||
if (is_userspace_context) {
|
||||
/* TODO: Create FW mem context */
|
||||
err = -ENODEV;
|
||||
goto err_put_ctx;
|
||||
err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_fwmemcontext),
|
||||
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
|
||||
fw_mem_context_init, vm_ctx, &vm_ctx->fw_mem_ctx_obj);
|
||||
|
||||
if (err)
|
||||
goto err_page_table_destroy;
|
||||
}
|
||||
|
||||
return vm_ctx;
|
||||
|
||||
err_page_table_destroy:
|
||||
pvr_mmu_context_destroy(vm_ctx->mmu_ctx);
|
||||
|
||||
err_put_ctx:
|
||||
pvr_vm_context_put(vm_ctx);
|
||||
|
||||
@@ -628,8 +644,8 @@ pvr_vm_context_release(struct kref *ref_count)
|
||||
struct pvr_vm_context *vm_ctx =
|
||||
container_of(ref_count, struct pvr_vm_context, ref_count);
|
||||
|
||||
/* TODO: Destroy FW mem context */
|
||||
WARN_ON(vm_ctx->fw_mem_ctx_obj);
|
||||
if (vm_ctx->fw_mem_ctx_obj)
|
||||
pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj);
|
||||
|
||||
WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start,
|
||||
vm_ctx->gpuvm_mgr.mm_range));
|
||||
|
||||
Reference in New Issue
Block a user