Files
NativeAdreno/NativeAdrenoCpInit.cpp
bluseliu50 1105b0ec8c feat(adreno): msm hangcheck and GPU restart with replay
A thread checks the ring every 500 ms as msm's hangcheck_handler does:
no fence progress with work pending and no CP progress (a6xx_progress,
at most three retries) is a hang. Recovery follows recover_worker: the
hung job's VM takes the fault and, unless it is the kernel's, becomes
unusable; the fence moves past it; the board's restart hook resets the
GPU and runs hw_init and CP init on the same ring; every later job is
replayed with its own fence. Clients read the global fault count plus
their own VM's, so only the guilty client sees its device lost. The
ring gains restart, unfenced submission, a6xx_idle, progress state and
the GBIF bus halt; CP init can run unfenced.
2026-11-01 05:20:57 +08:00

63 lines
2.8 KiB
C++

/* SPDX-License-Identifier: MIT */
#include "NativeAdrenoCpInit.h"
#include "AdrenoPM4.h"
#include <IOKit/IOLib.h>
/* Pinned definitions: CP_SQE_INSTR_BASE is the 64-bit register at dword offset
* 0x0830 for A6XX..A7XX (a6xx.xml). CP_THREAD_CONTROL is opcode 0x17 with
* CONCURRENT_BIN_DISABLE at bit 27, and CP_ME_INIT is opcode 0x48 with seven
* payload words (adreno_pm4.xml). The payload values are a7xx_cp_init in
* a6xx_gpu.c: mask BIT(0)|BIT(1)|BIT(3)|BIT(6)|BIT(8), three hardware
* contexts, 0x20000000 error detection, 0x00000002 operation mode, the
* power-up register list address and BIT(31) to read it. */
enum : uint32_t {
CpSqeInstrBase = 0x0830,
CpThreadControlOpcode = 0x17,
CpMeInitOpcode = 0x48,
CpConcurrentBinDisable = 1u << 27,
CpMeInitWords = 8,
CpInitWords = 10
};
IOReturn NativeAdrenoCpInit::poison()
{
poisoned_=true;return kIOReturnIOError;
}
uint32_t NativeAdrenoCpInit::meInitMask()
{
return (1u<<0)|(1u<<1)|(1u<<3)|(1u<<6)|(1u<<8);
}
IOReturn NativeAdrenoCpInit::run(const NativeAdrenoRingIo *io,NativeAdrenoRing *ring,
const NativeAdrenoCpInitConfig *config,uint32_t *fence,bool fenced)
{
if(!fence)return kIOReturnBadArgument;*fence=0;
if(done_ || poisoned_)return kIOReturnNotReady;
if(!io || !io->write32 || !ring || ring->poisoned())return kIOReturnBadArgument;
if(!config || !config->sqe_iova || (config->sqe_iova&4095) ||
config->sqe_iova>=UINT64_C(1)<<48 || !config->pwrup_reglist_iova ||
(config->pwrup_reglist_iova&3) || config->pwrup_reglist_iova>=UINT64_C(1)<<48)
return kIOReturnBadArgument;
if(config->sqe_base_programmed) {
uint32_t lo=0,hi=0;
if(!io->read32 || io->read32(io->cookie,CpSqeInstrBase,&lo) || io->read32(io->cookie,CpSqeInstrBase+1,&hi) ||
(((uint64_t)hi<<32)|lo)!=config->sqe_iova)return poison();
} else if(io->write32(io->cookie,CpSqeInstrBase,(uint32_t)config->sqe_iova) ||
io->write32(io->cookie,CpSqeInstrBase+1,(uint32_t)(config->sqe_iova>>32)))
return poison();
uint32_t words[CpInitWords]={0};
if(AdrenoPM4Type7(CpThreadControlOpcode,1,&words[0])!=AdrenoPM4Success ||
AdrenoPM4Type7(CpMeInitOpcode,7,&words[2])!=AdrenoPM4Success)return poison();
words[1]=CpConcurrentBinDisable;
words[3]=meInitMask();
words[4]=0x00000003; /* hardware contexts */
words[5]=0x20000000; /* error detection */
words[6]=0x00000002; /* operation mode */
words[7]=(uint32_t)config->pwrup_reglist_iova;
words[8]=(uint32_t)(config->pwrup_reglist_iova>>32);
words[9]=1u<<31; /* read registers from the list */
uint32_t value=0;
const IOReturn status=fenced?ring->submit(words,CpInitWords,&value):ring->submitUnfenced(words,CpInitWords);
if(status)return poison();
done_=true;*fence=value;
return kIOReturnSuccess;
}