/* Copyright (c) 2010, 2014, 2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/scm-boot.h>

/*
 * Set the cold/warm boot address for one of the CPU cores.
 */
int scm_set_boot_addr(phys_addr_t addr, unsigned int flags)
{
	struct {
		u32 flags;
		u32 addr;
	} cmd;
	struct scm_desc desc = {0};

	if (!is_scm_armv8()) {
		cmd.addr = addr;
		cmd.flags = flags;
		return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
				&cmd, sizeof(cmd), NULL, 0);
	}

	desc.args[0] = addr;
	desc.args[1] = flags;
	desc.arginfo = SCM_ARGS(2);

	return scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_BOOT_ADDR), &desc);
}
EXPORT_SYMBOL(scm_set_boot_addr);

/**
 *	scm_set_boot_addr_mc - Set entry physical address for cpus
 *	@addr:	32bit physical address
 *	@aff0:	Collective bitmask of the affinity-level-0 of the mpidr
 *		1<<aff0_CPU0| 1<<aff0_CPU1....... | 1<<aff0_CPU32
 *		Supports maximum 32 cpus under any affinity level.
 *	@aff1:	Collective bitmask of the affinity-level-1 of the mpidr
 *	@aff2:	Collective bitmask of the affinity-level-2 of the mpidr
 *	@flags:	Flag to differentiate between coldboot vs warmboot
 */
int scm_set_boot_addr_mc(phys_addr_t addr, u32 aff0,
		u32 aff1, u32 aff2, u32 flags)
{
	struct {
		u32 addr;
		u32 aff0;
		u32 aff1;
		u32 aff2;
		u32 reserved;
		u32 flags;
	} cmd;
	struct scm_desc desc = {0};

	if (!is_scm_armv8()) {
		cmd.addr = addr;
		cmd.aff0 = aff0;
		cmd.aff1 = aff1;
		cmd.aff2 = aff2;
		/*
		 * Reserved for future chips with affinity level 3 effectively
		 * 1 << 0
		 */
		cmd.reserved = ~0U;
		cmd.flags = flags | SCM_FLAG_HLOS;
		return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR_MC,
				&cmd, sizeof(cmd), NULL, 0);
	}

	flags = flags | SCM_FLAG_HLOS;
	desc.args[0] = addr;
	desc.args[1] = aff0;
	desc.args[2] = aff1;
	desc.args[3] = aff2;
	desc.args[4] = ~0ULL;
	desc.args[5] = flags;
	desc.arginfo = SCM_ARGS(6);

	return scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_BOOT_ADDR_MC), &desc);
}
EXPORT_SYMBOL(scm_set_boot_addr_mc);

/**
 *	scm_set_warm_boot_addr_mc_for_all -
 *	Set entry physical address for __all__ possible cpus
 *	This API passes all_set mask to secure-os and relies
 *	on secure-os to appropriately
 *	set the boot-address on the current system.
 *	@addr:	32bit physical address
 */

int scm_set_warm_boot_addr_mc_for_all(phys_addr_t addr)
{
	return scm_set_boot_addr_mc(addr, ~0U, ~0U, ~0U,
			SCM_FLAG_WARMBOOT_MC);
}
EXPORT_SYMBOL(scm_set_warm_boot_addr_mc_for_all);

/**
 *	scm_is_mc_boot_available -
 *	Checks if TZ supports the boot API for multi-cluster configuration
 *	Returns true if available and false otherwise
 */
int scm_is_mc_boot_available(void)
{
	return scm_is_call_available(SCM_SVC_BOOT, SCM_BOOT_ADDR_MC);
}
EXPORT_SYMBOL(scm_is_mc_boot_available);
