Commit 8167e6fa authored by Jonathan Marek's avatar Jonathan Marek Committed by Rob Clark
Browse files

drm/msm/a6xx: HFI v2 for A640 and A650



Add HFI v2 code paths required by Adreno 640 and 650 GPUs.

Signed-off-by: default avatarJonathan Marek <jonathan@marek.ca>
Reviewed-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
parent a83366ef
Loading
Loading
Loading
Loading
+52 −14
Original line number Original line Diff line number Diff line
@@ -136,8 +136,6 @@ static void __a6xx_gmu_set_freq(struct a6xx_gmu *gmu, int index)
	if (ret)
	if (ret)
		dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret);
		dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret);


	gmu->freq = gmu->gpu_freqs[index];

	/*
	/*
	 * Eventually we will want to scale the path vote with the frequency but
	 * Eventually we will want to scale the path vote with the frequency but
	 * for now leave it at max so that the performance is nominal.
	 * for now leave it at max so that the performance is nominal.
@@ -162,7 +160,12 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, unsigned long freq)


	gmu->current_perf_index = perf_index;
	gmu->current_perf_index = perf_index;


	if (gmu->legacy)
		__a6xx_gmu_set_freq(gmu, perf_index);
		__a6xx_gmu_set_freq(gmu, perf_index);
	else
		a6xx_hfi_set_freq(gmu, perf_index);

	gmu->freq = gmu->gpu_freqs[perf_index];
}
}


unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu)
unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu)
@@ -242,8 +245,13 @@ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)


	switch (state) {
	switch (state) {
	case GMU_OOB_GPU_SET:
	case GMU_OOB_GPU_SET:
		if (gmu->legacy) {
			request = GMU_OOB_GPU_SET_REQUEST;
			request = GMU_OOB_GPU_SET_REQUEST;
			ack = GMU_OOB_GPU_SET_ACK;
			ack = GMU_OOB_GPU_SET_ACK;
		} else {
			request = GMU_OOB_GPU_SET_REQUEST_NEW;
			ack = GMU_OOB_GPU_SET_ACK_NEW;
		}
		name = "GPU_SET";
		name = "GPU_SET";
		break;
		break;
	case GMU_OOB_BOOT_SLUMBER:
	case GMU_OOB_BOOT_SLUMBER:
@@ -282,6 +290,13 @@ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
/* Clear a pending OOB state in the GMU */
/* Clear a pending OOB state in the GMU */
void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
{
{
	if (!gmu->legacy) {
		WARN_ON(state != GMU_OOB_GPU_SET);
		gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET,
			1 << GMU_OOB_GPU_SET_CLEAR_NEW);
		return;
	}

	switch (state) {
	switch (state) {
	case GMU_OOB_GPU_SET:
	case GMU_OOB_GPU_SET:
		gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET,
		gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET,
@@ -304,6 +319,9 @@ static int a6xx_sptprac_enable(struct a6xx_gmu *gmu)
	int ret;
	int ret;
	u32 val;
	u32 val;


	if (!gmu->legacy)
		return 0;

	gmu_write(gmu, REG_A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, 0x778000);
	gmu_write(gmu, REG_A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, 0x778000);


	ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, val,
	ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, val,
@@ -323,6 +341,9 @@ static void a6xx_sptprac_disable(struct a6xx_gmu *gmu)
	u32 val;
	u32 val;
	int ret;
	int ret;


	if (!gmu->legacy)
		return;

	/* Make sure retention is on */
	/* Make sure retention is on */
	gmu_rmw(gmu, REG_A6XX_GPU_CC_GX_GDSCR, 0, (1 << 11));
	gmu_rmw(gmu, REG_A6XX_GPU_CC_GX_GDSCR, 0, (1 << 11));


@@ -366,6 +387,11 @@ static int a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu)
	if (gmu->idle_level < GMU_IDLE_STATE_SPTP)
	if (gmu->idle_level < GMU_IDLE_STATE_SPTP)
		a6xx_sptprac_disable(gmu);
		a6xx_sptprac_disable(gmu);


	if (!gmu->legacy) {
		ret = a6xx_hfi_send_prep_slumber(gmu);
		goto out;
	}

	/* Tell the GMU to get ready to slumber */
	/* Tell the GMU to get ready to slumber */
	gmu_write(gmu, REG_A6XX_GMU_BOOT_SLUMBER_OPTION, 1);
	gmu_write(gmu, REG_A6XX_GMU_BOOT_SLUMBER_OPTION, 1);


@@ -381,6 +407,7 @@ static int a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu)
		}
		}
	}
	}


