/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
 *
 * (C) COPYRIGHT 2019-2024 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU license.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 *
 */

#ifndef _MEMORY_GROUP_MANAGER_H_
#define _MEMORY_GROUP_MANAGER_H_

#include <linux/mm.h>
#include <linux/of.h>
#include <linux/version.h>

#if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE)
typedef int vm_fault_t;
#endif

#define MEMORY_GROUP_MANAGER_NR_GROUPS (16)

#define PTE_PBHA_SHIFT (59)
#define PTE_PBHA_MASK ((uint64_t)0xf << PTE_PBHA_SHIFT)
#define PTE_RES_BIT_MULTI_AS_SHIFT (63)
#define PTE_FLAGS_NONE (0)
#define PBHA_ID_DEFAULT (0)

struct memory_group_manager_device;
struct memory_group_manager_import_data;

enum mgm_pte_flags {
	MMA_VIOLATION = 0,
};

/**
 * struct memory_group_manager_ops - Callbacks for memory group manager
 *                                   operations
 *
 * @mgm_alloc_page:           Callback to allocate physical memory in a group
 * @mgm_free_page:            Callback to free physical memory in a group
 * @mgm_get_import_memory_id: Callback to get the group ID for imported memory
 * @mgm_update_gpu_pte:       Callback to modify a GPU page table entry
 * @mgm_pte_to_original_pte:  Callback to get the original PTE entry as given
 *                            to mgm_update_gpu_pte
 * @mgm_vmf_insert_pfn_prot:  Callback to map a physical memory page for the CPU
 * @mgm_get_import_memory_cached_access_permitted: Callback to query if a given imported
 *                            memory is allowed to be accessed as cached or not by the GPU
 */
struct memory_group_manager_ops {
	/*
	 * mgm_alloc_page - Allocate a physical memory page in a group
	 *
	 * @mgm_dev:  The memory group manager through which the request is
	 *            being made.
	 * @group_id: A physical memory group ID. The meaning of this is defined
	 *            by the systems integrator. Its valid range is
	 *            0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
	 * @gfp_mask: Bitmask of Get Free Page flags affecting allocator
	 *            behavior.
	 * @order:    Page order for physical page size.
	 *            order = 0 refers to small pages
	 *            order != 0 refers to 2 MB pages, so
	 *            order = 9 (when small page size is 4KB,  2^9 *  4KB = 2 MB)
	 *            order = 7 (when small page size is 16KB, 2^7 * 16KB = 2 MB)
	 *            order = 5 (when small page size is 64KB, 2^5 * 64KB = 2 MB)
	 *
	 * Return: Pointer to allocated page, or NULL if allocation failed.
	 */
	struct page *(*mgm_alloc_page)(struct memory_group_manager_device *mgm_dev,
				       unsigned int group_id, gfp_t gfp_mask, unsigned int order);

	/*
	 * mgm_free_page - Free a physical memory page in a group
	 *
	 * @mgm_dev:  The memory group manager through which the request
	 *            is being made.
	 * @group_id: A physical memory group ID. The meaning of this is
	 *            defined by the systems integrator. Its valid range is
	 *            0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
	 * @page:     Address of the struct associated with a page of physical
	 *            memory that was allocated by calling the mgm_alloc_page
	 *            method of the same memory pool with the same values of
	 *            @group_id and @order.
     * @order:    Page order for physical page size.
     *            order = 0 refers to small pages
     *            order != 0 refers to 2 MB pages.
	 */
	void (*mgm_free_page)(struct memory_group_manager_device *mgm_dev, unsigned int group_id,
			      struct page *page, unsigned int order);

	/*
	 * mgm_get_import_memory_id - Get the physical memory group ID for the
	 *                            imported memory
	 *
	 * @mgm_dev:     The memory group manager through which the request
	 *               is being made.
	 * @import_data: Pointer to the data which describes imported memory.
	 *
	 * Note that provision of this call back is optional, where it is not
	 * provided this call back pointer must be set to NULL to indicate it
	 * is not in use.
	 *
	 * Return: The memory group ID to use when mapping pages from this
	 *         imported memory.
	 */
	int (*mgm_get_import_memory_id)(struct memory_group_manager_device *mgm_dev,
					struct memory_group_manager_import_data *import_data);

