/*
 * harness_fixed.c — DF-4946/DF-4947 fix model.
 *
 * Same as harness_dcn10.c but with the two fixes applied:
 *   (1) DF-4946: every successful DDC-family branch writes
 *       *id = GPIO_ID_DDC_DATA before returning true
 *       (hw_translate_dcn10.c:165-189).
 *   (2) DF-4947: the DDC_DATA branch selects the mask per line; the I2C pad
 *       data mask is DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK (0x2) instead of
 *       the hardwired DDC6DATA mask (0x100).
 * T1 must now report *id written (garbage-id==HPD smuggling structurally
 * dead: dal_gpio_create_irq(DDC_DATA, .) takes the default branch -> NULL,
 * define_hpd_registers is never reached); T3 must report mask==0x2.
 */
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#define ASSERT_CRITICAL(x) printf("    [ASSERT_CRITICAL hit]\n")

enum gpio_id {
	GPIO_ID_UNKNOWN = (-1),
	GPIO_ID_DDC_DATA, GPIO_ID_DDC_CLOCK, GPIO_ID_GENERIC, GPIO_ID_HPD,
	GPIO_ID_GPIO_PAD, GPIO_ID_VIP_PAD, GPIO_ID_SYNC, GPIO_ID_GSL,
	GPIO_ID_COUNT,
};
struct gpio_pin_info {
	uint32_t offset, offset_y, offset_en, offset_mask;
	uint32_t mask, mask_y, mask_en, mask_mask;
};
enum gpio_ddc_line { GPIO_DDC_LINE_UNKNOWN=(-1), GPIO_DDC_LINE_DDC1,
	GPIO_DDC_LINE_DDC2, GPIO_DDC_LINE_DDC3, GPIO_DDC_LINE_DDC4,
	GPIO_DDC_LINE_DDC5, GPIO_DDC_LINE_DDC6, GPIO_DDC_LINE_DDC_VGA,
	GPIO_DDC_LINE_VIP_PAD, GPIO_DDC_LINE_I2C_PAD = GPIO_DDC_LINE_VIP_PAD,
	GPIO_DDC_LINE_COUNT };

#define BASE_INNER(seg)	DCE_BASE__INST0_SEG ## seg
#define BASE(seg)	BASE_INNER(seg)
#define REG(reg_name)	BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name
#define DCE_BASE__INST0_SEG2 0x34C0
#define mmDC_GPIO_DDC1_A	0x28d1
#define mmDC_GPIO_DDC1_A_BASE_IDX	2
#define mmDC_GPIO_DDC2_A	0x28d5
#define mmDC_GPIO_DDC2_A_BASE_IDX	2
#define mmDC_GPIO_DDC3_A	0x28d9
#define mmDC_GPIO_DDC3_A_BASE_IDX	2
#define mmDC_GPIO_DDC4_A	0x28dd
#define mmDC_GPIO_DDC4_A_BASE_IDX	2
#define mmDC_GPIO_DDC5_A	0x28e1
#define mmDC_GPIO_DDC5_A_BASE_IDX	2
#define mmDC_GPIO_DDC6_A	0x28e5
#define mmDC_GPIO_DDC6_A_BASE_IDX	2
#define mmDC_GPIO_DDCVGA_A	0x28e9
#define mmDC_GPIO_DDCVGA_A_BASE_IDX	2
#define mmDC_GPIO_I2CPAD_A	0x2901
#define mmDC_GPIO_I2CPAD_A_BASE_IDX	2
#define DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK 0x00000100L
#define DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK    0x00000002L

/* FIXED offset_to_id DDC section (only the DDC-family cases shown; the rest
 * of the switch is unchanged and unreachable from this test). */
