mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 04:42:04 -04:00
msm: kgsl: Create a6xx gmu power ops
Instead of overwhelming the legacy power up/down sequences, create a6xx gmu specific power up/down sequences. The gpudev power ops will be set during a6xx gmu probe so that we don't have to do gmu checks in legacy probe. Change-Id: I897d775bdc3c9f97d38d20bc39b56f2551703bce Signed-off-by: Harshdeep Dhatt <hdhatt@codeaurora.org>
This commit is contained in:
parent
251880f82b
commit
90da593e71
14 changed files with 2399 additions and 1989 deletions
|
|
@ -33,9 +33,11 @@ msm_kgsl-y += \
|
|||
adreno_a5xx_snapshot.o \
|
||||
adreno_a6xx.o \
|
||||
adreno_a6xx_gmu.o \
|
||||
adreno_a6xx_gmu_snapshot.o \
|
||||
adreno_a6xx_hfi.o \
|
||||
adreno_a6xx_preempt.o \
|
||||
adreno_a6xx_rgmu.o \
|
||||
adreno_a6xx_rpmh.o \
|
||||
adreno_a6xx_snapshot.o \
|
||||
adreno_cp_parser.o \
|
||||
adreno_dispatch.o \
|
||||
|
|
|
|||
|
|
@ -782,7 +782,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a630v2 = {
|
|||
.features = ADRENO_RPMH | ADRENO_IFPC |
|
||||
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION |
|
||||
ADRENO_IOCOHERENT | ADRENO_PREEMPTION,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_1M,
|
||||
.bus_width = 32,
|
||||
|
|
@ -881,7 +881,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a615 = {
|
|||
.features = ADRENO_RPMH | ADRENO_PREEMPTION |
|
||||
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
|
||||
ADRENO_IOCOHERENT,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_512K,
|
||||
.bus_width = 32,
|
||||
|
|
@ -908,7 +908,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a618 = {
|
|||
.features = ADRENO_RPMH | ADRENO_PREEMPTION |
|
||||
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
|
||||
ADRENO_IOCOHERENT,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_512K,
|
||||
.bus_width = 32,
|
||||
|
|
@ -935,7 +935,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a619 = {
|
|||
.features = ADRENO_RPMH | ADRENO_PREEMPTION |
|
||||
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
|
||||
ADRENO_IOCOHERENT,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_size = SZ_512K,
|
||||
.bus_width = 32,
|
||||
},
|
||||
|
|
@ -1080,7 +1080,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a620 = {
|
|||
ADRENO_CONTENT_PROTECTION | ADRENO_IOCOHERENT |
|
||||
ADRENO_IFPC | ADRENO_PREEMPTION | ADRENO_ACD |
|
||||
ADRENO_APRIV,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0,
|
||||
.gmem_size = SZ_512K,
|
||||
.bus_width = 32,
|
||||
|
|
@ -1170,7 +1170,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a640 = {
|
|||
.features = ADRENO_RPMH | ADRENO_GPMU |
|
||||
ADRENO_CONTENT_PROTECTION | ADRENO_IOCOHERENT |
|
||||
ADRENO_IFPC | ADRENO_PREEMPTION,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_1M, //Verified 1MB
|
||||
.bus_width = 32,
|
||||
|
|
@ -1250,7 +1250,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a650 = {
|
|||
.features = ADRENO_RPMH | ADRENO_GPMU |
|
||||
ADRENO_IOCOHERENT | ADRENO_CONTENT_PROTECTION |
|
||||
ADRENO_IFPC | ADRENO_APRIV,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0,
|
||||
.gmem_size = SZ_1M + SZ_128K, /* verified 1152kB */
|
||||
.bus_width = 32,
|
||||
|
|
@ -1279,7 +1279,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a650v2 = {
|
|||
ADRENO_IOCOHERENT | ADRENO_CONTENT_PROTECTION |
|
||||
ADRENO_IFPC | ADRENO_PREEMPTION | ADRENO_ACD |
|
||||
ADRENO_LM | ADRENO_APRIV,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0,
|
||||
.gmem_size = SZ_1M + SZ_128K, /* verified 1152kB */
|
||||
.bus_width = 32,
|
||||
|
|
@ -1305,7 +1305,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a680 = {
|
|||
.base = {
|
||||
DEFINE_ADRENO_REV(ADRENO_REV_A680, 6, 8, 0, ANY_ID),
|
||||
.features = ADRENO_RPMH | ADRENO_GPMU,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_2M,
|
||||
.bus_width = 32,
|
||||
|
|
@ -1407,7 +1407,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a616 = {
|
|||
.features = ADRENO_RPMH | ADRENO_PREEMPTION |
|
||||
ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
|
||||
ADRENO_IOCOHERENT,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0x100000,
|
||||
.gmem_size = SZ_512K,
|
||||
.bus_width = 32,
|
||||
|
|
@ -1548,7 +1548,7 @@ static const struct adreno_a6xx_core adreno_gpu_core_a660 = {
|
|||
.features = ADRENO_RPMH | ADRENO_GPMU | ADRENO_APRIV |
|
||||
ADRENO_IOCOHERENT | ADRENO_CONTENT_PROTECTION |
|
||||
ADRENO_IFPC,
|
||||
.gpudev = &adreno_a6xx_gpudev,
|
||||
.gpudev = &adreno_a6xx_gmu_gpudev,
|
||||
.gmem_base = 0,
|
||||
.gmem_size = SZ_1M + SZ_512K,
|
||||
.bus_width = 32,
|
||||
|
|
|
|||
|
|
@ -321,30 +321,6 @@ void adreno_fault_detect_stop(struct adreno_device *adreno_dev)
|
|||
adreno_dev->fast_hang_detect = 0;
|
||||
}
|
||||
|
||||
#define GMU_CM3_CFG_NONMASKINTR_SHIFT 9
|
||||
|
||||
/* Send an NMI to the GMU */
|
||||
void adreno_gmu_send_nmi(struct adreno_device *adreno_dev)
|
||||
{
|
||||
u32 val;
|
||||
/* Mask so there's no interrupt caused by NMI */
|
||||
adreno_write_gmureg(adreno_dev,
|
||||
ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF);
|
||||
|
||||
/* Make sure the interrupt is masked before causing it */
|
||||
wmb();
|
||||
if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
|
||||
adreno_write_gmureg(adreno_dev,
|
||||
ADRENO_REG_GMU_NMI_CONTROL_STATUS, 0);
|
||||
|
||||
adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_CM3_CFG, &val);
|
||||
val |= 1 << GMU_CM3_CFG_NONMASKINTR_SHIFT;
|
||||
adreno_write_gmureg(adreno_dev, ADRENO_REG_GMU_CM3_CFG, val);
|
||||
|
||||
/* Make sure the NMI is invoked before we proceed*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* A workqueue callback responsible for actually turning on the GPU after a
|
||||
* touch event. kgsl_pwrctrl_change_state(ACTIVE) is used without any
|
||||
|
|
@ -1831,7 +1807,7 @@ int adreno_switch_to_unsecure_mode(struct adreno_device *adreno_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
|
||||
void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
|
||||
{
|
||||
int i;
|
||||
struct adreno_ringbuffer *rb;
|
||||
|
|
|
|||
|
|
@ -905,6 +905,7 @@ extern unsigned int *adreno_ft_regs_val;
|
|||
extern struct adreno_gpudev adreno_a3xx_gpudev;
|
||||
extern struct adreno_gpudev adreno_a5xx_gpudev;
|
||||
extern struct adreno_gpudev adreno_a6xx_gpudev;
|
||||
extern struct adreno_gpudev adreno_a6xx_gmu_gpudev;
|
||||
|
||||
extern int adreno_wake_nice;
|
||||
extern unsigned int adreno_wake_timeout;
|
||||
|
|
@ -1758,7 +1759,6 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev,
|
|||
enum adreno_regs offset, unsigned int val,
|
||||
unsigned int fence_mask);
|
||||
int adreno_clear_pending_transactions(struct kgsl_device *device);
|
||||
void adreno_gmu_send_nmi(struct adreno_device *adreno_dev);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -1858,6 +1858,15 @@ int adreno_power_cycle_bool(struct adreno_device *adreno_dev,
|
|||
int adreno_power_cycle_u32(struct adreno_device *adreno_dev,
|
||||
u32 *flag, u32 val);
|
||||
|
||||
/**
|
||||
* adreno_set_active_ctxs_null - Give up active context refcount
|
||||
* @adreno_dev: Adreno GPU device handle
|
||||
*
|
||||
* This puts back the reference for that last active context on
|
||||
* each ringbuffer when going in and out of slumber.
|
||||
*/
|
||||
void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* adreno_get_bus_counters - Allocate the bus dcvs counters
|
||||
* @adreno_dev: Adreno GPU device handle
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ static u32 a615_pwrup_reglist[] = {
|
|||
|
||||
static int a6xx_get_cp_init_cmds(struct adreno_device *adreno_dev);
|
||||
|
||||
static int a6xx_init(struct adreno_device *adreno_dev)
|
||||
int a6xx_init(struct adreno_device *adreno_dev)
|
||||
{
|
||||
const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev);
|
||||
|
||||
|
|
@ -400,13 +400,7 @@ static void a6xx_set_secvid(struct kgsl_device *device)
|
|||
#define A6XX_APRIV_DEFAULT \
|
||||
((1 << 6) | (1 << 5) | (1 << 3) | (1 << 2) | (1 << 1))
|
||||
|
||||
/*
|
||||
* a6xx_start() - Device start
|
||||
* @adreno_dev: Pointer to adreno device
|
||||
*
|
||||
* a6xx device start
|
||||
*/
|
||||
static void a6xx_start(struct adreno_device *adreno_dev)
|
||||
void a6xx_start(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev);
|
||||
|
|
@ -860,11 +854,7 @@ static int a6xx_post_start(struct adreno_device *adreno_dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* a6xx_rb_start() - Start the ringbuffer
|
||||
* @adreno_dev: Pointer to adreno device
|
||||
*/
|
||||
static int a6xx_rb_start(struct adreno_device *adreno_dev)
|
||||
int a6xx_rb_start(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct adreno_ringbuffer *rb;
|
||||
|
|
@ -1030,11 +1020,7 @@ static bool a6xx_hw_isidle(struct adreno_device *adreno_dev)
|
|||
return (reg & BIT(23)) ? false : true;
|
||||
}
|
||||
|
||||
/*
|
||||
* a6xx_microcode_read() - Read microcode
|
||||
* @adreno_dev: Pointer to adreno device
|
||||
*/
|
||||
static int a6xx_microcode_read(struct adreno_device *adreno_dev)
|
||||
int a6xx_microcode_read(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct adreno_firmware *sqe_fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
|
||||
const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev);
|
||||
|
|
@ -2332,17 +2318,11 @@ static struct adreno_perfcounters a6xx_perfcounters = {
|
|||
ARRAY_SIZE(a6xx_perfcounter_groups),
|
||||
};
|
||||
|
||||
static int a6xx_probe(struct platform_device *pdev,
|
||||
u32 chipid, const struct adreno_gpu_core *gpucore)
|
||||
int a6xx_probe_common(struct platform_device *pdev,
|
||||
struct adreno_device *adreno_dev, u32 chipid,
|
||||
const struct adreno_gpu_core *gpucore)
|
||||
{
|
||||
struct adreno_device *adreno_dev;
|
||||
struct adreno_gpudev *gpudev = gpucore->gpudev;
|
||||
struct kgsl_device *device;
|
||||
|
||||
adreno_dev = (struct adreno_device *)
|
||||
of_device_get_match_data(&pdev->dev);
|
||||
|
||||
memset(adreno_dev, 0, sizeof(*adreno_dev));
|
||||
|
||||
adreno_dev->gpucore = gpucore;
|
||||
adreno_dev->chipid = chipid;
|
||||
|
|
@ -2385,13 +2365,33 @@ static int a6xx_probe(struct platform_device *pdev,
|
|||
adreno_dev->perfctr_ifpc_lo =
|
||||
A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L;
|
||||
|
||||
|
||||
return adreno_device_probe(pdev, adreno_dev);
|
||||
}
|
||||
|
||||
static int a6xx_probe(struct platform_device *pdev,
|
||||
u32 chipid, const struct adreno_gpu_core *gpucore)
|
||||
{
|
||||
struct adreno_device *adreno_dev;
|
||||
struct kgsl_device *device;
|
||||
int ret;
|
||||
|
||||
adreno_dev = (struct adreno_device *)
|
||||
of_device_get_match_data(&pdev->dev);
|
||||
|
||||
memset(adreno_dev, 0, sizeof(*adreno_dev));
|
||||
|
||||
ret = a6xx_probe_common(pdev, adreno_dev, chipid, gpucore);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device = KGSL_DEVICE(adreno_dev);
|
||||
|
||||
timer_setup(&device->idle_timer, kgsl_timer, 0);
|
||||
|
||||
INIT_WORK(&device->idle_check_ws, kgsl_idle_check);
|
||||
|
||||
return adreno_device_probe(pdev, adreno_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2685,3 +2685,38 @@ struct adreno_gpudev adreno_a6xx_gpudev = {
|
|||
.read_alwayson = a6xx_read_alwayson,
|
||||
.power_ops = &adreno_power_operations,
|
||||
};
|
||||
|
||||
struct adreno_gpudev adreno_a6xx_gmu_gpudev = {
|
||||
.reg_offsets = a6xx_register_offsets,
|
||||
.probe = a6xx_gmu_device_probe,
|
||||
.start = a6xx_start,
|
||||
.snapshot = a6xx_snapshot,
|
||||
.init = a6xx_init,
|
||||
.irq_handler = a6xx_irq_handler,
|
||||
.rb_start = a6xx_rb_start,
|
||||
.regulator_enable = a6xx_sptprac_enable,
|
||||
.regulator_disable = a6xx_sptprac_disable,
|
||||
.perfcounters = &a6xx_perfcounters,
|
||||
.read_throttling_counters = a6xx_read_throttling_counters,
|
||||
.microcode_read = a6xx_microcode_read,
|
||||
.gpu_keepalive = a6xx_gpu_keepalive,
|
||||
.hw_isidle = a6xx_hw_isidle,
|
||||
.iommu_fault_block = a6xx_iommu_fault_block,
|
||||
.reset = a6xx_reset,
|
||||
.preemption_pre_ibsubmit = a6xx_preemption_pre_ibsubmit,
|
||||
.preemption_post_ibsubmit = a6xx_preemption_post_ibsubmit,
|
||||
.preemption_init = a6xx_preemption_init,
|
||||
.preemption_schedule = a6xx_preemption_schedule,
|
||||
.set_marker = a6xx_set_marker,
|
||||
.preemption_context_init = a6xx_preemption_context_init,
|
||||
.preemption_context_destroy = a6xx_preemption_context_destroy,
|
||||
.sptprac_is_on = a6xx_sptprac_is_on,
|
||||
.ccu_invalidate = a6xx_ccu_invalidate,
|
||||
.perfcounter_update = a6xx_perfcounter_update,
|
||||
#ifdef CONFIG_QCOM_KGSL_CORESIGHT
|
||||
.coresight = {&a6xx_coresight, &a6xx_coresight_cx},
|
||||
#endif
|
||||
.clk_set_options = a6xx_clk_set_options,
|
||||
.read_alwayson = a6xx_read_alwayson,
|
||||
.power_ops = &a6xx_gmu_power_ops,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,8 +7,22 @@
|
|||
#define _ADRENO_A6XX_H_
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "a6xx_reg.h"
|
||||
#include "adreno_a6xx_gmu.h"
|
||||
|
||||
extern const struct adreno_power_ops a6xx_gmu_power_ops;
|
||||
|
||||
/**
|
||||
* struct a6xx_device - Container for the a6xx_device
|
||||
*/
|
||||
struct a6xx_device {
|
||||
/** @gmu: Container for the a6xx GMU device */
|
||||
struct a6xx_gmu_device gmu;
|
||||
/** @adreno_dev: Container for the generic adreno device */
|
||||
struct adreno_device adreno_dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct a6xx_protected_regs - container for a protect register span
|
||||
|
|
@ -174,41 +188,35 @@ to_a6xx_core(struct adreno_device *adreno_dev)
|
|||
return container_of(core, struct adreno_a6xx_core, base);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* timed_poll_check() - polling *gmu* register at given offset until
|
||||
* its value changed to match expected value. The function times
|
||||
* out and returns after given duration if register is not updated
|
||||
* as expected.
|
||||
*
|
||||
* @device: Pointer to KGSL device
|
||||
* @offset: Register offset
|
||||
* @offset: Register offset in dwords
|
||||
* @expected_ret: expected register value that stops polling
|
||||
* @timout: number of jiffies to abort the polling
|
||||
* @timeout_ms: time in milliseconds to poll the register
|
||||
* @mask: bitmask to filter register value to match expected_ret
|
||||
*/
|
||||
static inline int timed_poll_check(struct kgsl_device *device,
|
||||
unsigned int offset, unsigned int expected_ret,
|
||||
unsigned int timeout, unsigned int mask)
|
||||
unsigned int timeout_ms, unsigned int mask)
|
||||
{
|
||||
unsigned long t;
|
||||
unsigned int value;
|
||||
u32 val;
|
||||
void __iomem *addr = device->gmu_core.reg_virt +
|
||||
((offset - device->gmu_core.gmu2gpu_offset) << 2);
|
||||
|
||||
t = jiffies + msecs_to_jiffies(timeout);
|
||||
if (WARN(!gmu_core_is_register_offset(device, offset),
|
||||
"Out of bounds register read: 0x%x\n", offset))
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
gmu_core_regread(device, offset, &value);
|
||||
if ((value & mask) == expected_ret)
|
||||
return 0;
|
||||
/* Wait 100us to reduce unnecessary AHB bus traffic */
|
||||
usleep_range(10, 100);
|
||||
} while (!time_after(jiffies, t));
|
||||
if (readl_poll_timeout(addr, val, (val & mask) == expected_ret, 100,
|
||||
timeout_ms * 1000))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Double check one last time */
|
||||
gmu_core_regread(device, offset, &value);
|
||||
if ((value & mask) == expected_ret)
|
||||
return 0;
|
||||
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int timed_poll_check_rscc(struct kgsl_device *device,
|
||||
|
|
@ -219,14 +227,15 @@ static inline int timed_poll_check_rscc(struct kgsl_device *device,
|
|||
unsigned long t;
|
||||
unsigned int value;
|
||||
|
||||
if (!adreno_is_a650_family(adreno_dev))
|
||||
return timed_poll_check(device, offset + RSCC_OFFSET_LEGACY,
|
||||
expected_ret, timeout, mask);
|
||||
|
||||
t = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
do {
|
||||
if (adreno_is_a650_family(adreno_dev))
|
||||
adreno_rscc_regread(adreno_dev, offset, &value);
|
||||
else
|
||||
gmu_core_regread(device, offset + RSCC_OFFSET_LEGACY,
|
||||
&value);
|
||||
adreno_rscc_regread(adreno_dev, offset, &value);
|
||||
|
||||
if ((value & mask) == expected_ret)
|
||||
return 0;
|
||||
/* Wait 100us to reduce unnecessary AHB bus traffic */
|
||||
|
|
@ -236,8 +245,7 @@ static inline int timed_poll_check_rscc(struct kgsl_device *device,
|
|||
/* Double check one last time */
|
||||
if (adreno_is_a650_family(adreno_dev))
|
||||
adreno_rscc_regread(adreno_dev, offset, &value);
|
||||
else
|
||||
gmu_core_regread(device, offset + RSCC_OFFSET_LEGACY, &value);
|
||||
|
||||
if ((value & mask) == expected_ret)
|
||||
return 0;
|
||||
|
||||
|
|
@ -273,10 +281,67 @@ void a6xx_gmu_sptprac_disable(struct adreno_device *adreno_dev);
|
|||
bool a6xx_gmu_sptprac_is_on(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_read_alwayson: Read the current always on clock value
|
||||
* a6xx_read_alwayson - Read the current always on clock value
|
||||
* @adreno_dev: An Adreno GPU handle
|
||||
*
|
||||
* Return: The current value of the GMU always on counter
|
||||
*/
|
||||
u64 a6xx_read_alwayson(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_start - Program a6xx registers
|
||||
* @adreno_dev: An Adreno GPU handle
|
||||
*
|
||||
* This function does all a6xx register programming every
|
||||
* time we boot the gpu
|
||||
*/
|
||||
void a6xx_start(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_init - Initialize a6xx resources
|
||||
* @adreno_dev: An Adreno GPU handle
|
||||
*
|
||||
* This function does a6xx specific one time initialization
|
||||
* and is invoked when the very first client opens a
|
||||
* kgsl instance
|
||||
*
|
||||
* Return: Zero on success and negative error on failure
|
||||
*/
|
||||
int a6xx_init(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_rb_start - A6xx specific ringbuffer setup
|
||||
* @adreno_dev: An Adreno GPU handle
|
||||
*
|
||||
* This function does a6xx specific ringbuffer setup and
|
||||
* attempts to submit CP INIT and bring GPU out of secure mode
|
||||
*
|
||||
* Return: Zero on success and negative error on failure
|
||||
*/
|
||||
int a6xx_rb_start(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_microcode_read - Get the cp microcode from the filesystem
|
||||
* @adreno_dev: An Adreno GPU handle
|
||||
*
|
||||
* This function gets the firmware from filesystem and sets up
|
||||
* the micorocode global buffer
|
||||
*
|
||||
* Return: Zero on success and negative error on failure
|
||||
*/
|
||||
int a6xx_microcode_read(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_probe_common - Probe common a6xx resources
|
||||
* @pdev: Pointer to the platform device
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
* @chipid: Chipid of the target
|
||||
* @gpucore: Pointer to the gpucore strucure
|
||||
*
|
||||
* This function sets up the a6xx resources common across all
|
||||
* a6xx targets
|
||||
*/
|
||||
int a6xx_probe_common(struct platform_device *pdev,
|
||||
struct adreno_device *adreno_dev, u32 chipid,
|
||||
const struct adreno_gpu_core *gpucore);
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,11 +10,10 @@
|
|||
#include "adreno_a6xx_hfi.h"
|
||||
#include "kgsl_gmu_core.h"
|
||||
|
||||
#define GMU_PWR_LEVELS 2
|
||||
#define GMU_FREQUENCY 200000000
|
||||
#define MAX_GMUFW_SIZE 0x8000 /* in bytes */
|
||||
|
||||
#define BWMEM_SIZE (12 + (4 * NUM_BW_LEVELS)) /*in bytes*/
|
||||
#define GMU_PWR_LEVELS 2
|
||||
#define MAX_GMUFW_SIZE 0x8000 /* in bytes */
|
||||
|
||||
#define GMU_VER_MAJOR(ver) (((ver) >> 28) & 0xF)
|
||||
#define GMU_VER_MINOR(ver) (((ver) >> 16) & 0xFFF)
|
||||
|
|
@ -81,8 +80,6 @@ struct gmu_block_header {
|
|||
/* GMU memdesc entries */
|
||||
#define GMU_KERNEL_ENTRIES 16
|
||||
|
||||
#define A6XX_GMU_DEVICE(_a) ((struct a6xx_gmu_device *)((_a)->gmu_core.ptr))
|
||||
|
||||
enum gmu_mem_type {
|
||||
GMU_ITCM = 0,
|
||||
GMU_ICACHE,
|
||||
|
|
@ -105,16 +102,12 @@ enum gmu_context_index {
|
|||
* @gmuaddr: GPU virtual address
|
||||
* @physaddr: Physical address of the memory object
|
||||
* @size: Size of the memory object
|
||||
* @mem_type: memory type for this memory
|
||||
* @ctx_idx: GMU IOMMU context idx
|
||||
*/
|
||||
struct gmu_memdesc {
|
||||
void *hostptr;
|
||||
uint64_t gmuaddr;
|
||||
u32 gmuaddr;
|
||||
phys_addr_t physaddr;
|
||||
uint64_t size;
|
||||
enum gmu_mem_type mem_type;
|
||||
enum gmu_context_index ctx_idx;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct rpmh_votes_t {
|
||||
|
|
@ -129,10 +122,26 @@ struct kgsl_mailbox {
|
|||
|
||||
struct icc_path;
|
||||
|
||||
struct gmu_vma_entry {
|
||||
/** @start: Starting virtual address of the vma */
|
||||
u32 start;
|
||||
/** @size: Size of this vma */
|
||||
u32 size;
|
||||
/** @next_va: Next available virtual address in this vma */
|
||||
u32 next_va;
|
||||
};
|
||||
|
||||
enum {
|
||||
GMU_PRIV_FIRST_BOOT_DONE = 0,
|
||||
GMU_PRIV_GPU_STARTED,
|
||||
GMU_PRIV_HFI_STARTED,
|
||||
GMU_PRIV_RSCC_SLEEP_DONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct a6xx_gmu_device - GMU device structure
|
||||
* @ver: GMU Version information
|
||||
* @gmu_interrupt_num: GMU interrupt number
|
||||
* @irq: GMU interrupt number
|
||||
* @fw_image: GMU FW image
|
||||
* @hfi_mem: pointer to HFI shared memory
|
||||
* @dump_mem: pointer to GMU debug dump memory
|
||||
|
|
@ -162,39 +171,98 @@ struct a6xx_gmu_device {
|
|||
u32 hfi;
|
||||
} ver;
|
||||
struct platform_device *pdev;
|
||||
int gmu_interrupt_num;
|
||||
int irq;
|
||||
const struct firmware *fw_image;
|
||||
struct gmu_memdesc *hfi_mem;
|
||||
struct gmu_memdesc *dump_mem;
|
||||
struct gmu_memdesc *gmu_log;
|
||||
struct a6xx_hfi hfi;
|
||||
/** @pwrlevels: Array of GMU power levels */
|
||||
struct {
|
||||
/** @freq: GPU frequency */
|
||||
unsigned long freq;
|
||||
/** @level: Voltage level */
|
||||
u32 level;
|
||||
} pwrlevels[MAX_GX_LEVELS];
|
||||
unsigned int num_gpupwrlevels;
|
||||
unsigned int num_bwlevels;
|
||||
unsigned int num_cnocbwlevels;
|
||||
struct rpmh_votes_t rpmh_votes;
|
||||
struct regulator *cx_gdsc;
|
||||
struct regulator *gx_gdsc;
|
||||
struct clk_bulk_data *clks;
|
||||
/** @num_clks: Number of entries in the @clks array */
|
||||
int num_clks;
|
||||
unsigned int wakeup_pwrlevel;
|
||||
unsigned int idle_level;
|
||||
unsigned int fault_count;
|
||||
struct kgsl_mailbox mailbox;
|
||||
bool preallocations;
|
||||
struct gmu_memdesc kmem_entries[GMU_KERNEL_ENTRIES];
|
||||
unsigned long kmem_bitmap;
|
||||
const struct gmu_vma_entry *vma;
|
||||
/** @gmu_globals: Array to store gmu global buffers */
|
||||
struct gmu_memdesc gmu_globals[GMU_KERNEL_ENTRIES];
|
||||
/** @global_entries: To keep track of number of gmu buffers */
|
||||
u32 global_entries;
|
||||
struct gmu_vma_entry *vma;
|
||||
unsigned int log_wptr_retention;
|
||||
/** @cm3_fault: whether gmu received a cm3 fault interrupt */
|
||||
atomic_t cm3_fault;
|
||||
/**
|
||||
* @itcm_shadow: Copy of the itcm block in firmware binary used for
|
||||
* snapshot
|
||||
*/
|
||||
void *itcm_shadow;
|
||||
/** @flags: Internal gmu flags */
|
||||
unsigned long flags;
|
||||
/** @fault: To track if we hit a gmu fault */
|
||||
bool fault;
|
||||
};
|
||||
|
||||
/* Helper function to get to a6xx gmu device from adreno device */
|
||||
struct a6xx_gmu_device *to_a6xx_gmu(struct adreno_device *adreno_dev);
|
||||
|
||||
/* Helper function to get to adreno device from a6xx gmu device */
|
||||
struct adreno_device *a6xx_gmu_to_adreno(struct a6xx_gmu_device *gmu);
|
||||
|
||||
/**
|
||||
* reserve_gmu_kernel_block() - Allocate a gmu buffer
|
||||
* @gmu: Pointer to the a6xx gmu device
|
||||
* @addr: Desired gmu virtual address
|
||||
* @size: Size of the buffer in bytes
|
||||
* @vma_id: Target gmu vma where this bufer should be mapped
|
||||
*
|
||||
* This function allocates a buffer and maps it in
|
||||
* the desired gmu vma
|
||||
*
|
||||
* Return: Pointer to the memory descriptor or error pointer on failure
|
||||
*/
|
||||
struct gmu_memdesc *reserve_gmu_kernel_block(struct a6xx_gmu_device *gmu,
|
||||
u32 addr, u32 size, u32 vma_id);
|
||||
|
||||
/**
|
||||
* a6xx_build_rpmh_tables - Build the rpmh tables
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
*
|
||||
* This function creates the gpu dcvs and bw tables
|
||||
*
|
||||
* Return: 0 on success and negative error on failure
|
||||
*/
|
||||
int a6xx_build_rpmh_tables(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_gmu_gx_is_on - Check if GX is on
|
||||
* @device: Pointer to KGSL device
|
||||
*
|
||||
* This function reads pwr status registers to check if GX
|
||||
* is on or off
|
||||
*/
|
||||
bool a6xx_gmu_gx_is_on(struct kgsl_device *device);
|
||||
|
||||
/**
|
||||
* a6xx_gmu_device_snapshot - A6XX GMU snapshot function
|
||||
* @device: Device being snapshotted
|
||||
* @snapshot: Pointer to the snapshot instance
|
||||
*
|
||||
* This is where all of the A6XX GMU specific bits and pieces are grabbed
|
||||
* into the snapshot memory
|
||||
*/
|
||||
void a6xx_gmu_device_snapshot(struct kgsl_device *device,
|
||||
struct kgsl_snapshot *snapshot);
|
||||
|
||||
/**
|
||||
* a6xx_gmu_device_probe - A6XX GMU snapshot function
|
||||
* @pdev: Pointer to the platform device
|
||||
* @chipid: Chipid of the target
|
||||
* @gpucore: Pointer to the gpucore
|
||||
*
|
||||
* The target specific probe function for gmu based a6xx targets.
|
||||
*/
|
||||
int a6xx_gmu_device_probe(struct platform_device *pdev,
|
||||
u32 chipid, const struct adreno_gpu_core *gpucore);
|
||||
#endif
|
||||
|
|
|
|||
279
drivers/gpu/msm/adreno_a6xx_gmu_snapshot.c
Normal file
279
drivers/gpu/msm/adreno_a6xx_gmu_snapshot.c
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "a6xx_reg.h"
|
||||
#include "adreno.h"
|
||||
#include "adreno_a6xx_gmu.h"
|
||||
#include "adreno_snapshot.h"
|
||||
#include "kgsl_device.h"
|
||||
|
||||
static const unsigned int a6xx_gmu_gx_registers[] = {
|
||||
/* GMU GX */
|
||||
0x1A800, 0x1A800, 0x1A810, 0x1A813, 0x1A816, 0x1A816, 0x1A818, 0x1A81B,
|
||||
0x1A81E, 0x1A81E, 0x1A820, 0x1A823, 0x1A826, 0x1A826, 0x1A828, 0x1A82B,
|
||||
0x1A82E, 0x1A82E, 0x1A830, 0x1A833, 0x1A836, 0x1A836, 0x1A838, 0x1A83B,
|
||||
0x1A83E, 0x1A83E, 0x1A840, 0x1A843, 0x1A846, 0x1A846, 0x1A880, 0x1A884,
|
||||
0x1A900, 0x1A92B, 0x1A940, 0x1A940,
|
||||
};
|
||||
|
||||
static const unsigned int a6xx_gmu_tcm_registers[] = {
|
||||
/* ITCM */
|
||||
0x1B400, 0x1C3FF,
|
||||
/* DTCM */
|
||||
0x1C400, 0x1D3FF,
|
||||
};
|
||||
|
||||
static const unsigned int a6xx_gmu_registers[] = {
|
||||
/* GMU CX */
|
||||
0x1F400, 0x1F407, 0x1F410, 0x1F412, 0x1F500, 0x1F500, 0x1F507, 0x1F50A,
|
||||
0x1F800, 0x1F804, 0x1F807, 0x1F808, 0x1F80B, 0x1F80C, 0x1F80F, 0x1F81C,
|
||||
0x1F824, 0x1F82A, 0x1F82D, 0x1F830, 0x1F840, 0x1F853, 0x1F887, 0x1F889,
|
||||
0x1F8A0, 0x1F8A2, 0x1F8A4, 0x1F8AF, 0x1F8C0, 0x1F8C3, 0x1F8D0, 0x1F8D0,
|
||||
0x1F8E4, 0x1F8E4, 0x1F8E8, 0x1F8EC, 0x1F900, 0x1F903, 0x1F940, 0x1F940,
|
||||
0x1F942, 0x1F944, 0x1F94C, 0x1F94D, 0x1F94F, 0x1F951, 0x1F954, 0x1F954,
|
||||
0x1F957, 0x1F958, 0x1F95D, 0x1F95D, 0x1F962, 0x1F962, 0x1F964, 0x1F965,
|
||||
0x1F980, 0x1F986, 0x1F990, 0x1F99E, 0x1F9C0, 0x1F9C0, 0x1F9C5, 0x1F9CC,
|
||||
0x1F9E0, 0x1F9E2, 0x1F9F0, 0x1F9F0, 0x1FA00, 0x1FA01,
|
||||
/* GMU AO */
|
||||
0x23B00, 0x23B16,
|
||||
/* GPU CC */
|
||||
0x24000, 0x24012, 0x24040, 0x24052, 0x24400, 0x24404, 0x24407, 0x2440B,
|
||||
0x24415, 0x2441C, 0x2441E, 0x2442D, 0x2443C, 0x2443D, 0x2443F, 0x24440,
|
||||
0x24442, 0x24449, 0x24458, 0x2445A, 0x24540, 0x2455E, 0x24800, 0x24802,
|
||||
0x24C00, 0x24C02, 0x25400, 0x25402, 0x25800, 0x25802, 0x25C00, 0x25C02,
|
||||
0x26000, 0x26002,
|
||||
/* GPU CC ACD */
|
||||
0x26400, 0x26416, 0x26420, 0x26427,
|
||||
};
|
||||
|
||||
static const unsigned int a660_gmu_registers[] = {
|
||||
/* GMU CX */
|
||||
0x1F408, 0x1F40D, 0x1F40F, 0x1F40F, 0x1F50B, 0x1F50B, 0x1F860, 0x1F860,
|
||||
0x1F870, 0x1F877, 0x1F8C4, 0x1F8C4, 0x1F8F0, 0x1F8F1, 0x1F948, 0x1F94A,
|
||||
0x1F966, 0x1F96B, 0x1F970, 0x1F970, 0x1F972, 0x1F979, 0x1F9CD, 0x1F9D4,
|
||||
0x1FA02, 0x1FA03, 0x20000, 0x20001, 0x20004, 0x20004, 0x20008, 0x20012,
|
||||
0x20018, 0x20018,
|
||||
};
|
||||
|
||||
struct gmu_mem_type_desc {
|
||||
struct gmu_memdesc *memdesc;
|
||||
uint32_t type;
|
||||
};
|
||||
|
||||
static size_t a6xx_snapshot_gmu_mem(struct kgsl_device *device,
|
||||
u8 *buf, size_t remain, void *priv)
|
||||
{
|
||||
struct kgsl_snapshot_gmu_mem *mem_hdr =
|
||||
(struct kgsl_snapshot_gmu_mem *)buf;
|
||||
unsigned int *data = (unsigned int *)
|
||||
(buf + sizeof(*mem_hdr));
|
||||
struct gmu_mem_type_desc *desc = priv;
|
||||
|
||||
if (priv == NULL)
|
||||
return 0;
|
||||
|
||||
if (remain < desc->memdesc->size + sizeof(*mem_hdr)) {
|
||||
dev_err(device->dev,
|
||||
"snapshot: Not enough memory for the gmu section %d\n",
|
||||
desc->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(mem_hdr, 0, sizeof(*mem_hdr));
|
||||
mem_hdr->type = desc->type;
|
||||
mem_hdr->hostaddr = (uintptr_t)desc->memdesc->hostptr;
|
||||
mem_hdr->gmuaddr = desc->memdesc->gmuaddr;
|
||||
mem_hdr->gpuaddr = 0;
|
||||
|
||||
/* Just copy the ringbuffer, there are no active IBs */
|
||||
memcpy(data, desc->memdesc->hostptr, desc->memdesc->size);
|
||||
|
||||
return desc->memdesc->size + sizeof(*mem_hdr);
|
||||
}
|
||||
|
||||
static size_t a6xx_gmu_snapshot_dtcm(struct kgsl_device *device,
|
||||
u8 *buf, size_t remain, void *priv)
|
||||
{
|
||||
struct kgsl_snapshot_gmu_mem *mem_hdr =
|
||||
(struct kgsl_snapshot_gmu_mem *)buf;
|
||||
struct a6xx_gmu_device *gmu = (struct a6xx_gmu_device *)priv;
|
||||
u32 *data = (u32 *)(buf + sizeof(*mem_hdr));
|
||||
u32 i;
|
||||
|
||||
if (remain < gmu->vma[GMU_DTCM].size + sizeof(*mem_hdr)) {
|
||||
SNAPSHOT_ERR_NOMEM(device, "GMU DTCM Memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mem_hdr->type = SNAPSHOT_GMU_MEM_BIN_BLOCK;
|
||||
mem_hdr->hostaddr = 0;
|
||||
mem_hdr->gmuaddr = gmu->vma[GMU_DTCM].start;
|
||||
mem_hdr->gpuaddr = 0;
|
||||
|
||||
for (i = 0; i < (gmu->vma[GMU_DTCM].size >> 2); i++)
|
||||
gmu_core_regread(device, A6XX_GMU_CM3_DTCM_START + i, data++);
|
||||
|
||||
return gmu->vma[GMU_DTCM].size + sizeof(*mem_hdr);
|
||||
}
|
||||
|
||||
static size_t a6xx_gmu_snapshot_itcm(struct kgsl_device *device,
|
||||
u8 *buf, size_t remain, void *priv)
|
||||
{
|
||||
struct kgsl_snapshot_gmu_mem *mem_hdr =
|
||||
(struct kgsl_snapshot_gmu_mem *)buf;
|
||||
void *dest = buf + sizeof(*mem_hdr);
|
||||
struct a6xx_gmu_device *gmu = (struct a6xx_gmu_device *)priv;
|
||||
|
||||
if (!gmu->itcm_shadow) {
|
||||
dev_err(&gmu->pdev->dev, "ITCM not captured\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (remain < gmu->vma[GMU_ITCM].size + sizeof(*mem_hdr)) {
|
||||
SNAPSHOT_ERR_NOMEM(device, "GMU ITCM Memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mem_hdr->type = SNAPSHOT_GMU_MEM_BIN_BLOCK;
|
||||
mem_hdr->hostaddr = 0;
|
||||
mem_hdr->gmuaddr = gmu->vma[GMU_ITCM].start;
|
||||
mem_hdr->gpuaddr = 0;
|
||||
|
||||
memcpy(dest, gmu->itcm_shadow, gmu->vma[GMU_ITCM].size);
|
||||
|
||||
return gmu->vma[GMU_ITCM].size + sizeof(*mem_hdr);
|
||||
}
|
||||
|
||||
static void a6xx_gmu_snapshot_memories(struct kgsl_device *device,
|
||||
struct a6xx_gmu_device *gmu, struct kgsl_snapshot *snapshot)
|
||||
{
|
||||
struct gmu_mem_type_desc desc;
|
||||
struct gmu_memdesc *md;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gmu->gmu_globals); i++) {
|
||||
|
||||
md = &gmu->gmu_globals[i];
|
||||
if (!md->size)
|
||||
continue;
|
||||
|
||||
desc.memdesc = md;
|
||||
if (md == gmu->hfi.hfi_mem)
|
||||
desc.type = SNAPSHOT_GMU_MEM_HFI;
|
||||
else if (md == gmu->gmu_log)
|
||||
desc.type = SNAPSHOT_GMU_MEM_LOG;
|
||||
else if (md == gmu->dump_mem)
|
||||
desc.type = SNAPSHOT_GMU_MEM_DEBUG;
|
||||
else
|
||||
desc.type = SNAPSHOT_GMU_MEM_BIN_BLOCK;
|
||||
|
||||
kgsl_snapshot_add_section(device,
|
||||
KGSL_SNAPSHOT_SECTION_GMU_MEMORY,
|
||||
snapshot, a6xx_snapshot_gmu_mem, &desc);
|
||||
}
|
||||
}
|
||||
|
||||
struct kgsl_snapshot_gmu_version {
|
||||
uint32_t type;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
static size_t a6xx_snapshot_gmu_version(struct kgsl_device *device,
|
||||
u8 *buf, size_t remain, void *priv)
|
||||
{
|
||||
struct kgsl_snapshot_debug *header = (struct kgsl_snapshot_debug *)buf;
|
||||
uint32_t *data = (uint32_t *) (buf + sizeof(*header));
|
||||
struct kgsl_snapshot_gmu_version *ver = priv;
|
||||
|
||||
if (remain < DEBUG_SECTION_SZ(1)) {
|
||||
SNAPSHOT_ERR_NOMEM(device, "GMU Version");
|
||||
return 0;
|
||||
}
|
||||
|
||||
header->type = ver->type;
|
||||
header->size = 1;
|
||||
|
||||
*data = ver->value;
|
||||
|
||||
return DEBUG_SECTION_SZ(1);
|
||||
}
|
||||
|
||||
static void a6xx_gmu_snapshot_versions(struct kgsl_device *device,
|
||||
struct a6xx_gmu_device *gmu,
|
||||
struct kgsl_snapshot *snapshot)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct kgsl_snapshot_gmu_version gmu_vers[] = {
|
||||
{ .type = SNAPSHOT_DEBUG_GMU_CORE_VERSION,
|
||||
.value = gmu->ver.core, },
|
||||
{ .type = SNAPSHOT_DEBUG_GMU_CORE_DEV_VERSION,
|
||||
.value = gmu->ver.core_dev, },
|
||||
{ .type = SNAPSHOT_DEBUG_GMU_PWR_VERSION,
|
||||
.value = gmu->ver.pwr, },
|
||||
{ .type = SNAPSHOT_DEBUG_GMU_PWR_DEV_VERSION,
|
||||
.value = gmu->ver.pwr_dev, },
|
||||
{ .type = SNAPSHOT_DEBUG_GMU_HFI_VERSION,
|
||||
.value = gmu->ver.hfi, },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gmu_vers); i++)
|
||||
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_DEBUG,
|
||||
snapshot, a6xx_snapshot_gmu_version,
|
||||
&gmu_vers[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* a6xx_gmu_device_snapshot() - A6XX GMU snapshot function
|
||||
* @device: Device being snapshotted
|
||||
* @snapshot: Pointer to the snapshot instance
|
||||
*
|
||||
* This is where all of the A6XX GMU specific bits and pieces are grabbed
|
||||
* into the snapshot memory
|
||||
*/
|
||||
void a6xx_gmu_device_snapshot(struct kgsl_device *device,
|
||||
struct kgsl_snapshot *snapshot)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(ADRENO_DEVICE(device));
|
||||
unsigned int val;
|
||||
|
||||
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_GMU_MEMORY,
|
||||
snapshot, a6xx_gmu_snapshot_itcm, gmu);
|
||||
|
||||
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_GMU_MEMORY,
|
||||
snapshot, a6xx_gmu_snapshot_dtcm, gmu);
|
||||
|
||||
a6xx_gmu_snapshot_versions(device, gmu, snapshot);
|
||||
|
||||
a6xx_gmu_snapshot_memories(device, gmu, snapshot);
|
||||
|
||||
/* Snapshot tcms as registers for legacy targets */
|
||||
if (adreno_is_a630(ADRENO_DEVICE(device)) ||
|
||||
adreno_is_a615_family(ADRENO_DEVICE(device)))
|
||||
adreno_snapshot_registers(device, snapshot,
|
||||
a6xx_gmu_tcm_registers,
|
||||
ARRAY_SIZE(a6xx_gmu_tcm_registers) / 2);
|
||||
|
||||
adreno_snapshot_registers(device, snapshot, a6xx_gmu_registers,
|
||||
ARRAY_SIZE(a6xx_gmu_registers) / 2);
|
||||
|
||||
/* Snapshot A660 specific GMU registers */
|
||||
if (adreno_is_a660(ADRENO_DEVICE(device)))
|
||||
adreno_snapshot_registers(device, snapshot, a660_gmu_registers,
|
||||
ARRAY_SIZE(a660_gmu_registers) / 2);
|
||||
|
||||
if (!a6xx_gmu_gx_is_on(device))
|
||||
return;
|
||||
|
||||
/* Set fence to ALLOW mode so registers can be read */
|
||||
kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
|
||||
/* Make sure the previous write posted before reading */
|
||||
wmb();
|
||||
kgsl_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val);
|
||||
|
||||
adreno_snapshot_registers(device, snapshot,
|
||||
a6xx_gmu_gx_registers,
|
||||
ARRAY_SIZE(a6xx_gmu_gx_registers) / 2);
|
||||
}
|
||||
|
|
@ -8,11 +8,23 @@
|
|||
|
||||
#include "adreno.h"
|
||||
#include "adreno_a6xx.h"
|
||||
#include "adreno_a6xx_gmu.h"
|
||||
#include "adreno_a6xx_hfi.h"
|
||||
#include "kgsl_device.h"
|
||||
#include "kgsl_trace.h"
|
||||
|
||||
/* Below section is for all structures related to HFI queues */
|
||||
#define HFI_QUEUE_DEFAULT_CNT 3
|
||||
#define HFI_QUEUE_DISPATCH_CNT 1
|
||||
#define HFI_QUEUE_MAX (HFI_QUEUE_DEFAULT_CNT + HFI_QUEUE_DISPATCH_CNT)
|
||||
|
||||
struct hfi_queue_table {
|
||||
struct hfi_queue_table_header qtbl_hdr;
|
||||
struct hfi_queue_header qhdr[HFI_QUEUE_MAX];
|
||||
};
|
||||
|
||||
/* Total header sizes + queue sizes + 16 for alignment */
|
||||
#define HFIMEM_SIZE (sizeof(struct hfi_queue_table) + 16 + \
|
||||
(HFI_QUEUE_SIZE * HFI_QUEUE_MAX))
|
||||
|
||||
#define HFI_QUEUE_OFFSET(i) \
|
||||
(ALIGN(sizeof(struct hfi_queue_table), SZ_16) + \
|
||||
((i) * HFI_QUEUE_SIZE))
|
||||
|
|
@ -28,12 +40,6 @@
|
|||
#define MSG_HDR_GET_TYPE(hdr) (((hdr) >> 16) & 0xF)
|
||||
#define MSG_HDR_GET_SEQNUM(hdr) (((hdr) >> 20) & 0xFFF)
|
||||
|
||||
/* Size is converted from Bytes to DWords */
|
||||
#define CREATE_MSG_HDR(id, size, type) \
|
||||
(((type) << 16) | ((((size) >> 2) & 0xFF) << 8) | ((id) & 0xFF))
|
||||
#define CMD_MSG_HDR(id, size) CREATE_MSG_HDR(id, size, HFI_MSG_CMD)
|
||||
#define ACK_MSG_HDR(id, size) CREATE_MSG_HDR(id, size, HFI_MSG_ACK)
|
||||
|
||||
static void a6xx_hfi_process_queue(struct a6xx_gmu_device *gmu,
|
||||
uint32_t queue_idx, struct pending_cmd *ret_cmd);
|
||||
|
||||
|
|
@ -41,7 +47,7 @@ static void a6xx_hfi_process_queue(struct a6xx_gmu_device *gmu,
|
|||
static int a6xx_hfi_queue_read(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
||||
unsigned int *output, unsigned int max_size)
|
||||
{
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi_mem;
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi.hfi_mem;
|
||||
struct hfi_queue_table *tbl = mem_addr->hostptr;
|
||||
struct hfi_queue_header *hdr = &tbl->qhdr[queue_idx];
|
||||
uint32_t *queue;
|
||||
|
|
@ -97,15 +103,16 @@ done:
|
|||
}
|
||||
|
||||
/* Size in below functions are in unit of dwords */
|
||||
static int a6xx_hfi_queue_write(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
||||
uint32_t *msg)
|
||||
static int a6xx_hfi_queue_write(struct adreno_device *adreno_dev,
|
||||
uint32_t queue_idx, uint32_t *msg)
|
||||
{
|
||||
struct hfi_queue_table *tbl = gmu->hfi_mem->hostptr;
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct hfi_queue_table *tbl = gmu->hfi.hfi_mem->hostptr;
|
||||
struct hfi_queue_header *hdr = &tbl->qhdr[queue_idx];
|
||||
uint32_t *queue;
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
uint32_t i, write, empty_space;
|
||||
uint32_t size = MSG_HDR_GET_SIZE(*msg);
|
||||
u32 align_size = ALIGN(size, SZ_4);
|
||||
uint32_t id = MSG_HDR_GET_ID(*msg);
|
||||
|
||||
if (hdr->status == HFI_QUEUE_STATUS_DISABLED)
|
||||
|
|
@ -118,24 +125,16 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
queue = HOST_QUEUE_START_ADDR(gmu->hfi_mem, queue_idx);
|
||||
queue = HOST_QUEUE_START_ADDR(gmu->hfi.hfi_mem, queue_idx);
|
||||
|
||||
trace_kgsl_hfi_send(id, size, MSG_HDR_GET_SEQNUM(*msg));
|
||||
|
||||
mutex_lock(&hfi->cmdq_mutex);
|
||||
|
||||
empty_space = (hdr->write_index >= hdr->read_index) ?
|
||||
(hdr->queue_size - (hdr->write_index - hdr->read_index))
|
||||
: (hdr->read_index - hdr->write_index);
|
||||
|
||||
if (empty_space < size) {
|
||||
dev_err(&gmu->pdev->dev,
|
||||
"Insufficient bufsize %d for msg id=%d of size %d\n",
|
||||
empty_space, id, size);
|
||||
|
||||
mutex_unlock(&hfi->cmdq_mutex);
|
||||
if (empty_space <= align_size)
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
write = hdr->write_index;
|
||||
|
||||
|
|
@ -146,14 +145,14 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
|||
|
||||
/* Cookify any non used data at the end of the write buffer */
|
||||
if (GMU_VER_MAJOR(gmu->ver.hfi) >= 2) {
|
||||
for (; write % 4; write = (write + 1) % hdr->queue_size)
|
||||
for (; i < align_size; i++) {
|
||||
queue[write] = 0xFAFAFAFA;
|
||||
write = (write + 1) % hdr->queue_size;
|
||||
}
|
||||
}
|
||||
|
||||
hdr->write_index = write;
|
||||
|
||||
mutex_unlock(&hfi->cmdq_mutex);
|
||||
|
||||
/*
|
||||
* Memory barrier to make sure packet and write index are written before
|
||||
* an interrupt is raised
|
||||
|
|
@ -161,7 +160,7 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
|||
wmb();
|
||||
|
||||
/* Send interrupt to GMU to receive the message */
|
||||
adreno_write_gmureg(ADRENO_DEVICE(hfi->kgsldev),
|
||||
adreno_write_gmureg(adreno_dev,
|
||||
ADRENO_REG_GMU_HOST2GMU_INTR_SET, 0x1);
|
||||
|
||||
return 0;
|
||||
|
|
@ -173,11 +172,10 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
|||
|
||||
|
||||
/* Sizes of the queue and message are in unit of dwords */
|
||||
void a6xx_hfi_init(struct a6xx_gmu_device *gmu)
|
||||
static void init_queues(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(hfi->kgsldev);
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi_mem;
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi.hfi_mem;
|
||||
int i;
|
||||
struct hfi_queue_table *tbl;
|
||||
struct hfi_queue_header *hdr;
|
||||
|
|
@ -222,8 +220,22 @@ void a6xx_hfi_init(struct a6xx_gmu_device *gmu)
|
|||
hdr->status = queue[i].status;
|
||||
hdr->queue_size = HFI_QUEUE_SIZE >> 2; /* convert to dwords */
|
||||
}
|
||||
}
|
||||
|
||||
mutex_init(&hfi->cmdq_mutex);
|
||||
int a6xx_hfi_init(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
|
||||
/* Allocates & maps memory for HFI */
|
||||
if (IS_ERR_OR_NULL(hfi->hfi_mem)) {
|
||||
hfi->hfi_mem = reserve_gmu_kernel_block(gmu, 0, HFIMEM_SIZE,
|
||||
GMU_NONCACHED_KERNEL);
|
||||
if (!IS_ERR(hfi->hfi_mem))
|
||||
init_queues(adreno_dev);
|
||||
}
|
||||
|
||||
return PTR_ERR_OR_ZERO(hfi->hfi_mem);
|
||||
}
|
||||
|
||||
#define HDR_CMP_SEQNUM(out_hdr, in_hdr) \
|
||||
|
|
@ -232,10 +244,10 @@ void a6xx_hfi_init(struct a6xx_gmu_device *gmu)
|
|||
static void receive_ack_cmd(struct a6xx_gmu_device *gmu, void *rcvd,
|
||||
struct pending_cmd *ret_cmd)
|
||||
{
|
||||
struct adreno_device *adreno_dev = a6xx_gmu_to_adreno(gmu);
|
||||
uint32_t *ack = rcvd;
|
||||
uint32_t hdr = ack[0];
|
||||
uint32_t req_hdr = ack[1];
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
|
||||
if (ret_cmd == NULL)
|
||||
return;
|
||||
|
|
@ -254,8 +266,8 @@ static void receive_ack_cmd(struct a6xx_gmu_device *gmu, void *rcvd,
|
|||
"HFI ACK: Cannot find sender for 0x%8.8x Waiter: 0x%8.8x\n",
|
||||
req_hdr, ret_cmd->sent_hdr);
|
||||
|
||||
adreno_set_gpu_fault(ADRENO_DEVICE(hfi->kgsldev), ADRENO_GMU_FAULT);
|
||||
adreno_dispatcher_schedule(hfi->kgsldev);
|
||||
adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
|
||||
adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev));
|
||||
}
|
||||
|
||||
#define MSG_HDR_SET_SEQNUM(hdr, num) \
|
||||
|
|
@ -267,7 +279,7 @@ static int poll_adreno_gmu_reg(struct adreno_device *adreno_dev,
|
|||
{
|
||||
unsigned int val;
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct a6xx_gmu_device *gmu = A6XX_GMU_DEVICE(device);
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
u64 ao_pre_poll, ao_post_poll;
|
||||
|
||||
|
|
@ -293,22 +305,22 @@ static int poll_adreno_gmu_reg(struct adreno_device *adreno_dev,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_cmd(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
||||
void *data, struct pending_cmd *ret_cmd)
|
||||
static int a6xx_hfi_send_cmd(struct adreno_device *adreno_dev,
|
||||
uint32_t queue_idx, void *data, struct pending_cmd *ret_cmd)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
int rc;
|
||||
uint32_t *cmd = data;
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
unsigned int seqnum = atomic_inc_return(&hfi->seqnum);
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(hfi->kgsldev);
|
||||
|
||||
*cmd = MSG_HDR_SET_SEQNUM(*cmd, seqnum);
|
||||
if (ret_cmd == NULL)
|
||||
return a6xx_hfi_queue_write(gmu, queue_idx, cmd);
|
||||
return a6xx_hfi_queue_write(adreno_dev, queue_idx, cmd);
|
||||
|
||||
ret_cmd->sent_hdr = cmd[0];
|
||||
|
||||
rc = a6xx_hfi_queue_write(gmu, queue_idx, cmd);
|
||||
rc = a6xx_hfi_queue_write(adreno_dev, queue_idx, cmd);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
@ -333,7 +345,7 @@ static int a6xx_hfi_send_cmd(struct a6xx_gmu_device *gmu, uint32_t queue_idx,
|
|||
|
||||
#define HFI_ACK_ERROR 0xffffffff
|
||||
|
||||
static int a6xx_hfi_send_generic_req(struct a6xx_gmu_device *gmu,
|
||||
static int a6xx_hfi_send_generic_req(struct adreno_device *adreno_dev,
|
||||
uint32_t queue, void *cmd)
|
||||
{
|
||||
struct pending_cmd ret_cmd;
|
||||
|
|
@ -341,9 +353,11 @@ static int a6xx_hfi_send_generic_req(struct a6xx_gmu_device *gmu,
|
|||
|
||||
memset(&ret_cmd, 0, sizeof(ret_cmd));
|
||||
|
||||
rc = a6xx_hfi_send_cmd(gmu, queue, cmd, &ret_cmd);
|
||||
rc = a6xx_hfi_send_cmd(adreno_dev, queue, cmd, &ret_cmd);
|
||||
|
||||
if (!rc && ret_cmd.results[2] == HFI_ACK_ERROR) {
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
|
||||
dev_err(&gmu->pdev->dev, "HFI ACK failure: Req 0x%8.8X\n",
|
||||
ret_cmd.results[1]);
|
||||
return -EINVAL;
|
||||
|
|
@ -352,23 +366,24 @@ static int a6xx_hfi_send_generic_req(struct a6xx_gmu_device *gmu,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_gmu_init(struct a6xx_gmu_device *gmu,
|
||||
uint32_t boot_state)
|
||||
static int a6xx_hfi_send_gmu_init(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct hfi_gmu_init_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_INIT, sizeof(cmd)),
|
||||
.seg_id = 0,
|
||||
.dbg_buffer_addr = (unsigned int) gmu->dump_mem->gmuaddr,
|
||||
.dbg_buffer_size = (unsigned int) gmu->dump_mem->size,
|
||||
.boot_state = boot_state,
|
||||
.boot_state = 0x1,
|
||||
};
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, &cmd);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_get_fw_version(struct a6xx_gmu_device *gmu,
|
||||
static int a6xx_hfi_get_fw_version(struct adreno_device *adreno_dev,
|
||||
uint32_t expected_ver, uint32_t *ver)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct hfi_fw_version_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_FW_VER, sizeof(cmd)),
|
||||
.supported_ver = expected_ver,
|
||||
|
|
@ -378,7 +393,7 @@ static int a6xx_hfi_get_fw_version(struct a6xx_gmu_device *gmu,
|
|||
|
||||
memset(&ret_cmd, 0, sizeof(ret_cmd));
|
||||
|
||||
rc = a6xx_hfi_send_cmd(gmu, HFI_CMD_ID, &cmd, &ret_cmd);
|
||||
rc = a6xx_hfi_send_cmd(adreno_dev, HFI_CMD_ID, &cmd, &ret_cmd);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
@ -392,14 +407,14 @@ static int a6xx_hfi_get_fw_version(struct a6xx_gmu_device *gmu,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_core_fw_start(struct a6xx_gmu_device *gmu)
|
||||
static int a6xx_hfi_send_core_fw_start(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct hfi_core_fw_start_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_CORE_FW_START, sizeof(cmd)),
|
||||
.handle = 0x0,
|
||||
};
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, &cmd);
|
||||
}
|
||||
|
||||
static const char * const a6xx_hfi_features[] = {
|
||||
|
|
@ -416,9 +431,10 @@ static const char *feature_to_string(uint32_t feature)
|
|||
return "unknown";
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_feature_ctrl(struct a6xx_gmu_device *gmu,
|
||||
uint32_t feature, uint32_t enable, uint32_t data)
|
||||
static int a6xx_hfi_send_feature_ctrl(struct adreno_device *adreno_dev,
|
||||
uint32_t feature, uint32_t enable, uint32_t data)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct hfi_feature_ctrl_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_FEATURE_CTRL, sizeof(cmd)),
|
||||
.feature = feature,
|
||||
|
|
@ -427,7 +443,7 @@ static int a6xx_hfi_send_feature_ctrl(struct a6xx_gmu_device *gmu,
|
|||
};
|
||||
int ret;
|
||||
|
||||
ret = a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
ret = a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, &cmd);
|
||||
if (ret)
|
||||
dev_err(&gmu->pdev->dev,
|
||||
"Unable to %s feature %s (%d)\n",
|
||||
|
|
@ -437,30 +453,31 @@ static int a6xx_hfi_send_feature_ctrl(struct a6xx_gmu_device *gmu,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_dcvstbl_v1(struct a6xx_gmu_device *gmu)
|
||||
static int a6xx_hfi_send_dcvstbl_v1(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct hfi_dcvstable_cmd *table = &gmu->hfi.dcvs_table;
|
||||
struct hfi_dcvstable_v1_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_PERF_TBL, sizeof(cmd)),
|
||||
.gpu_level_num = gmu->num_gpupwrlevels,
|
||||
.gmu_level_num = GMU_PWR_LEVELS,
|
||||
.gpu_level_num = table->gpu_level_num,
|
||||
.gmu_level_num = table->gmu_level_num,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gmu->num_gpupwrlevels; i++) {
|
||||
cmd.gx_votes[i].vote = gmu->rpmh_votes.gx_votes[i];
|
||||
/* Divide by 1000 to convert to kHz */
|
||||
cmd.gx_votes[i].freq = gmu->pwrlevels[i].freq / 1000;
|
||||
for (i = 0; i < table->gpu_level_num; i++) {
|
||||
cmd.gx_votes[i].vote = table->gx_votes[i].vote;
|
||||
cmd.gx_votes[i].freq = table->gx_votes[i].freq;
|
||||
}
|
||||
|
||||
cmd.cx_votes[0].vote = gmu->rpmh_votes.cx_votes[0];
|
||||
cmd.cx_votes[0].freq = 0;
|
||||
cmd.cx_votes[1].vote = gmu->rpmh_votes.cx_votes[1];
|
||||
cmd.cx_votes[1].freq = GMU_FREQUENCY / 1000;
|
||||
cmd.cx_votes[0].vote = table->cx_votes[0].vote;
|
||||
cmd.cx_votes[0].freq = table->cx_votes[0].freq;
|
||||
cmd.cx_votes[1].vote = table->cx_votes[1].vote;
|
||||
cmd.cx_votes[1].freq = table->cx_votes[1].freq;
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, &cmd);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_get_value(struct a6xx_gmu_device *gmu,
|
||||
static int a6xx_hfi_send_get_value(struct adreno_device *adreno_dev,
|
||||
struct hfi_get_value_req *req)
|
||||
{
|
||||
struct hfi_get_value_cmd *cmd = &req->cmd;
|
||||
|
|
@ -471,7 +488,7 @@ static int a6xx_hfi_send_get_value(struct a6xx_gmu_device *gmu,
|
|||
|
||||
cmd->hdr = CMD_MSG_HDR(H2F_MSG_GET_VALUE, sizeof(*cmd));
|
||||
|
||||
rc = a6xx_hfi_send_cmd(gmu, HFI_CMD_ID, cmd, &ret_cmd);
|
||||
rc = a6xx_hfi_send_cmd(adreno_dev, HFI_CMD_ID, cmd, &ret_cmd);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
@ -481,56 +498,13 @@ static int a6xx_hfi_send_get_value(struct a6xx_gmu_device *gmu,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_dcvstbl(struct a6xx_gmu_device *gmu)
|
||||
{
|
||||
struct hfi_dcvstable_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_PERF_TBL, sizeof(cmd)),
|
||||
.gpu_level_num = gmu->num_gpupwrlevels,
|
||||
.gmu_level_num = GMU_PWR_LEVELS,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gmu->num_gpupwrlevels; i++) {
|
||||
cmd.gx_votes[i].vote = gmu->rpmh_votes.gx_votes[i];
|
||||
/* Hardcode this to the max threshold since it is not used */
|
||||
cmd.gx_votes[i].acd = 0xFFFFFFFF;
|
||||
/* Divide by 1000 to convert to kHz */
|
||||
cmd.gx_votes[i].freq = gmu->pwrlevels[i].freq / 1000;
|
||||
}
|
||||
|
||||
cmd.cx_votes[0].vote = gmu->rpmh_votes.cx_votes[0];
|
||||
cmd.cx_votes[0].freq = 0;
|
||||
cmd.cx_votes[1].vote = gmu->rpmh_votes.cx_votes[1];
|
||||
cmd.cx_votes[1].freq = GMU_FREQUENCY / 1000;
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_bwtbl(struct a6xx_gmu_device *gmu)
|
||||
{
|
||||
struct hfi_bwtable_cmd *cmd = &gmu->hfi.bwtbl_cmd;
|
||||
|
||||
cmd->hdr = CMD_MSG_HDR(H2F_MSG_BW_VOTE_TBL, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, cmd);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_acd_tbl(struct a6xx_gmu_device *gmu)
|
||||
{
|
||||
struct hfi_acd_table_cmd *cmd = &gmu->hfi.acd_tbl_cmd;
|
||||
|
||||
cmd->hdr = CMD_MSG_HDR(H2F_MSG_ACD_TBL, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_IDX, cmd);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_test(struct a6xx_gmu_device *gmu)
|
||||
static int a6xx_hfi_send_test(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct hfi_test_cmd cmd = {
|
||||
.hdr = CMD_MSG_HDR(H2F_MSG_TEST, sizeof(cmd)),
|
||||
};
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, &cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, &cmd);
|
||||
}
|
||||
|
||||
static void receive_err_req(struct a6xx_gmu_device *gmu, void *rcvd)
|
||||
|
|
@ -611,17 +585,9 @@ static void a6xx_hfi_process_queue(struct a6xx_gmu_device *gmu,
|
|||
}
|
||||
}
|
||||
|
||||
void a6xx_hfi_receiver(unsigned long data)
|
||||
static int a6xx_hfi_verify_fw_version(struct adreno_device *adreno_dev)
|
||||
{
|
||||
/* Process all asynchronous read (firmware to host) queues */
|
||||
a6xx_hfi_process_queue((struct a6xx_gmu_device *) data, HFI_DBG_ID,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int a6xx_hfi_verify_fw_version(struct kgsl_device *device,
|
||||
struct a6xx_gmu_device *gmu)
|
||||
{
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev);
|
||||
int result;
|
||||
unsigned int ver, major, minor;
|
||||
|
|
@ -633,7 +599,8 @@ static int a6xx_hfi_verify_fw_version(struct kgsl_device *device,
|
|||
major = a6xx_core->gmu_major;
|
||||
minor = a6xx_core->gmu_minor;
|
||||
|
||||
result = a6xx_hfi_get_fw_version(gmu, GMU_VERSION(major, minor), &ver);
|
||||
result = a6xx_hfi_get_fw_version(adreno_dev, GMU_VERSION(major, minor),
|
||||
&ver);
|
||||
if (result) {
|
||||
dev_err_once(&gmu->pdev->dev,
|
||||
"Failed to get FW version via HFI\n");
|
||||
|
|
@ -657,8 +624,7 @@ static int a6xx_hfi_verify_fw_version(struct kgsl_device *device,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_lm_feature_ctrl(struct a6xx_gmu_device *gmu,
|
||||
struct adreno_device *adreno_dev)
|
||||
static int a6xx_hfi_send_lm_feature_ctrl(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct hfi_set_value_cmd req;
|
||||
|
|
@ -676,42 +642,40 @@ static int a6xx_hfi_send_lm_feature_ctrl(struct a6xx_gmu_device *gmu,
|
|||
req.subtype = 0;
|
||||
req.data = slope;
|
||||
|
||||
ret = a6xx_hfi_send_feature_ctrl(gmu, HFI_FEATURE_LM, 1,
|
||||
ret = a6xx_hfi_send_feature_ctrl(adreno_dev, HFI_FEATURE_LM, 1,
|
||||
device->pwrctrl.throttle_mask);
|
||||
|
||||
if (!ret)
|
||||
ret = a6xx_hfi_send_req(gmu, H2F_MSG_SET_VALUE, &req);
|
||||
ret = a6xx_hfi_send_req(adreno_dev, H2F_MSG_SET_VALUE, &req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int a6xx_hfi_send_acd_feature_ctrl(struct a6xx_gmu_device *gmu,
|
||||
struct adreno_device *adreno_dev)
|
||||
static int a6xx_hfi_send_acd_feature_ctrl(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
int ret = 0;
|
||||
|
||||
if (adreno_dev->acd_enabled) {
|
||||
ret = a6xx_hfi_send_acd_tbl(gmu);
|
||||
ret = a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID,
|
||||
&gmu->hfi.acd_table);
|
||||
if (!ret)
|
||||
ret = a6xx_hfi_send_feature_ctrl(gmu, HFI_FEATURE_ACD,
|
||||
1, 0);
|
||||
ret = a6xx_hfi_send_feature_ctrl(adreno_dev,
|
||||
HFI_FEATURE_ACD, 1, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int a6xx_hfi_start(struct kgsl_device *device,
|
||||
struct a6xx_gmu_device *gmu, uint32_t boot_state)
|
||||
int a6xx_hfi_start(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi_mem;
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi.hfi_mem;
|
||||
struct hfi_queue_table *tbl = mem_addr->hostptr;
|
||||
struct hfi_queue_header *hdr;
|
||||
int result, i;
|
||||
|
||||
if (test_bit(GMU_HFI_ON, &device->gmu_core.flags))
|
||||
return 0;
|
||||
|
||||
/* Force read_index to the write_index no matter what */
|
||||
for (i = 0; i < HFI_QUEUE_MAX; i++) {
|
||||
hdr = &tbl->qhdr[i];
|
||||
|
|
@ -728,25 +692,27 @@ int a6xx_hfi_start(struct kgsl_device *device,
|
|||
|
||||
/* This is legacy HFI message for A630 and A615 family firmware */
|
||||
if (adreno_is_a630(adreno_dev) || adreno_is_a615_family(adreno_dev)) {
|
||||
result = a6xx_hfi_send_gmu_init(gmu, boot_state);
|
||||
result = a6xx_hfi_send_gmu_init(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
}
|
||||
|
||||
result = a6xx_hfi_verify_fw_version(device, gmu);
|
||||
result = a6xx_hfi_verify_fw_version(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
|
||||
if (GMU_VER_MAJOR(gmu->ver.hfi) < 2)
|
||||
result = a6xx_hfi_send_dcvstbl_v1(gmu);
|
||||
result = a6xx_hfi_send_dcvstbl_v1(adreno_dev);
|
||||
else
|
||||
result = a6xx_hfi_send_dcvstbl(gmu);
|
||||
result = a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID,
|
||||
&gmu->hfi.dcvs_table);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
|
||||
result = a6xx_hfi_send_bwtbl(gmu);
|
||||
result = a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID,
|
||||
&gmu->hfi.bw_table);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* If quirk is enabled send H2F_MSG_TEST and tell the GMU
|
||||
|
|
@ -754,42 +720,52 @@ int a6xx_hfi_start(struct kgsl_device *device,
|
|||
* send H2F_MSG_CORE_FW_START and features for A640 devices
|
||||
*/
|
||||
if (GMU_VER_MAJOR(gmu->ver.hfi) >= 2) {
|
||||
|
||||
result = a6xx_hfi_send_acd_feature_ctrl(gmu, adreno_dev);
|
||||
result = a6xx_hfi_send_acd_feature_ctrl(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
|
||||
result = a6xx_hfi_send_lm_feature_ctrl(gmu, adreno_dev);
|
||||
result = a6xx_hfi_send_lm_feature_ctrl(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
|
||||
result = a6xx_hfi_send_core_fw_start(gmu);
|
||||
result = a6xx_hfi_send_core_fw_start(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
} else {
|
||||
if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) {
|
||||
result = a6xx_hfi_send_test(gmu);
|
||||
result = a6xx_hfi_send_test(adreno_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
set_bit(GMU_HFI_ON, &device->gmu_core.flags);
|
||||
return 0;
|
||||
|
||||
set_bit(GMU_PRIV_HFI_STARTED, &gmu->flags);
|
||||
|
||||
/* Request default DCVS level */
|
||||
result = kgsl_pwrctrl_set_default_gpu_pwrlevel(device);
|
||||
if (result)
|
||||
goto err;
|
||||
|
||||
/* Request default BW vote */
|
||||
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
|
||||
|
||||
err:
|
||||
if (result)
|
||||
a6xx_hfi_stop(adreno_dev);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void a6xx_hfi_stop(struct a6xx_gmu_device *gmu)
|
||||
void a6xx_hfi_stop(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi_mem;
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct gmu_memdesc *mem_addr = gmu->hfi.hfi_mem;
|
||||
struct hfi_queue_table *tbl = mem_addr->hostptr;
|
||||
struct hfi_queue_header *hdr;
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
struct kgsl_device *device = hfi->kgsldev;
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
unsigned int i;
|
||||
|
||||
|
||||
if (!test_bit(GMU_HFI_ON, &device->gmu_core.flags))
|
||||
return;
|
||||
|
||||
/* Flush HFI queues */
|
||||
for (i = 0; i < HFI_QUEUE_MAX; i++) {
|
||||
hdr = &tbl->qhdr[i];
|
||||
|
|
@ -802,11 +778,14 @@ void a6xx_hfi_stop(struct a6xx_gmu_device *gmu)
|
|||
i, hdr->read_index, hdr->write_index);
|
||||
}
|
||||
|
||||
clear_bit(GMU_HFI_ON, &device->gmu_core.flags);
|
||||
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
|
||||
|
||||
clear_bit(GMU_PRIV_HFI_STARTED, &gmu->flags);
|
||||
|
||||
}
|
||||
|
||||
/* Entry point for external HFI requests */
|
||||
int a6xx_hfi_send_req(struct a6xx_gmu_device *gmu, unsigned int id, void *data)
|
||||
int a6xx_hfi_send_req(struct adreno_device *adreno_dev, unsigned int id,
|
||||
void *data)
|
||||
{
|
||||
switch (id) {
|
||||
case H2F_MSG_GX_BW_PERF_VOTE: {
|
||||
|
|
@ -814,7 +793,7 @@ int a6xx_hfi_send_req(struct a6xx_gmu_device *gmu, unsigned int id, void *data)
|
|||
|
||||
cmd->hdr = CMD_MSG_HDR(id, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, cmd);
|
||||
}
|
||||
case H2F_MSG_PREPARE_SLUMBER: {
|
||||
struct hfi_prep_slumber_cmd *cmd = data;
|
||||
|
|
@ -824,24 +803,24 @@ int a6xx_hfi_send_req(struct a6xx_gmu_device *gmu, unsigned int id, void *data)
|
|||
|
||||
cmd->hdr = CMD_MSG_HDR(id, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, cmd);
|
||||
}
|
||||
case H2F_MSG_START: {
|
||||
struct hfi_start_cmd *cmd = data;
|
||||
|
||||
cmd->hdr = CMD_MSG_HDR(id, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, cmd);
|
||||
}
|
||||
case H2F_MSG_GET_VALUE: {
|
||||
return a6xx_hfi_send_get_value(gmu, data);
|
||||
return a6xx_hfi_send_get_value(adreno_dev, data);
|
||||
}
|
||||
case H2F_MSG_SET_VALUE: {
|
||||
struct hfi_set_value_cmd *cmd = data;
|
||||
|
||||
cmd->hdr = CMD_MSG_HDR(id, sizeof(*cmd));
|
||||
|
||||
return a6xx_hfi_send_generic_req(gmu, HFI_CMD_ID, cmd);
|
||||
return a6xx_hfi_send_generic_req(adreno_dev, HFI_CMD_ID, cmd);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
|
|
@ -854,17 +833,17 @@ int a6xx_hfi_send_req(struct a6xx_gmu_device *gmu, unsigned int id, void *data)
|
|||
irqreturn_t a6xx_hfi_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct kgsl_device *device = data;
|
||||
struct a6xx_gmu_device *gmu = A6XX_GMU_DEVICE(device);
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
unsigned int status = 0;
|
||||
|
||||
adreno_read_gmureg(ADRENO_DEVICE(device),
|
||||
adreno_read_gmureg(adreno_dev,
|
||||
ADRENO_REG_GMU_GMU2HOST_INTR_INFO, &status);
|
||||
adreno_write_gmureg(ADRENO_DEVICE(device),
|
||||
adreno_write_gmureg(adreno_dev,
|
||||
ADRENO_REG_GMU_GMU2HOST_INTR_CLR, HFI_IRQ_MASK);
|
||||
|
||||
if (status & HFI_IRQ_DBGQ_MASK)
|
||||
tasklet_hi_schedule(&hfi->tasklet);
|
||||
a6xx_hfi_process_queue(gmu, HFI_DBG_ID, NULL);
|
||||
if (status & HFI_IRQ_CM3_FAULT_MASK) {
|
||||
dev_err_ratelimited(&gmu->pdev->dev,
|
||||
"GMU CM3 fault interrupt received\n");
|
||||
|
|
|
|||
|
|
@ -10,17 +10,6 @@
|
|||
#define MAX_RCVD_SIZE (MAX_RCVD_PAYLOAD_SIZE + 3) /* dwords */
|
||||
#define HFI_MAX_MSG_SIZE (SZ_1K>>2) /* dwords */
|
||||
|
||||
/* Below section is for all structures related to HFI queues */
|
||||
#define HFI_QUEUE_DEFAULT_CNT 3
|
||||
#define HFI_QUEUE_DISPATCH_CNT 1
|
||||
#define HFI_QUEUE_MAX (HFI_QUEUE_DEFAULT_CNT + HFI_QUEUE_DISPATCH_CNT)
|
||||
|
||||
struct hfi_queue_table;
|
||||
|
||||
/* Total header sizes + queue sizes + 16 for alignment */
|
||||
#define HFIMEM_SIZE (sizeof(struct hfi_queue_table) + 16 + \
|
||||
(HFI_QUEUE_SIZE * HFI_QUEUE_MAX))
|
||||
|
||||
#define HFI_CMD_ID 0
|
||||
#define HFI_MSG_ID 1
|
||||
#define HFI_DBG_ID 2
|
||||
|
|
@ -47,7 +36,6 @@ struct hfi_queue_table;
|
|||
#define HFI_DSP_PRI_0 20
|
||||
|
||||
#define HFI_RSP_TIMEOUT 100 /* msec */
|
||||
#define HFI_H2F_CMD_IRQ_MASK BIT(0)
|
||||
|
||||
#define HFI_IRQ_MSGQ_MASK BIT(0)
|
||||
#define HFI_IRQ_SIDEMSGQ_MASK BIT(1)
|
||||
|
|
@ -58,11 +46,6 @@ struct hfi_queue_table;
|
|||
HFI_IRQ_DBGQ_MASK |\
|
||||
HFI_IRQ_CM3_FAULT_MASK)
|
||||
|
||||
#define CLKSET_OPTION_DEFAULT 0
|
||||
#define CLKSET_OPTION_CLOSEST 1
|
||||
#define CLKSET_OPTION_ATMOST 2
|
||||
#define CLKSET_OPTION_ATLEAST 3
|
||||
|
||||
#define DCVS_ACK_NONBLOCK 0
|
||||
#define DCVS_ACK_BLOCK 1
|
||||
|
||||
|
|
@ -144,17 +127,16 @@ struct hfi_queue_header {
|
|||
uint32_t write_index;
|
||||
};
|
||||
|
||||
struct hfi_queue_table {
|
||||
struct hfi_queue_table_header qtbl_hdr;
|
||||
struct hfi_queue_header qhdr[HFI_QUEUE_MAX];
|
||||
};
|
||||
#define HFI_MSG_CMD 0 /* V1 and V2 */
|
||||
#define HFI_MSG_ACK 1 /* V2 only */
|
||||
#define HFI_V1_MSG_POST 1 /* V1 only */
|
||||
#define HFI_V1_MSG_ACK 2/* V1 only */
|
||||
|
||||
enum hfi_msg_type {
|
||||
HFI_MSG_CMD = 0, /* V1 and V2 */
|
||||
HFI_MSG_ACK = 1, /* V2 only */
|
||||
HFI_V1_MSG_POST = 1, /* V1 only */
|
||||
HFI_V1_MSG_ACK = 2, /* V1 only */
|
||||
};
|
||||
/* Size is converted from Bytes to DWords */
|
||||
#define CREATE_MSG_HDR(id, size, type) \
|
||||
(((type) << 16) | ((((size) >> 2) & 0xFF) << 8) | ((id) & 0xFF))
|
||||
#define CMD_MSG_HDR(id, size) CREATE_MSG_HDR(id, size, HFI_MSG_CMD)
|
||||
#define ACK_MSG_HDR(id, size) CREATE_MSG_HDR(id, size, HFI_MSG_ACK)
|
||||
|
||||
#define H2F_MSG_INIT 0
|
||||
#define H2F_MSG_FW_VER 1
|
||||
|
|
@ -174,6 +156,7 @@ enum hfi_msg_type {
|
|||
#define H2F_MSG_PREPARE_SLUMBER 33
|
||||
#define F2H_MSG_ERR 100
|
||||
#define F2H_MSG_DEBUG 101
|
||||
#define F2H_MSG_LOG_BLOCK 102
|
||||
#define F2H_MSG_GMU_CNTR_REGISTER 110
|
||||
#define F2H_MSG_GMU_CNTR_RELEASE 111
|
||||
#define F2H_MSG_ACK 126 /* Deprecated for v2.0*/
|
||||
|
|
@ -195,20 +178,13 @@ struct hfi_gmu_init_cmd {
|
|||
uint32_t dbg_buffer_addr;
|
||||
uint32_t dbg_buffer_size;
|
||||
uint32_t boot_state;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_fw_version_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t supported_ver;
|
||||
};
|
||||
|
||||
#define ARC_VOTE_GET_PRI(_v) ((_v) & 0xFF)
|
||||
#define ARC_VOTE_GET_SEC(_v) (((_v) >> 8) & 0xFF)
|
||||
#define ARC_VOTE_GET_VLVL(_v) (((_v) >> 16) & 0xFFFF)
|
||||
|
||||
#define ARC_VOTE_SET(pri, sec, vlvl) \
|
||||
((((vlvl) & 0xFFFF) << 16) | (((sec) & 0xFF) << 8) | ((pri) & 0xFF))
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_bwtable_cmd {
|
||||
|
|
@ -222,7 +198,7 @@ struct hfi_bwtable_cmd {
|
|||
uint32_t cnoc_cmd_data[MAX_CNOC_LEVELS][MAX_CNOC_CMDS];
|
||||
uint32_t ddr_cmd_addrs[MAX_BW_CMDS];
|
||||
uint32_t ddr_cmd_data[MAX_GX_LEVELS][MAX_BW_CMDS];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct opp_gx_desc {
|
||||
uint32_t vote;
|
||||
|
|
@ -242,7 +218,7 @@ struct hfi_dcvstable_v1_cmd {
|
|||
uint32_t gmu_level_num;
|
||||
struct opp_desc gx_votes[MAX_GX_LEVELS];
|
||||
struct opp_desc cx_votes[MAX_CX_LEVELS];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_dcvstable_cmd {
|
||||
|
|
@ -251,9 +227,8 @@ struct hfi_dcvstable_cmd {
|
|||
uint32_t gmu_level_num;
|
||||
struct opp_gx_desc gx_votes[MAX_GX_LEVELS];
|
||||
struct opp_desc cx_votes[MAX_CX_LEVELS];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define HFI_ACD_INIT_VERSION 1
|
||||
#define MAX_ACD_STRIDE 2
|
||||
#define MAX_ACD_NUM_LEVELS 6
|
||||
|
||||
|
|
@ -265,18 +240,18 @@ struct hfi_acd_table_cmd {
|
|||
uint32_t stride;
|
||||
uint32_t num_levels;
|
||||
uint32_t data[MAX_ACD_NUM_LEVELS * MAX_ACD_STRIDE];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_test_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t data;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_start_cmd {
|
||||
uint32_t hdr;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_feature_ctrl_cmd {
|
||||
|
|
@ -284,27 +259,27 @@ struct hfi_feature_ctrl_cmd {
|
|||
uint32_t feature;
|
||||
uint32_t enable;
|
||||
uint32_t data;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_get_value_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t type;
|
||||
uint32_t subtype;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* Internal */
|
||||
struct hfi_get_value_req {
|
||||
struct hfi_get_value_cmd cmd;
|
||||
uint32_t data[16];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_get_value_reply_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t req_hdr;
|
||||
uint32_t data[16];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_set_value_cmd {
|
||||
|
|
@ -312,53 +287,13 @@ struct hfi_set_value_cmd {
|
|||
uint32_t type;
|
||||
uint32_t subtype;
|
||||
uint32_t data;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_core_fw_start_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t handle;
|
||||
};
|
||||
|
||||
/* CP/GFX pipeline can access, The mem_kind may imply restrictions for non-CP */
|
||||
#define MEMFLAG_GFX_ACC BIT(0)
|
||||
/* Buffer has APRIV protection in GFX PTEs */
|
||||
#define MEMFLAG_GFX_PRIV BIT(1)
|
||||
/* Buffer is read-write for GFX PTEs. A 0 indicates read-only */
|
||||
#define MEMFLAG_GFX_WRITEABLE BIT(2)
|
||||
/* GMU can access */
|
||||
#define MEMFLAG_GMU_ACC BIT(3)
|
||||
/* Buffer has APRIV protection in GMU PTEs */
|
||||
#define MEMFLAG_GMU_PRIV BIT(4)
|
||||
/* Buffer is read-write for GMU PTEs. A 0 indicates read-only */
|
||||
#define MEMFLAG_GMU_WRITEABLE BIT(5)
|
||||
/* Buffer is located in GMU's non-cached bufferable VA range */
|
||||
#define MEMFLAG_GMU_BUFFERABLE BIT(6)
|
||||
/* Buffer is located in GMU's cacheable VA range */
|
||||
#define MEMFLAG_GMU_CACHEABLE BIT(7)
|
||||
/* Host can access */
|
||||
#define MEMFLAG_HOST_ACC BIT(8)
|
||||
/*
|
||||
* Request that Host initialize the buffer.
|
||||
* Implies zero-init, unless Memkind implies otherwise
|
||||
*/
|
||||
#define MEMFLAG_HOST_INIT BIT(9)
|
||||
|
||||
#define HFI_MEMKIND_GENERIC 0
|
||||
#define HFI_MEMKIND_RB 1
|
||||
#define HFI_MEMKIND_MEMSTORE 2
|
||||
#define HFI_MEMKIND_CSW_SMMU_INFO 3
|
||||
#define HFI_MEMKIND_CSW_PRIV_NON_SECURE 4
|
||||
#define HFI_MEMKIND_CSW_PRIV_SECURE 5
|
||||
#define HFI_MEMKIND_CSW_NON_PRIV 6
|
||||
#define HFI_MEMKIND_CSW_COUNTER 7
|
||||
#define HFI_MEMKIND_CTXTREC_PERF_CNTR_SAVE_RESTORE 8
|
||||
#define HFI_MEMKIND_CTXTREC_PREEMPT_CNTR 9
|
||||
#define HFI_MEMKIND_SYS_LOG 10
|
||||
#define HFI_MEMKIND_CRASH_DUMP 11
|
||||
#define HFI_MEMKIND_MMIO_DPU 12
|
||||
#define HFI_MEMKIND_MMIO_TCSR 13
|
||||
#define HFI_MEMKIND_MMIO_QDSS_STM 14
|
||||
} __packed;
|
||||
|
||||
struct hfi_mem_alloc_desc {
|
||||
uint64_t gpu_addr;
|
||||
|
|
@ -368,21 +303,21 @@ struct hfi_mem_alloc_desc {
|
|||
uint32_t gmu_mem_handle;
|
||||
uint32_t gmu_addr;
|
||||
uint32_t size; /* Bytes */
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_mem_alloc_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t reserved; /* Padding to ensure alignment of 'desc' below */
|
||||
struct hfi_mem_alloc_desc desc;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_mem_alloc_reply_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t req_hdr;
|
||||
struct hfi_mem_alloc_desc desc;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_gx_bw_perf_vote_cmd {
|
||||
|
|
@ -390,27 +325,27 @@ struct hfi_gx_bw_perf_vote_cmd {
|
|||
uint32_t ack_type;
|
||||
uint32_t freq;
|
||||
uint32_t bw;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_fw_halt_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t en_halt;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_prep_slumber_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t bw;
|
||||
uint32_t freq;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_err_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t error_code;
|
||||
uint32_t data[16];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_debug_cmd {
|
||||
|
|
@ -418,14 +353,14 @@ struct hfi_debug_cmd {
|
|||
uint32_t type;
|
||||
uint32_t timestamp;
|
||||
uint32_t data;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_gmu_cntr_register_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t group_id;
|
||||
uint32_t countable;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_gmu_cntr_register_reply_cmd {
|
||||
|
|
@ -434,37 +369,14 @@ struct hfi_gmu_cntr_register_reply_cmd {
|
|||
uint32_t group_id;
|
||||
uint32_t countable;
|
||||
uint64_t counter_addr;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_gmu_cntr_release_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t group_id;
|
||||
uint32_t countable;
|
||||
};
|
||||
|
||||
#define CTXT_FLAG_PMODE 0x00000001
|
||||
#define CTXT_FLAG_SWITCH_INTERNAL 0x00000002
|
||||
#define CTXT_FLAG_SWITCH 0x00000008
|
||||
#define CTXT_FLAG_NOTIFY 0x00000020
|
||||
#define CTXT_FLAG_NO_FAULT_TOLERANCE 0x00000200
|
||||
#define CTXT_FLAG_PWR_RULE 0x00000800
|
||||
#define CTXT_FLAG_PRIORITY_MASK 0x0000F000
|
||||
#define CTXT_FLAG_IFH_NOP 0x00010000
|
||||
#define CTXT_FLAG_SECURE 0x00020000
|
||||
#define CTXT_FLAG_TYPE_MASK 0x01F00000
|
||||
#define CTXT_FLAG_TYPE_SHIFT 20
|
||||
#define CTXT_FLAG_TYPE_ANY 0
|
||||
#define CTXT_FLAG_TYPE_GL 1
|
||||
#define CTXT_FLAG_TYPE_CL 2
|
||||
#define CTXT_FLAG_TYPE_C2D 3
|
||||
#define CTXT_FLAG_TYPE_RS 4
|
||||
#define CTXT_FLAG_TYPE_UNKNOWN 0x1E
|
||||
#define CTXT_FLAG_PREEMPT_STYLE_MASK 0x0E000000
|
||||
#define CTXT_FLAG_PREEMPT_STYLE_SHIFT 25
|
||||
#define CTXT_FLAG_PREEMPT_STYLE_ANY 0
|
||||
#define CTXT_FLAG_PREEMPT_STYLE_RB 1
|
||||
#define CTXT_FLAG_PREEMPT_STYLE_FG 2
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_register_ctxt_cmd {
|
||||
|
|
@ -474,25 +386,19 @@ struct hfi_register_ctxt_cmd {
|
|||
uint64_t pt_addr;
|
||||
uint32_t ctxt_idr;
|
||||
uint32_t ctxt_bank;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_unregister_ctxt_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t ctxt_id;
|
||||
uint32_t ts;
|
||||
};
|
||||
|
||||
#define CMDBATCH_SWITCH CTXT_FLAG_SWITCH
|
||||
#define CMDBATCH_NOTIFY CTXT_FLAG_NOTIFY
|
||||
#define CMDBATCH_PROFILING 0x00000010
|
||||
#define CMDBATCH_EOF 0x00000100
|
||||
#define CMDBATCH_PWR_STRICT CTXT_FLAG_PWR_RULE
|
||||
} __packed;
|
||||
|
||||
struct hfi_issue_ib {
|
||||
uint64_t addr;
|
||||
uint32_t size;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_issue_cmd_cmd {
|
||||
|
|
@ -502,21 +408,21 @@ struct hfi_issue_cmd_cmd {
|
|||
uint32_t ts;
|
||||
uint32_t count;
|
||||
struct hfi_issue_ib *ibs[];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* Internal */
|
||||
struct hfi_issue_cmd_req {
|
||||
uint32_t queue;
|
||||
uint32_t ctxt_id;
|
||||
struct hfi_issue_cmd_cmd cmd;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
/* The length of *buf will be embedded in the hdr */
|
||||
struct hfi_issue_cmd_raw_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t *buf;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* Internal */
|
||||
struct hfi_issue_cmd_raw_req {
|
||||
|
|
@ -524,27 +430,27 @@ struct hfi_issue_cmd_raw_req {
|
|||
uint32_t ctxt_id;
|
||||
uint32_t len;
|
||||
uint32_t *buf;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_ts_notify_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t ctxt_id;
|
||||
uint32_t ts;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
#define TS_RETIRE_FLUSH 1
|
||||
#define TS_RETIRE_ERROR 2
|
||||
#define TS_RETIRE_PAST 3
|
||||
#define TS_RETIRE_DONE 4
|
||||
#define CMDBATCH_SUCCESS 0
|
||||
#define CMDBATCH_RETIRED 1
|
||||
#define CMDBATCH_ERROR 2
|
||||
#define CMDBATCH_SKIP 3
|
||||
|
||||
/* F2H */
|
||||
struct hfi_ts_retire_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t ctxt_id;
|
||||
uint32_t ts;
|
||||
uint32_t type;
|
||||
};
|
||||
uint32_t ret;
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_context_pointers_cmd {
|
||||
|
|
@ -552,7 +458,7 @@ struct hfi_context_pointers_cmd {
|
|||
uint32_t ctxt_id;
|
||||
uint64_t sop_addr;
|
||||
uint64_t eop_addr;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_context_rule_cmd {
|
||||
|
|
@ -560,7 +466,7 @@ struct hfi_context_rule_cmd {
|
|||
uint32_t ctxt_id;
|
||||
uint32_t type;
|
||||
uint32_t status;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* F2H */
|
||||
struct hfi_context_bad_cmd {
|
||||
|
|
@ -568,13 +474,13 @@ struct hfi_context_bad_cmd {
|
|||
uint32_t ctxt_id;
|
||||
uint32_t status;
|
||||
uint32_t error;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* H2F */
|
||||
struct hfi_context_bad_reply_cmd {
|
||||
uint32_t hdr;
|
||||
uint32_t req_hdr;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct pending_cmd - data structure to track outstanding HFI
|
||||
|
|
@ -589,35 +495,63 @@ struct pending_cmd {
|
|||
|
||||
/**
|
||||
* struct a6xx_hfi - HFI control structure
|
||||
* @kgsldev: Point to the kgsl device
|
||||
* @hfi_interrupt_num: number of GMU asserted HFI interrupt
|
||||
* @cmdq_mutex: mutex to protect command queue access from multiple senders
|
||||
* @tasklet: the thread handling received messages from GMU
|
||||
* @seqnum: atomic counter that is incremented for each message sent. The
|
||||
* value of the counter is used as sequence number for HFI message
|
||||
* @bwtbl_cmd: HFI BW table buffer
|
||||
* @acd_tbl_cmd: HFI table for ACD data
|
||||
* @bw_table: HFI BW table buffer
|
||||
* @acd_table: HFI table for ACD data
|
||||
*/
|
||||
struct a6xx_hfi {
|
||||
struct kgsl_device *kgsldev;
|
||||
int hfi_interrupt_num;
|
||||
struct mutex cmdq_mutex;
|
||||
struct tasklet_struct tasklet;
|
||||
/** @irq: HFI interrupt line */
|
||||
int irq;
|
||||
atomic_t seqnum;
|
||||
struct hfi_bwtable_cmd bwtbl_cmd;
|
||||
struct hfi_acd_table_cmd acd_tbl_cmd;
|
||||
/** @hfi_mem: Memory descriptor for the hfi memory */
|
||||
struct gmu_memdesc *hfi_mem;
|
||||
struct hfi_bwtable_cmd bw_table;
|
||||
struct hfi_acd_table_cmd acd_table;
|
||||
/** @dcvs_table: HFI table for gpu dcvs levels */
|
||||
struct hfi_dcvstable_cmd dcvs_table;
|
||||
};
|
||||
|
||||
struct a6xx_gmu_device;
|
||||
struct gmu_memdesc;
|
||||
|
||||
/* a6xx_hfi_irq_handler - IRQ handler for HFI interripts */
|
||||
irqreturn_t a6xx_hfi_irq_handler(int irq, void *data);
|
||||
int a6xx_hfi_start(struct kgsl_device *device, struct a6xx_gmu_device *gmu,
|
||||
uint32_t boot_state);
|
||||
void a6xx_hfi_stop(struct a6xx_gmu_device *gmu);
|
||||
void a6xx_hfi_receiver(unsigned long data);
|
||||
void a6xx_hfi_init(struct a6xx_gmu_device *gmu);
|
||||
|
||||
/* hfi_send_req is only for external (to HFI) requests */
|
||||
int a6xx_hfi_send_req(struct a6xx_gmu_device *gmu, unsigned int id, void *data);
|
||||
#endif /* __ADRENO_A6XX_HFI_H */
|
||||
/**
|
||||
* a6xx_hfi_start - Send the various HFIs during device boot up
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
*
|
||||
* Return: 0 on success or negative error on failure
|
||||
*/
|
||||
int a6xx_hfi_start(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_hfi_start - Send the various HFIs during device boot up
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
*
|
||||
* Return: 0 on success or negative error on failure
|
||||
*/
|
||||
void a6xx_hfi_stop(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_hfi_init - Initialize hfi resources
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
*
|
||||
* This function allocates and sets up hfi queues
|
||||
* when a process creates the very first kgsl instance
|
||||
*
|
||||
* Return: 0 on success or negative error on failure
|
||||
*/
|
||||
int a6xx_hfi_init(struct adreno_device *adreno_dev);
|
||||
|
||||
/**
|
||||
* a6xx_hfi_send_req - Send an HFI packet to GMU
|
||||
* @adreno_dev: Pointer to the adreno device
|
||||
* @id: Packet id to be sent
|
||||
* @data: Container for the data sent as part of this pcket
|
||||
*
|
||||
* Return: 0 on success or negative error on failure
|
||||
*/
|
||||
int a6xx_hfi_send_req(struct adreno_device *adreno_dev,
|
||||
unsigned int id, void *data);
|
||||
#endif
|
||||
|
|
|
|||
462
drivers/gpu/msm/adreno_a6xx_rpmh.c
Normal file
462
drivers/gpu/msm/adreno_a6xx_rpmh.c
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/regulator/qcom,rpmh-regulator-levels.h>
|
||||
#include <linux/types.h>
|
||||
#include <soc/qcom/cmd-db.h>
|
||||
#include <soc/qcom/tcs.h>
|
||||
|
||||
#include "adreno.h"
|
||||
#include "adreno_a6xx.h"
|
||||
#include "kgsl_bus.h"
|
||||
#include "kgsl_device.h"
|
||||
|
||||
struct rpmh_arc_vals {
|
||||
u32 num;
|
||||
const u16 *val;
|
||||
};
|
||||
|
||||
struct bcm {
|
||||
const char *name;
|
||||
u32 buswidth;
|
||||
u32 channels;
|
||||
u32 unit;
|
||||
u16 width;
|
||||
u8 vcd;
|
||||
bool fixed;
|
||||
};
|
||||
|
||||
struct bcm_data {
|
||||
__le32 unit;
|
||||
__le16 width;
|
||||
u8 vcd;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
struct rpmh_bw_votes {
|
||||
u32 wait_bitmask;
|
||||
u32 num_cmds;
|
||||
u32 *addrs;
|
||||
u32 num_levels;
|
||||
u32 **cmds;
|
||||
};
|
||||
|
||||
#define ARC_VOTE_SET(pri, sec, vlvl) \
|
||||
((((vlvl) & 0xFFFF) << 16) | (((sec) & 0xFF) << 8) | ((pri) & 0xFF))
|
||||
|
||||
static int rpmh_arc_cmds(struct rpmh_arc_vals *arc, const char *res_id)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
arc->val = cmd_db_read_aux_data(res_id, &len);
|
||||
|
||||
/*
|
||||
* cmd_db_read_aux_data() gives us a zero-padded table of
|
||||
* size len that contains the arc values. To determine the
|
||||
* number of arc values, we loop through the table and count
|
||||
* them until we get to the end of the buffer or hit the
|
||||
* zero padding.
|
||||
*/
|
||||
for (arc->num = 1; arc->num < (len >> 1); arc->num++) {
|
||||
if (arc->val[arc->num - 1] != 0 && arc->val[arc->num] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_volt_dependency_tbl(uint32_t *votes,
|
||||
struct rpmh_arc_vals *pri_rail, struct rpmh_arc_vals *sec_rail,
|
||||
u16 *vlvl, unsigned int num_entries)
|
||||
{
|
||||
int i, j, k;
|
||||
uint16_t cur_vlvl;
|
||||
bool found_match;
|
||||
|
||||
/* i tracks current KGSL GPU frequency table entry
|
||||
* j tracks secondary rail voltage table entry
|
||||
* k tracks primary rail voltage table entry
|
||||
*/
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
found_match = false;
|
||||
|
||||
/* Look for a primary rail voltage that matches a VLVL level */
|
||||
for (k = 0; k < pri_rail->num; k++) {
|
||||
if (pri_rail->val[k] >= vlvl[i]) {
|
||||
cur_vlvl = pri_rail->val[k];
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we did not find a matching VLVL level then abort */
|
||||
if (!found_match)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Look for a secondary rail index whose VLVL value
|
||||
* is greater than or equal to the VLVL value of the
|
||||
* corresponding index of the primary rail
|
||||
*/
|
||||
for (j = 0; j < sec_rail->num; j++) {
|
||||
if (sec_rail->val[j] >= cur_vlvl ||
|
||||
j + 1 == sec_rail->num)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == sec_rail->num)
|
||||
j = 0;
|
||||
|
||||
votes[i] = ARC_VOTE_SET(k, j, cur_vlvl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Generate a set of bandwidth votes for the list of BCMs */
|
||||
static void tcs_cmd_data(struct bcm *bcms, int count, u32 ab, u32 ib,
|
||||
u32 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
bool valid = true;
|
||||
bool commit = false;
|
||||
u64 avg, peak, x, y;
|
||||
|
||||
if (i == count - 1 || bcms[i].vcd != bcms[i + 1].vcd)
|
||||
commit = true;
|
||||
|
||||
/*
|
||||
* On a660, the "ACV" y vote should be 0x08 if there is a valid
|
||||
* vote and 0x00 if not. This is kind of hacky and a660 specific
|
||||
* but we can clean it up when we add a new target
|
||||
*/
|
||||
if (bcms[i].fixed) {
|
||||
if (!ab && !ib)
|
||||
data[i] = BCM_TCS_CMD(commit, false, 0x0, 0x0);
|
||||
else
|
||||
data[i] = BCM_TCS_CMD(commit, true, 0x0, 0x8);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Multiple the bandwidth by the width of the connection */
|
||||
avg = ((u64) ab) * bcms[i].width;
|
||||
|
||||
/* And then divide by the total width across channels */
|
||||
do_div(avg, bcms[i].buswidth * bcms[i].channels);
|
||||
|
||||
peak = ((u64) ib) * bcms[i].width;
|
||||
do_div(peak, bcms[i].buswidth);
|
||||
|
||||
/* Input bandwidth value is in KBps */
|
||||
x = avg * 1000ULL;
|
||||
do_div(x, bcms[i].unit);
|
||||
|
||||
/* Input bandwidth value is in KBps */
|
||||
y = peak * 1000ULL;
|
||||
do_div(y, bcms[i].unit);
|
||||
|
||||
/*
|
||||
* If a bandwidth value was specified but the calculation ends
|
||||
* rounding down to zero, set a minimum level
|
||||
*/
|
||||
if (ab && x == 0)
|
||||
x = 1;
|
||||
|
||||
if (ib && y == 0)
|
||||
y = 1;
|
||||
|
||||
x = min_t(u64, x, BCM_TCS_CMD_VOTE_MASK);
|
||||
y = min_t(u64, y, BCM_TCS_CMD_VOTE_MASK);
|
||||
|
||||
if (!x && !y)
|
||||
valid = false;
|
||||
|
||||
data[i] = BCM_TCS_CMD(commit, valid, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_rpmh_bw_votes(struct rpmh_bw_votes *votes)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!votes)
|
||||
return;
|
||||
|
||||
for (i = 0; votes->cmds && i < votes->num_levels; i++)
|
||||
kfree(votes->cmds[i]);
|
||||
|
||||
kfree(votes->cmds);
|
||||
kfree(votes->addrs);
|
||||
kfree(votes);
|
||||
}
|
||||
|
||||
/* Build the votes table from the specified bandwidth levels */
|
||||
static struct rpmh_bw_votes *build_rpmh_bw_votes(struct bcm *bcms,
|
||||
int bcm_count, u32 *levels, int levels_count)
|
||||
{
|
||||
struct rpmh_bw_votes *votes;
|
||||
int i;
|
||||
|
||||
votes = kzalloc(sizeof(*votes), GFP_KERNEL);
|
||||
if (!votes)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
votes->addrs = kcalloc(bcm_count, sizeof(*votes->cmds), GFP_KERNEL);
|
||||
if (!votes->addrs) {
|
||||
free_rpmh_bw_votes(votes);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
votes->cmds = kcalloc(levels_count, sizeof(*votes->cmds), GFP_KERNEL);
|
||||
if (!votes->cmds) {
|
||||
free_rpmh_bw_votes(votes);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
votes->num_cmds = bcm_count;
|
||||
votes->num_levels = levels_count;
|
||||
|
||||
/* Get the cmd-db information for each BCM */
|
||||
for (i = 0; i < bcm_count; i++) {
|
||||
size_t l;
|
||||
const struct bcm_data *data;
|
||||
|
||||
data = cmd_db_read_aux_data(bcms[i].name, &l);
|
||||
|
||||
votes->addrs[i] = cmd_db_read_addr(bcms[i].name);
|
||||
|
||||
bcms[i].unit = le32_to_cpu(data->unit);
|
||||
bcms[i].width = le16_to_cpu(data->width);
|
||||
bcms[i].vcd = data->vcd;
|
||||
}
|
||||
|
||||
for (i = 0; i < bcm_count; i++) {
|
||||
if (i == (bcm_count - 1) || bcms[i].vcd != bcms[i + 1].vcd)
|
||||
votes->wait_bitmask |= (1 << i);
|
||||
}
|
||||
|
||||
for (i = 0; i < levels_count; i++) {
|
||||
votes->cmds[i] = kcalloc(bcm_count, sizeof(u32), GFP_KERNEL);
|
||||
if (!votes->cmds[i]) {
|
||||
free_rpmh_bw_votes(votes);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
tcs_cmd_data(bcms, bcm_count, 0, levels[i], votes->cmds[i]);
|
||||
}
|
||||
|
||||
return votes;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup_gmu_arc_votes - Build the gmu voting table
|
||||
* @hfi: Pointer to hfi device
|
||||
* @pri_rail: Pointer to primary power rail vlvl table
|
||||
* @sec_rail: Pointer to second/dependent power rail vlvl table
|
||||
*
|
||||
* This function initializes the cx votes for all gmu frequencies
|
||||
* for gmu dcvs
|
||||
*/
|
||||
static int setup_cx_arc_votes(struct a6xx_hfi *hfi,
|
||||
struct rpmh_arc_vals *pri_rail, struct rpmh_arc_vals *sec_rail)
|
||||
{
|
||||
/* Hardcoded values of GMU CX voltage levels */
|
||||
u16 gmu_cx_vlvl[] = { 0, RPMH_REGULATOR_LEVEL_MIN_SVS };
|
||||
u32 cx_votes[MAX_CX_LEVELS];
|
||||
struct hfi_dcvstable_cmd *table = &hfi->dcvs_table;
|
||||
int ret, i;
|
||||
|
||||
table->gmu_level_num = 2;
|
||||
|
||||
table->cx_votes[0].freq = 0;
|
||||
table->cx_votes[1].freq = GMU_FREQUENCY / 1000;
|
||||
|
||||
ret = setup_volt_dependency_tbl(cx_votes, pri_rail,
|
||||
sec_rail, gmu_cx_vlvl, table->gmu_level_num);
|
||||
if (!ret) {
|
||||
for (i = 0; i < table->gmu_level_num; i++)
|
||||
table->cx_votes[i].vote = cx_votes[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup_gx_arc_votes - Build the gpu dcvs voting table
|
||||
* @hfi: Pointer to hfi device
|
||||
* @pri_rail: Pointer to primary power rail vlvl table
|
||||
* @sec_rail: Pointer to second/dependent power rail vlvl table
|
||||
*
|
||||
* This function initializes the gx votes for all gpu frequencies
|
||||
* for gpu dcvs
|
||||
*/
|
||||
static int setup_gx_arc_votes(struct adreno_device *adreno_dev,
|
||||
struct rpmh_arc_vals *pri_rail, struct rpmh_arc_vals *sec_rail)
|
||||
{
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
||||
struct hfi_dcvstable_cmd *table = &gmu->hfi.dcvs_table;
|
||||
u32 index;
|
||||
u16 vlvl_tbl[MAX_GX_LEVELS];
|
||||
u32 gx_votes[MAX_GX_LEVELS];
|
||||
int ret, i;
|
||||
|
||||
/* Add the zero powerlevel for the perf table */
|
||||
table->gpu_level_num = device->pwrctrl.num_pwrlevels + 1;
|
||||
|
||||
if (table->gpu_level_num > pri_rail->num ||
|
||||
table->gpu_level_num > ARRAY_SIZE(vlvl_tbl)) {
|
||||
dev_err(&gmu->pdev->dev,
|
||||
"Defined more GPU DCVS levels than RPMh can support\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
memset(vlvl_tbl, 0, sizeof(vlvl_tbl));
|
||||
|
||||
table->gx_votes[0].freq = 0;
|
||||
|
||||
/* GMU power levels are in ascending order */
|
||||
for (index = 1, i = pwr->num_pwrlevels - 1; i >= 0; i--, index++) {
|
||||
vlvl_tbl[index] = pwr->pwrlevels[i].voltage_level;
|
||||
table->gx_votes[index].freq = pwr->pwrlevels[i].gpu_freq / 1000;
|
||||
}
|
||||
|
||||
ret = setup_volt_dependency_tbl(gx_votes, pri_rail,
|
||||
sec_rail, vlvl_tbl, table->gpu_level_num);
|
||||
if (!ret) {
|
||||
for (i = 0; i < table->gpu_level_num; i++) {
|
||||
table->gx_votes[i].vote = gx_votes[i];
|
||||
table->gx_votes[i].acd = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int build_dcvs_table(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct a6xx_hfi *hfi = &gmu->hfi;
|
||||
struct rpmh_arc_vals gx_arc, cx_arc, mx_arc;
|
||||
int ret;
|
||||
|
||||
hfi->dcvs_table.hdr = CMD_MSG_HDR(H2F_MSG_PERF_TBL,
|
||||
sizeof(hfi->dcvs_table));
|
||||
|
||||
ret = rpmh_arc_cmds(&gx_arc, "gfx.lvl");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rpmh_arc_cmds(&cx_arc, "cx.lvl");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rpmh_arc_cmds(&mx_arc, "mx.lvl");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = setup_cx_arc_votes(hfi, &cx_arc, &mx_arc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return setup_gx_arc_votes(adreno_dev, &gx_arc, &mx_arc);
|
||||
}
|
||||
|
||||
/*
|
||||
* List of Bus Control Modules (BCMs) that need to be configured for the GPU
|
||||
* to access DDR. For each bus level we will generate a vote each BC
|
||||
*/
|
||||
static struct bcm a660_ddr_bcms[] = {
|
||||
{ .name = "SH0", .buswidth = 16 },
|
||||
{ .name = "MC0", .buswidth = 4 },
|
||||
{ .name = "ACV", .fixed = true },
|
||||
};
|
||||
|
||||
/* Same as above, but for the CNOC BCMs */
|
||||
static struct bcm a660_cnoc_bcms[] = {
|
||||
{ .name = "CN0", .buswidth = 4 },
|
||||
};
|
||||
|
||||
static void build_bw_table_cmd(struct hfi_bwtable_cmd *cmd,
|
||||
struct rpmh_bw_votes *ddr, struct rpmh_bw_votes *cnoc)
|
||||
{
|
||||
u32 i, j;
|
||||
|
||||
cmd->hdr = CMD_MSG_HDR(H2F_MSG_BW_VOTE_TBL, sizeof(*cmd));
|
||||
cmd->bw_level_num = ddr->num_levels;
|
||||
cmd->ddr_cmds_num = ddr->num_cmds;
|
||||
cmd->ddr_wait_bitmask = ddr->wait_bitmask;
|
||||
|
||||
for (i = 0; i < ddr->num_cmds; i++)
|
||||
cmd->ddr_cmd_addrs[i] = ddr->addrs[i];
|
||||
|
||||
for (i = 0; i < ddr->num_levels; i++)
|
||||
for (j = 0; j < ddr->num_cmds; j++)
|
||||
cmd->ddr_cmd_data[i][j] = (u32) ddr->cmds[i][j];
|
||||
|
||||
if (!cnoc)
|
||||
return;
|
||||
|
||||
cmd->cnoc_cmds_num = cnoc->num_cmds;
|
||||
cmd->cnoc_wait_bitmask = cnoc->wait_bitmask;
|
||||
|
||||
for (i = 0; i < cnoc->num_cmds; i++)
|
||||
cmd->cnoc_cmd_addrs[i] = cnoc->addrs[i];
|
||||
|
||||
for (i = 0; i < cnoc->num_levels; i++)
|
||||
for (j = 0; j < cnoc->num_cmds; j++)
|
||||
cmd->cnoc_cmd_data[i][j] = (u32) cnoc->cmds[i][j];
|
||||
}
|
||||
|
||||
static int build_bw_table(struct adreno_device *adreno_dev)
|
||||
{
|
||||
struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev);
|
||||
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
||||
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
||||
struct rpmh_bw_votes *ddr, *cnoc = NULL;
|
||||
u32 *cnoc_table;
|
||||
u32 count;
|
||||
|
||||
ddr = build_rpmh_bw_votes(a660_ddr_bcms, ARRAY_SIZE(a660_ddr_bcms),
|
||||
pwr->ddr_table, pwr->ddr_table_count);
|
||||
if (IS_ERR(ddr))
|
||||
return PTR_ERR(ddr);
|
||||
|
||||
cnoc_table = kgsl_bus_get_table(device->pdev, "qcom,bus-table-cnoc",
|
||||
&count);
|
||||
|
||||
if (count > 0)
|
||||
cnoc = build_rpmh_bw_votes(a660_cnoc_bcms,
|
||||
ARRAY_SIZE(a660_cnoc_bcms), cnoc_table, count);
|
||||
|
||||
kfree(cnoc_table);
|
||||
|
||||
if (IS_ERR(cnoc)) {
|
||||
free_rpmh_bw_votes(ddr);
|
||||
return PTR_ERR(cnoc);
|
||||
}
|
||||
|
||||
build_bw_table_cmd(&gmu->hfi.bw_table, ddr, cnoc);
|
||||
|
||||
free_rpmh_bw_votes(ddr);
|
||||
free_rpmh_bw_votes(cnoc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int a6xx_build_rpmh_tables(struct adreno_device *adreno_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = build_dcvs_table(adreno_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return build_bw_table(adreno_dev);
|
||||
}
|
||||
|
|
@ -42,7 +42,6 @@ static const char * const clocks[] = {
|
|||
|
||||
static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
|
||||
int requested_state);
|
||||
static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state);
|
||||
static int kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state);
|
||||
static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
|
||||
unsigned int state);
|
||||
|
|
@ -1276,7 +1275,7 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
|
|||
}
|
||||
}
|
||||
|
||||
static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
|
||||
void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
|
||||
{
|
||||
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
||||
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ int kgsl_pwrctrl_set_default_gpu_pwrlevel(struct kgsl_device *device);
|
|||
*/
|
||||
void kgsl_pwrctrl_request_state(struct kgsl_device *device, u32 state);
|
||||
|
||||
/**
|
||||
* kgsl_pwrctrl_axi - Propagate bus votes during slumber entry and exit
|
||||
* @device: Pointer to the kgsl device
|
||||
* @state: Whether we are going to slumber or coming out of slumber
|
||||
*
|
||||
* This function will propagate the default bus vote when coming out of
|
||||
* slumber and set bus bandwidth to 0 when going into slumber
|
||||
*/
|
||||
void kgsl_pwrctrl_axi(struct kgsl_device *device, int state);
|
||||
|
||||
/**
|
||||
* kgsl_idle_check - kgsl idle function
|
||||
* @work: work item being run by the function
|
||||
|
|
|
|||
Loading…
Reference in a new issue