android_kernel_samsung_msm8976/drivers/iommu/msm_iommu_dev-v1.c

738 lines
18 KiB
C
Raw Permalink Normal View History

/* Copyright (c) 2012-2015, 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/msm-bus.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/iommu.h>
#include <linux/interrupt.h>
#include <linux/msm-bus.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include "msm_iommu_hw-v1.h"
#include <linux/qcom_iommu.h>
#include "msm_iommu_perfmon.h"
static struct of_device_id msm_iommu_ctx_match_table[];
#ifdef CONFIG_IOMMU_LPAE
static const char *BFB_REG_NODE_NAME = "qcom,iommu-lpae-bfb-regs";
static const char *BFB_DATA_NODE_NAME = "qcom,iommu-lpae-bfb-data";
#else
static const char *BFB_REG_NODE_NAME = "qcom,iommu-bfb-regs";
static const char *BFB_DATA_NODE_NAME = "qcom,iommu-bfb-data";
#endif
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
struct msm_iommu_bfb_settings *bfb_settings;
u32 nreg, nval;
int ret;
/*
* It is not valid for a device to have the BFB_REG_NODE_NAME
* property but not the BFB_DATA_NODE_NAME property, and vice versa.
*/
if (!of_get_property(pdev->dev.of_node, BFB_REG_NODE_NAME, &nreg)) {
if (of_get_property(pdev->dev.of_node, BFB_DATA_NODE_NAME,
&nval))
return -EINVAL;
return 0;
}
if (!of_get_property(pdev->dev.of_node, BFB_DATA_NODE_NAME, &nval))
return -EINVAL;
if (nreg >= sizeof(bfb_settings->regs))
return -EINVAL;
if (nval >= sizeof(bfb_settings->data))
return -EINVAL;
if (nval != nreg)
return -EINVAL;
bfb_settings = devm_kzalloc(&pdev->dev, sizeof(*bfb_settings),
GFP_KERNEL);
if (!bfb_settings)
return -ENOMEM;
ret = of_property_read_u32_array(pdev->dev.of_node,
BFB_REG_NODE_NAME,
bfb_settings->regs,
nreg / sizeof(*bfb_settings->regs));
if (ret)
return ret;
ret = of_property_read_u32_array(pdev->dev.of_node,
BFB_DATA_NODE_NAME,
bfb_settings->data,
nval / sizeof(*bfb_settings->data));
if (ret)
return ret;
bfb_settings->length = nreg / sizeof(*bfb_settings->regs);
drvdata->bfb_settings = bfb_settings;
return 0;
}
static int __get_bus_vote_client(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
int ret = 0;
struct msm_bus_scale_pdata *bs_table;
const char *dummy;
/* Check whether bus scaling has been specified for this node */
ret = of_property_read_string(pdev->dev.of_node, "qcom,msm-bus,name",
&dummy);
if (ret)
return 0;
bs_table = msm_bus_cl_get_pdata(pdev);
if (bs_table) {
drvdata->bus_client = msm_bus_scale_register_client(bs_table);
if (IS_ERR(&drvdata->bus_client)) {
pr_err("%s(): Bus client register failed.\n", __func__);
ret = -EINVAL;
}
}
return ret;
}
static void __put_bus_vote_client(struct msm_iommu_drvdata *drvdata)
{
msm_bus_scale_unregister_client(drvdata->bus_client);
drvdata->bus_client = 0;
}
/*
* CONFIG_IOMMU_NON_SECURE allows us to override the secure
* designation of SMMUs in device tree. With this config enabled
* all SMMUs will be programmed by this driver.
*/
#ifdef CONFIG_IOMMU_NON_SECURE
static inline void get_secure_id(struct device_node *node,
struct msm_iommu_drvdata *drvdata)
{
}
static inline void get_secure_ctx(struct device_node *node,
struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
ctx_drvdata->secure_context = 0;
}
#else
static inline int is_vfe_smmu(char const *iommu_name)
{
return (strcmp(iommu_name, "vfe_iommu") == 0);
}
static void get_secure_id(struct device_node *node,
struct msm_iommu_drvdata *drvdata)
{
if (msm_iommu_get_scm_call_avail()) {
if (!is_vfe_smmu(drvdata->name) || is_vfe_secure())
of_property_read_u32(node, "qcom,iommu-secure-id",
&drvdata->sec_id);
else
pr_info("vfe_iommu: Keeping vfe non-secure\n");
}
}
static void get_secure_ctx(struct device_node *node,
struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
u32 secure_ctx = 0;
if (msm_iommu_get_scm_call_avail()) {
if (!is_vfe_smmu(iommu_drvdata->name) || is_vfe_secure()) {
secure_ctx =
of_property_read_bool(node, "qcom,secure-context");
}
}
ctx_drvdata->secure_context = secure_ctx;
}
#endif
static int msm_iommu_parse_dt(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
struct device_node *child;
int ret = 0;
struct resource *r;
drvdata->dev = &pdev->dev;
ret = __get_bus_vote_client(pdev, drvdata);
if (ret)
goto fail;
ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
if (ret)
goto fail;
for_each_available_child_of_node(pdev->dev.of_node, child)
drvdata->ncb++;
ret = of_property_read_string(pdev->dev.of_node, "label",
&drvdata->name);
if (ret)
goto fail;
drvdata->sec_id = -1;
get_secure_id(pdev->dev.of_node, drvdata);
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
if (r) {
drvdata->clk_reg_virt = devm_ioremap(&pdev->dev, r->start,
resource_size(r));
if (!drvdata->clk_reg_virt) {
pr_err("Failed to map resource for iommu clk: %pr\n",
r);
ret = -ENOMEM;
goto fail;
}
}
drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node,
"qcom,iommu-enable-halt");
msm_iommu_add_drv(drvdata);
return 0;
fail:
__put_bus_vote_client(drvdata);
return ret;
}
static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
struct iommu_pmon *pmon_info)
{
int ret = 0;
int irq = platform_get_irq(pdev, 0);
unsigned int cls_prop_size;
if (irq > 0) {
pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,iommu-pmu-ngroups",
&pmon_info->num_groups);
if (ret) {
pr_err("Error reading qcom,iommu-pmu-ngroups\n");
goto fail;
}
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,iommu-pmu-ncounters",
&pmon_info->num_counters);
if (ret) {
pr_err("Error reading qcom,iommu-pmu-ncounters\n");
goto fail;
}
if (!of_get_property(pdev->dev.of_node,
"qcom,iommu-pmu-event-classes",
&cls_prop_size)) {
pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return -EINVAL;
}
pmon_info->event_cls_supported =
devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
if (!pmon_info->event_cls_supported) {
pr_err("Unable to get memory for event class array\n");
return -ENOMEM;
}
pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
ret = of_property_read_u32_array(pdev->dev.of_node,
"qcom,iommu-pmu-event-classes",
pmon_info->event_cls_supported,
pmon_info->nevent_cls_supported);
if (ret) {
pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return ret;
}
} else {
pmon_info->iommu.evt_irq = -1;
ret = irq;
}
fail:
return ret;
}
static int msm_iommu_probe(struct platform_device *pdev)
{
struct iommu_pmon *pmon_info;
struct msm_iommu_drvdata *drvdata;
struct resource *r;
int ret, needs_alt_core_clk, needs_alt_iface_clk;
int global_cfg_irq, global_client_irq;
u32 temp;
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iommu_base");
if (!r)
return -EINVAL;
drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!drvdata->base)
return -ENOMEM;
drvdata->phys_base = r->start;
if (IS_ENABLED(CONFIG_MSM_IOMMU_VBIF_CHECK)) {
drvdata->vbif_base =
ioremap(drvdata->phys_base - (phys_addr_t) 0x4000,
0x1000);
WARN_ON_ONCE(!drvdata->vbif_base);
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"smmu_local_base");
if (r) {
drvdata->smmu_local_base =
devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!drvdata->smmu_local_base)
return -ENOMEM;
}
drvdata->glb_base = drvdata->base;
if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-mmu-500"))
drvdata->model = MMU_500;
if (of_get_property(pdev->dev.of_node, "vdd-supply", NULL)) {
drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(drvdata->gdsc))
return PTR_ERR(drvdata->gdsc);
drvdata->alt_gdsc = devm_regulator_get(&pdev->dev,
"qcom,alt-vdd");
if (IS_ERR(drvdata->alt_gdsc))
drvdata->alt_gdsc = NULL;
} else {
pr_debug("Warning: No regulator specified for IOMMU\n");
}
drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(drvdata->pclk)) {
ret = PTR_ERR(drvdata->pclk);
drvdata->pclk = NULL;
goto fail;
}
ret = clk_prepare(drvdata->pclk);
if (ret)
return ret;
drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
if (IS_ERR(drvdata->clk)) {
ret = PTR_ERR(drvdata->clk);
drvdata->clk = NULL;
goto fail;
}
ret = clk_prepare(drvdata->clk);
if (ret)
goto fail;
needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
"qcom,needs-alt-core-clk");
if (needs_alt_core_clk) {
drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
if (IS_ERR(drvdata->aclk)) {
ret = PTR_ERR(drvdata->aclk);
drvdata->aclk = NULL;
goto fail;
}
ret = clk_prepare(drvdata->aclk);
if (ret)
goto fail;
}
needs_alt_iface_clk = of_property_read_bool(pdev->dev.of_node,
"qcom,needs-alt-iface-clk");
if (needs_alt_iface_clk) {
drvdata->aiclk = devm_clk_get(&pdev->dev, "alt_iface_clk");
if (IS_ERR(drvdata->aiclk)) {
ret = PTR_ERR(drvdata->aiclk);
drvdata->aiclk = NULL;
goto fail;
}
ret = clk_prepare(drvdata->aiclk);
if (ret)
goto fail;
}
if (!of_property_read_u32(pdev->dev.of_node,
"qcom,cb-base-offset",
&temp))
drvdata->cb_base = drvdata->base + temp;
else
drvdata->cb_base = drvdata->base + 0x8000;
if (clk_get_rate(drvdata->clk) == 0) {
ret = clk_round_rate(drvdata->clk, 1000);
clk_set_rate(drvdata->clk, ret);
}
if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
ret = clk_round_rate(drvdata->aclk, 1000);
clk_set_rate(drvdata->aclk, ret);
}
if (drvdata->aiclk && clk_get_rate(drvdata->aiclk) == 0) {
ret = clk_round_rate(drvdata->aiclk, 1000);
clk_set_rate(drvdata->aiclk, ret);
}
ret = msm_iommu_parse_dt(pdev, drvdata);
if (ret)
return ret;
dev_info(&pdev->dev,
"device %s (model: %d) mapped at %p, with %d ctx banks\n",
drvdata->name, drvdata->model, drvdata->base, drvdata->ncb);
platform_set_drvdata(pdev, drvdata);
pmon_info = msm_iommu_pm_alloc(&pdev->dev);
if (pmon_info != NULL) {
ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
if (ret) {
msm_iommu_pm_free(&pdev->dev);
pr_info("%s: pmon not available.\n", drvdata->name);
} else {
pmon_info->iommu.base = drvdata->base;
pmon_info->iommu.ops = msm_get_iommu_access_ops();
pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v1();
pmon_info->iommu.iommu_name = drvdata->name;
ret = msm_iommu_pm_iommu_register(pmon_info);
if (ret) {
pr_err("%s iommu register fail\n",
drvdata->name);
msm_iommu_pm_free(&pdev->dev);
} else {
pr_debug("%s iommu registered for pmon\n",
pmon_info->iommu.iommu_name);
}
}
}
global_cfg_irq =
platform_get_irq_byname(pdev, "global_cfg_NS_irq");
if (global_cfg_irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, global_cfg_irq,
NULL,
msm_iommu_global_fault_handler,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_RISING,
"msm_iommu_global_cfg_irq", pdev);
if (ret < 0)
pr_err("Request Global CFG IRQ %d failed with ret=%d\n",
global_cfg_irq, ret);
}
global_client_irq =
platform_get_irq_byname(pdev, "global_client_NS_irq");
if (global_client_irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, global_client_irq,
NULL,
msm_iommu_global_fault_handler,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_RISING,
"msm_iommu_global_client_irq", pdev);
if (ret < 0)
pr_err("Request Global Client IRQ %d failed with ret=%d\n",
global_client_irq, ret);
}
iommu: msm: Populate context banks at the end of IOMMU probe When calling of_platform_populate() on the context bank child nodes the probe function for the context bank might actually run at that moment. However, the context bank probe function requires data that is provided by the caller (IOMMU probe) of of_platform_populte(). If the IOMMU probe function does not complete before the context bank probe function executes we might crash. Instead move the call to of_platform_populate() the end of the IOMMU probe function to ensure all the data is available when context bank probe function executes. Stack trace: [ 3.757438] Unable to handle kernel NULL pointer dereference at virtual address 00000050 [ 3.757447] pgd = ffffffc00007d000 [ 3.757467] [00000050] *pgd=000000000e70b003, *pmd=000000000e70c003, *pte=00e00000f9000407 [ 3.757483] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 3.757492] Modules linked in: [ 3.757510] CPU: 2 PID: 6 Comm: kworker/u16:0 Not tainted 3.10.49-perf-g2a22a7c #2 [ 3.757543] Workqueue: deferwq deferred_probe_work_func [ 3.757554] task: ffffffc00e7a3480 ti: ffffffc00e7d4000 task.ti: ffffffc00e7d4000 [ 3.757575] PC is at msm_iommu_ctx_probe+0x78/0x2fc [ 3.757590] LR is at msm_iommu_ctx_probe+0x68/0x2fc [ 3.757600] pc : [<ffffffc000932d84>] lr : [<ffffffc000932d74>] pstate: 60000005 [ 3.757605] sp : ffffffc00e7d78b0 [ 3.757620] x29: ffffffc00e7d78b0 x28: 0000000000000089 [ 3.757634] x27: ffffffc00153c000 x26: 0000000000000000 [ 3.757647] x25: ffffffc0be7cf410 x24: 0000000000000072 [ 3.757661] x23: 0000000000000000 x22: ffffffc0be37d410 [ 3.757675] x21: ffffffc0be078818 x20: ffffffc0be37d400 [ 3.757688] x19: ffffffc0c32abdb8 x18: 0000000000000000 [ 3.757701] x17: 0000000000000000 x16: 0000000000000000 [ 3.757714] x15: 0000000000000000 x14: 0fffffffffffffff [ 3.757727] x13: 0000000000000030 x12: 0000000000000030 [ 3.757741] x11: 0101010101010101 x10: ffffffffffffffff [ 3.757754] x9 : 0000000000000000 x8 : ffffffc0be079000 [ 3.757767] x7 : 0000000000000000 x6 : 000000000000003f [ 3.757779] x5 : 0000000000000040 x4 : 0000000000000000 [ 3.757802] x3 : 0000000000000000 x2 : 0000000000000003 [ 3.757815] x1 : ffffffc001028000 x0 : 0000000000000001 [ 3.757823] .... [ 3.758780] Process kworker/u16:0 (pid: 6, stack limit = 0xffffffc00e7d4058) [ 3.758784] Call trace: [ 3.758805] [<ffffffc000932d84>] msm_iommu_ctx_probe+0x78/0x2fc [ 3.758820] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.758839] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.758857] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.758874] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.758891] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.758908] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.758923] [<ffffffc0005b1010>] device_add+0x434/0x594 [ 3.758941] [<ffffffc00089ed38>] of_device_add+0x38/0x44 [ 3.758957] [<ffffffc00089f3ac>] of_platform_device_create_pdata+0x54/0x78 [ 3.758974] [<ffffffc00089f598>] of_platform_bus_create+0x1ac/0x228 [ 3.758990] [<ffffffc00089f73c>] of_platform_populate+0x68/0x90 [ 3.759008] [<ffffffc000932bbc>] msm_iommu_probe+0x528/0x678 [ 3.759021] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.759039] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.759056] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.759072] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.759088] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.759105] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.759122] [<ffffffc0005b2e7c>] deferred_probe_work_func+0x70/0xa0 [ 3.759138] [<ffffffc000235ac4>] process_one_work+0x260/0x3c4 [ 3.759151] [<ffffffc000236b14>] worker_thread+0x1f8/0x348 [ 3.759169] [<ffffffc00023bf54>] kthread+0xac/0xb8 Change-Id: I6ee8ffdfc31ad65251c95d9cafd3d6f49b92ff8e CRs-fixed: 771980 Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
2014-12-16 20:14:12 +00:00
ret = of_platform_populate(pdev->dev.of_node, msm_iommu_ctx_match_table,
NULL, &pdev->dev);
fail:
if (ret) {
clk_unprepare(drvdata->clk);
clk_unprepare(drvdata->pclk);
clk_unprepare(drvdata->aclk);
clk_unprepare(drvdata->aiclk);
iommu: msm: Populate context banks at the end of IOMMU probe When calling of_platform_populate() on the context bank child nodes the probe function for the context bank might actually run at that moment. However, the context bank probe function requires data that is provided by the caller (IOMMU probe) of of_platform_populte(). If the IOMMU probe function does not complete before the context bank probe function executes we might crash. Instead move the call to of_platform_populate() the end of the IOMMU probe function to ensure all the data is available when context bank probe function executes. Stack trace: [ 3.757438] Unable to handle kernel NULL pointer dereference at virtual address 00000050 [ 3.757447] pgd = ffffffc00007d000 [ 3.757467] [00000050] *pgd=000000000e70b003, *pmd=000000000e70c003, *pte=00e00000f9000407 [ 3.757483] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 3.757492] Modules linked in: [ 3.757510] CPU: 2 PID: 6 Comm: kworker/u16:0 Not tainted 3.10.49-perf-g2a22a7c #2 [ 3.757543] Workqueue: deferwq deferred_probe_work_func [ 3.757554] task: ffffffc00e7a3480 ti: ffffffc00e7d4000 task.ti: ffffffc00e7d4000 [ 3.757575] PC is at msm_iommu_ctx_probe+0x78/0x2fc [ 3.757590] LR is at msm_iommu_ctx_probe+0x68/0x2fc [ 3.757600] pc : [<ffffffc000932d84>] lr : [<ffffffc000932d74>] pstate: 60000005 [ 3.757605] sp : ffffffc00e7d78b0 [ 3.757620] x29: ffffffc00e7d78b0 x28: 0000000000000089 [ 3.757634] x27: ffffffc00153c000 x26: 0000000000000000 [ 3.757647] x25: ffffffc0be7cf410 x24: 0000000000000072 [ 3.757661] x23: 0000000000000000 x22: ffffffc0be37d410 [ 3.757675] x21: ffffffc0be078818 x20: ffffffc0be37d400 [ 3.757688] x19: ffffffc0c32abdb8 x18: 0000000000000000 [ 3.757701] x17: 0000000000000000 x16: 0000000000000000 [ 3.757714] x15: 0000000000000000 x14: 0fffffffffffffff [ 3.757727] x13: 0000000000000030 x12: 0000000000000030 [ 3.757741] x11: 0101010101010101 x10: ffffffffffffffff [ 3.757754] x9 : 0000000000000000 x8 : ffffffc0be079000 [ 3.757767] x7 : 0000000000000000 x6 : 000000000000003f [ 3.757779] x5 : 0000000000000040 x4 : 0000000000000000 [ 3.757802] x3 : 0000000000000000 x2 : 0000000000000003 [ 3.757815] x1 : ffffffc001028000 x0 : 0000000000000001 [ 3.757823] .... [ 3.758780] Process kworker/u16:0 (pid: 6, stack limit = 0xffffffc00e7d4058) [ 3.758784] Call trace: [ 3.758805] [<ffffffc000932d84>] msm_iommu_ctx_probe+0x78/0x2fc [ 3.758820] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.758839] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.758857] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.758874] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.758891] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.758908] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.758923] [<ffffffc0005b1010>] device_add+0x434/0x594 [ 3.758941] [<ffffffc00089ed38>] of_device_add+0x38/0x44 [ 3.758957] [<ffffffc00089f3ac>] of_platform_device_create_pdata+0x54/0x78 [ 3.758974] [<ffffffc00089f598>] of_platform_bus_create+0x1ac/0x228 [ 3.758990] [<ffffffc00089f73c>] of_platform_populate+0x68/0x90 [ 3.759008] [<ffffffc000932bbc>] msm_iommu_probe+0x528/0x678 [ 3.759021] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.759039] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.759056] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.759072] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.759088] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.759105] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.759122] [<ffffffc0005b2e7c>] deferred_probe_work_func+0x70/0xa0 [ 3.759138] [<ffffffc000235ac4>] process_one_work+0x260/0x3c4 [ 3.759151] [<ffffffc000236b14>] worker_thread+0x1f8/0x348 [ 3.759169] [<ffffffc00023bf54>] kthread+0xac/0xb8 Change-Id: I6ee8ffdfc31ad65251c95d9cafd3d6f49b92ff8e CRs-fixed: 771980 Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
2014-12-16 20:14:12 +00:00
pr_err("Failed to create iommu context device\n");
}
iommu: msm: Populate context banks at the end of IOMMU probe When calling of_platform_populate() on the context bank child nodes the probe function for the context bank might actually run at that moment. However, the context bank probe function requires data that is provided by the caller (IOMMU probe) of of_platform_populte(). If the IOMMU probe function does not complete before the context bank probe function executes we might crash. Instead move the call to of_platform_populate() the end of the IOMMU probe function to ensure all the data is available when context bank probe function executes. Stack trace: [ 3.757438] Unable to handle kernel NULL pointer dereference at virtual address 00000050 [ 3.757447] pgd = ffffffc00007d000 [ 3.757467] [00000050] *pgd=000000000e70b003, *pmd=000000000e70c003, *pte=00e00000f9000407 [ 3.757483] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 3.757492] Modules linked in: [ 3.757510] CPU: 2 PID: 6 Comm: kworker/u16:0 Not tainted 3.10.49-perf-g2a22a7c #2 [ 3.757543] Workqueue: deferwq deferred_probe_work_func [ 3.757554] task: ffffffc00e7a3480 ti: ffffffc00e7d4000 task.ti: ffffffc00e7d4000 [ 3.757575] PC is at msm_iommu_ctx_probe+0x78/0x2fc [ 3.757590] LR is at msm_iommu_ctx_probe+0x68/0x2fc [ 3.757600] pc : [<ffffffc000932d84>] lr : [<ffffffc000932d74>] pstate: 60000005 [ 3.757605] sp : ffffffc00e7d78b0 [ 3.757620] x29: ffffffc00e7d78b0 x28: 0000000000000089 [ 3.757634] x27: ffffffc00153c000 x26: 0000000000000000 [ 3.757647] x25: ffffffc0be7cf410 x24: 0000000000000072 [ 3.757661] x23: 0000000000000000 x22: ffffffc0be37d410 [ 3.757675] x21: ffffffc0be078818 x20: ffffffc0be37d400 [ 3.757688] x19: ffffffc0c32abdb8 x18: 0000000000000000 [ 3.757701] x17: 0000000000000000 x16: 0000000000000000 [ 3.757714] x15: 0000000000000000 x14: 0fffffffffffffff [ 3.757727] x13: 0000000000000030 x12: 0000000000000030 [ 3.757741] x11: 0101010101010101 x10: ffffffffffffffff [ 3.757754] x9 : 0000000000000000 x8 : ffffffc0be079000 [ 3.757767] x7 : 0000000000000000 x6 : 000000000000003f [ 3.757779] x5 : 0000000000000040 x4 : 0000000000000000 [ 3.757802] x3 : 0000000000000000 x2 : 0000000000000003 [ 3.757815] x1 : ffffffc001028000 x0 : 0000000000000001 [ 3.757823] .... [ 3.758780] Process kworker/u16:0 (pid: 6, stack limit = 0xffffffc00e7d4058) [ 3.758784] Call trace: [ 3.758805] [<ffffffc000932d84>] msm_iommu_ctx_probe+0x78/0x2fc [ 3.758820] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.758839] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.758857] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.758874] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.758891] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.758908] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.758923] [<ffffffc0005b1010>] device_add+0x434/0x594 [ 3.758941] [<ffffffc00089ed38>] of_device_add+0x38/0x44 [ 3.758957] [<ffffffc00089f3ac>] of_platform_device_create_pdata+0x54/0x78 [ 3.758974] [<ffffffc00089f598>] of_platform_bus_create+0x1ac/0x228 [ 3.758990] [<ffffffc00089f73c>] of_platform_populate+0x68/0x90 [ 3.759008] [<ffffffc000932bbc>] msm_iommu_probe+0x528/0x678 [ 3.759021] [<ffffffc0005b485c>] platform_drv_probe+0x14/0x20 [ 3.759039] [<ffffffc0005b3568>] driver_probe_device+0xd4/0x240 [ 3.759056] [<ffffffc0005b36fc>] __device_attach+0x28/0x4c [ 3.759072] [<ffffffc0005b1a4c>] bus_for_each_drv+0x70/0x90 [ 3.759088] [<ffffffc0005b3448>] device_attach+0x68/0x98 [ 3.759105] [<ffffffc0005b298c>] bus_probe_device+0x28/0xa0 [ 3.759122] [<ffffffc0005b2e7c>] deferred_probe_work_func+0x70/0xa0 [ 3.759138] [<ffffffc000235ac4>] process_one_work+0x260/0x3c4 [ 3.759151] [<ffffffc000236b14>] worker_thread+0x1f8/0x348 [ 3.759169] [<ffffffc00023bf54>] kthread+0xac/0xb8 Change-Id: I6ee8ffdfc31ad65251c95d9cafd3d6f49b92ff8e CRs-fixed: 771980 Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
2014-12-16 20:14:12 +00:00
return ret;
}
static int msm_iommu_remove(struct platform_device *pdev)
{
struct msm_iommu_drvdata *drv = NULL;
msm_iommu_pm_iommu_unregister(&pdev->dev);
msm_iommu_pm_free(&pdev->dev);
drv = platform_get_drvdata(pdev);
if (drv) {
__put_bus_vote_client(drv);
clk_unprepare(drv->clk);
clk_unprepare(drv->pclk);
clk_unprepare(drv->aclk);
clk_unprepare(drv->aiclk);
msm_iommu_remove_drv(drv);
platform_set_drvdata(pdev, NULL);
}
return 0;
}
static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
struct resource *r, rp;
int irq = 0, ret = 0;
struct msm_iommu_drvdata *drvdata;
u32 nsid;
u32 n_sid_mask;
unsigned long cb_offset;
drvdata = dev_get_drvdata(pdev->dev.parent);
get_secure_ctx(pdev->dev.of_node, drvdata, ctx_drvdata);
if (drvdata->sec_id != -1) {
irq = platform_get_irq(pdev, 1);
/*
* This is for supporting old DTs where it was assumed
* that interrupt 0 is only required as their CB is
* non-secure.
*/
if (irq < 0)
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
msm_iommu_secure_fault_handler_v2,
IRQF_ONESHOT | IRQF_SHARED,
"msm_iommu_secure_irq", pdev);
if (ret) {
pr_err("Request IRQ %d failed with ret=%d\n",
irq, ret);
return ret;
}
}
} else {
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
msm_iommu_fault_handler_v2,
IRQF_ONESHOT | IRQF_SHARED,
"msm_iommu_nonsecure_irq", pdev);
if (ret) {
pr_err("Request IRQ %d failed with ret=%d\n",
irq, ret);
goto out;
}
}
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -EINVAL;
goto out;
}
ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
if (ret)
goto out;
/* Calculate the context bank number using the base addresses.
* Typically CB0 base address is 0x8000 pages away if the number
* of CBs are <=8. So, assume the offset 0x8000 until mentioned
* explicitely.
*/
cb_offset = drvdata->cb_base - drvdata->base;
ctx_drvdata->num = ((r->start - rp.start - cb_offset)
>> CTX_SHIFT);
if (of_property_read_string(pdev->dev.of_node, "label",
&ctx_drvdata->name))
ctx_drvdata->name = dev_name(&pdev->dev);
ctx_drvdata->report_error_on_fault =
of_property_read_bool(pdev->dev.of_node,
"qcom,report-error-on-fault");
if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid)) {
ret = -EINVAL;
goto out;
}
if (nsid >= sizeof(ctx_drvdata->sids)) {
ret = -EINVAL;
goto out;
}
if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
ctx_drvdata->sids,
nsid / sizeof(*ctx_drvdata->sids))) {
ret = -EINVAL;
goto out;
}
ctx_drvdata->nsid = nsid;
ctx_drvdata->asid = -1;
if (!of_get_property(pdev->dev.of_node, "qcom,iommu-sid-mask",
&n_sid_mask)) {
memset(ctx_drvdata->sid_mask, 0, MAX_NUM_SMR);
goto out;
}
if (n_sid_mask != nsid) {
ret = -EINVAL;
goto out;
}
if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-sid-mask",
ctx_drvdata->sid_mask,
n_sid_mask / sizeof(*ctx_drvdata->sid_mask))) {
ret = -EINVAL;
goto out;
}
ctx_drvdata->n_sid_mask = n_sid_mask;
out:
return ret;
}
static int msm_iommu_ctx_probe(struct platform_device *pdev)
{
struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
int ret;
if (!pdev->dev.parent)
return -EINVAL;
ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
GFP_KERNEL);
if (!ctx_drvdata)
return -ENOMEM;
ctx_drvdata->pdev = pdev;
INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
if (!ret) {
platform_set_drvdata(pdev, ctx_drvdata);
dev_info(&pdev->dev, "context %s using bank %d\n",
ctx_drvdata->name, ctx_drvdata->num);
}
return ret;
}
static int msm_iommu_ctx_remove(struct platform_device *pdev)
{
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct of_device_id msm_iommu_match_table[] = {
{ .compatible = "qcom,msm-smmu-v1", },
{ .compatible = "qcom,msm-smmu-v2", },
{}
};
static struct platform_driver msm_iommu_driver = {
.driver = {
.name = "msm_iommu",
.of_match_table = msm_iommu_match_table,
},
.probe = msm_iommu_probe,
.remove = msm_iommu_remove,
};
static struct of_device_id msm_iommu_ctx_match_table[] = {
{ .compatible = "qcom,msm-smmu-v1-ctx", },
{ .compatible = "qcom,msm-smmu-v2-ctx", },
{}
};
static struct platform_driver msm_iommu_ctx_driver = {
.driver = {
.name = "msm_iommu_ctx",
.of_match_table = msm_iommu_ctx_match_table,
},
.probe = msm_iommu_ctx_probe,
.remove = msm_iommu_ctx_remove,
};
static int __init msm_iommu_driver_init(void)
{
int ret;
msm_iommu_check_scm_call_avail();
msm_set_iommu_access_ops(&iommu_access_ops_v1);
msm_iommu_sec_set_access_ops(&iommu_access_ops_v1);
ret = platform_driver_register(&msm_iommu_driver);
if (ret != 0) {
pr_err("Failed to register IOMMU driver\n");
goto error;
}
ret = platform_driver_register(&msm_iommu_ctx_driver);
if (ret != 0) {
pr_err("Failed to register IOMMU context driver\n");
goto error;
}
error:
return ret;
}
static void __exit msm_iommu_driver_exit(void)
{
platform_driver_unregister(&msm_iommu_ctx_driver);
platform_driver_unregister(&msm_iommu_driver);
}
subsys_initcall(msm_iommu_driver_init);
module_exit(msm_iommu_driver_exit);
MODULE_LICENSE("GPL v2");