/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
 */

#ifndef __QCOM_IOMMU_UTIL_H
#define __QCOM_IOMMU_UTIL_H

#include <linux/iommu.h>
#include <linux/dma-mapping.h>
#include <linux/iova.h>

#include <soc/qcom/secure_buffer.h>

/* IOMMU fault behaviors */
#define QCOM_IOMMU_FAULT_MODEL_NON_FATAL	BIT(0)
#define QCOM_IOMMU_FAULT_MODEL_NO_CFRE		BIT(1)
#define QCOM_IOMMU_FAULT_MODEL_NO_STALL		BIT(2)
#define QCOM_IOMMU_FAULT_MODEL_HUPCF		BIT(3)

/* IOMMU mapping configurations */
#define QCOM_IOMMU_MAPPING_CONF_S1_BYPASS	BIT(0)
#define QCOM_IOMMU_MAPPING_CONF_ATOMIC		BIT(1)
#define QCOM_IOMMU_MAPPING_CONF_FAST		BIT(2)

/* iommu transaction flags */
/* 1 Write, 0 Read */
#define QCOM_IOMMU_ATOS_TRANS_WRITE	BIT(0)
/* 1 Privileged, 0 Unprivileged */
#define QCOM_IOMMU_ATOS_TRANS_PRIV	BIT(1)
/* 1 Instruction fetch, 0 Data access */
#define QCOM_IOMMU_ATOS_TRANS_INST	BIT(2)
/* Non secure unprivileged Data read operation */
#define QCOM_IOMMU_ATOS_TRANS_DEFAULT	(0U)

#ifndef IOMMU_SYS_CACHE
/* Attributes are not supported, so render them ineffective. */
#define IOMMU_SYS_CACHE		(0)
#define IOMMU_SYS_CACHE_NWA	(0)
#endif

/* Use upstream device's bus attribute */
#define IOMMU_USE_UPSTREAM_HINT	(IOMMU_SYS_CACHE)

/* Use upstream device's bus attribute with no write-allocate cache policy */
#define IOMMU_USE_LLC_NWA	(IOMMU_SYS_CACHE_NWA)

/* vendor iommu fault flags */
#define IOMMU_FAULT_TRANSLATION         (1 << 2)
#define IOMMU_FAULT_PERMISSION          (1 << 3)
#define IOMMU_FAULT_EXTERNAL            (1 << 4)
#define IOMMU_FAULT_TRANSACTION_STALLED (1 << 5)

/* iommu transaction flags */
#define IOMMU_TRANS_WRITE	BIT(0)	/* 1 Write, 0 Read */
#define IOMMU_TRANS_PRIV	BIT(1)	/* 1 Privileged, 0 Unprivileged */
#define IOMMU_TRANS_INST	BIT(2)	/* 1 Instruction fetch, 0 Data access */
#define IOMMU_TRANS_SEC	BIT(3)	/* 1 Secure, 0 Non-secure access*/

/* Non secure unprivileged Data read operation */
#define IOMMU_TRANS_DEFAULT	(0U)

struct iommu_pgtbl_info {
	void *ops;
};

struct qcom_iommu_atos_txn {
	u64 addr;
	u32 flags;
	u32 id;
};

enum sid_switch_direction {
	SID_ACQUIRE,
	SID_RELEASE,
};

struct qcom_iommu_fault_ids {
	u32 bid;
	u32 pid;
	u32 mid;
};

/*
 * @sid_switch: add/remove all SIDS in the iommu domain containing dev from
 *              iommu registers.
 */
struct qcom_iommu_ops {
	phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
					struct qcom_iommu_atos_txn *txn);
	int (*sid_switch)(struct device *dev, enum sid_switch_direction dir);
	int (*get_fault_ids)(struct iommu_domain *domain,
			struct qcom_iommu_fault_ids *ids);
	int (*get_context_bank_nr)(struct iommu_domain *domain);
	int (*get_asid_nr)(struct iommu_domain *domain);
	int (*set_secure_vmid)(struct iommu_domain *domain, enum vmid vmid);
	int (*set_fault_model)(struct iommu_domain *domain, int fault_model);
	int (*enable_s1_translation)(struct iommu_domain *domain);
	int (*get_mappings_configuration)(struct iommu_domain *domain);
	void (*skip_tlb_management)(struct iommu_domain *domain, bool skip);
	struct iommu_ops iommu_ops;
};
#define to_qcom_iommu_ops(x) (container_of(x, struct qcom_iommu_ops, iommu_ops))

struct device_node *qcom_iommu_group_parse_phandle(struct device *dev);
int qcom_iommu_generate_dma_regions(struct device *dev,
				    struct list_head *head);
void qcom_iommu_generate_resv_regions(struct device *dev,
				      struct list_head *list);
int qcom_iommu_get_fast_iova_range(struct device *dev,
				   dma_addr_t *ret_iova_base,
				   dma_addr_t *ret_iova_end);

/* Remove once these functions are exported by upstream kernel */
void qcom_iommu_get_resv_regions(struct device *dev, struct list_head *list);
void qcom_iommu_put_resv_regions(struct device *dev, struct list_head *list);

phys_addr_t qcom_iommu_iova_to_phys_hard(struct iommu_domain *domain,
				    struct qcom_iommu_atos_txn *txn);

int qcom_iommu_sid_switch(struct device *dev, enum sid_switch_direction dir);

int qcom_skip_tlb_management(struct device *dev, bool skip);

extern int qcom_iommu_get_fault_ids(struct iommu_domain *domain,
				struct qcom_iommu_fault_ids *f_ids);
extern int qcom_iommu_get_msi_size(struct device *dev, u32 *msi_size);

int qcom_iommu_get_context_bank_nr(struct iommu_domain *domain);

int qcom_iommu_get_asid_nr(struct iommu_domain *domain);

int qcom_iommu_set_secure_vmid(struct iommu_domain *domain, enum vmid vmid);

int qcom_iommu_set_fault_model(struct iommu_domain *domain, int fault_model);

int qcom_iommu_enable_s1_translation(struct iommu_domain *domain);

int qcom_iommu_get_mappings_configuration(struct iommu_domain *domain);

#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE
int __init qcom_arm_lpae_do_selftests(void);
#else
static inline int __init qcom_arm_lpae_do_selftests(void)
{
	return 0;
}
#endif
#endif /* __QCOM_IOMMU_UTIL_H */
