/*
 * DF-5148 analysis harness — proves the control-flow arithmetic of the AUX
 * completion poll loop in sys/dev/drm/radeon/radeon_dp_auxch.c:152-164.
 *
 * The kernel loop (exact shape):
 *
 *      int retry_count = 0;
 *      do {
 *              tmp = RREG32(AUX_SW_STATUS + aux_offset[instance]);
 *              if (tmp & AUX_SW_DONE)
 *                      break;
 *              usleep_range(100, 200);          -> DELAY(min), bounded
 *      } while (retry_count++ < 1000);
 *
 *      if (retry_count >= 1000) {              <- keyed on the COUNTER
 *              DRM_ERROR("auxch hw never signalled completion ...");
 *              ret = -EIO;
 *              goto done;
 *      }
 *
 * Body executes with retry_count == 0..1000 (1001 executions). If the HW
 * asserts AUX_SW_DONE during the 1001st body (retry_count == 1000 at entry),
 * the loop breaks with retry_count == 1000 and the counter-based gate then
 * misreports a *completed* transaction as -EIO.
 *
 * This harness models RREG32 with a "DONE asserts at body-entry index N"
 * stimulus (or never) and compares the shipped gate against the proposed
 * status-based gate. It is a control-flow proof, not an on-HW reproduction:
 * exercising the real loop requires radeon DisplayPort hardware.
 *
 * Build: cc -O2 -o loop_offbyone loop_offbyone.c
 * Run:   ./loop_offbyone
 */
#include <stdio.h>
#include <stdint.h>

#define AUX_SW_DONE		(1u << 0)
#define EIO_LIKE		(-5)	/* stands in for -EIO */

/* stimulus: DONE appears when the body is entered with retry_count == done_at;
 * done_at < 0 means the HW never asserts DONE */
static uint32_t mock_status(int retry_count, int done_at)
{
	return (done_at >= 0 && retry_count >= done_at) ? AUX_SW_DONE : 0;
}

/* shipped code: gate on the counter (radeon_dp_auxch.c:152-164) */
static int classify_current(int done_at)
{
	int retry_count = 0;
	uint32_t tmp = 0;
	int guard = 0;

	do {
		if (guard++ > 100000)	/* paranoia: model must terminate */
			return -99;
		tmp = mock_status(retry_count, done_at);
		if (tmp & AUX_SW_DONE)
			break;
	} while (retry_count++ < 1000);

	if (retry_count >= 1000)
		return EIO_LIKE;	/* "never signalled completion" */
	return 0;			/* success path continues */
}

/* proposed fix: gate on the observed status word */
static int classify_fixed(int done_at)
{
	int retry_count = 0;
	uint32_t tmp = 0;
	int guard = 0;

	do {
		if (guard++ > 100000)
			return -99;
		tmp = mock_status(retry_count, done_at);
		if (tmp & AUX_SW_DONE)
			break;
	} while (retry_count++ < 1000);

	if (!(tmp & AUX_SW_DONE))
		return EIO_LIKE;
	return 0;
}

int main(void)
{
	struct { int done_at; const char *what; } cases[] = {
		{ 0,	"DONE on 1st poll     (rc=0)" },
		{ 500,	"DONE on 501st poll   (rc=500)" },
		{ 999,	"DONE on 1000th poll  (rc=999)" },
		{ 1000,	"DONE on 1001st poll  (rc=1000)  <-- last body" },
		{ -1,	"never DONE (true HW timeout)" },
	};
	size_t i;
	int bad = 0;

	printf("%-38s %-14s %-14s %s\n", "stimulus", "shipped gate", "fixed gate", "expected");
	for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
		int cur = classify_current(cases[i].done_at);
		int fix = classify_fixed(cases[i].done_at);
		int exp = (cases[i].done_at < 0) ? EIO_LIKE : 0;
		printf("%-38s %-14d %-14d %d%s\n", cases[i].what, cur, fix, exp,
		       (cur != exp) ? "   BUG: completed xfer misreported as -EIO"
				    : (fix != exp ? "   FIX WRONG" : ""));
		if ((cur != exp) || (fix != exp))
			bad |= (cur != exp) ? 1 : 2;
	}
	if (bad & 1)
		printf("\nRESULT: shipped loop misclassifies a DONE-on-last-poll "
		       "transfer as -EIO (radeon_dp_auxch.c:160).\n");
	if (bad & 2)
		printf("\nRESULT: proposed fix changes behavior for cases it "
		       "must not (harness error).\n");
	if (!bad)
		printf("\nRESULT: no divergence found.\n");
	return (bad & 1) ? 1 : 0;
}
