/* Copyright (c) 2002,2007-2014, 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. * */ #ifndef __KGSL_MMU_H #define __KGSL_MMU_H #include #include "kgsl_iommu.h" /* * These defines control the address range for allocations that * are mapped into all pagetables. */ #define KGSL_GLOBAL_PT_SIZE SZ_4M #define KGSL_MMU_GLOBAL_MEM_BASE 0xf8000000 /* Virtual memory range to map non-kgsl allocations */ #define KGSL_MMU_MAPPED_MEM_BASE KGSL_SVM_UPPER_BOUND #define KGSL_MMU_MAPPED_MEM_SIZE (KGSL_MMU_GLOBAL_MEM_BASE - \ KGSL_MMU_MAPPED_MEM_BASE - \ SZ_1M) /* * These defines control the address range for allocations that * are mapped into secure pagetable. */ #define KGSL_IOMMU_SECURE_MEM_BASE 0xe8000000 #define KGSL_IOMMU_SECURE_MEM_SIZE SZ_256M /* defconfig option for disabling per process pagetables */ #ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE #define KGSL_MMU_USE_PER_PROCESS_PT true #else #define KGSL_MMU_USE_PER_PROCESS_PT false #endif /* Identifier for the global page table */ /* Per process page tables will probably pass in the thread group as an identifier */ #define KGSL_MMU_GLOBAL_PT 0 #define KGSL_MMU_SECURE_PT 1 #define KGSL_MMU_PRIV_PT 0xFFFFFFFF struct kgsl_device; enum kgsl_mmutype { KGSL_MMU_TYPE_IOMMU = 0, KGSL_MMU_TYPE_NONE }; struct kgsl_pagetable { spinlock_t lock; struct kref refcount; struct gen_pool *pool; struct list_head list; unsigned int name; struct kobject *kobj; struct { unsigned int entries; unsigned int mapped; unsigned int max_mapped; } stats; const struct kgsl_mmu_pt_ops *pt_ops; unsigned int fault_addr; void *priv; struct kgsl_mmu *mmu; unsigned long *mem_bitmap; }; struct kgsl_mmu; struct kgsl_mmu_ops { int (*mmu_init) (struct kgsl_mmu *mmu); int (*mmu_close) (struct kgsl_mmu *mmu); int (*mmu_start) (struct kgsl_mmu *mmu); void (*mmu_stop) (struct kgsl_mmu *mmu); int (*mmu_set_pt) (struct kgsl_mmu *mmu, struct kgsl_pagetable *pt); phys_addr_t (*mmu_get_current_ptbase) (struct kgsl_mmu *mmu); void (*mmu_pagefault_resume) (struct kgsl_mmu *mmu); void (*mmu_enable_clk) (struct kgsl_mmu *mmu, int unit); void (*mmu_disable_clk) (struct kgsl_mmu *mmu, int unit); uint64_t (*mmu_get_default_ttbr0)(struct kgsl_mmu *mmu, unsigned int unit_id, enum kgsl_iommu_context_id ctx_id); unsigned int (*mmu_get_reg_gpuaddr)(struct kgsl_mmu *mmu, int iommu_unit_num, int ctx_id, int reg); unsigned int (*mmu_get_reg_ahbaddr)(struct kgsl_mmu *mmu, int iommu_unit_num, int ctx_id, enum kgsl_iommu_reg_map reg); int (*mmu_get_num_iommu_units)(struct kgsl_mmu *mmu); int (*mmu_pt_equal) (struct kgsl_mmu *mmu, struct kgsl_pagetable *pt, phys_addr_t pt_base); phys_addr_t (*mmu_get_pt_base_addr) (struct kgsl_mmu *mmu, struct kgsl_pagetable *pt); unsigned int (*mmu_sync_lock) (struct kgsl_mmu *mmu, unsigned int *cmds); unsigned int (*mmu_sync_unlock) (struct kgsl_mmu *mmu, unsigned int *cmds); int (*mmu_hw_halt_supported)(struct kgsl_mmu *mmu, int iommu_unit_num); int (*mmu_set_pf_policy)(struct kgsl_mmu *mmu, unsigned int pf_policy); void (*mmu_set_pagefault)(struct kgsl_mmu *mmu); struct kgsl_protected_registers *(*mmu_get_prot_regs) (struct kgsl_mmu *mmu); }; struct kgsl_mmu_pt_ops { int (*mmu_map) (struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc); int (*mmu_unmap) (struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc); void *(*mmu_create_pagetable) (void); void *(*mmu_create_secure_pagetable) (void); void (*mmu_destroy_pagetable) (struct kgsl_pagetable *); phys_addr_t (*get_ptbase) (struct kgsl_pagetable *); }; #define KGSL_MMU_FLAGS_IOMMU_SYNC BIT(31) struct kgsl_mmu { uint32_t flags; struct kgsl_device *device; struct kgsl_memdesc setstate_memory; /* current page table object being used by device mmu */ struct kgsl_pagetable *defaultpagetable; /* secure global pagetable device mmu */ struct kgsl_pagetable *securepagetable; /* pagetable object used for priv bank of IOMMU */ struct kgsl_pagetable *priv_bank_table; const struct kgsl_mmu_ops *mmu_ops; void *priv; atomic_t fault; unsigned long pt_base; unsigned long pt_size; bool secured; }; extern struct kgsl_mmu_ops kgsl_iommu_ops; extern struct kgsl_mmu_pt_ops iommu_pt_ops; struct kgsl_pagetable *kgsl_mmu_getpagetable(struct kgsl_mmu *, unsigned long name); struct kgsl_pagetable *kgsl_mmu_getpagetable_ptbase(struct kgsl_mmu *, phys_addr_t ptbase); void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable); int kgsl_mmu_init(struct kgsl_device *device); int kgsl_mmu_start(struct kgsl_device *device); int kgsl_mmu_close(struct kgsl_device *device); int kgsl_mmu_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); int kgsl_mmu_get_gpuaddr(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); int kgsl_mmu_put_gpuaddr(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr); int kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, phys_addr_t pt_base); unsigned int kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu, phys_addr_t pt_base, unsigned int addr); int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt, enum kgsl_deviceid id); int kgsl_mmu_enabled(void); void kgsl_mmu_set_mmutype(char *mmutype); enum kgsl_mmutype kgsl_mmu_get_mmutype(void); int kgsl_mmu_gpuaddr_in_range(struct kgsl_pagetable *pt, unsigned int gpuaddr); int kgsl_add_global_pt_entry(struct kgsl_device *device, struct kgsl_memdesc *memdesc); void kgsl_remove_global_pt_entry(struct kgsl_memdesc *memdesc); struct kgsl_memdesc *kgsl_search_global_pt_entries(unsigned int gpuaddr, unsigned int size); /* * Static inline functions of MMU that simply call the SMMU specific * function using a function pointer. These functions can be thought * of as wrappers around the actual function */ static inline phys_addr_t kgsl_mmu_get_current_ptbase(struct kgsl_mmu *mmu) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_current_ptbase) return mmu->mmu_ops->mmu_get_current_ptbase(mmu); else return 0; } static inline int kgsl_mmu_set_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pagetable) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_set_pt) return mmu->mmu_ops->mmu_set_pt(mmu, pagetable); return 0; } static inline void kgsl_mmu_stop(struct kgsl_mmu *mmu) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_stop) mmu->mmu_ops->mmu_stop(mmu); } static inline int kgsl_mmu_pt_equal(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt, phys_addr_t pt_base) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_pt_equal) return mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base); else return 1; } static inline phys_addr_t kgsl_mmu_get_pt_base_addr(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_pt_base_addr) return mmu->mmu_ops->mmu_get_pt_base_addr(mmu, pt); else return 0; } static inline phys_addr_t kgsl_mmu_get_default_ttbr0(struct kgsl_mmu *mmu, unsigned int unit_id, enum kgsl_iommu_context_id ctx_id) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_default_ttbr0) return mmu->mmu_ops->mmu_get_default_ttbr0(mmu, unit_id, ctx_id); else return 0; } static inline void kgsl_mmu_enable_clk(struct kgsl_mmu *mmu, int unit) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_enable_clk) mmu->mmu_ops->mmu_enable_clk(mmu, unit); else return; } static inline void kgsl_mmu_disable_clk(struct kgsl_mmu *mmu, int unit) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_disable_clk) mmu->mmu_ops->mmu_disable_clk(mmu, unit); } static inline unsigned int kgsl_mmu_get_reg_gpuaddr(struct kgsl_mmu *mmu, int iommu_unit_num, int ctx_id, int reg) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_reg_gpuaddr) return mmu->mmu_ops->mmu_get_reg_gpuaddr(mmu, iommu_unit_num, ctx_id, reg); else return 0; } /* * kgsl_mmu_get_reg_ahbaddr() - Calls the mmu specific function pointer to * return the address that GPU can use to access register * @mmu: Pointer to the device mmu * @iommu_unit_num: There can be multiple iommu units used for graphics. * This parameter is an index to the iommu unit being used * @ctx_id: The context id within the iommu unit * @reg: Register whose address is to be returned * * Returns the ahb address of reg else 0 */ static inline unsigned int kgsl_mmu_get_reg_ahbaddr(struct kgsl_mmu *mmu, int iommu_unit_num, int ctx_id, enum kgsl_iommu_reg_map reg) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_reg_ahbaddr) return mmu->mmu_ops->mmu_get_reg_ahbaddr(mmu, iommu_unit_num, ctx_id, reg); else return 0; } static inline int kgsl_mmu_get_num_iommu_units(struct kgsl_mmu *mmu) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_num_iommu_units) return mmu->mmu_ops->mmu_get_num_iommu_units(mmu); else return 0; } /* * kgsl_mmu_hw_halt_supported() - Runtime check for iommu hw halt * @mmu: the mmu * * Returns non-zero if the iommu supports hw halt, * 0 if not. */ static inline int kgsl_mmu_hw_halt_supported(struct kgsl_mmu *mmu, int iommu_unit_num) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_hw_halt_supported) return mmu->mmu_ops->mmu_hw_halt_supported(mmu, iommu_unit_num); else return 0; } /* * kgsl_mmu_is_perprocess() - Runtime check for per-process * pagetables. * @mmu: the mmu * * Returns true if per-process pagetables are enabled, * false if not. */ static inline int kgsl_mmu_is_perprocess(struct kgsl_mmu *mmu) { return KGSL_MMU_USE_PER_PROCESS_PT; } /* * kgsl_mmu_use_cpu_map() - Runtime check for matching the CPU * address space on the GPU. * @mmu: the mmu * * Returns true if supported false if not. */ static inline int kgsl_mmu_use_cpu_map(struct kgsl_mmu *mmu) { return kgsl_mmu_is_perprocess(mmu); } static inline int kgsl_mmu_sync_lock(struct kgsl_mmu *mmu, unsigned int *cmds) { if ((mmu->flags & KGSL_MMU_FLAGS_IOMMU_SYNC) && mmu->mmu_ops && mmu->mmu_ops->mmu_sync_lock) return mmu->mmu_ops->mmu_sync_lock(mmu, cmds); else return 0; } static inline int kgsl_mmu_sync_unlock(struct kgsl_mmu *mmu, unsigned int *cmds) { if ((mmu->flags & KGSL_MMU_FLAGS_IOMMU_SYNC) && mmu->mmu_ops && mmu->mmu_ops->mmu_sync_unlock) return mmu->mmu_ops->mmu_sync_unlock(mmu, cmds); else return 0; } static inline int kgsl_mmu_set_pagefault_policy(struct kgsl_mmu *mmu, unsigned int pf_policy) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_set_pf_policy) return mmu->mmu_ops->mmu_set_pf_policy(mmu, pf_policy); else return 0; } static inline void kgsl_mmu_set_pagefault(struct kgsl_mmu *mmu) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_set_pagefault) return mmu->mmu_ops->mmu_set_pagefault(mmu); } static inline struct kgsl_protected_registers *kgsl_mmu_get_prot_regs (struct kgsl_mmu *mmu) { if (mmu->mmu_ops && mmu->mmu_ops->mmu_get_prot_regs) return mmu->mmu_ops->mmu_get_prot_regs(mmu); else return NULL; } static inline int kgsl_mmu_is_secured(struct kgsl_mmu *mmu) { return mmu && (mmu->secured) && (mmu->securepagetable); } static inline phys_addr_t kgsl_mmu_pagetable_get_ptbase(struct kgsl_pagetable *pagetable) { if (pagetable && pagetable->pt_ops->get_ptbase) return pagetable->pt_ops->get_ptbase(pagetable); return 0; } #endif /* __KGSL_MMU_H */