/*
 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
 *	      http://www.samsung.com/
 *
 * EXYNOS - Stage 2 Protection Unit(S2MPU)
 * Author: Junho Choi <junhosj.choi@samsung.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef __EXYNOS_S2MPU_H__
#define __EXYNOS_S2MPU_H__

#define EXYNOS_MAX_S2MPU_INSTANCE		(64)
#define EXYNOS_MAX_SUBSYSTEM			(32)
#define EXYNOS_MAX_SUBSYSTEM_NAME_LEN		(16)

#define SUBSYSTEM_FW_NUM_SHIFT			(32)
#define SUBSYSTEM_INDEX_SHIFT			(0)

/* Error */
#define ERROR_INVALID_SUBSYSTEM_NAME		(0x1)
#define ERROR_DO_NOT_SUPPORT_SUBSYSTEM		(0x2)
#define ERROR_INVALID_FW_BIN_INFO		(0x3)
#define ERROR_INVALID_NOTIFIER_BLOCK		(0x4)
#define ERROR_INVALID_PARAMETER			(0x5)
#define ERROR_S2MPU_NOT_INITIALIZED		(0xE12E0001)

/* Backup and restore S2MPU */
#define EXYNOS_PD_S2MPU_BACKUP			(0)
#define EXYNOS_PD_S2MPU_RESTORE			(1)

/* S2MPU fault */
#define MAX_VID_OF_S2MPU_FAULT_INFO		(8)

#define FAULT_TYPE_MPTW_ACCESS_FAULT		(0x1)
#define FAULT_TYPE_AP_FAULT			(0x2)
#define FAULT_TYPE_CONTEXT_FAULT		(0x4)
#define FAULT_THIS_ADDR_IS_NOT_BLACKLIST	(0xFFFF)

#define S2MPU_CONTEXT_VALID_SHIFT		(3)
#define S2MPU_CONTEXT_VID_SHIFT			(0)
#define S2MPU_CONTEXT_VALID_MASK		(0x1)
#define S2MPU_CONTEXT_VID_MASK			(0x7)

#define OWNER_IS_KERNEL_RO			(99)
#define OWNER_IS_TEST				(98)

#define ERROR_NOTHING_S2MPU_FAULT		(0x2)

/* Return values from SMC */
#define S2MPUFD_ERROR_INVALID_CH_NUM		(0x600)
#define S2MPUFD_ERROR_INVALID_FAULT_INFO_SIZE	(0x601)

/* S2MPU Notifier */
#define S2MPU_NOTIFIER_SET			(1)
#define S2MPU_NOTIFIER_UNSET			(0)

#define S2MPU_NOTIFIER_PANIC			(0xFF00000000)
#define S2MPU_NOTIFY_OK				(0)
#define S2MPU_NOTIFY_BAD			(1)

/* S2MPU enable check */
#define S2_BUF_SIZE 				(30)

/* MPTC */
#define NUM_OF_MPTC_WAY				(4)

#define S2MPU_MAX_NUM_OF_FAULT_MPTC		(2)
#define S2MPU_FAULT_STLB_INDEX			(0)
#define S2MPU_FAULT_PTLB_INDEX			(1)

/* For backward compatibility */
#define exynos_verify_subsystem_fw		exynos_s2mpu_verify_subsystem_fw
#define exynos_request_fw_stage2_ap		exynos_s2mpu_request_fw_stage2_ap

/* PM QoS for SSS */
#define PM_QOS_SSS_UPDATE			(true)
#define PM_QOS_SSS_RELEASE			(false)

#define PM_QOS_SSS_FREQ_DOMAIN_LEN		(8)

#ifndef __ASSEMBLY__

#include <linux/mutex.h>

#include <soc/samsung/exynos_pm_qos.h>

/* S2MPU access permission */
enum stage2_ap {
	ATTR_NO_ACCESS = 0x0,
	ATTR_RO = 0x1,
	ATTR_WO = 0x2,
	ATTR_RW = 0x3
};

/* Registers of S2MPUFD Fault Information */
struct s2mpu_mptc {
	uint32_t valid;
	uint32_t ppn;
	uint32_t vid;
	uint32_t granule;
	uint32_t ap;
};

struct s2mpu_fault_mptc {
	uint32_t set;
	struct s2mpu_mptc mptc[NUM_OF_MPTC_WAY];
};

struct __s2mpu_fault_info {
	unsigned int fault_addr_low;
	unsigned int fault_addr_high;
	unsigned int vid;
	unsigned int type;
	unsigned int rw;
	unsigned int len;
	unsigned int axid;
	unsigned int context;
	unsigned int subsystem;
	unsigned int blacklist_owner;
	struct s2mpu_fault_mptc fault_mptc[S2MPU_MAX_NUM_OF_FAULT_MPTC];
};

struct s2mpu_fault_info {
	unsigned int fault_cnt;
	struct __s2mpu_fault_info info[MAX_VID_OF_S2MPU_FAULT_INFO];
};

