Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 769f5496 authored by Kyle Piefer's avatar Kyle Piefer
Browse files

msm: kgsl: Check again if HFI fails to find ack



When an interrupt comes to tell the host that there
is a new HFI message from the GMU, KGSL schedules
a tasklet to process the message queues.
If the message is an ack, the sender of the message
being acked is waiting for a completion to be signaled.
But when the system is very busy, the HFI tasklet might not get
to run before the completion times out.

When sending a message, check the queues one more
time before returning failure to prevent these false
instances of not receiving an ack. In order to make sure
this second check does not read the queues at the same
time as the tasklet, add a new HFI spinlock.

Change-Id: Ib0537af9137e5e243067519c26dc4939217b76e7
Signed-off-by: default avatarKyle Piefer <kpiefer@codeaurora.org>
parent 47cb4b24
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1307,9 +1307,10 @@ static int gmu_probe(struct kgsl_device *device, struct device_node *node)
	disable_irq(gmu->gmu_interrupt_num);
	disable_irq(hfi->hfi_interrupt_num);

	tasklet_init(&hfi->tasklet, hfi_receiver, (unsigned long)device);
	tasklet_init(&hfi->tasklet, hfi_receiver, (unsigned long) gmu);
	INIT_LIST_HEAD(&hfi->msglist);
	spin_lock_init(&hfi->msglock);
	spin_lock_init(&hfi->read_queue_lock);
	hfi->kgsldev = device;

	/* Retrieves GMU/GPU power level configurations*/
+62 −62
Original line number Diff line number Diff line
@@ -45,10 +45,13 @@
	 (((minor) & 0x7FFFFF) << 5) | \
	 ((branch) & 0x1F))

static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx);

/* Size in below functions are in unit of dwords */
static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
		unsigned int *output, unsigned int max_size)
{
	struct kgsl_hfi *hfi = &gmu->hfi;
	struct gmu_memdesc *mem_addr = gmu->hfi_mem;
	struct hfi_queue_table *tbl = mem_addr->hostptr;
	struct hfi_queue_header *hdr = &tbl->qhdr[queue_idx];
@@ -61,9 +64,12 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
	if (hdr->status == HFI_QUEUE_STATUS_DISABLED)
		return -EINVAL;

	spin_lock_bh(&hfi->read_queue_lock);

	if (hdr->read_index == hdr->write_index) {
		hdr->rx_req = 1;
		return -ENODATA;
		result = -ENODATA;
		goto done;
	}

	/* Clear the output data before populating */
@@ -77,7 +83,8 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,
		dev_err(&gmu->pdev->dev,
		"HFI message too big: hdr:0x%x rd idx=%d\n",
			msg_hdr, hdr->read_index);
		return -EMSGSIZE;
		result = -EMSGSIZE;
		goto done;
	}

	read = hdr->read_index;
@@ -101,6 +108,8 @@ static int hfi_queue_read(struct gmu_device *gmu, uint32_t queue_idx,

	hdr->read_index = read;

done:
	spin_unlock_bh(&hfi->read_queue_lock);
	return result;
}

@@ -231,8 +240,7 @@ void hfi_init(struct kgsl_hfi *hfi, struct gmu_memdesc *mem_addr,
#define HDR_CMP_SEQNUM(out_hdr, in_hdr) \
	(MSG_HDR_GET_SEQNUM(out_hdr) == MSG_HDR_GET_SEQNUM(in_hdr))

static void receive_ack_cmd(struct kgsl_device *device,
		struct gmu_device *gmu, void *rcvd)
static void receive_ack_cmd(struct gmu_device *gmu, void *rcvd)
{
	uint32_t *ack = rcvd;
	uint32_t hdr = ack[0];
@@ -266,8 +274,8 @@ static void receive_ack_cmd(struct kgsl_device *device,
				"HFI ACK: Waiters: 0x%8.8X\n", waiters[j]);
	}

	adreno_set_gpu_fault(ADRENO_DEVICE(device), ADRENO_GMU_FAULT);
	adreno_dispatcher_schedule(device);
	adreno_set_gpu_fault(ADRENO_DEVICE(hfi->kgsldev), ADRENO_GMU_FAULT);
	adreno_dispatcher_schedule(hfi->kgsldev);
}

