/* * MC StreamID configuration * * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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, see . */ #define pr_fmt(fmt) "%s(): " fmt, __func__ #include #include #include #include #include #include #include #include #define TO_MC_SID_STREAMID_SECURITY_CONFIG(addr) (addr + sizeof(u32)) #define SMMU_BYPASS_SID 0x7f struct tegra_mc_sid { struct device *dev; void __iomem *base; void __iomem *sid_base; const struct tegra_mc_sid_soc_data *soc_data; u32 smmu_bypass_sid; struct dentry *debugfs_root; }; static struct tegra_mc_sid *mc_sid; /* * Return the by-pass-smmu StreamID. */ u32 tegra_mc_get_smmu_bypass_sid(void) { if (!mc_sid) return SMMU_BYPASS_SID; return mc_sid->smmu_bypass_sid; } EXPORT_SYMBOL(tegra_mc_get_smmu_bypass_sid); /* * Return a string with the name associated with the passed StreamID. */ const char *tegra_mc_get_sid_name(int sid) { int i; struct sid_to_oids *entry; if (!mc_sid) { pr_err("mc-sid isn't populated yet\n"); goto end; } for (i = 0; i < mc_sid->soc_data->nsid_to_oids; i++) { entry = &mc_sid->soc_data->sid_to_oids[i]; if (entry->sid == sid) { if (!entry->name) pr_err("Entry is missing name\n"); return entry->name; } } end: if (sid > TEGRA_SID_PASSTHROUGH) return "Invalid SID"; else return "Unassigned SID"; } static void __mc_override_sid(int sid, int oid, enum mc_overrides ord) { volatile void __iomem *addr; u32 val; int offs = mc_sid->soc_data->sid_override_reg[oid].offs; BUG_ON(oid >= mc_sid->soc_data->max_oids); addr = TO_MC_SID_STREAMID_SECURITY_CONFIG(mc_sid->sid_base + offs); val = readl_relaxed(addr); if (!(val & SCEW_STREAMID_OVERRIDE) && (val & SCEW_STREAMID_WRITE_ACCESS_DISABLED)) return; /* * Only valid when kernel runs in secure mode. * Otherwise, no effect on MC_SID_STREAMID_SECURITY_CONFIG_*. */ if ((ord == OVERRIDE) || (tegra_platform_is_sim() && ord == SIM_OVERRIDE)) val = SCEW_STREAMID_OVERRIDE | SCEW_NS; else val = SCEW_NS; writel_relaxed(val, addr); addr = mc_sid->sid_base + offs; writel_relaxed(sid, addr); pr_debug("override sid=%d oid=%d ord=%d at offset=%x\n", sid, oid, ord, offs); } void platform_override_streamid(int sid) { int i; if (!mc_sid || !mc_sid->sid_base) { pr_err("mc-sid isn't populated\n"); return; } for (i = 0; i < mc_sid->soc_data->nsid_to_oids; i++) { struct sid_to_oids *conf; int j; conf = &mc_sid->soc_data->sid_to_oids[i]; BUG_ON(conf->noids > MAX_OIDS_IN_SID); if (sid != conf->sid) continue; for (j = 0; j < conf->noids; j++) __mc_override_sid(sid, conf->oid[j], conf->ord); } } #if defined(CONFIG_DEBUG_FS) enum { ORD, SEC, TXN, MAX_REGS_TYPE}; static const char * const mc_regs_type[] = { "ord", "sec", "txn", }; static int mc_reg32_debugfs_set(void *data, u64 val) { writel(val, data); return 0; } static int mc_reg32_debugfs_get(void *data, u64 *val) { *val = readl(data); return 0; } DEFINE_SIMPLE_ATTRIBUTE(mc_reg32_debugfs_fops, mc_reg32_debugfs_get, mc_reg32_debugfs_set, "%08llx\n"); static void tegra_mc_sid_create_debugfs(void) { int i, j; mc_sid->debugfs_root = debugfs_create_dir("tegra_mc_sid", NULL); if (!mc_sid->debugfs_root) return; for (i = 0; i < MAX_REGS_TYPE; i++) { void __iomem *base; struct dentry *dent; if (i == SEC) base = mc_sid->sid_base + sizeof(u32); else if (i == TXN) base = mc_sid->base + 0x1000; else base = mc_sid->sid_base; dent = debugfs_create_dir(mc_regs_type[i], mc_sid->debugfs_root); if (!dent) continue; for (j = 0; j < mc_sid->soc_data->nsid_override_reg; j++) { void *addr; addr = base + mc_sid->soc_data->sid_override_reg[j].offs; debugfs_create_file( mc_sid->soc_data->sid_override_reg[j].name, S_IRUGO | S_IWUSR, dent, addr, &mc_reg32_debugfs_fops); } } } static void tegra_mc_sid_remove_debugfs(void) { debugfs_remove_recursive(mc_sid->debugfs_root); } #else static inline void tegra_mc_sid_create_debugfs(void) { } static void tegra_mc_sid_remove_debugfs(void) { } #endif /* CONFIG_DEBUG_FS */ int tegra_mc_sid_probe(struct platform_device *pdev, const struct tegra_mc_sid_soc_data *soc_data) { struct resource *res; static void __iomem *addr; mc_sid = devm_kzalloc(&pdev->dev, sizeof(*mc_sid), GFP_KERNEL); if (!mc_sid) return -ENOMEM; mc_sid->dev = &pdev->dev; mc_sid->soc_data = soc_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); addr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(addr)) return PTR_ERR(addr); mc_sid->sid_base = addr; /* Read the bypass streamid. If not found, assign default value. */ if (of_property_read_u32(pdev->dev.of_node, "nvidia,by-pass-smmu-streamid", &mc_sid->smmu_bypass_sid)) mc_sid->smmu_bypass_sid = SMMU_BYPASS_SID; /* FIXME: wait for MC driver */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); addr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(addr)) return PTR_ERR(addr); mc_sid->base = addr; writel_relaxed(TBU_BYPASS_SID, mc_sid->base + MC_SMMU_BYPASS_CONFIG_0); tegra_mc_sid_create_debugfs(); return 0; } EXPORT_SYMBOL_GPL(tegra_mc_sid_probe); int tegra_mc_sid_remove(struct platform_device *pdev) { tegra_mc_sid_remove_debugfs(); return 0; } EXPORT_SYMBOL_GPL(tegra_mc_sid_remove); MODULE_DESCRIPTION("MC StreamID configuration"); MODULE_AUTHOR("Hiroshi DOYU , Pritesh Raithatha "); MODULE_LICENSE("GPL v2");