out:
	/* Put fence into allow mode */
	/* Put fence into allow mode */
	gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
	gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
	return ret;
	return ret;
@@ -650,9 +677,11 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
	if (ret)
	if (ret)
		return ret;
		return ret;


	if (gmu->legacy) {
		ret = a6xx_gmu_gfx_rail_on(gmu);
		ret = a6xx_gmu_gfx_rail_on(gmu);
		if (ret)
		if (ret)
			return ret;
			return ret;
	}


	/* Enable SPTP_PC if the CPU is responsible for it */
	/* Enable SPTP_PC if the CPU is responsible for it */
	if (gmu->idle_level < GMU_IDLE_STATE_SPTP) {
	if (gmu->idle_level < GMU_IDLE_STATE_SPTP) {
@@ -771,7 +800,10 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
	enable_irq(gmu->hfi_irq);
	enable_irq(gmu->hfi_irq);


	/* Set the GPU to the current freq */
	/* Set the GPU to the current freq */
	if (gmu->legacy)
		__a6xx_gmu_set_freq(gmu, gmu->current_perf_index);
		__a6xx_gmu_set_freq(gmu, gmu->current_perf_index);
	else
		a6xx_hfi_set_freq(gmu, gmu->current_perf_index);


	/*
	/*
	 * "enable" the GX power domain which won't actually do anything but it
	 * "enable" the GX power domain which won't actually do anything but it
@@ -1270,6 +1302,7 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu)


int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
{
{
	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
	struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
	struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
	struct platform_device *pdev = of_find_device_by_node(node);
	struct platform_device *pdev = of_find_device_by_node(node);
	int ret;
	int ret;
@@ -1295,15 +1328,20 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
	if (ret)
	if (ret)
		goto err_put_device;
		goto err_put_device;


	/* Allocate memory for for the HFI queues */
	if (!adreno_is_a640(adreno_gpu) && !adreno_is_a650(adreno_gpu)) {
	ret = a6xx_gmu_memory_alloc(gmu, &gmu->hfi, SZ_16K, 0);
		/* HFI v1, has sptprac */
	if (ret)
		gmu->legacy = true;
		goto err_memory;


		/* Allocate memory for the GMU debug region */
		/* Allocate memory for the GMU debug region */
		ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_16K, 0);
		ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_16K, 0);
		if (ret)
		if (ret)
			goto err_memory;
			goto err_memory;
	}

	/* Allocate memory for for the HFI queues */
	ret = a6xx_gmu_memory_alloc(gmu, &gmu->hfi, SZ_16K, 0);
	if (ret)
		goto err_memory;


	/* Map the GMU registers */
	/* Map the GMU registers */
	gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu");
	gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu");
+7 −0
Original line number Original line Diff line number Diff line
@@ -79,6 +79,7 @@ struct a6xx_gmu {


	bool initialized;
	bool initialized;
	bool hung;
	bool hung;
	bool legacy; /* a618 or a630 */
};
};


