/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Intel Core SoC Power Management Controller Header File
 *
 * Copyright (c) 2025, Intel Corporation.
 * All Rights Reserved.
 *
 */
#ifndef INTEL_PMC_IPC_H
#define INTEL_PMC_IPC_H
#include <linux/acpi.h>
#include <linux/cleanup.h>

#define IPC_SOC_REGISTER_ACCESS			0xAA
#define IPC_SOC_SUB_CMD_READ			0x00
#define IPC_SOC_SUB_CMD_WRITE			0x01
#define PMC_IPCS_PARAM_COUNT			7
#define VALID_IPC_RESPONSE			5

struct pmc_ipc_cmd {
	u32 cmd;
	u32 sub_cmd;
	u32 size;
	u32 wbuf[4];
};

struct pmc_ipc_rbuf {
	u32 buf[4];
};

/**
 * intel_pmc_ipc() - PMC IPC Mailbox accessor
 * @ipc_cmd:  Prepared input command to send
 * @rbuf:     Allocated array for returned IPC data
 *
 * Return: 0 on success. Non-zero on mailbox error
 */
static inline int intel_pmc_ipc(struct pmc_ipc_cmd *ipc_cmd, struct pmc_ipc_rbuf *rbuf)
{
#ifdef CONFIG_ACPI
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object params[PMC_IPCS_PARAM_COUNT] = {
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
	};
	struct acpi_object_list arg_list = { PMC_IPCS_PARAM_COUNT, params };
	int status;

	if (!ipc_cmd || !rbuf)
		return -EINVAL;

	/*
	 * 0: IPC Command
	 * 1: IPC Sub Command
	 * 2: Size
	 * 3-6: Write Buffer for offset
	 */
	params[0].integer.value = ipc_cmd->cmd;
	params[1].integer.value = ipc_cmd->sub_cmd;
	params[2].integer.value = ipc_cmd->size;
	params[3].integer.value = ipc_cmd->wbuf[0];
	params[4].integer.value = ipc_cmd->wbuf[1];
	params[5].integer.value = ipc_cmd->wbuf[2];
	params[6].integer.value = ipc_cmd->wbuf[3];

	status = acpi_evaluate_object(NULL, "\\IPCS", &arg_list, &buffer);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	union acpi_object *obj __free(kfree) = buffer.pointer;

	if (obj && obj->type == ACPI_TYPE_PACKAGE &&
	    obj->package.count == VALID_IPC_RESPONSE) {
		const union acpi_object *objs = obj->package.elements;

		if ((u8)objs[0].integer.value != 0)
			return -EINVAL;

		rbuf->buf[0] = objs[1].integer.value;
		rbuf->buf[1] = objs[2].integer.value;
		rbuf->buf[2] = objs[3].integer.value;
		rbuf->buf[3] = objs[4].integer.value;
	} else {
		return -EINVAL;
	}

	return 0;
#else
	return -ENODEV;
#endif /* CONFIG_ACPI */
}

#endif /* INTEL_PMC_IPC_H */