#define MSG_HDR_SET_SEQNUM(hdr, num) \
@@ -300,14 +308,17 @@ static int hfi_send_cmd(struct gmu_device *gmu, uint32_t queue_idx,
			&ret_cmd->msg_complete,
			msecs_to_jiffies(HFI_RSP_TIMEOUT));
	if (!rc) {
		/* Check one more time to make sure there is no response */
		hfi_process_queue(gmu, HFI_MSG_IDX);
		if (!completion_done(&ret_cmd->msg_complete)) {
			dev_err(&gmu->pdev->dev,
				"Receiving GMU ack %d timed out\n",
				MSG_HDR_GET_ID(*cmd));
				"Timed out waiting on ack for 0x%8.8x (id %d, sequence %d)\n",
				cmd[0],
				MSG_HDR_GET_ID(*cmd),
				MSG_HDR_GET_SEQNUM(*cmd));
			rc = -ETIMEDOUT;
		goto done;
		}

	/* If we got here we succeeded */
	} else
		rc = 0;
done:
	spin_lock_bh(&hfi->msglock);
@@ -524,12 +535,11 @@ static void receive_debug_req(struct gmu_device *gmu, void *rcvd)
			cmd->type, cmd->timestamp, cmd->data);
}

static void hfi_v1_receiver(struct kgsl_device *device,
		struct gmu_device *gmu, uint32_t *rcvd)
static void hfi_v1_receiver(struct gmu_device *gmu, uint32_t *rcvd)
{
	/* V1 ACK Handler */
	if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_V1_MSG_ACK) {
		receive_ack_cmd(device, gmu, rcvd);
		receive_ack_cmd(gmu, rcvd);
		return;
	}

@@ -549,36 +559,20 @@ static void hfi_v1_receiver(struct kgsl_device *device,
	}
}

void hfi_receiver(unsigned long data)
static void hfi_process_queue(struct gmu_device *gmu, uint32_t queue_idx)
{
	struct kgsl_device *device;
	struct gmu_device *gmu;
	uint32_t rcvd[MAX_RCVD_SIZE];
	int read_queue[] = {
		HFI_MSG_IDX,
		HFI_DBG_IDX,
	};
	int q;

	if (!data)
		return;

	device = (struct kgsl_device *)data;
	gmu = KGSL_GMU_DEVICE(device);

	/* While we are here, check all of the queues for messages */
	for (q = 0; q < ARRAY_SIZE(read_queue); q++) {
		while (hfi_queue_read(gmu, read_queue[q],
				rcvd, sizeof(rcvd)) > 0) {
	while (hfi_queue_read(gmu, queue_idx, rcvd, sizeof(rcvd)) > 0) {
		/* Special case if we're v1 */
		if (HFI_VER_MAJOR(&gmu->hfi) < 2) {
				hfi_v1_receiver(device, gmu, rcvd);
			hfi_v1_receiver(gmu, rcvd);
			continue;
		}

		/* V2 ACK Handler */
		if (MSG_HDR_GET_TYPE(rcvd[0]) == HFI_MSG_ACK) {
				receive_ack_cmd(device, gmu, rcvd);
			receive_ack_cmd(gmu, rcvd);
			continue;
		}

@@ -596,10 +590,16 @@ void hfi_receiver(unsigned long data)
				MSG_HDR_GET_ID(rcvd[0]));
			break;
		}
		};
	}
}

void hfi_receiver(unsigned long data)
{
	/* Process all read (firmware to host) queues */
	hfi_process_queue((struct gmu_device *) data, HFI_MSG_IDX);
	hfi_process_queue((struct gmu_device *) data, HFI_DBG_IDX);
}

#define GMU_VER_MAJOR(ver) (((ver) >> 28) & 0xF)
#define GMU_VER_MINOR(ver) (((ver) >> 16) & 0xFFF)
#define GMU_VERSION(major, minor) \
+2 −0
Original line number Diff line number Diff line
@@ -605,6 +605,7 @@ struct pending_cmd {
 * @kgsldev: Point to the kgsl device
 * @hfi_interrupt_num: number of GMU asserted HFI interrupt
 * @msglock: spinlock to protect access to outstanding command message list
 * @read_queue_lock: spinlock to protect against concurrent reading of queues
 * @cmdq_mutex: mutex to protect command queue access from multiple senders
 * @msglist: outstanding command message list. Each message in the list
 *	is waiting for ACK from GMU
@@ -618,6 +619,7 @@ struct kgsl_hfi {
	struct kgsl_device *kgsldev;
	int hfi_interrupt_num;
	spinlock_t msglock;
	spinlock_t read_queue_lock;
	struct mutex cmdq_mutex;
	struct list_head msglist;
	struct tasklet_struct tasklet;