static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset)
static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset)
@@ -159,10 +160,16 @@ enum a6xx_gmu_oob_state {
#define GMU_OOB_GPU_SET_ACK	24
#define GMU_OOB_GPU_SET_ACK	24
#define GMU_OOB_GPU_SET_CLEAR	24
#define GMU_OOB_GPU_SET_CLEAR	24


#define GMU_OOB_GPU_SET_REQUEST_NEW	30
#define GMU_OOB_GPU_SET_ACK_NEW		31
#define GMU_OOB_GPU_SET_CLEAR_NEW	31



void a6xx_hfi_init(struct a6xx_gmu *gmu);
void a6xx_hfi_init(struct a6xx_gmu *gmu);
int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state);
int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state);
void a6xx_hfi_stop(struct a6xx_gmu *gmu);
void a6xx_hfi_stop(struct a6xx_gmu *gmu);
int a6xx_hfi_send_prep_slumber(struct a6xx_gmu *gmu);
int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, int index);


bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu);
bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu);
bool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu);
bool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu);
+4 −2
Original line number Original line Diff line number Diff line
@@ -566,8 +566,10 @@ out:
	 */
	 */
	a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET);
	a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET);


	if (a6xx_gpu->gmu.legacy) {
		/* Take the GMU out of its special boot mode */
		/* Take the GMU out of its special boot mode */
		a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_BOOT_SLUMBER);
		a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_BOOT_SLUMBER);
	}


	return ret;
	return ret;
}
}
+111 −6
Original line number Original line Diff line number Diff line
@@ -17,10 +17,14 @@ static const char * const a6xx_hfi_msg_id[] = {
	HFI_MSG_ID(HFI_H2F_MSG_BW_TABLE),
	HFI_MSG_ID(HFI_H2F_MSG_BW_TABLE),
	HFI_MSG_ID(HFI_H2F_MSG_PERF_TABLE),
	HFI_MSG_ID(HFI_H2F_MSG_PERF_TABLE),
	HFI_MSG_ID(HFI_H2F_MSG_TEST),
	HFI_MSG_ID(HFI_H2F_MSG_TEST),
	HFI_MSG_ID(HFI_H2F_MSG_START),
	HFI_MSG_ID(HFI_H2F_MSG_CORE_FW_START),
	HFI_MSG_ID(HFI_H2F_MSG_GX_BW_PERF_VOTE),
	HFI_MSG_ID(HFI_H2F_MSG_PREPARE_SLUMBER),
};
};


static int a6xx_hfi_queue_read(struct a6xx_hfi_queue *queue, u32 *data,
static int a6xx_hfi_queue_read(struct a6xx_gmu *gmu,
		u32 dwords)
	struct a6xx_hfi_queue *queue, u32 *data, u32 dwords)
{
{
	struct a6xx_hfi_queue_header *header = queue->header;
	struct a6xx_hfi_queue_header *header = queue->header;
	u32 i, hdr, index = header->read_index;
	u32 i, hdr, index = header->read_index;
@@ -48,6 +52,9 @@ static int a6xx_hfi_queue_read(struct a6xx_hfi_queue *queue, u32 *data,
		index = (index + 1) % header->size;
		index = (index + 1) % header->size;
	}
	}


	if (!gmu->legacy)
		index = ALIGN(index, 4) % header->size;

	header->read_index = index;
	header->read_index = index;
	return HFI_HEADER_SIZE(hdr);
	return HFI_HEADER_SIZE(hdr);
}
}
@@ -73,6 +80,12 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu *gmu,
		index = (index + 1) % header->size;
		index = (index + 1) % header->size;
	}
	}


	/* Cookify any non used data at the end of the write buffer */
	if (!gmu->legacy) {
		for (; index % 4; index = (index + 1) % header->size)
			queue->data[index] = 0xfafafafa;
	}

	header->write_index = index;
	header->write_index = index;
	spin_unlock(&queue->lock);
	spin_unlock(&queue->lock);


@@ -106,7 +119,7 @@ static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum,
		struct a6xx_hfi_msg_response resp;
		struct a6xx_hfi_msg_response resp;


		/* Get the next packet */
		/* Get the next packet */
		ret = a6xx_hfi_queue_read(queue, (u32 *) &resp,
		ret = a6xx_hfi_queue_read(gmu, queue, (u32 *) &resp,
			sizeof(resp) >> 2);
			sizeof(resp) >> 2);


		/* If the queue is empty our response never made it */
		/* If the queue is empty our response never made it */
