diff --git a/drivers/platform/tegra/mc-hwpm.c b/drivers/platform/tegra/mc-hwpm.c index 50dd358a..217c416a 100644 --- a/drivers/platform/tegra/mc-hwpm.c +++ b/drivers/platform/tegra/mc-hwpm.c @@ -1,6 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. */ #define pr_fmt(fmt) "mc-hwpm: " fmt @@ -11,235 +11,114 @@ #include #include #include -#include -#include "soc/tegra/mc.h" #include -#define MC_EMEM_ADR_CFG_CHANNEL_ENABLE_0 0xdf8 -#define CH_MASK 0xFFFF /* Change bit counting if this mask changes */ -#define MC_MAX_CHANNELS 16 -/* Total channels = Broadcast channel + MC_MAX_CHANNELS */ -#define TOTAL_CHANNELS (1 + MC_MAX_CHANNELS) -static void __iomem *memctlr_regs[TOTAL_CHANNELS]; -static u32 platform_ch_nums; +/* Broadcast Channel + 16 MC Channels */ +#define MAX_MC_CHANNELS 17 -static bool is_tegra_safety_build(void) -{ -#ifdef CONFIG_OF - return of_property_read_bool(of_chosen, - "nvidia,tegra-safety-build"); -#else - return false; -#endif -} -/** - * Read from the MC. - * - * @idx The MC channel to read from. - * @reg The offset of the register to read. - * - * Read from the specified MC channel: 0 -> Broadcast channel/ Global channel - * 1 -> MC0, 16 -> MC16, etc. If @idx - * corresponds to a non-existent channel then 0 is returned. - */ -static u32 memctrl_readl(u32 chnl_no, u32 reg) -{ - static bool warned; +static struct tegra_soc_hwpm_ip_ops hwpm_ip_ops; - if (is_tegra_safety_build()) { - if (!warned) { - pr_warn("WARNING: VM isn't allowed to read MC register space in Safety Build"); - warned = true; - } - return 0xffff; - } - - if (chnl_no > platform_ch_nums) - return 0; - - return readl(memctlr_regs[chnl_no] + reg); -} +struct tegra_mc_hwpm { + struct device *dev; + void __iomem **ch_regs; + u32 no_ch; + u64 base_addr; +}; /** - * Write to the MC. - * - * @idx The MC channel to write to. - * @val Value to write. - * @reg The offset of the register to write. - * - * Write to the specified MC channel: 0 -> MC0, 1 -> MC1, etc. For writes there - * is a special channel, %MC_BROADCAST_CHANNEL, which writes to all channels. If - * @idx corresponds to a non-existent channel then the write is dropped. + * ch_no == 0 = Broadcast Channel + * ch_no == 1 = MC0 + * ch_no == 2 = MC1 + * . + * . + * ch_no == 16 = MC15 */ -static void memctrl_writel(u32 chnl_no, u32 val, u32 reg) +static u32 mc_readl(struct tegra_mc_hwpm *mc, u32 ch_no, u32 reg) { - static bool warned; - - if (is_tegra_safety_build()) { - if (!warned) { - pr_warn("WARNING: VM isn't allowed to write into MC register space in Safety Build"); - warned = true; - } - return; - } - - if (chnl_no > platform_ch_nums) { - pr_err("Incorrect channel number: %u\n", chnl_no); - return; - } - - writel(val, memctlr_regs[chnl_no] + reg); + return readl(mc->ch_regs[ch_no] + reg); } -static u32 get_platform_dram_num_channels(void) +static void mc_writel(struct tegra_mc_hwpm *mc, u32 ch_no, u32 val, u32 reg) { - u32 plt_ch_nums = 0; - u32 ch; - - ch = memctrl_readl(0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE_0) & CH_MASK; - while (ch) { - if (ch & 1) - plt_ch_nums++; - ch >>= 1; - } - - return plt_ch_nums; + writel(val, mc->ch_regs[ch_no] + reg); } static int tegra_mc_hwpm_reg_op(void *ip_dev, enum tegra_soc_hwpm_ip_reg_op reg_op, u32 inst_element_index, u64 reg_offset, u32 *reg_data) { - if (reg_offset > 0x10000) { - pr_err("Incorrect reg offset: %llu\n", reg_offset); - return -EPERM; + struct device *dev = (struct device *)ip_dev; + struct tegra_mc_hwpm *mc; + + mc = dev_get_drvdata(dev); + if (!mc) { + pr_err("tegra-mc-hwpm: Invalid device\n"); + return -ENODEV; } - if (inst_element_index > platform_ch_nums) { - pr_err("Incorrect channel number: %u\n", inst_element_index); - return -EPERM; + if (inst_element_index >= mc->no_ch) { + dev_err(mc->dev, "Incorrect channel number: %u\n", inst_element_index); + return -EINVAL; } - if (reg_op == TEGRA_SOC_HWPM_IP_REG_OP_READ) - *reg_data = memctrl_readl(inst_element_index, (u32)reg_offset); - else if (reg_op == TEGRA_SOC_HWPM_IP_REG_OP_WRITE) - memctrl_writel(inst_element_index, *reg_data, (u32)reg_offset); + if (reg_op == TEGRA_SOC_HWPM_IP_REG_OP_READ) { + *reg_data = mc_readl(mc, inst_element_index, (u32)reg_offset); + } else if (reg_op == TEGRA_SOC_HWPM_IP_REG_OP_WRITE) { + mc_writel(mc, inst_element_index, *reg_data, (u32)reg_offset); + } else { + dev_err(mc->dev, "Invalid operation\n"); + return -EINVAL; + } return 0; } -/* - * Map an MC register space. Each MC has a set of register ranges which must - * be parsed. The first starting address in the set of ranges is returned as - * it is expected that the DT file has the register ranges in ascending - * order. - * - * device 0 = global channel. - * device n = specific channel device-1, e.g device = 1 ==> channel 0. - */ -static void __iomem *tegra_mc_hwpm_map_regs(struct platform_device *pdev, u32 device) -{ - struct resource res; - const void *prop; - void __iomem *regs; - void __iomem *regs_start = NULL; - u32 reg_ranges; - u32 i, start; - - prop = of_get_property(pdev->dev.of_node, "reg-ranges", NULL); - if (!prop) { - pr_err("Failed to get MC MMIO region\n"); - pr_err(" device = %d: missing reg-ranges\n", device); - return NULL; - } - - reg_ranges = be32_to_cpup(prop); - start = device * reg_ranges; - - for (i = 0; i < reg_ranges; i++) { - regs = of_iomap(pdev->dev.of_node, start + i); - if (!regs) { - pr_err("Failed to get MC MMIO region\n"); - pr_err(" device = %d, range = %u\n", device, i); - return NULL; - } - - if (i == 0) - regs_start = regs; - } - - if (of_address_to_resource(pdev->dev.of_node, start, &res)) - return NULL; - - pr_debug("mapped MMIO address: 0x%p -> 0x%lx\n", - regs_start, (unsigned long)res.start); - - return regs_start; -} - -static struct tegra_soc_hwpm_ip_ops hwpm_ip_ops; - static const struct of_device_id mc_hwpm_of_ids[] = { { .compatible = "nvidia,tegra-t23x-mc-hwpm" }, { } }; +MODULE_DEVICE_TABLE(of, mc_hwpm_of_ids); -/* - * MC driver init. - */ static int tegra_mc_hwpm_hwpm_probe(struct platform_device *pdev) { - const struct of_device_id *match; - u32 dram_channels; - const void *prop; - int i; + struct tegra_mc_hwpm *mc; + struct resource *res; + u32 i; - if (!pdev->dev.of_node) - return -EINVAL; - - match = of_match_device(mc_hwpm_of_ids, &pdev->dev); - if (!match) { - pr_err("Missing DT entry!\n"); - return -EINVAL; - } - - /* - * Channel count. - */ - prop = of_get_property(pdev->dev.of_node, "channels", NULL); - if (!prop) - dram_channels = 1; - else - dram_channels = be32_to_cpup(prop); - - platform_ch_nums = get_platform_dram_num_channels(); - if (platform_ch_nums > dram_channels) { - pr_err("channel mismatch error: max_soc_channels: %u platform_channels %u\n", - dram_channels, - platform_ch_nums); - return -EINVAL; - } - - /* - * Store reg mapping for broadcast channel - */ - memctlr_regs[0] = tegra_mc_hwpm_map_regs(pdev, 0); - if (!memctlr_regs[0]) + mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); + if (!mc) return -ENOMEM; - /* Populate the rest of the channels... */ - if (platform_ch_nums > 1) { - for (i = 1; i <= platform_ch_nums; i++) { - memctlr_regs[i] = tegra_mc_hwpm_map_regs(pdev, i); - if (!memctlr_regs[i]) - return -ENOMEM; + platform_set_drvdata(pdev, mc); + mc->dev = &pdev->dev; + + mc->no_ch = MAX_MC_CHANNELS; + mc->ch_regs = devm_kcalloc(mc->dev, mc->no_ch, sizeof(*mc->ch_regs), + GFP_KERNEL); + if (!mc->ch_regs) + return -ENOMEM; + + for (i = 0; i < mc->no_ch; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + dev_err(mc->dev, "Missing MC channels in device tree\n"); + return -ENODEV; } + + mc->ch_regs[i] = devm_ioremap(mc->dev, res->start, resource_size(res)); + if (IS_ERR(mc->ch_regs[i])) { + dev_err(mc->dev, "Failed to ioremap MC channels aperture\n"); + return PTR_ERR(mc->ch_regs[i]); + } + + if (i == 0) + mc->base_addr = res->start; } - hwpm_ip_ops.ip_dev = (void *)pdev; + hwpm_ip_ops.ip_dev = (void *)mc->dev; hwpm_ip_ops.resource_enum = TEGRA_SOC_HWPM_RESOURCE_MSS_CHANNEL; - hwpm_ip_ops.ip_base_address = pdev->resource[0].start; + hwpm_ip_ops.ip_base_address = mc->base_addr; hwpm_ip_ops.hwpm_ip_reg_op = &tegra_mc_hwpm_reg_op; tegra_soc_hwpm_ip_register(&hwpm_ip_ops); @@ -248,9 +127,18 @@ static int tegra_mc_hwpm_hwpm_probe(struct platform_device *pdev) static int tegra_mc_hwpm_remove(struct platform_device *pdev) { - hwpm_ip_ops.ip_dev = (void *)pdev; + struct device *dev = &pdev->dev; + struct tegra_mc_hwpm *mc; + + mc = dev_get_drvdata(dev); + if (!mc) { + pr_err("tegra-mc-hwpm: Invalid device\n"); + return -ENODEV; + } + + hwpm_ip_ops.ip_dev = (void *)mc->dev; hwpm_ip_ops.resource_enum = TEGRA_SOC_HWPM_RESOURCE_MSS_CHANNEL; - hwpm_ip_ops.ip_base_address = pdev->resource[0].start; + hwpm_ip_ops.ip_base_address = mc->base_addr; hwpm_ip_ops.hwpm_ip_reg_op = NULL; tegra_soc_hwpm_ip_unregister(&hwpm_ip_ops); @@ -270,21 +158,17 @@ static struct platform_driver mc_hwpm_driver = { static int __init tegra_mc_hwpm_init(void) { - int ret; - - ret = platform_driver_register(&mc_hwpm_driver); - if (ret) - return ret; - - return 0; + return platform_driver_register(&mc_hwpm_driver); } module_init(tegra_mc_hwpm_init); -static void __exit tegra_mc_hwpm_fini(void) +static void __exit tegra_mc_hwpm_exit(void) { + platform_driver_unregister(&mc_hwpm_driver); } -module_exit(tegra_mc_hwpm_fini); +module_exit(tegra_mc_hwpm_exit); -MODULE_DESCRIPTION("MC Hardware Performace Monitor driver"); MODULE_AUTHOR("Puneet Saxena "); +MODULE_AUTHOR("Ashish Mhetre "); +MODULE_DESCRIPTION("MC Hardware Performace Monitor Interface driver"); MODULE_LICENSE("GPL v2");