/******************************************************************************
* Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <xaiengine.h>

/* IPU requires BASE address to be 0.
 * In ert_mgmt.c code, in aie_ipu_load_pdi() DevInst is initialized with
 * Base address = base_address + (start_col << XAIE_COL_SHIFT)
 * The column offset and base address is added to the address generated by the
 * XPdi_Load().
 */
#define XAIE_BASE_ADDR		0x00000000000
#define XAIE_NUM_ROWS		6
#define XAIE_NUM_COLS		5
#define XAIE_COL_SHIFT		25
#define XAIE_ROW_SHIFT		20
#define XAIE_SHIM_ROW		0
#define XAIE_RES_TILE_ROW_START	1
#define XAIE_RES_TILE_NUM_ROWS	1
#define XAIE_AIE_TILE_ROW_START	2
#define XAIE_AIE_TILE_NUM_ROWS	4

#define CDO_WRITE32_OP		0x020103
#define CDO_MASK_WRITE32_OP	0x030102

FILE *fd;

void cdo_Write32(void *addr, u32 val)
{
	u32 op = CDO_WRITE32_OP;
	size_t sz;

	printf("op: 0x%x addr: 0x%x val: 0x%x\n", op, addr, val);
	sz = fwrite(&op, sizeof(op), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");

	sz = fwrite(&addr, sizeof(u32), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");

	sz = fwrite(&val, sizeof(val), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");
}

void cdo_MaskWrite32(void *addr, u32 mask, u32 val)
{
	u32 op = CDO_MASK_WRITE32_OP;
	size_t sz;

	printf("op: 0x%x addr: 0x%x mask: 0x%x val: 0x%x\n", op, addr, mask, val);

	sz = fwrite(&op, sizeof(op), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");

	sz = fwrite(&addr, sizeof(u32), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");

	sz = fwrite(&mask, sizeof(mask), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");

	sz = fwrite(&val, sizeof(val), 1, fd);
	if (sz != 1)
		fprintf(stderr, "Failed to write to cdo file\n");
}

void cdo_MaskPoll(void *addr, u32 mask, u32 value, u32 timeout_us)
{
	fprintf(stderr, "%s not implemented\n", __func__);
}

void cdo_BlockSet32(void *addr, u32 data, u32 size)
{
	int i;

	for (i = 0; i < size; i++) {
		cdo_Write32((char *)addr + i * 4U, data);
	}
}

void cdo_BlockWrite32(void *addr, u32 *data, u32 size)
{
	int i;

	for (i = 0; i < size; i++) {
		cdo_Write32((char *)addr + i * 4U, data + i);
	}
}

/* This driver needs to be compiled with cdo backend.
 * #make -f Makefile.Linux cdo
 * This program is used to generate the cdo file. Current implementation is to
 * write to a file which can be used to regenerate pdi file for testing.
 * Remove call to freopen() to generate rest of the cdo.
 */
int main(int argc, char *argv[])
{
	int RC = XAIE_OK;
	XAie_SetupConfig(ConfigPtr, XAIE_DEV_GEN_AIE2IPU, XAIE_BASE_ADDR,
			XAIE_COL_SHIFT, XAIE_ROW_SHIFT,
			XAIE_NUM_COLS, XAIE_NUM_ROWS, XAIE_SHIM_ROW,
			XAIE_RES_TILE_ROW_START, XAIE_RES_TILE_NUM_ROWS,
			XAIE_AIE_TILE_ROW_START, XAIE_AIE_TILE_NUM_ROWS);
	XAie_InstDeclare(DevInst, &ConfigPtr);
	char *filename;

	if (argc != 2) {
		printf("Usage: %s <cdo write filename>\n", argv[0]);
		return -1;
	}
	filename = argv[1];
	fd = fopen(filename, "w+");
	if (!fd) {
		perror("failed to open file");
		return -1;
	}

	RC = XAie_SetupPartitionConfig(&DevInst, XAIE_BASE_ADDR, 1, 1);
	if(RC != XAIE_OK) {
		printf("Failed configure partition.\n");
		return -1;
	}

	RC = XAie_CfgInitialize(&DevInst, &ConfigPtr);
	if(RC != XAIE_OK) {
		printf("Driver initialization failed.\n");
		return -1;
	}

	RC = XAie_PmRequestTiles(&DevInst, NULL, 0);
	if(RC != XAIE_OK) {
		printf("Failed to request tiles.\n");
		return -1;
	}

	RC = XAie_PartitionInitialize(&DevInst, NULL);
	if(RC != XAIE_OK) {
		printf("Partition initialization failed.\n");
		return -1;
	}

	/* To dump only cdo generated by interrupt init() */
	fd = freopen(filename, "w+", fd);
	RC = XAie_ErrorHandlingInit(&DevInst);
	if(RC != XAIE_OK) {
		printf("Broadcast error network setup failed.\n");
		return -1;
	}

	fclose(fd);
	return 0;
}