@@ -195,6 +208,28 @@ static int a6xx_hfi_get_fw_version(struct a6xx_gmu *gmu, u32 *version)
		version, sizeof(*version));
		version, sizeof(*version));
}
}


static int a6xx_hfi_send_perf_table_v1(struct a6xx_gmu *gmu)
{
	struct a6xx_hfi_msg_perf_table_v1 msg = { 0 };
	int i;

	msg.num_gpu_levels = gmu->nr_gpu_freqs;
	msg.num_gmu_levels = gmu->nr_gmu_freqs;

	for (i = 0; i < gmu->nr_gpu_freqs; i++) {
		msg.gx_votes[i].vote = gmu->gx_arc_votes[i];
		msg.gx_votes[i].freq = gmu->gpu_freqs[i] / 1000;
	}

	for (i = 0; i < gmu->nr_gmu_freqs; i++) {
		msg.cx_votes[i].vote = gmu->cx_arc_votes[i];
		msg.cx_votes[i].freq = gmu->gmu_freqs[i] / 1000;
	}

	return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_PERF_TABLE, &msg, sizeof(msg),
		NULL, 0);
}

static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
{
{
	struct a6xx_hfi_msg_perf_table msg = { 0 };
	struct a6xx_hfi_msg_perf_table msg = { 0 };
@@ -205,6 +240,7 @@ static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)


	for (i = 0; i < gmu->nr_gpu_freqs; i++) {
	for (i = 0; i < gmu->nr_gpu_freqs; i++) {
		msg.gx_votes[i].vote = gmu->gx_arc_votes[i];
		msg.gx_votes[i].vote = gmu->gx_arc_votes[i];
		msg.gx_votes[i].acd = 0xffffffff;
		msg.gx_votes[i].freq = gmu->gpu_freqs[i] / 1000;
		msg.gx_votes[i].freq = gmu->gpu_freqs[i] / 1000;
	}
	}


@@ -306,7 +342,45 @@ static int a6xx_hfi_send_test(struct a6xx_gmu *gmu)
		NULL, 0);
		NULL, 0);
}
}


int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
int a6xx_hfi_send_start(struct a6xx_gmu *gmu)
{
	struct a6xx_hfi_msg_start msg = { 0 };

	return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_START, &msg, sizeof(msg),
		NULL, 0);
}

int a6xx_hfi_send_core_fw_start(struct a6xx_gmu *gmu)
{
	struct a6xx_hfi_msg_core_fw_start msg = { 0 };

	return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_CORE_FW_START, &msg,
		sizeof(msg), NULL, 0);
}

int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, int index)
{
	struct a6xx_hfi_gx_bw_perf_vote_cmd msg = { 0 };

	msg.ack_type = 1; /* blocking */
	msg.freq = index;
	msg.bw = 0; /* TODO: bus scaling */

	return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_GX_BW_PERF_VOTE, &msg,
		sizeof(msg), NULL, 0);
}

int a6xx_hfi_send_prep_slumber(struct a6xx_gmu *gmu)
{
	struct a6xx_hfi_prep_slumber_cmd msg = { 0 };

	/* TODO: should freq and bw fields be non-zero ? */

	return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_PREPARE_SLUMBER, &msg,
		sizeof(msg), NULL, 0);
}

static int a6xx_hfi_start_v1(struct a6xx_gmu *gmu, int boot_state)
{
{
	int ret;
	int ret;


@@ -324,7 +398,7 @@ int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
	 * the GMU firmware
	 * the GMU firmware
	 */
	 */


	ret = a6xx_hfi_send_perf_table(gmu);
	ret = a6xx_hfi_send_perf_table_v1(gmu);
	if (ret)
	if (ret)
		return ret;
		return ret;


@@ -341,6 +415,37 @@ int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
	return 0;
	return 0;
}
}