	/*
	 * mgm_update_gpu_pte - Modify a GPU page table entry for a memory group
	 *
	 * @mgm_dev:   The memory group manager through which the request
	 *             is being made.
	 * @group_id:  A physical memory group ID. The meaning of this is
	 *             defined by the systems integrator. Its valid range is
	 *             0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
	 *
	 * @pbha_id:   PBHA Overrride ID to encode into the PTE
	 * @pte_flags: PTE related flags, defined in enum mgm_pte_flags
	 *
	 *
	 * @mmu_level: The level of the page table entry in @ate.
	 * @pte:       The page table entry to modify, in LPAE or AArch64 format
	 *             (depending on the driver's configuration). This should be
	 *             decoded to determine the physical address and any other
	 *             properties of the mapping the manager requires.
	 *
	 * This function allows the memory group manager to modify a GPU page
	 * table entry before it is stored by the kbase module (controller
	 * driver). It may set certain bits in the page table entry attributes
	 * or modify the physical address, based on the physical memory group ID,
	 * PBHA ID, PTE flags and/or additional data in struct memory_group_manager_device.
	 *
	 * Return: A modified GPU page table entry to be stored in a page table.
	 */
	u64 (*mgm_update_gpu_pte)(struct memory_group_manager_device *mgm_dev,
				  unsigned int group_id, unsigned int pbha_id,
				  unsigned int pte_flags, int mmu_level, u64 pte);

	/*
	 * mgm_pte_to_original_pte - Undo any modification done during mgm_update_gpu_pte()
	 *
	 * @mgm_dev:   The memory group manager through which the request
	 *             is being made.
	 * @group_id:  A physical memory group ID. The meaning of this is
	 *             defined by the systems integrator. Its valid range is
	 *             0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
	 * @mmu_level: The level of the page table entry in @ate.
	 * @pte:       The page table entry to restore the original representation for,
	 *             in LPAE or AArch64 format (depending on the driver's configuration).
	 *
	 * Undo any modifications done during mgm_update_gpu_pte().
	 * This function allows getting back the original PTE entry as given
	 * to mgm_update_gpu_pte().
	 *
	 * Return: PTE entry as originally specified to mgm_update_gpu_pte()
	 */
	u64 (*mgm_pte_to_original_pte)(struct memory_group_manager_device *mgm_dev,
				       unsigned int group_id, int mmu_level, u64 pte);

	/*
	 * mgm_vmf_insert_pfn_prot - Map a physical page in a group for the CPU
	 *
	 * @mgm_dev:   The memory group manager through which the request
	 *             is being made.
	 * @group_id:  A physical memory group ID. The meaning of this is
	 *             defined by the systems integrator. Its valid range is
	 *             0 .. MEMORY_GROUP_MANAGER_NR_GROUPS-1.
	 * @vma:       The virtual memory area to insert the page into.
	 * @addr:      A virtual address (in @vma) to assign to the page.
	 * @pfn:       The kernel Page Frame Number to insert at @addr in @vma.
	 * @pgprot:    Protection flags for the inserted page.
	 *
	 * Called from a CPU virtual memory page fault handler. This function
	 * creates a page table entry from the given parameter values and stores
	 * it at the appropriate location (unlike mgm_update_gpu_pte, which
	 * returns a modified entry).
	 *
	 * Return: Type of fault that occurred or VM_FAULT_NOPAGE if the page
	 *         table entry was successfully installed.
	 */
	vm_fault_t (*mgm_vmf_insert_pfn_prot)(struct memory_group_manager_device *mgm_dev,
					      unsigned int group_id, struct vm_area_struct *vma,
					      unsigned long addr, unsigned long pfn,
					      pgprot_t pgprot);

	/*
	 * mgm_get_import_memory_cached_access_permitted - Check if a given imported memory
	 *                            is allowed to be accessed as cached or not by the GPU
	 *
	 * @mgm_dev:     The memory group manager through which the request
	 *               is being made.
	 * @import_data: Pointer to the data which describes imported memory.
	 *
	 * Return: true if cached access is permitted, false otherwise
	 */
	bool (*mgm_get_import_memory_cached_access_permitted)(
		struct memory_group_manager_device *mgm_dev,
		struct memory_group_manager_import_data *import_data);
};

/**
 * struct memory_group_manager_device - Device structure for a memory group
 *                                      manager
 *
 * @ops:   Callbacks associated with this device
 * @data:  Pointer to device private data
 * @owner: pointer to owning module
 *
 * In order for a systems integrator to provide custom behaviors for memory
 * operations performed by the kbase module (controller driver), they must
 * provide a platform-specific driver module which implements this interface.
 *
 * This structure should be registered with the platform device using
 * platform_set_drvdata().
 */
struct memory_group_manager_device {
	struct memory_group_manager_ops ops;
	void *data;
	struct module *owner;
};

enum memory_group_manager_import_type { MEMORY_GROUP_MANAGER_IMPORT_TYPE_DMA_BUF };

/**
 * struct memory_group_manager_import_data - Structure describing the imported
 *                                           memory
 *
 * @type:      type of imported memory
 * @u:         Union describing the imported memory
 * @u.dma_buf: imported memory
 *
 */
struct memory_group_manager_import_data {
	enum memory_group_manager_import_type type;
	union {
		struct dma_buf *dma_buf;
	} u;
};

#endif /* _MEMORY_GROUP_MANAGER_H_ */
