162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/sort.h> 1862306a36Sopenharmony_ci#include <linux/tegra-icc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <soc/tegra/fuse.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "mc.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const struct of_device_id tegra_mc_of_match[] = { 2562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_2x_SOC 2662306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_3x_SOC 2962306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_114_SOC 3262306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_124_SOC 3562306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_132_SOC 3862306a36Sopenharmony_ci { .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc }, 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_210_SOC 4162306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-mc", .data = &tegra210_mc_soc }, 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_186_SOC 4462306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-mc", .data = &tegra186_mc_soc }, 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_194_SOC 4762306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc }, 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_234_SOC 5062306a36Sopenharmony_ci { .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc }, 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci { /* sentinel */ } 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_mc_of_match); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void tegra_mc_devm_action_put_device(void *data) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct tegra_mc *mc = data; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci put_device(mc->dev); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * devm_tegra_memory_controller_get() - get Tegra Memory Controller handle 6562306a36Sopenharmony_ci * @dev: device pointer for the consumer device 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * This function will search for the Memory Controller node in a device-tree 6862306a36Sopenharmony_ci * and retrieve the Memory Controller handle. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct tegra_mc. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistruct tegra_mc *devm_tegra_memory_controller_get(struct device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct platform_device *pdev; 7562306a36Sopenharmony_ci struct device_node *np; 7662306a36Sopenharmony_ci struct tegra_mc *mc; 7762306a36Sopenharmony_ci int err; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "nvidia,memory-controller", 0); 8062306a36Sopenharmony_ci if (!np) 8162306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pdev = of_find_device_by_node(np); 8462306a36Sopenharmony_ci of_node_put(np); 8562306a36Sopenharmony_ci if (!pdev) 8662306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci mc = platform_get_drvdata(pdev); 8962306a36Sopenharmony_ci if (!mc) { 9062306a36Sopenharmony_ci put_device(&pdev->dev); 9162306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci err = devm_add_action_or_reset(dev, tegra_mc_devm_action_put_device, mc); 9562306a36Sopenharmony_ci if (err) 9662306a36Sopenharmony_ci return ERR_PTR(err); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return mc; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_tegra_memory_controller_get); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint tegra_mc_probe_device(struct tegra_mc *mc, struct device *dev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci if (mc->soc->ops && mc->soc->ops->probe_device) 10562306a36Sopenharmony_ci return mc->soc->ops->probe_device(mc, dev); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_mc_probe_device); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id, 11262306a36Sopenharmony_ci phys_addr_t *base, u64 *size) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 offset; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (id < 1 || id >= mc->soc->num_carveouts) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (id < 6) 12062306a36Sopenharmony_ci offset = 0xc0c + 0x50 * (id - 1); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci offset = 0x2004 + 0x50 * (id - 6); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci *base = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x0); 12562306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 12662306a36Sopenharmony_ci *base |= (phys_addr_t)mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x4) << 32; 12762306a36Sopenharmony_ci#endif 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (size) 13062306a36Sopenharmony_ci *size = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x8) << 17; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_mc_get_carveout_info); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int tegra_mc_block_dma_common(struct tegra_mc *mc, 13762306a36Sopenharmony_ci const struct tegra_mc_reset *rst) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci unsigned long flags; 14062306a36Sopenharmony_ci u32 value; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci spin_lock_irqsave(&mc->lock, flags); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci value = mc_readl(mc, rst->control) | BIT(rst->bit); 14562306a36Sopenharmony_ci mc_writel(mc, value, rst->control); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_unlock_irqrestore(&mc->lock, flags); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic bool tegra_mc_dma_idling_common(struct tegra_mc *mc, 15362306a36Sopenharmony_ci const struct tegra_mc_reset *rst) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int tegra_mc_unblock_dma_common(struct tegra_mc *mc, 15962306a36Sopenharmony_ci const struct tegra_mc_reset *rst) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned long flags; 16262306a36Sopenharmony_ci u32 value; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock_irqsave(&mc->lock, flags); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci value = mc_readl(mc, rst->control) & ~BIT(rst->bit); 16762306a36Sopenharmony_ci mc_writel(mc, value, rst->control); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci spin_unlock_irqrestore(&mc->lock, flags); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int tegra_mc_reset_status_common(struct tegra_mc *mc, 17562306a36Sopenharmony_ci const struct tegra_mc_reset *rst) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciconst struct tegra_mc_reset_ops tegra_mc_reset_ops_common = { 18162306a36Sopenharmony_ci .block_dma = tegra_mc_block_dma_common, 18262306a36Sopenharmony_ci .dma_idling = tegra_mc_dma_idling_common, 18362306a36Sopenharmony_ci .unblock_dma = tegra_mc_unblock_dma_common, 18462306a36Sopenharmony_ci .reset_status = tegra_mc_reset_status_common, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return container_of(rcdev, struct tegra_mc, reset); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc, 19362306a36Sopenharmony_ci unsigned long id) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci unsigned int i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci for (i = 0; i < mc->soc->num_resets; i++) 19862306a36Sopenharmony_ci if (mc->soc->resets[i].id == id) 19962306a36Sopenharmony_ci return &mc->soc->resets[i]; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return NULL; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, 20562306a36Sopenharmony_ci unsigned long id) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct tegra_mc *mc = reset_to_mc(rcdev); 20862306a36Sopenharmony_ci const struct tegra_mc_reset_ops *rst_ops; 20962306a36Sopenharmony_ci const struct tegra_mc_reset *rst; 21062306a36Sopenharmony_ci int retries = 500; 21162306a36Sopenharmony_ci int err; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci rst = tegra_mc_reset_find(mc, id); 21462306a36Sopenharmony_ci if (!rst) 21562306a36Sopenharmony_ci return -ENODEV; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci rst_ops = mc->soc->reset_ops; 21862306a36Sopenharmony_ci if (!rst_ops) 21962306a36Sopenharmony_ci return -ENODEV; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* DMA flushing will fail if reset is already asserted */ 22262306a36Sopenharmony_ci if (rst_ops->reset_status) { 22362306a36Sopenharmony_ci /* check whether reset is asserted */ 22462306a36Sopenharmony_ci if (rst_ops->reset_status(mc, rst)) 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (rst_ops->block_dma) { 22962306a36Sopenharmony_ci /* block clients DMA requests */ 23062306a36Sopenharmony_ci err = rst_ops->block_dma(mc, rst); 23162306a36Sopenharmony_ci if (err) { 23262306a36Sopenharmony_ci dev_err(mc->dev, "failed to block %s DMA: %d\n", 23362306a36Sopenharmony_ci rst->name, err); 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (rst_ops->dma_idling) { 23962306a36Sopenharmony_ci /* wait for completion of the outstanding DMA requests */ 24062306a36Sopenharmony_ci while (!rst_ops->dma_idling(mc, rst)) { 24162306a36Sopenharmony_ci if (!retries--) { 24262306a36Sopenharmony_ci dev_err(mc->dev, "failed to flush %s DMA\n", 24362306a36Sopenharmony_ci rst->name); 24462306a36Sopenharmony_ci return -EBUSY; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci usleep_range(10, 100); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (rst_ops->hotreset_assert) { 25262306a36Sopenharmony_ci /* clear clients DMA requests sitting before arbitration */ 25362306a36Sopenharmony_ci err = rst_ops->hotreset_assert(mc, rst); 25462306a36Sopenharmony_ci if (err) { 25562306a36Sopenharmony_ci dev_err(mc->dev, "failed to hot reset %s: %d\n", 25662306a36Sopenharmony_ci rst->name, err); 25762306a36Sopenharmony_ci return err; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, 26562306a36Sopenharmony_ci unsigned long id) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct tegra_mc *mc = reset_to_mc(rcdev); 26862306a36Sopenharmony_ci const struct tegra_mc_reset_ops *rst_ops; 26962306a36Sopenharmony_ci const struct tegra_mc_reset *rst; 27062306a36Sopenharmony_ci int err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci rst = tegra_mc_reset_find(mc, id); 27362306a36Sopenharmony_ci if (!rst) 27462306a36Sopenharmony_ci return -ENODEV; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci rst_ops = mc->soc->reset_ops; 27762306a36Sopenharmony_ci if (!rst_ops) 27862306a36Sopenharmony_ci return -ENODEV; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (rst_ops->hotreset_deassert) { 28162306a36Sopenharmony_ci /* take out client from hot reset */ 28262306a36Sopenharmony_ci err = rst_ops->hotreset_deassert(mc, rst); 28362306a36Sopenharmony_ci if (err) { 28462306a36Sopenharmony_ci dev_err(mc->dev, "failed to deassert hot reset %s: %d\n", 28562306a36Sopenharmony_ci rst->name, err); 28662306a36Sopenharmony_ci return err; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (rst_ops->unblock_dma) { 29162306a36Sopenharmony_ci /* allow new DMA requests to proceed to arbitration */ 29262306a36Sopenharmony_ci err = rst_ops->unblock_dma(mc, rst); 29362306a36Sopenharmony_ci if (err) { 29462306a36Sopenharmony_ci dev_err(mc->dev, "failed to unblock %s DMA : %d\n", 29562306a36Sopenharmony_ci rst->name, err); 29662306a36Sopenharmony_ci return err; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev, 30462306a36Sopenharmony_ci unsigned long id) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct tegra_mc *mc = reset_to_mc(rcdev); 30762306a36Sopenharmony_ci const struct tegra_mc_reset_ops *rst_ops; 30862306a36Sopenharmony_ci const struct tegra_mc_reset *rst; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci rst = tegra_mc_reset_find(mc, id); 31162306a36Sopenharmony_ci if (!rst) 31262306a36Sopenharmony_ci return -ENODEV; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci rst_ops = mc->soc->reset_ops; 31562306a36Sopenharmony_ci if (!rst_ops) 31662306a36Sopenharmony_ci return -ENODEV; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return rst_ops->reset_status(mc, rst); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const struct reset_control_ops tegra_mc_reset_ops = { 32262306a36Sopenharmony_ci .assert = tegra_mc_hotreset_assert, 32362306a36Sopenharmony_ci .deassert = tegra_mc_hotreset_deassert, 32462306a36Sopenharmony_ci .status = tegra_mc_hotreset_status, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int tegra_mc_reset_setup(struct tegra_mc *mc) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int err; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci mc->reset.ops = &tegra_mc_reset_ops; 33262306a36Sopenharmony_ci mc->reset.owner = THIS_MODULE; 33362306a36Sopenharmony_ci mc->reset.of_node = mc->dev->of_node; 33462306a36Sopenharmony_ci mc->reset.of_reset_n_cells = 1; 33562306a36Sopenharmony_ci mc->reset.nr_resets = mc->soc->num_resets; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci err = reset_controller_register(&mc->reset); 33862306a36Sopenharmony_ci if (err < 0) 33962306a36Sopenharmony_ci return err; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciint tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci unsigned int i; 34762306a36Sopenharmony_ci struct tegra_mc_timing *timing = NULL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci for (i = 0; i < mc->num_timings; i++) { 35062306a36Sopenharmony_ci if (mc->timings[i].rate == rate) { 35162306a36Sopenharmony_ci timing = &mc->timings[i]; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!timing) { 35762306a36Sopenharmony_ci dev_err(mc->dev, "no memory timing registered for rate %lu\n", 35862306a36Sopenharmony_ci rate); 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci for (i = 0; i < mc->soc->num_emem_regs; ++i) 36362306a36Sopenharmony_ci mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_mc_write_emem_configuration); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciunsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci u8 dram_count; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci dram_count = mc_readl(mc, MC_EMEM_ADR_CFG); 37462306a36Sopenharmony_ci dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV; 37562306a36Sopenharmony_ci dram_count++; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return dram_count; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ 38262306a36Sopenharmony_ci defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 38362306a36Sopenharmony_ci defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 38462306a36Sopenharmony_ci defined(CONFIG_ARCH_TEGRA_132_SOC) || \ 38562306a36Sopenharmony_ci defined(CONFIG_ARCH_TEGRA_210_SOC) 38662306a36Sopenharmony_cistatic int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci unsigned long long tick; 38962306a36Sopenharmony_ci unsigned int i; 39062306a36Sopenharmony_ci u32 value; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* compute the number of MC clock cycles per tick */ 39362306a36Sopenharmony_ci tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk); 39462306a36Sopenharmony_ci do_div(tick, NSEC_PER_SEC); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci value = mc_readl(mc, MC_EMEM_ARB_CFG); 39762306a36Sopenharmony_ci value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; 39862306a36Sopenharmony_ci value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); 39962306a36Sopenharmony_ci mc_writel(mc, value, MC_EMEM_ARB_CFG); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* write latency allowance defaults */ 40262306a36Sopenharmony_ci for (i = 0; i < mc->soc->num_clients; i++) { 40362306a36Sopenharmony_ci const struct tegra_mc_client *client = &mc->soc->clients[i]; 40462306a36Sopenharmony_ci u32 value; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci value = mc_readl(mc, client->regs.la.reg); 40762306a36Sopenharmony_ci value &= ~(client->regs.la.mask << client->regs.la.shift); 40862306a36Sopenharmony_ci value |= (client->regs.la.def & client->regs.la.mask) << client->regs.la.shift; 40962306a36Sopenharmony_ci mc_writel(mc, value, client->regs.la.reg); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* latch new values */ 41362306a36Sopenharmony_ci mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int load_one_timing(struct tegra_mc *mc, 41962306a36Sopenharmony_ci struct tegra_mc_timing *timing, 42062306a36Sopenharmony_ci struct device_node *node) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci int err; 42362306a36Sopenharmony_ci u32 tmp; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci err = of_property_read_u32(node, "clock-frequency", &tmp); 42662306a36Sopenharmony_ci if (err) { 42762306a36Sopenharmony_ci dev_err(mc->dev, 42862306a36Sopenharmony_ci "timing %pOFn: failed to read rate\n", node); 42962306a36Sopenharmony_ci return err; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci timing->rate = tmp; 43362306a36Sopenharmony_ci timing->emem_data = devm_kcalloc(mc->dev, mc->soc->num_emem_regs, 43462306a36Sopenharmony_ci sizeof(u32), GFP_KERNEL); 43562306a36Sopenharmony_ci if (!timing->emem_data) 43662306a36Sopenharmony_ci return -ENOMEM; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci err = of_property_read_u32_array(node, "nvidia,emem-configuration", 43962306a36Sopenharmony_ci timing->emem_data, 44062306a36Sopenharmony_ci mc->soc->num_emem_regs); 44162306a36Sopenharmony_ci if (err) { 44262306a36Sopenharmony_ci dev_err(mc->dev, 44362306a36Sopenharmony_ci "timing %pOFn: failed to read EMEM configuration\n", 44462306a36Sopenharmony_ci node); 44562306a36Sopenharmony_ci return err; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int load_timings(struct tegra_mc *mc, struct device_node *node) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct device_node *child; 45462306a36Sopenharmony_ci struct tegra_mc_timing *timing; 45562306a36Sopenharmony_ci int child_count = of_get_child_count(node); 45662306a36Sopenharmony_ci int i = 0, err; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci mc->timings = devm_kcalloc(mc->dev, child_count, sizeof(*timing), 45962306a36Sopenharmony_ci GFP_KERNEL); 46062306a36Sopenharmony_ci if (!mc->timings) 46162306a36Sopenharmony_ci return -ENOMEM; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci mc->num_timings = child_count; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci for_each_child_of_node(node, child) { 46662306a36Sopenharmony_ci timing = &mc->timings[i++]; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci err = load_one_timing(mc, timing, child); 46962306a36Sopenharmony_ci if (err) { 47062306a36Sopenharmony_ci of_node_put(child); 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int tegra_mc_setup_timings(struct tegra_mc *mc) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct device_node *node; 48162306a36Sopenharmony_ci u32 ram_code, node_ram_code; 48262306a36Sopenharmony_ci int err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ram_code = tegra_read_ram_code(); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mc->num_timings = 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci for_each_child_of_node(mc->dev->of_node, node) { 48962306a36Sopenharmony_ci err = of_property_read_u32(node, "nvidia,ram-code", 49062306a36Sopenharmony_ci &node_ram_code); 49162306a36Sopenharmony_ci if (err || (node_ram_code != ram_code)) 49262306a36Sopenharmony_ci continue; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci err = load_timings(mc, node); 49562306a36Sopenharmony_ci of_node_put(node); 49662306a36Sopenharmony_ci if (err) 49762306a36Sopenharmony_ci return err; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (mc->num_timings == 0) 50262306a36Sopenharmony_ci dev_warn(mc->dev, 50362306a36Sopenharmony_ci "no memory timings for RAM code %u registered\n", 50462306a36Sopenharmony_ci ram_code); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint tegra30_mc_probe(struct tegra_mc *mc) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci mc->clk = devm_clk_get_optional(mc->dev, "mc"); 51462306a36Sopenharmony_ci if (IS_ERR(mc->clk)) { 51562306a36Sopenharmony_ci dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk)); 51662306a36Sopenharmony_ci return PTR_ERR(mc->clk); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* ensure that debug features are disabled */ 52062306a36Sopenharmony_ci mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci err = tegra_mc_setup_latency_allowance(mc); 52362306a36Sopenharmony_ci if (err < 0) { 52462306a36Sopenharmony_ci dev_err(mc->dev, "failed to setup latency allowance: %d\n", err); 52562306a36Sopenharmony_ci return err; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci err = tegra_mc_setup_timings(mc); 52962306a36Sopenharmony_ci if (err < 0) { 53062306a36Sopenharmony_ci dev_err(mc->dev, "failed to setup timings: %d\n", err); 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciconst struct tegra_mc_ops tegra30_mc_ops = { 53862306a36Sopenharmony_ci .probe = tegra30_mc_probe, 53962306a36Sopenharmony_ci .handle_irq = tegra30_mc_handle_irq, 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci#endif 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status, 54462306a36Sopenharmony_ci unsigned int *mc_channel) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci if ((status & mc->soc->ch_intmask) == 0) 54762306a36Sopenharmony_ci return -EINVAL; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci *mc_channel = __ffs((status & mc->soc->ch_intmask) >> 55062306a36Sopenharmony_ci mc->soc->global_intstatus_channel_shift); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc, 55662306a36Sopenharmony_ci unsigned int channel) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci return BIT(channel) << mc->soc->global_intstatus_channel_shift; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciirqreturn_t tegra30_mc_handle_irq(int irq, void *data) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct tegra_mc *mc = data; 56462306a36Sopenharmony_ci unsigned int bit, channel; 56562306a36Sopenharmony_ci unsigned long status; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (mc->soc->num_channels) { 56862306a36Sopenharmony_ci u32 global_status; 56962306a36Sopenharmony_ci int err; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS); 57262306a36Sopenharmony_ci err = mc_global_intstatus_to_channel(mc, global_status, &channel); 57362306a36Sopenharmony_ci if (err < 0) { 57462306a36Sopenharmony_ci dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n", 57562306a36Sopenharmony_ci global_status); 57662306a36Sopenharmony_ci return IRQ_NONE; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* mask all interrupts to avoid flooding */ 58062306a36Sopenharmony_ci status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask; 58162306a36Sopenharmony_ci } else { 58262306a36Sopenharmony_ci status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!status) 58662306a36Sopenharmony_ci return IRQ_NONE; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci for_each_set_bit(bit, &status, 32) { 58962306a36Sopenharmony_ci const char *error = tegra_mc_status_names[bit] ?: "unknown"; 59062306a36Sopenharmony_ci const char *client = "unknown", *desc; 59162306a36Sopenharmony_ci const char *direction, *secure; 59262306a36Sopenharmony_ci u32 status_reg, addr_reg; 59362306a36Sopenharmony_ci u32 intmask = BIT(bit); 59462306a36Sopenharmony_ci phys_addr_t addr = 0; 59562306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 59662306a36Sopenharmony_ci u32 addr_hi_reg = 0; 59762306a36Sopenharmony_ci#endif 59862306a36Sopenharmony_ci unsigned int i; 59962306a36Sopenharmony_ci char perm[7]; 60062306a36Sopenharmony_ci u8 id, type; 60162306a36Sopenharmony_ci u32 value; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci switch (intmask) { 60462306a36Sopenharmony_ci case MC_INT_DECERR_VPR: 60562306a36Sopenharmony_ci status_reg = MC_ERR_VPR_STATUS; 60662306a36Sopenharmony_ci addr_reg = MC_ERR_VPR_ADR; 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci case MC_INT_SECERR_SEC: 61062306a36Sopenharmony_ci status_reg = MC_ERR_SEC_STATUS; 61162306a36Sopenharmony_ci addr_reg = MC_ERR_SEC_ADR; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci case MC_INT_DECERR_MTS: 61562306a36Sopenharmony_ci status_reg = MC_ERR_MTS_STATUS; 61662306a36Sopenharmony_ci addr_reg = MC_ERR_MTS_ADR; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci case MC_INT_DECERR_GENERALIZED_CARVEOUT: 62062306a36Sopenharmony_ci status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS; 62162306a36Sopenharmony_ci addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci case MC_INT_DECERR_ROUTE_SANITY: 62562306a36Sopenharmony_ci status_reg = MC_ERR_ROUTE_SANITY_STATUS; 62662306a36Sopenharmony_ci addr_reg = MC_ERR_ROUTE_SANITY_ADR; 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci default: 63062306a36Sopenharmony_ci status_reg = MC_ERR_STATUS; 63162306a36Sopenharmony_ci addr_reg = MC_ERR_ADR; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 63462306a36Sopenharmony_ci if (mc->soc->has_addr_hi_reg) 63562306a36Sopenharmony_ci addr_hi_reg = MC_ERR_ADR_HI; 63662306a36Sopenharmony_ci#endif 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (mc->soc->num_channels) 64162306a36Sopenharmony_ci value = mc_ch_readl(mc, channel, status_reg); 64262306a36Sopenharmony_ci else 64362306a36Sopenharmony_ci value = mc_readl(mc, status_reg); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 64662306a36Sopenharmony_ci if (mc->soc->num_address_bits > 32) { 64762306a36Sopenharmony_ci if (addr_hi_reg) { 64862306a36Sopenharmony_ci if (mc->soc->num_channels) 64962306a36Sopenharmony_ci addr = mc_ch_readl(mc, channel, addr_hi_reg); 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci addr = mc_readl(mc, addr_hi_reg); 65262306a36Sopenharmony_ci } else { 65362306a36Sopenharmony_ci addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & 65462306a36Sopenharmony_ci MC_ERR_STATUS_ADR_HI_MASK); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci addr <<= 32; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci#endif 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (value & MC_ERR_STATUS_RW) 66162306a36Sopenharmony_ci direction = "write"; 66262306a36Sopenharmony_ci else 66362306a36Sopenharmony_ci direction = "read"; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (value & MC_ERR_STATUS_SECURITY) 66662306a36Sopenharmony_ci secure = "secure "; 66762306a36Sopenharmony_ci else 66862306a36Sopenharmony_ci secure = ""; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci id = value & mc->soc->client_id_mask; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci for (i = 0; i < mc->soc->num_clients; i++) { 67362306a36Sopenharmony_ci if (mc->soc->clients[i].id == id) { 67462306a36Sopenharmony_ci client = mc->soc->clients[i].name; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci type = (value & MC_ERR_STATUS_TYPE_MASK) >> 68062306a36Sopenharmony_ci MC_ERR_STATUS_TYPE_SHIFT; 68162306a36Sopenharmony_ci desc = tegra_mc_error_names[type]; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci switch (value & MC_ERR_STATUS_TYPE_MASK) { 68462306a36Sopenharmony_ci case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: 68562306a36Sopenharmony_ci perm[0] = ' '; 68662306a36Sopenharmony_ci perm[1] = '['; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (value & MC_ERR_STATUS_READABLE) 68962306a36Sopenharmony_ci perm[2] = 'R'; 69062306a36Sopenharmony_ci else 69162306a36Sopenharmony_ci perm[2] = '-'; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (value & MC_ERR_STATUS_WRITABLE) 69462306a36Sopenharmony_ci perm[3] = 'W'; 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci perm[3] = '-'; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (value & MC_ERR_STATUS_NONSECURE) 69962306a36Sopenharmony_ci perm[4] = '-'; 70062306a36Sopenharmony_ci else 70162306a36Sopenharmony_ci perm[4] = 'S'; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci perm[5] = ']'; 70462306a36Sopenharmony_ci perm[6] = '\0'; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci default: 70862306a36Sopenharmony_ci perm[0] = '\0'; 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (mc->soc->num_channels) 71362306a36Sopenharmony_ci value = mc_ch_readl(mc, channel, addr_reg); 71462306a36Sopenharmony_ci else 71562306a36Sopenharmony_ci value = mc_readl(mc, addr_reg); 71662306a36Sopenharmony_ci addr |= value; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", 71962306a36Sopenharmony_ci client, secure, direction, &addr, error, 72062306a36Sopenharmony_ci desc, perm); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* clear interrupts */ 72462306a36Sopenharmony_ci if (mc->soc->num_channels) { 72562306a36Sopenharmony_ci mc_ch_writel(mc, channel, status, MC_INTSTATUS); 72662306a36Sopenharmony_ci mc_ch_writel(mc, MC_BROADCAST_CHANNEL, 72762306a36Sopenharmony_ci mc_channel_to_global_intstatus(mc, channel), 72862306a36Sopenharmony_ci MC_GLOBAL_INTSTATUS); 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci mc_writel(mc, status, MC_INTSTATUS); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return IRQ_HANDLED; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciconst char *const tegra_mc_status_names[32] = { 73762306a36Sopenharmony_ci [ 1] = "External interrupt", 73862306a36Sopenharmony_ci [ 6] = "EMEM address decode error", 73962306a36Sopenharmony_ci [ 7] = "GART page fault", 74062306a36Sopenharmony_ci [ 8] = "Security violation", 74162306a36Sopenharmony_ci [ 9] = "EMEM arbitration error", 74262306a36Sopenharmony_ci [10] = "Page fault", 74362306a36Sopenharmony_ci [11] = "Invalid APB ASID update", 74462306a36Sopenharmony_ci [12] = "VPR violation", 74562306a36Sopenharmony_ci [13] = "Secure carveout violation", 74662306a36Sopenharmony_ci [16] = "MTS carveout violation", 74762306a36Sopenharmony_ci [17] = "Generalized carveout violation", 74862306a36Sopenharmony_ci [20] = "Route Sanity error", 74962306a36Sopenharmony_ci}; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ciconst char *const tegra_mc_error_names[8] = { 75262306a36Sopenharmony_ci [2] = "EMEM decode error", 75362306a36Sopenharmony_ci [3] = "TrustZone violation", 75462306a36Sopenharmony_ci [4] = "Carveout violation", 75562306a36Sopenharmony_ci [6] = "SMMU translation error", 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistruct icc_node *tegra_mc_icc_xlate(struct of_phandle_args *spec, void *data) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct tegra_mc *mc = icc_provider_to_tegra_mc(data); 76162306a36Sopenharmony_ci struct icc_node *node; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci list_for_each_entry(node, &mc->provider.nodes, node_list) { 76462306a36Sopenharmony_ci if (node->id == spec->args[0]) 76562306a36Sopenharmony_ci return node; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * If a client driver calls devm_of_icc_get() before the MC driver 77062306a36Sopenharmony_ci * is probed, then return EPROBE_DEFER to the client driver. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int tegra_mc_icc_get(struct icc_node *node, u32 *average, u32 *peak) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci *average = 0; 77862306a36Sopenharmony_ci *peak = 0; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int tegra_mc_icc_set(struct icc_node *src, struct icc_node *dst) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ciconst struct tegra_mc_icc_ops tegra_mc_icc_ops = { 78962306a36Sopenharmony_ci .xlate = tegra_mc_icc_xlate, 79062306a36Sopenharmony_ci .aggregate = icc_std_aggregate, 79162306a36Sopenharmony_ci .get_bw = tegra_mc_icc_get, 79262306a36Sopenharmony_ci .set = tegra_mc_icc_set, 79362306a36Sopenharmony_ci}; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* 79662306a36Sopenharmony_ci * Memory Controller (MC) has few Memory Clients that are issuing memory 79762306a36Sopenharmony_ci * bandwidth allocation requests to the MC interconnect provider. The MC 79862306a36Sopenharmony_ci * provider aggregates the requests and then sends the aggregated request 79962306a36Sopenharmony_ci * up to the External Memory Controller (EMC) interconnect provider which 80062306a36Sopenharmony_ci * re-configures hardware interface to External Memory (EMEM) in accordance 80162306a36Sopenharmony_ci * to the required bandwidth. Each MC interconnect node represents an 80262306a36Sopenharmony_ci * individual Memory Client. 80362306a36Sopenharmony_ci * 80462306a36Sopenharmony_ci * Memory interconnect topology: 80562306a36Sopenharmony_ci * 80662306a36Sopenharmony_ci * +----+ 80762306a36Sopenharmony_ci * +--------+ | | 80862306a36Sopenharmony_ci * | TEXSRD +--->+ | 80962306a36Sopenharmony_ci * +--------+ | | 81062306a36Sopenharmony_ci * | | +-----+ +------+ 81162306a36Sopenharmony_ci * ... | MC +--->+ EMC +--->+ EMEM | 81262306a36Sopenharmony_ci * | | +-----+ +------+ 81362306a36Sopenharmony_ci * +--------+ | | 81462306a36Sopenharmony_ci * | DISP.. +--->+ | 81562306a36Sopenharmony_ci * +--------+ | | 81662306a36Sopenharmony_ci * +----+ 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_cistatic int tegra_mc_interconnect_setup(struct tegra_mc *mc) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct icc_node *node; 82162306a36Sopenharmony_ci unsigned int i; 82262306a36Sopenharmony_ci int err; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* older device-trees don't have interconnect properties */ 82562306a36Sopenharmony_ci if (!device_property_present(mc->dev, "#interconnect-cells") || 82662306a36Sopenharmony_ci !mc->soc->icc_ops) 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci mc->provider.dev = mc->dev; 83062306a36Sopenharmony_ci mc->provider.data = &mc->provider; 83162306a36Sopenharmony_ci mc->provider.set = mc->soc->icc_ops->set; 83262306a36Sopenharmony_ci mc->provider.aggregate = mc->soc->icc_ops->aggregate; 83362306a36Sopenharmony_ci mc->provider.get_bw = mc->soc->icc_ops->get_bw; 83462306a36Sopenharmony_ci mc->provider.xlate = mc->soc->icc_ops->xlate; 83562306a36Sopenharmony_ci mc->provider.xlate_extended = mc->soc->icc_ops->xlate_extended; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci icc_provider_init(&mc->provider); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* create Memory Controller node */ 84062306a36Sopenharmony_ci node = icc_node_create(TEGRA_ICC_MC); 84162306a36Sopenharmony_ci if (IS_ERR(node)) 84262306a36Sopenharmony_ci return PTR_ERR(node); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci node->name = "Memory Controller"; 84562306a36Sopenharmony_ci icc_node_add(node, &mc->provider); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* link Memory Controller to External Memory Controller */ 84862306a36Sopenharmony_ci err = icc_link_create(node, TEGRA_ICC_EMC); 84962306a36Sopenharmony_ci if (err) 85062306a36Sopenharmony_ci goto remove_nodes; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci for (i = 0; i < mc->soc->num_clients; i++) { 85362306a36Sopenharmony_ci /* create MC client node */ 85462306a36Sopenharmony_ci node = icc_node_create(mc->soc->clients[i].id); 85562306a36Sopenharmony_ci if (IS_ERR(node)) { 85662306a36Sopenharmony_ci err = PTR_ERR(node); 85762306a36Sopenharmony_ci goto remove_nodes; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci node->name = mc->soc->clients[i].name; 86162306a36Sopenharmony_ci icc_node_add(node, &mc->provider); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* link Memory Client to Memory Controller */ 86462306a36Sopenharmony_ci err = icc_link_create(node, TEGRA_ICC_MC); 86562306a36Sopenharmony_ci if (err) 86662306a36Sopenharmony_ci goto remove_nodes; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci node->data = (struct tegra_mc_client *)&(mc->soc->clients[i]); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci err = icc_provider_register(&mc->provider); 87262306a36Sopenharmony_ci if (err) 87362306a36Sopenharmony_ci goto remove_nodes; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciremove_nodes: 87862306a36Sopenharmony_ci icc_nodes_remove(&mc->provider); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return err; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void tegra_mc_num_channel_enabled(struct tegra_mc *mc) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci unsigned int i; 88662306a36Sopenharmony_ci u32 value; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci value = mc_ch_readl(mc, 0, MC_EMEM_ADR_CFG_CHANNEL_ENABLE); 88962306a36Sopenharmony_ci if (value <= 0) { 89062306a36Sopenharmony_ci mc->num_channels = mc->soc->num_channels; 89162306a36Sopenharmony_ci return; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 89562306a36Sopenharmony_ci if (value & BIT(i)) 89662306a36Sopenharmony_ci mc->num_channels++; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic int tegra_mc_probe(struct platform_device *pdev) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct tegra_mc *mc; 90362306a36Sopenharmony_ci u64 mask; 90462306a36Sopenharmony_ci int err; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 90762306a36Sopenharmony_ci if (!mc) 90862306a36Sopenharmony_ci return -ENOMEM; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci platform_set_drvdata(pdev, mc); 91162306a36Sopenharmony_ci spin_lock_init(&mc->lock); 91262306a36Sopenharmony_ci mc->soc = of_device_get_match_data(&pdev->dev); 91362306a36Sopenharmony_ci mc->dev = &pdev->dev; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci mask = DMA_BIT_MASK(mc->soc->num_address_bits); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci err = dma_coerce_mask_and_coherent(&pdev->dev, mask); 91862306a36Sopenharmony_ci if (err < 0) { 91962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); 92062306a36Sopenharmony_ci return err; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* length of MC tick in nanoseconds */ 92462306a36Sopenharmony_ci mc->tick = 30; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci mc->regs = devm_platform_ioremap_resource(pdev, 0); 92762306a36Sopenharmony_ci if (IS_ERR(mc->regs)) 92862306a36Sopenharmony_ci return PTR_ERR(mc->regs); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci mc->debugfs.root = debugfs_create_dir("mc", NULL); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (mc->soc->ops && mc->soc->ops->probe) { 93362306a36Sopenharmony_ci err = mc->soc->ops->probe(mc); 93462306a36Sopenharmony_ci if (err < 0) 93562306a36Sopenharmony_ci return err; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci tegra_mc_num_channel_enabled(mc); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (mc->soc->ops && mc->soc->ops->handle_irq) { 94162306a36Sopenharmony_ci mc->irq = platform_get_irq(pdev, 0); 94262306a36Sopenharmony_ci if (mc->irq < 0) 94362306a36Sopenharmony_ci return mc->irq; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (mc->soc->num_channels) 94862306a36Sopenharmony_ci mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask, 94962306a36Sopenharmony_ci MC_INTMASK); 95062306a36Sopenharmony_ci else 95162306a36Sopenharmony_ci mc_writel(mc, mc->soc->intmask, MC_INTMASK); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0, 95462306a36Sopenharmony_ci dev_name(&pdev->dev), mc); 95562306a36Sopenharmony_ci if (err < 0) { 95662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, 95762306a36Sopenharmony_ci err); 95862306a36Sopenharmony_ci return err; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (mc->soc->reset_ops) { 96362306a36Sopenharmony_ci err = tegra_mc_reset_setup(mc); 96462306a36Sopenharmony_ci if (err < 0) 96562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register reset controller: %d\n", err); 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci err = tegra_mc_interconnect_setup(mc); 96962306a36Sopenharmony_ci if (err < 0) 97062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize interconnect: %d\n", 97162306a36Sopenharmony_ci err); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { 97462306a36Sopenharmony_ci mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); 97562306a36Sopenharmony_ci if (IS_ERR(mc->smmu)) { 97662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", 97762306a36Sopenharmony_ci PTR_ERR(mc->smmu)); 97862306a36Sopenharmony_ci mc->smmu = NULL; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) { 98362306a36Sopenharmony_ci mc->gart = tegra_gart_probe(&pdev->dev, mc); 98462306a36Sopenharmony_ci if (IS_ERR(mc->gart)) { 98562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to probe GART: %ld\n", 98662306a36Sopenharmony_ci PTR_ERR(mc->gart)); 98762306a36Sopenharmony_ci mc->gart = NULL; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int __maybe_unused tegra_mc_suspend(struct device *dev) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct tegra_mc *mc = dev_get_drvdata(dev); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (mc->soc->ops && mc->soc->ops->suspend) 99962306a36Sopenharmony_ci return mc->soc->ops->suspend(mc); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return 0; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic int __maybe_unused tegra_mc_resume(struct device *dev) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct tegra_mc *mc = dev_get_drvdata(dev); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (mc->soc->ops && mc->soc->ops->resume) 100962306a36Sopenharmony_ci return mc->soc->ops->resume(mc); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void tegra_mc_sync_state(struct device *dev) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct tegra_mc *mc = dev_get_drvdata(dev); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* check whether ICC provider is registered */ 101962306a36Sopenharmony_ci if (mc->provider.dev == dev) 102062306a36Sopenharmony_ci icc_sync_state(dev); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_mc_pm_ops = { 102462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(tegra_mc_suspend, tegra_mc_resume) 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic struct platform_driver tegra_mc_driver = { 102862306a36Sopenharmony_ci .driver = { 102962306a36Sopenharmony_ci .name = "tegra-mc", 103062306a36Sopenharmony_ci .of_match_table = tegra_mc_of_match, 103162306a36Sopenharmony_ci .pm = &tegra_mc_pm_ops, 103262306a36Sopenharmony_ci .suppress_bind_attrs = true, 103362306a36Sopenharmony_ci .sync_state = tegra_mc_sync_state, 103462306a36Sopenharmony_ci }, 103562306a36Sopenharmony_ci .prevent_deferred_probe = true, 103662306a36Sopenharmony_ci .probe = tegra_mc_probe, 103762306a36Sopenharmony_ci}; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int tegra_mc_init(void) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci return platform_driver_register(&tegra_mc_driver); 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ciarch_initcall(tegra_mc_init); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 104662306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver"); 1047