int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
{
	int ret;

	if (gmu->legacy)
		return a6xx_hfi_start_v1(gmu, boot_state);


	ret = a6xx_hfi_send_perf_table(gmu);
	if (ret)
		return ret;

	ret = a6xx_hfi_send_bw_table(gmu);
	if (ret)
		return ret;

	ret = a6xx_hfi_send_core_fw_start(gmu);
	if (ret)
		return ret;

	/*
	 * Downstream driver sends this in its "a6xx_hw_init" equivalent,
	 * but seems to be no harm in sending it here
	 */
	ret = a6xx_hfi_send_start(gmu);
	if (ret)
		return ret;

	return 0;
}

void a6xx_hfi_stop(struct a6xx_gmu *gmu)
void a6xx_hfi_stop(struct a6xx_gmu *gmu)
{
{
	int i;
	int i;
@@ -415,5 +520,5 @@ void a6xx_hfi_init(struct a6xx_gmu *gmu)
	/* GMU response queue */
	/* GMU response queue */
	offset += SZ_4K;
	offset += SZ_4K;
	a6xx_hfi_queue_init(&gmu->queues[1], &headers[1], hfi->virt + offset,
	a6xx_hfi_queue_init(&gmu->queues[1], &headers[1], hfi->virt + offset,
		hfi->iova + offset, 4);
		hfi->iova + offset, gmu->legacy ? 4 : 1);
}
}
+48 −2
Original line number Original line Diff line number Diff line
@@ -51,7 +51,8 @@ struct a6xx_hfi_queue {
/* HFI message types */
/* HFI message types */


#define HFI_MSG_CMD 0
#define HFI_MSG_CMD 0
#define HFI_MSG_ACK 2
#define HFI_MSG_ACK 1
#define HFI_MSG_ACK_V1 2


#define HFI_F2H_MSG_ACK 126
#define HFI_F2H_MSG_ACK 126


@@ -94,7 +95,13 @@ struct perf_level {
	u32 freq;
	u32 freq;
};
};


struct a6xx_hfi_msg_perf_table {
struct perf_gx_level {
	u32 vote;
	u32 acd;
	u32 freq;
};

struct a6xx_hfi_msg_perf_table_v1 {
	u32 header;
	u32 header;
	u32 num_gpu_levels;
	u32 num_gpu_levels;
	u32 num_gmu_levels;
	u32 num_gmu_levels;
@@ -103,6 +110,15 @@ struct a6xx_hfi_msg_perf_table {
	struct perf_level cx_votes[4];
	struct perf_level cx_votes[4];
};
};


struct a6xx_hfi_msg_perf_table {
	u32 header;
	u32 num_gpu_levels;
	u32 num_gmu_levels;

	struct perf_gx_level gx_votes[16];
	struct perf_level cx_votes[4];
};

#define HFI_H2F_MSG_BW_TABLE 3
#define HFI_H2F_MSG_BW_TABLE 3


struct a6xx_hfi_msg_bw_table {
struct a6xx_hfi_msg_bw_table {
@@ -124,4 +140,34 @@ struct a6xx_hfi_msg_test {
	u32 header;
	u32 header;
};
};


#define HFI_H2F_MSG_START 10

struct a6xx_hfi_msg_start {
	u32 header;
};

#define HFI_H2F_MSG_CORE_FW_START 14

struct a6xx_hfi_msg_core_fw_start {
	u32 header;
	u32 handle;
};

#define HFI_H2F_MSG_GX_BW_PERF_VOTE 30

struct a6xx_hfi_gx_bw_perf_vote_cmd {
	u32 header;
	u32 ack_type;
	u32 freq;
	u32 bw;
};

#define HFI_H2F_MSG_PREPARE_SLUMBER 33

struct a6xx_hfi_prep_slumber_cmd {
	u32 header;
	u32 bw;
	u32 freq;
};

#endif
#endif