static bool offset_to_id_fixed(
	uint32_t offset,
	uint32_t mask,
	enum gpio_id *id,
	uint32_t *en)
{
	(void)mask;
	switch (offset) {
	case REG(DC_GPIO_DDC1_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC1;
		return true;
	case REG(DC_GPIO_DDC2_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC2;
		return true;
	case REG(DC_GPIO_DDC3_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC3;
		return true;
	case REG(DC_GPIO_DDC4_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC4;
		return true;
	case REG(DC_GPIO_DDC5_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC5;
		return true;
	case REG(DC_GPIO_DDC6_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC6;
		return true;
	case REG(DC_GPIO_DDCVGA_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_DDC_VGA;
		return true;
	case REG(DC_GPIO_I2CPAD_A):
		*id = GPIO_ID_DDC_DATA;		/* FIX */
		*en = GPIO_DDC_LINE_I2C_PAD;
		return true;
	default:
		return false;
	}
}

/* FIXED id_to_offset DDC_DATA section */
static bool id_to_offset_fixed(enum gpio_id id, uint32_t en,
	struct gpio_pin_info *info)
{
	bool result = true;

	if (id != GPIO_ID_DDC_DATA)
		return false;

	switch (en) {
	case GPIO_DDC_LINE_DDC1:
		info->offset = REG(DC_GPIO_DDC1_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC2:
		info->offset = REG(DC_GPIO_DDC2_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC3:
		info->offset = REG(DC_GPIO_DDC3_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC4:
		info->offset = REG(DC_GPIO_DDC4_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC5:
		info->offset = REG(DC_GPIO_DDC5_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC6:
		info->offset = REG(DC_GPIO_DDC6_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
	break;
	case GPIO_DDC_LINE_DDC_VGA:
		info->offset = REG(DC_GPIO_DDCVGA_A);
		info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; /* DDCVGADATA==0x100: same layout */
	break;
	case GPIO_DDC_LINE_I2C_PAD:
		info->offset = REG(DC_GPIO_I2CPAD_A);
		info->mask = DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK;	/* FIX: was DDC6DATA 0x100 */
	break;
	default:
		result = false;
	}
	return result;
}

int main(void)
{
	int fails = 0;
	enum gpio_id id;
	uint32_t en;
	bool r;
	struct gpio_pin_info info;
	int i;
	static const struct { uint32_t off; const char *nm; } ddc8[] = {
		{ REG(DC_GPIO_DDC1_A),   "DDC1" },
		{ REG(DC_GPIO_DDC2_A),   "DDC2" },
		{ REG(DC_GPIO_DDC3_A),   "DDC3" },
		{ REG(DC_GPIO_DDC4_A),   "DDC4" },
		{ REG(DC_GPIO_DDC5_A),   "DDC5" },
		{ REG(DC_GPIO_DDC6_A),   "DDC6" },
		{ REG(DC_GPIO_DDCVGA_A), "DDCVGA" },
		{ REG(DC_GPIO_I2CPAD_A), "I2CPAD" },
	};

	printf("== FIXED T1: *id is now written on every DDC-family success ==\n");
	for (i = 0; i < 8; i++) {
		id = (enum gpio_id)0x7B7B7B7B;
		r = offset_to_id_fixed(ddc8[i].off, 0x100, &id, &en);
		printf("offset_to_id(REG(%-6s),0x100) = %-5s en=%d id=%d (%s)\n",
			ddc8[i].nm, r ? "true" : "false", en, (int)id,
			id == GPIO_ID_DDC_DATA ? "WRITTEN" : "still untouched");
		if (!r || id != GPIO_ID_DDC_DATA) fails++;
	}
	printf("  -> with id==GPIO_ID_DDC_DATA, dal_gpio_create_irq (gpio_service.c:373-381)\n"
	       "     takes the default branch and returns NULL: define_hpd_registers\n"
	       "     (hw_factory_dcn10.c:149-157) can never be reached with en=6/7 -> no OOB\n");

	printf("\n== FIXED T3: I2C_PAD data mask is now the real SDA field ==\n");
	memset(&info, 0, sizeof(info));
	r = id_to_offset_fixed(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD, &info);
	printf("id_to_offset(DDC_DATA, I2C_PAD) = %s offset=0x%x mask=0x%x (SDA_A_MASK=0x%x: %s)\n",
		r ? "true" : "false", info.offset, info.mask,
		(unsigned)DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK,
		info.mask == DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK ? "CORRECT" : "WRONG");
	if (!r || info.mask != DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK) fails++;

	printf("\n%d residual violations (0 expected)\n", fails);
	return (fails == 0) ? 0 : 1;
}