/* Structure of notifier info */
struct s2mpu_notifier_info {
	const char *subsystem;
	unsigned long fault_addr;
	unsigned int fault_rw;
	unsigned int fault_len;
	unsigned int fault_type;
};

/* Structure of notifier block */
struct s2mpu_notifier_block {
	const char *subsystem;
	int (*notifier_call)(struct s2mpu_notifier_block *,
			     struct s2mpu_notifier_info *);
};

/* Data structure for S2MPU Fault Information */
struct s2mpu_info_data {
	struct device *dev;
	struct s2mpu_fault_info *fault_info;
	dma_addr_t fault_info_pa;
	unsigned int instance_num;
	unsigned int irq[EXYNOS_MAX_S2MPU_INSTANCE];
	unsigned int irqcnt;
	struct s2mpu_notifier_info *noti_info;
	unsigned int *notifier_flag;
};

/* PM QoS for SSS */
struct s2mpu_pm_qos_sss {
	struct exynos_pm_qos_request qos_sss;
	unsigned int sss_freq_domain;
	unsigned int qos_sss_freq;
	unsigned int qos_count;
	struct mutex qos_count_lock;
	bool need_qos_sss;
};


unsigned long exynos_s2mpu_set_stage2_ap(const char *subsystem,
					 unsigned long base,
					 unsigned long size,
					 unsigned int ap);
unsigned long exynos_s2mpu_set_blacklist(const char *subsystem,
					 unsigned long base,
					 unsigned long size);
unsigned long exynos_s2mpu_set_all_blacklist(unsigned long base,
					     unsigned long size);

/**
 * exynos_s2mpu_verify_subsystem_fw - Verify the signature of sub-system FW.
 *
 * @subsystem:	 Sub-system name.
 *		 Please refer to subsystem-names property of s2mpu node
 *		 in exynosXXXX-security.dtsi.
 * @fw_id:	 FW ID of this subsystem.
 * @fw_base:	 FW base address.
 *		 It's physical address(PA) and should be aligned with 64KB
 *		 because of S2MPU granule.
 * @fw_bin_size: FW binary size.
 *		 It should be aligned with 4bytes because of the limit of
 *		 signature verification.
 * @fw_mem_size: The size to be used by FW.
 *		 This memory region needs to be protected from other
 *		 sub-systems. It should be aligned with 64KB like fw_base
 *		 because of S2MPU granlue.
 */
unsigned long exynos_s2mpu_verify_subsystem_fw(const char *subsystem,
					       unsigned int fw_id,
					       unsigned long fw_base,
					       size_t fw_bin_size,
					       size_t fw_mem_size);

/**
 * exynos_s2mpu_request_fw_stage2_ap - Request Stage 2 access permission
 *				       of FW to allow access memory.
 *
 * @subsystem: Sub-system name.
 *	       Please refer to subsystem-names property of s2mpu node
 *	       in exynosXXXX-security.dtsi.
 *
 * This function must be called in case that only sub-system FW is
 * verified.
 */
unsigned long exynos_s2mpu_request_fw_stage2_ap(const char *subsystem);

/**
 * exynos_s2mpu_release_fw_stage2_ap - Release Stage 2 access permission
 *				       for sub-system FW region and block
 *				       all Stage 2 access permission of
 *				       the sub-system.
 *
 * @subsystem:	 Sub-system name
 *		 Please refer to subsystem-names property of
 *		 s2mpu node in exynosXXXX-security.dtsi.
 * @fw_id:	 FW ID of this subsystem
 *
 * This function must be called in case that only sub-system FW is verified.
 */
unsigned long exynos_s2mpu_release_fw_stage2_ap(const char *subsystem,
						uint32_t fw_id);

/**
 * exynos_s2mpu_notifier_call_register - Register subsystem's S2MPU notifier call.
 *
 * @s2mpu_notifier_block:	S2MPU Notifier structure
 *				It should have two elements.
 *				One is Sub-system's name, 'subsystem'.
 *				The other is notifier call function pointer,
 *				s2mpu_notifier_fn_t 'notifier_call'.
 * @'subsystem':		Please refer to subsystem-names property of s2mpu
 *				node in exynosXXXX-security.dtsi.
 * @'notifier_call'		It's type is s2mpu_notifier_fn_t.
 *				And it should return S2MPU_NOTIFY
 */
unsigned long exynos_s2mpu_notifier_call_register(struct s2mpu_notifier_block *snb);

#ifdef CONFIG_EXYNOS_S2MPU_PD
unsigned long exynos_pd_backup_s2mpu(unsigned int pd);
unsigned long exynos_pd_restore_s2mpu(unsigned int pd);
#else
static inline unsigned long exynos_pd_backup_s2mpu(unsigned int pd)
{
	return 0;
}

static inline unsigned long exynos_pd_restore_s2mpu(unsigned int pd)
{
	return 0;
}
#endif

#endif	/* __ASSEMBLY__ */
#endif	/* __EXYNOS_S2MPU_H__ */