kgsl mmu
Posted bubbleben
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kgsl mmu相关的知识,希望对你有一定的参考价值。
&soc
....
kgsl_msm_iommu: qcom,kgsl-iommu@0x02CA0000
compatible = "qcom,kgsl-smmu-v2";
reg = <0x02CA0000 0x10000>;
/* CB5(ATOS) & CB5/6/7 are protected by HYP*/
qcom,protect = <0xa0000 0xc000>;
clocks = <&clock_gcc_GCC_GPU_CFG_AHB_CLK>,
<&clock_gcc_GCC_DDRSS_GPU_AXI_CLK>,
<&clock_gcc_GCC_GPU_MEMNOC_GFX_CLK>,
clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
qcom,secure_align_mask = <0xfff>;
qcom,retention;
qcom,hyp_secure_alloc;
gfx3d_user: gfx3d_user
compatible = "qcom,smmu-kgsl-cb";
label = "gfx3d_user";
iommus = <&kgsl_smmu 0x0 0x401>;
qcom,gpu-offset = <0xa8000>;
;
gfx3d_secure: gfx3d_secure
compatible = "qcom,smmu-kgsl-cb";
label = "gfx3d_secure";
iommus = <&kgsl_smmu 0x2 0x400>;
;
;
....
1. kgsl_mmu_probe
int kgsl_mmu_probe(struct kgsl_device *device)
//
struct kgsl_mmu *mmu = &device->mmu;
int ret;
/*
* Try to probe for the IOMMU and if it doesn't exist for some reason
* go for the NOMMU option instead
*/
// 2节
ret = kgsl_iommu_probe(device);
if (!ret || ret == -EPROBE_DEFER)
return ret;
mmu->mmu_ops = &kgsl_nommu_ops;
mmu->type = KGSL_MMU_TYPE_NONE;
return 0;
1.1 kgsl_mmu
/**
* struct kgsl_mmu - Master definition for KGSL MMU devices
* @flags: MMU device flags
* @type: Type of MMU that is attached
* @subtype: Sub Type of MMU that is attached
* @defaultpagetable: Default pagetable object for the MMU
* @securepagetable: Default secure pagetable object for the MMU
* @mmu_ops: Function pointers for the MMU sub-type
* @secured: True if the MMU needs to be secured
* @feature: Static list of MMU features
*/
struct kgsl_mmu
unsigned long flags;
enum kgsl_mmutype type;
u32 subtype;
struct kgsl_pagetable *defaultpagetable;
struct kgsl_pagetable *securepagetable;
// 1.1.1节
const struct kgsl_mmu_ops *mmu_ops;
bool secured;
unsigned long features;
/** @pfpolicy: The current pagefault policy for the device */
unsigned long pfpolicy;
/** mmu: Pointer to the IOMMU sub-device */
// IOMMU子设备[见1.1.2节]
struct kgsl_iommu iommu;
;
1.1.1 kgsl_mmu_ops
struct kgsl_mmu_ops
void (*mmu_close)(struct kgsl_mmu *mmu);
int (*mmu_start)(struct kgsl_mmu *mmu);
uint64_t (*mmu_get_current_ttbr0)(struct kgsl_mmu *mmu);
void (*mmu_pagefault_resume)(struct kgsl_mmu *mmu, bool terminate);
void (*mmu_clear_fsr)(struct kgsl_mmu *mmu);
void (*mmu_enable_clk)(struct kgsl_mmu *mmu);
void (*mmu_disable_clk)(struct kgsl_mmu *mmu);
int (*mmu_set_pf_policy)(struct kgsl_mmu *mmu, unsigned long pf_policy);
int (*mmu_init_pt)(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt);
struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
unsigned long name);
void (*mmu_map_global)(struct kgsl_mmu *mmu,
struct kgsl_memdesc *memdesc, u32 padding);
;
1.1.2 kgsl_iommu
/*
* struct kgsl_iommu - Structure holding iommu data for kgsl driver
* @regbase: Virtual address of the IOMMU register base
* @regstart: Physical address of the iommu registers
* @regsize: Length of the iommu register region.
* @setstate: Scratch GPU memory for IOMMU operations
* @clk_enable_count: The ref count of clock enable calls
* @clks: Array of pointers to IOMMU clocks
* @smmu_info: smmu info used in a5xx preemption
*/
struct kgsl_iommu
/** @user_context: Container for the user iommu context */
struct kgsl_iommu_context user_context;
/** @secure_context: Container for the secure iommu context */
struct kgsl_iommu_context secure_context;
/** @lpac_context: Container for the LPAC iommu context */
struct kgsl_iommu_context lpac_context;
void __iomem *regbase;
struct kgsl_memdesc *setstate;
atomic_t clk_enable_count;
struct clk_bulk_data *clks;
int num_clks;
struct kgsl_memdesc *smmu_info;
/** @pdev: Pointer to the platform device for the IOMMU device */
struct platform_device *pdev;
/**
* @ppt_active: Set when the first per process pagetable is created.
* This is used to warn when global buffers are created that might not
* be mapped in all contexts
*/
bool ppt_active;
/** @cb0_offset: Offset of context bank 0 from iommu register base */
u32 cb0_offset;
/** @pagesize: Size of each context bank register space */
u32 pagesize;
/** @cx_gdsc: CX GDSC handle in case the IOMMU needs it */
struct regulator *cx_gdsc;
;
2. kgsl_iommu_probe
int kgsl_mmu_probe(struct kgsl_device *device)
struct kgsl_mmu *mmu = &device->mmu;
int ret;
/*
* Try to probe for the IOMMU and if it doesn't exist for some reason
* go for the NOMMU option instead
*/
// 2.1
ret = kgsl_iommu_probe(device);
if (!ret || ret == -EPROBE_DEFER)
return ret;
mmu->mmu_ops = &kgsl_nommu_ops;
mmu->type = KGSL_MMU_TYPE_NONE;
return 0;
2.1 kgsl_iommu_probe
int kgsl_iommu_probe(struct kgsl_device *device)
u32 val[2];
int ret, i;
struct kgsl_iommu *iommu = KGSL_IOMMU(device);
struct platform_device *pdev;
struct kgsl_mmu *mmu = &device->mmu;
struct device_node *node;
struct kgsl_global_memdesc *md;
// 读取qcom,kgsl-smmu-v2节点
node = of_find_compatible_node(NULL, NULL, "qcom,kgsl-smmu-v2");
if (!node)
return -ENODEV;
/* Create a kmem cache for the pagetable address objects */
if (!addr_entry_cache)
// 创建kgsl_iommu_addr_entry缓存
addr_entry_cache = KMEM_CACHE(kgsl_iommu_addr_entry, 0);
if (!addr_entry_cache)
ret = -ENOMEM;
goto err;
// 读reg节点
ret = of_property_read_u32_array(node, "reg", val, 2);
if (ret)
dev_err(device->dev,
"%pOF: Unable to read KGSL IOMMU register range\\n",
node);
goto err;
// 根据读取的reg值初始化kgsl_iommu的IOMMU寄存器的虚拟基地址
iommu->regbase = devm_ioremap(&device->pdev->dev, val[0], val[1]);
if (!iommu->regbase)
dev_err(&device->pdev->dev, "Couldn't map IOMMU registers\\n");
ret = -ENOMEM;
goto err;
pdev = of_find_device_by_node(node);
iommu->pdev = pdev;
iommu->num_clks = 0;
// 创建kgsl_iommu的clk_bulk_data数组
iommu->clks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(kgsl_iommu_clocks),
sizeof(*iommu->clks), GFP_KERNEL);
if (!iommu->clks)
platform_device_put(pdev);
ret = -ENOMEM;
goto err;
// 解析clocks节点
for (i = 0; i < ARRAY_SIZE(kgsl_iommu_clocks); i++)
struct clk *c;
c = devm_clk_get(&device->pdev->dev, kgsl_iommu_clocks[i]);
if (IS_ERR(c))
continue;
iommu->clks[iommu->num_clks].id = kgsl_iommu_clocks[i];
iommu->clks[iommu->num_clks++].clk = c;
/* Get the CX regulator if it is available */
iommu->cx_gdsc = devm_regulator_get(&pdev->dev, "vddcx");
set_bit(KGSL_MMU_PAGED, &mmu->features);
// kgsl_mmu的类型为IOMMU
mmu->type = KGSL_MMU_TYPE_IOMMU;
// kgsl_mmu的kgsl_mmu_ops为kgsl_iommu_ops[见2.2节]
mmu->mmu_ops = &kgsl_iommu_ops;
/* Fill out the rest of the devices in the node */
of_platform_populate(node, NULL, NULL, &pdev->dev);
/* Peek at the phandle to set up configuration */
kgsl_iommu_check_config(mmu, node);
/* Probe the default pagetable */
// 解析gfx3d_user节点[见2.3节]
ret = iommu_probe_user_context(device, node);
if (ret)
of_platform_depopulate(&pdev->dev);
platform_device_put(pdev);
goto err;
/* Probe the secure pagetable (this is optional) */
// 解析gfx3d_secure节点
iommu_probe_secure_context(device, node);
of_node_put(node);
/* Map any globals that might have been created early */
list_for_each_entry(md, &device->globals, node)
if (md->memdesc.flags & KGSL_MEMFLAGS_SECURE)
if (IS_ERR_OR_NULL(mmu->securepagetable))
continue;
kgsl_iommu_secure_map(mmu->securepagetable,
&md->memdesc);
else
kgsl_iommu_default_map(mmu->defaultpagetable,
&md->memdesc);
/* QDSS is supported only when QCOM_KGSL_QDSS_STM is enabled */
if (IS_ENABLED(CONFIG_QCOM_KGSL_QDSS_STM))
device->qdss_desc = kgsl_allocate_global_fixed(device,
"qcom,gpu-qdss-stm", "gpu-qdss");
device->qtimer_desc = kgsl_allocate_global_fixed(device,
"qcom,gpu-timer", "gpu-qtimer");
/*
* Only support VBOs on MMU500 hardware that supports the PRR
* marker register to ignore writes to the zero page
*/
if (mmu->subtype == KGSL_IOMMU_SMMU_V500)
/*
* We need to allocate a page because we need a known physical
* address to program in the PRR register but the hardware
* should intercept accesses to the page before they go to DDR
* so this should be mostly just a placeholder
*/
kgsl_vbo_zero_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
__GFP_NORETRY | __GFP_HIGHMEM);
if (kgsl_vbo_zero_page)
set_bit(KGSL_MMU_SUPPORT_VBO, &mmu->features);
return 0;
err:
kmem_cache_destroy(addr_entry_cache);
addr_entry_cache = NULL;
of_node_put(node);
return ret;
2.2 kgsl_iommu_ops
static const struct kgsl_mmu_ops kgsl_iommu_ops =
.mmu_close = kgsl_iommu_close,
.mmu_start = kgsl_iommu_start,
.mmu_clear_fsr = kgsl_iommu_clear_fsr,
.mmu_get_current_ttbr0 = kgsl_iommu_get_current_ttbr0,
.mmu_enable_clk = kgsl_iommu_enable_clk,
.mmu_disable_clk = kgsl_iommu_disable_clk,
.mmu_set_pf_policy = kgsl_iommu_set_pf_policy,
.mmu_pagefault_resume = kgsl_iommu_pagefault_resume,
// 获取页表
.mmu_getpagetable = kgsl_iommu_getpagetable,
.mmu_map_global = kgsl_iommu_map_global,
;
2.2.1 kgsl_iommu_getpagetable
static struct kgsl_pagetable *kgsl_iommu_getpagetable(struct kgsl_mmu *mmu,
unsigned long name)
struct kgsl_pagetable *pt;
/* If we already know the pagetable, return it */
pt = kgsl_get_pagetable(name);
if (pt)
return pt;
/* If io-pgtables are not in effect, just use the default pagetable */
if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
return mmu->defaultpagetable;
// 2.2.2
pt = kgsl_iopgtbl_pagetable(mmu, name);
/*
* If the io-pgtable allocation didn't work then fall back to the
* default pagetable for this cycle
*/
if (!pt)
return mmu->defaultpagetable;
return pt;
2.2.2 kgsl_iopgtbl_pagetable
static struct kgsl_pagetable *kgsl_iopgtbl_pagetable(struct kgsl_mmu *mmu, u32 name)
struct kgsl_iommu *iommu = &mmu->iommu;
struct kgsl_iommu_pt *pt;
int ret;
pt = kzalloc(sizeof(*pt), GFP_KERNEL);
if (!pt)
return ERR_PTR(-ENOMEM);
kgsl_mmu_pagetable_init(mmu, &pt->base, name);
pt->base.fault_addr = U64_MAX;
pt->base.rbtree = RB_ROOT;
// 设置kgsl_mmu_pt_ops[见2.2.3节]
pt->base.pt_ops = &iopgtbl_pt_ops;
if (test_bit(KGSL_MMU_64BIT, &mmu->features))
pt->base.compat_va_start = KGSL_IOMMU_SVM_BASE32;
pt->base.compat_va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
pt->base.va_start = KGSL_IOMMU_VA_BASE64;
pt->base.va_end = KGSL_IOMMU_VA_END64;
if (is_compat_task())
pt->base.svm_start = KGSL_IOMMU_SVM_BASE32;
pt->base.svm_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
else
pt->base.svm_start = KGSL_IOMMU_SVM_BASE64;
pt->base.svm_end = KGSL_IOMMU_SVM_END64;
else
pt->base.va_start = KGSL_IOMMU_SVM_BASE32;
if (mmu->secured)
pt->base.va_end = KGSL_IOMMU_SECURE_BASE(mmu);
else
pt->base.va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
pt->base.compat_va_start = pt->base.va_start;
pt->base.compat_va_end = pt->base.va_end;
pt->base.svm_start = KGSL_IOMMU_SVM_BASE32;
pt->base.svm_end = KGSL_IOMMU_SVM_END32;
ret = kgsl_iopgtbl_alloc(&iommu->user_context, pt);
if (ret)
kfree(pt);
return ERR_PTR(ret);
kgsl_mmu_pagetable_add(mmu, &pt->base);
return &pt->base;
2.2.3 iopgtbl_pt_ops
static const struct kgsl_mmu_pt_ops iopgtbl_pt_ops =
.mmu_map = kgsl_iopgtbl_map,
.mmu_map_child = kgsl_iopgtbl_map_child,
.mmu_map_zero_page_to_range = kgsl_iopgtbl_map_zero_page_to_range,
.mmu_unmap = kgsl_iopgtbl_unmap,
.mmu_unmap_range = kgsl_iopgtbl_unmap_range,
.mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
.get_ttbr0 = kgsl_iommu_get_ttbr0,
.get_context_bank = kgsl_iommu_get_context_bank,
// 获取GPU虚拟地址
.get_gpuaddr = kgsl_iommu_get_gpuaddr,
.put_gpuaddr = kgsl_iommu_put_gpuaddr,
.set_svm_region = kgsl_iommu_set_svm_region,
.find_svm_region = kgsl_iommu_find_svm_region,
.svm_range = kgsl_iommu_svm_range,
.addr_in_range = kgsl_iommu_addr_in_range,
;
2.3 iommu_probe_user_context
static int iommu_probe_user_context(struct kgsl_device *device,
struct device_node *node)
struct kgsl_iommu *iommu = KGSL_IOMMU(device);
struct kgsl_mmu *mmu = &device->mmu;
int ret;
// 初始化kgsl_iommu的kgsl_iommu_context
ret = kgsl_iommu_setup_context(mmu, node, &iommu->user_context,
"gfx3d_user", kgsl_iommu_default_fault_handler);
if (ret)
return ret;
/* LPAC is optional so don't worry if it returns error */
kgsl_iommu_setup_context(mmu, node, &iommu->lpac_context,
"gfx3d_lpac", kgsl_iommu_lpac_fault_handler);
/*
* FIXME: If adreno_smmu->cookie wasn't initialized then we can't do
* IOPGTABLE
*/
/* Make the default pagetable */
// 设置kgsl_mmu的默认页表[见2.3.1节]
mmu->defaultpagetable = kgsl_iommu_default_pagetable(mmu);
if (IS_ERR(mmu->defaultpagetable))
return PTR_ERR(mmu->defaultpagetable);
/* If IOPGTABLE isn't enabled then we are done */
if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
return 0;
/* Enable TTBR0 on the default and LPAC contexts */
kgsl_iommu_enable_ttbr0(&以上是关于kgsl mmu的主要内容,如果未能解决你的问题,请参考以下文章