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:
Harshdeep Dhatt 2020-04-04 15:32:59 -06:00
commit 90da593e71
14 changed files with 2399 additions and 1989 deletions

View file

@ -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 \

View file

@ -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,

View file

@ -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;

View file

@ -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

View file

@ -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,
};

View file

@ -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

View file

@ -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

View 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);
}

View file

@ -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");

View file

@ -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

View 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);
}

View file

@ -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;

View file

@ -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