1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 289184651SThierry Reding /* 389184651SThierry Reding * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. 489184651SThierry Reding */ 589184651SThierry Reding 689184651SThierry Reding #include <linux/clk.h> 720e92462SDmitry Osipenko #include <linux/delay.h> 8c4c21f22SThierry Reding #include <linux/dma-mapping.h> 90c56eda8SDmitry Osipenko #include <linux/export.h> 1089184651SThierry Reding #include <linux/interrupt.h> 1189184651SThierry Reding #include <linux/kernel.h> 1289184651SThierry Reding #include <linux/module.h> 1389184651SThierry Reding #include <linux/of.h> 1459cd046fSDmitry Osipenko #include <linux/of_device.h> 1589184651SThierry Reding #include <linux/platform_device.h> 1689184651SThierry Reding #include <linux/slab.h> 173d9dd6fdSMikko Perttunen #include <linux/sort.h> 183d9dd6fdSMikko Perttunen 193d9dd6fdSMikko Perttunen #include <soc/tegra/fuse.h> 2089184651SThierry Reding 2189184651SThierry Reding #include "mc.h" 2289184651SThierry Reding 2389184651SThierry Reding static const struct of_device_id tegra_mc_of_match[] = { 24a8d502fdSDmitry Osipenko #ifdef CONFIG_ARCH_TEGRA_2x_SOC 2596efa118SDmitry Osipenko { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, 26a8d502fdSDmitry Osipenko #endif 2789184651SThierry Reding #ifdef CONFIG_ARCH_TEGRA_3x_SOC 2889184651SThierry Reding { .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, 2989184651SThierry Reding #endif 3089184651SThierry Reding #ifdef CONFIG_ARCH_TEGRA_114_SOC 3189184651SThierry Reding { .compatible = "nvidia,tegra114-mc", .data = &tegra114_mc_soc }, 3289184651SThierry Reding #endif 3389184651SThierry Reding #ifdef CONFIG_ARCH_TEGRA_124_SOC 3489184651SThierry Reding { .compatible = "nvidia,tegra124-mc", .data = &tegra124_mc_soc }, 3589184651SThierry Reding #endif 36242b1d71SThierry Reding #ifdef CONFIG_ARCH_TEGRA_132_SOC 37242b1d71SThierry Reding { .compatible = "nvidia,tegra132-mc", .data = &tegra132_mc_soc }, 38242b1d71SThierry Reding #endif 39588c43a7SThierry Reding #ifdef CONFIG_ARCH_TEGRA_210_SOC 40588c43a7SThierry Reding { .compatible = "nvidia,tegra210-mc", .data = &tegra210_mc_soc }, 41588c43a7SThierry Reding #endif 427355c7b9SThierry Reding #ifdef CONFIG_ARCH_TEGRA_186_SOC 437355c7b9SThierry Reding { .compatible = "nvidia,tegra186-mc", .data = &tegra186_mc_soc }, 447355c7b9SThierry Reding #endif 457355c7b9SThierry Reding #ifdef CONFIG_ARCH_TEGRA_194_SOC 467355c7b9SThierry Reding { .compatible = "nvidia,tegra194-mc", .data = &tegra194_mc_soc }, 477355c7b9SThierry Reding #endif 4872c81bb6SThierry Reding #ifdef CONFIG_ARCH_TEGRA_234_SOC 4972c81bb6SThierry Reding { .compatible = "nvidia,tegra234-mc", .data = &tegra234_mc_soc }, 5072c81bb6SThierry Reding #endif 517355c7b9SThierry Reding { /* sentinel */ } 5289184651SThierry Reding }; 5389184651SThierry Reding MODULE_DEVICE_TABLE(of, tegra_mc_of_match); 5489184651SThierry Reding 556c6bd207SDmitry Osipenko static void tegra_mc_devm_action_put_device(void *data) 566c6bd207SDmitry Osipenko { 576c6bd207SDmitry Osipenko struct tegra_mc *mc = data; 586c6bd207SDmitry Osipenko 596c6bd207SDmitry Osipenko put_device(mc->dev); 606c6bd207SDmitry Osipenko } 616c6bd207SDmitry Osipenko 626c6bd207SDmitry Osipenko /** 636c6bd207SDmitry Osipenko * devm_tegra_memory_controller_get() - get Tegra Memory Controller handle 646c6bd207SDmitry Osipenko * @dev: device pointer for the consumer device 656c6bd207SDmitry Osipenko * 666c6bd207SDmitry Osipenko * This function will search for the Memory Controller node in a device-tree 676c6bd207SDmitry Osipenko * and retrieve the Memory Controller handle. 686c6bd207SDmitry Osipenko * 696c6bd207SDmitry Osipenko * Return: ERR_PTR() on error or a valid pointer to a struct tegra_mc. 706c6bd207SDmitry Osipenko */ 716c6bd207SDmitry Osipenko struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev) 726c6bd207SDmitry Osipenko { 736c6bd207SDmitry Osipenko struct platform_device *pdev; 746c6bd207SDmitry Osipenko struct device_node *np; 756c6bd207SDmitry Osipenko struct tegra_mc *mc; 766c6bd207SDmitry Osipenko int err; 776c6bd207SDmitry Osipenko 786c6bd207SDmitry Osipenko np = of_parse_phandle(dev->of_node, "nvidia,memory-controller", 0); 796c6bd207SDmitry Osipenko if (!np) 806c6bd207SDmitry Osipenko return ERR_PTR(-ENOENT); 816c6bd207SDmitry Osipenko 826c6bd207SDmitry Osipenko pdev = of_find_device_by_node(np); 836c6bd207SDmitry Osipenko of_node_put(np); 846c6bd207SDmitry Osipenko if (!pdev) 856c6bd207SDmitry Osipenko return ERR_PTR(-ENODEV); 866c6bd207SDmitry Osipenko 876c6bd207SDmitry Osipenko mc = platform_get_drvdata(pdev); 886c6bd207SDmitry Osipenko if (!mc) { 896c6bd207SDmitry Osipenko put_device(&pdev->dev); 906c6bd207SDmitry Osipenko return ERR_PTR(-EPROBE_DEFER); 916c6bd207SDmitry Osipenko } 926c6bd207SDmitry Osipenko 931d8e0223SCai Huoqing err = devm_add_action_or_reset(dev, tegra_mc_devm_action_put_device, mc); 941d8e0223SCai Huoqing if (err) 956c6bd207SDmitry Osipenko return ERR_PTR(err); 966c6bd207SDmitry Osipenko 976c6bd207SDmitry Osipenko return mc; 986c6bd207SDmitry Osipenko } 996c6bd207SDmitry Osipenko EXPORT_SYMBOL_GPL(devm_tegra_memory_controller_get); 1006c6bd207SDmitry Osipenko 101393d66fdSThierry Reding int tegra_mc_probe_device(struct tegra_mc *mc, struct device *dev) 102393d66fdSThierry Reding { 103393d66fdSThierry Reding if (mc->soc->ops && mc->soc->ops->probe_device) 104393d66fdSThierry Reding return mc->soc->ops->probe_device(mc, dev); 105393d66fdSThierry Reding 106393d66fdSThierry Reding return 0; 107393d66fdSThierry Reding } 108393d66fdSThierry Reding EXPORT_SYMBOL_GPL(tegra_mc_probe_device); 109393d66fdSThierry Reding 110*7946920dSMikko Perttunen int tegra_mc_get_carveout_info(struct tegra_mc *mc, unsigned int id, 111*7946920dSMikko Perttunen phys_addr_t *base, u64 *size) 112*7946920dSMikko Perttunen { 113*7946920dSMikko Perttunen u32 offset; 114*7946920dSMikko Perttunen 115*7946920dSMikko Perttunen if (id < 1 || id >= mc->soc->num_carveouts) 116*7946920dSMikko Perttunen return -EINVAL; 117*7946920dSMikko Perttunen 118*7946920dSMikko Perttunen if (id < 6) 119*7946920dSMikko Perttunen offset = 0xc0c + 0x50 * (id - 1); 120*7946920dSMikko Perttunen else 121*7946920dSMikko Perttunen offset = 0x2004 + 0x50 * (id - 6); 122*7946920dSMikko Perttunen 123*7946920dSMikko Perttunen *base = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x0); 124*7946920dSMikko Perttunen #ifdef CONFIG_PHYS_ADDR_T_64BIT 125*7946920dSMikko Perttunen *base |= (phys_addr_t)mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x4) << 32; 126*7946920dSMikko Perttunen #endif 127*7946920dSMikko Perttunen 128*7946920dSMikko Perttunen if (size) 129*7946920dSMikko Perttunen *size = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, offset + 0x8) << 17; 130*7946920dSMikko Perttunen 131*7946920dSMikko Perttunen return 0; 132*7946920dSMikko Perttunen } 133*7946920dSMikko Perttunen EXPORT_SYMBOL_GPL(tegra_mc_get_carveout_info); 134*7946920dSMikko Perttunen 135cb2b5839SThierry Reding static int tegra_mc_block_dma_common(struct tegra_mc *mc, 13620e92462SDmitry Osipenko const struct tegra_mc_reset *rst) 13720e92462SDmitry Osipenko { 13820e92462SDmitry Osipenko unsigned long flags; 13920e92462SDmitry Osipenko u32 value; 14020e92462SDmitry Osipenko 14120e92462SDmitry Osipenko spin_lock_irqsave(&mc->lock, flags); 14220e92462SDmitry Osipenko 14320e92462SDmitry Osipenko value = mc_readl(mc, rst->control) | BIT(rst->bit); 14420e92462SDmitry Osipenko mc_writel(mc, value, rst->control); 14520e92462SDmitry Osipenko 14620e92462SDmitry Osipenko spin_unlock_irqrestore(&mc->lock, flags); 14720e92462SDmitry Osipenko 14820e92462SDmitry Osipenko return 0; 14920e92462SDmitry Osipenko } 15020e92462SDmitry Osipenko 151cb2b5839SThierry Reding static bool tegra_mc_dma_idling_common(struct tegra_mc *mc, 15220e92462SDmitry Osipenko const struct tegra_mc_reset *rst) 15320e92462SDmitry Osipenko { 15420e92462SDmitry Osipenko return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0; 15520e92462SDmitry Osipenko } 15620e92462SDmitry Osipenko 157cb2b5839SThierry Reding static int tegra_mc_unblock_dma_common(struct tegra_mc *mc, 15820e92462SDmitry Osipenko const struct tegra_mc_reset *rst) 15920e92462SDmitry Osipenko { 16020e92462SDmitry Osipenko unsigned long flags; 16120e92462SDmitry Osipenko u32 value; 16220e92462SDmitry Osipenko 16320e92462SDmitry Osipenko spin_lock_irqsave(&mc->lock, flags); 16420e92462SDmitry Osipenko 16520e92462SDmitry Osipenko value = mc_readl(mc, rst->control) & ~BIT(rst->bit); 16620e92462SDmitry Osipenko mc_writel(mc, value, rst->control); 16720e92462SDmitry Osipenko 16820e92462SDmitry Osipenko spin_unlock_irqrestore(&mc->lock, flags); 16920e92462SDmitry Osipenko 17020e92462SDmitry Osipenko return 0; 17120e92462SDmitry Osipenko } 17220e92462SDmitry Osipenko 173cb2b5839SThierry Reding static int tegra_mc_reset_status_common(struct tegra_mc *mc, 17420e92462SDmitry Osipenko const struct tegra_mc_reset *rst) 17520e92462SDmitry Osipenko { 17620e92462SDmitry Osipenko return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0; 17720e92462SDmitry Osipenko } 17820e92462SDmitry Osipenko 179cb2b5839SThierry Reding const struct tegra_mc_reset_ops tegra_mc_reset_ops_common = { 180cb2b5839SThierry Reding .block_dma = tegra_mc_block_dma_common, 181cb2b5839SThierry Reding .dma_idling = tegra_mc_dma_idling_common, 182cb2b5839SThierry Reding .unblock_dma = tegra_mc_unblock_dma_common, 183cb2b5839SThierry Reding .reset_status = tegra_mc_reset_status_common, 18420e92462SDmitry Osipenko }; 18520e92462SDmitry Osipenko 18620e92462SDmitry Osipenko static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev) 18720e92462SDmitry Osipenko { 18820e92462SDmitry Osipenko return container_of(rcdev, struct tegra_mc, reset); 18920e92462SDmitry Osipenko } 19020e92462SDmitry Osipenko 19120e92462SDmitry Osipenko static const struct tegra_mc_reset *tegra_mc_reset_find(struct tegra_mc *mc, 19220e92462SDmitry Osipenko unsigned long id) 19320e92462SDmitry Osipenko { 19420e92462SDmitry Osipenko unsigned int i; 19520e92462SDmitry Osipenko 19620e92462SDmitry Osipenko for (i = 0; i < mc->soc->num_resets; i++) 19720e92462SDmitry Osipenko if (mc->soc->resets[i].id == id) 19820e92462SDmitry Osipenko return &mc->soc->resets[i]; 19920e92462SDmitry Osipenko 20020e92462SDmitry Osipenko return NULL; 20120e92462SDmitry Osipenko } 20220e92462SDmitry Osipenko 20320e92462SDmitry Osipenko static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, 20420e92462SDmitry Osipenko unsigned long id) 20520e92462SDmitry Osipenko { 20620e92462SDmitry Osipenko struct tegra_mc *mc = reset_to_mc(rcdev); 20720e92462SDmitry Osipenko const struct tegra_mc_reset_ops *rst_ops; 20820e92462SDmitry Osipenko const struct tegra_mc_reset *rst; 20920e92462SDmitry Osipenko int retries = 500; 21020e92462SDmitry Osipenko int err; 21120e92462SDmitry Osipenko 21220e92462SDmitry Osipenko rst = tegra_mc_reset_find(mc, id); 21320e92462SDmitry Osipenko if (!rst) 21420e92462SDmitry Osipenko return -ENODEV; 21520e92462SDmitry Osipenko 21620e92462SDmitry Osipenko rst_ops = mc->soc->reset_ops; 21720e92462SDmitry Osipenko if (!rst_ops) 21820e92462SDmitry Osipenko return -ENODEV; 21920e92462SDmitry Osipenko 2206ce84ab6SDmitry Osipenko /* DMA flushing will fail if reset is already asserted */ 2216ce84ab6SDmitry Osipenko if (rst_ops->reset_status) { 2226ce84ab6SDmitry Osipenko /* check whether reset is asserted */ 2236ce84ab6SDmitry Osipenko if (rst_ops->reset_status(mc, rst)) 2246ce84ab6SDmitry Osipenko return 0; 2256ce84ab6SDmitry Osipenko } 2266ce84ab6SDmitry Osipenko 22720e92462SDmitry Osipenko if (rst_ops->block_dma) { 22820e92462SDmitry Osipenko /* block clients DMA requests */ 22920e92462SDmitry Osipenko err = rst_ops->block_dma(mc, rst); 23020e92462SDmitry Osipenko if (err) { 231f2dcded1SDmitry Osipenko dev_err(mc->dev, "failed to block %s DMA: %d\n", 23220e92462SDmitry Osipenko rst->name, err); 23320e92462SDmitry Osipenko return err; 23420e92462SDmitry Osipenko } 23520e92462SDmitry Osipenko } 23620e92462SDmitry Osipenko 23720e92462SDmitry Osipenko if (rst_ops->dma_idling) { 23820e92462SDmitry Osipenko /* wait for completion of the outstanding DMA requests */ 23920e92462SDmitry Osipenko while (!rst_ops->dma_idling(mc, rst)) { 24020e92462SDmitry Osipenko if (!retries--) { 241f2dcded1SDmitry Osipenko dev_err(mc->dev, "failed to flush %s DMA\n", 24220e92462SDmitry Osipenko rst->name); 24320e92462SDmitry Osipenko return -EBUSY; 24420e92462SDmitry Osipenko } 24520e92462SDmitry Osipenko 24620e92462SDmitry Osipenko usleep_range(10, 100); 24720e92462SDmitry Osipenko } 24820e92462SDmitry Osipenko } 24920e92462SDmitry Osipenko 25020e92462SDmitry Osipenko if (rst_ops->hotreset_assert) { 25120e92462SDmitry Osipenko /* clear clients DMA requests sitting before arbitration */ 25220e92462SDmitry Osipenko err = rst_ops->hotreset_assert(mc, rst); 25320e92462SDmitry Osipenko if (err) { 254f2dcded1SDmitry Osipenko dev_err(mc->dev, "failed to hot reset %s: %d\n", 25520e92462SDmitry Osipenko rst->name, err); 25620e92462SDmitry Osipenko return err; 25720e92462SDmitry Osipenko } 25820e92462SDmitry Osipenko } 25920e92462SDmitry Osipenko 26020e92462SDmitry Osipenko return 0; 26120e92462SDmitry Osipenko } 26220e92462SDmitry Osipenko 26320e92462SDmitry Osipenko static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, 26420e92462SDmitry Osipenko unsigned long id) 26520e92462SDmitry Osipenko { 26620e92462SDmitry Osipenko struct tegra_mc *mc = reset_to_mc(rcdev); 26720e92462SDmitry Osipenko const struct tegra_mc_reset_ops *rst_ops; 26820e92462SDmitry Osipenko const struct tegra_mc_reset *rst; 26920e92462SDmitry Osipenko int err; 27020e92462SDmitry Osipenko 27120e92462SDmitry Osipenko rst = tegra_mc_reset_find(mc, id); 27220e92462SDmitry Osipenko if (!rst) 27320e92462SDmitry Osipenko return -ENODEV; 27420e92462SDmitry Osipenko 27520e92462SDmitry Osipenko rst_ops = mc->soc->reset_ops; 27620e92462SDmitry Osipenko if (!rst_ops) 27720e92462SDmitry Osipenko return -ENODEV; 27820e92462SDmitry Osipenko 27920e92462SDmitry Osipenko if (rst_ops->hotreset_deassert) { 28020e92462SDmitry Osipenko /* take out client from hot reset */ 28120e92462SDmitry Osipenko err = rst_ops->hotreset_deassert(mc, rst); 28220e92462SDmitry Osipenko if (err) { 283f2dcded1SDmitry Osipenko dev_err(mc->dev, "failed to deassert hot reset %s: %d\n", 28420e92462SDmitry Osipenko rst->name, err); 28520e92462SDmitry Osipenko return err; 28620e92462SDmitry Osipenko } 28720e92462SDmitry Osipenko } 28820e92462SDmitry Osipenko 28920e92462SDmitry Osipenko if (rst_ops->unblock_dma) { 29020e92462SDmitry Osipenko /* allow new DMA requests to proceed to arbitration */ 29120e92462SDmitry Osipenko err = rst_ops->unblock_dma(mc, rst); 29220e92462SDmitry Osipenko if (err) { 293f2dcded1SDmitry Osipenko dev_err(mc->dev, "failed to unblock %s DMA : %d\n", 29420e92462SDmitry Osipenko rst->name, err); 29520e92462SDmitry Osipenko return err; 29620e92462SDmitry Osipenko } 29720e92462SDmitry Osipenko } 29820e92462SDmitry Osipenko 29920e92462SDmitry Osipenko return 0; 30020e92462SDmitry Osipenko } 30120e92462SDmitry Osipenko 30220e92462SDmitry Osipenko static int tegra_mc_hotreset_status(struct reset_controller_dev *rcdev, 30320e92462SDmitry Osipenko unsigned long id) 30420e92462SDmitry Osipenko { 30520e92462SDmitry Osipenko struct tegra_mc *mc = reset_to_mc(rcdev); 30620e92462SDmitry Osipenko const struct tegra_mc_reset_ops *rst_ops; 30720e92462SDmitry Osipenko const struct tegra_mc_reset *rst; 30820e92462SDmitry Osipenko 30920e92462SDmitry Osipenko rst = tegra_mc_reset_find(mc, id); 31020e92462SDmitry Osipenko if (!rst) 31120e92462SDmitry Osipenko return -ENODEV; 31220e92462SDmitry Osipenko 31320e92462SDmitry Osipenko rst_ops = mc->soc->reset_ops; 31420e92462SDmitry Osipenko if (!rst_ops) 31520e92462SDmitry Osipenko return -ENODEV; 31620e92462SDmitry Osipenko 31720e92462SDmitry Osipenko return rst_ops->reset_status(mc, rst); 31820e92462SDmitry Osipenko } 31920e92462SDmitry Osipenko 32020e92462SDmitry Osipenko static const struct reset_control_ops tegra_mc_reset_ops = { 32120e92462SDmitry Osipenko .assert = tegra_mc_hotreset_assert, 32220e92462SDmitry Osipenko .deassert = tegra_mc_hotreset_deassert, 32320e92462SDmitry Osipenko .status = tegra_mc_hotreset_status, 32420e92462SDmitry Osipenko }; 32520e92462SDmitry Osipenko 32620e92462SDmitry Osipenko static int tegra_mc_reset_setup(struct tegra_mc *mc) 32720e92462SDmitry Osipenko { 32820e92462SDmitry Osipenko int err; 32920e92462SDmitry Osipenko 33020e92462SDmitry Osipenko mc->reset.ops = &tegra_mc_reset_ops; 33120e92462SDmitry Osipenko mc->reset.owner = THIS_MODULE; 33220e92462SDmitry Osipenko mc->reset.of_node = mc->dev->of_node; 33320e92462SDmitry Osipenko mc->reset.of_reset_n_cells = 1; 33420e92462SDmitry Osipenko mc->reset.nr_resets = mc->soc->num_resets; 33520e92462SDmitry Osipenko 33620e92462SDmitry Osipenko err = reset_controller_register(&mc->reset); 33720e92462SDmitry Osipenko if (err < 0) 33820e92462SDmitry Osipenko return err; 33920e92462SDmitry Osipenko 34020e92462SDmitry Osipenko return 0; 34120e92462SDmitry Osipenko } 34220e92462SDmitry Osipenko 343e34212c7SDmitry Osipenko int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate) 3443d9dd6fdSMikko Perttunen { 3453d9dd6fdSMikko Perttunen unsigned int i; 3463d9dd6fdSMikko Perttunen struct tegra_mc_timing *timing = NULL; 3473d9dd6fdSMikko Perttunen 3483d9dd6fdSMikko Perttunen for (i = 0; i < mc->num_timings; i++) { 3493d9dd6fdSMikko Perttunen if (mc->timings[i].rate == rate) { 3503d9dd6fdSMikko Perttunen timing = &mc->timings[i]; 3513d9dd6fdSMikko Perttunen break; 3523d9dd6fdSMikko Perttunen } 3533d9dd6fdSMikko Perttunen } 3543d9dd6fdSMikko Perttunen 3553d9dd6fdSMikko Perttunen if (!timing) { 3563d9dd6fdSMikko Perttunen dev_err(mc->dev, "no memory timing registered for rate %lu\n", 3573d9dd6fdSMikko Perttunen rate); 358e34212c7SDmitry Osipenko return -EINVAL; 3593d9dd6fdSMikko Perttunen } 3603d9dd6fdSMikko Perttunen 3613d9dd6fdSMikko Perttunen for (i = 0; i < mc->soc->num_emem_regs; ++i) 3623d9dd6fdSMikko Perttunen mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]); 363e34212c7SDmitry Osipenko 364e34212c7SDmitry Osipenko return 0; 3653d9dd6fdSMikko Perttunen } 3660c56eda8SDmitry Osipenko EXPORT_SYMBOL_GPL(tegra_mc_write_emem_configuration); 3673d9dd6fdSMikko Perttunen 3683d9dd6fdSMikko Perttunen unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc) 3693d9dd6fdSMikko Perttunen { 3703d9dd6fdSMikko Perttunen u8 dram_count; 3713d9dd6fdSMikko Perttunen 3723d9dd6fdSMikko Perttunen dram_count = mc_readl(mc, MC_EMEM_ADR_CFG); 3733d9dd6fdSMikko Perttunen dram_count &= MC_EMEM_ADR_CFG_EMEM_NUMDEV; 3743d9dd6fdSMikko Perttunen dram_count++; 3753d9dd6fdSMikko Perttunen 3763d9dd6fdSMikko Perttunen return dram_count; 3773d9dd6fdSMikko Perttunen } 3780c56eda8SDmitry Osipenko EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count); 3793d9dd6fdSMikko Perttunen 380ddeceab0SThierry Reding #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \ 381ddeceab0SThierry Reding defined(CONFIG_ARCH_TEGRA_114_SOC) || \ 382ddeceab0SThierry Reding defined(CONFIG_ARCH_TEGRA_124_SOC) || \ 383ddeceab0SThierry Reding defined(CONFIG_ARCH_TEGRA_132_SOC) || \ 384ddeceab0SThierry Reding defined(CONFIG_ARCH_TEGRA_210_SOC) 385ddeceab0SThierry Reding static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) 386ddeceab0SThierry Reding { 387ddeceab0SThierry Reding unsigned long long tick; 388ddeceab0SThierry Reding unsigned int i; 389ddeceab0SThierry Reding u32 value; 390ddeceab0SThierry Reding 391ddeceab0SThierry Reding /* compute the number of MC clock cycles per tick */ 392ddeceab0SThierry Reding tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk); 393ddeceab0SThierry Reding do_div(tick, NSEC_PER_SEC); 394ddeceab0SThierry Reding 395ddeceab0SThierry Reding value = mc_readl(mc, MC_EMEM_ARB_CFG); 396ddeceab0SThierry Reding value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; 397ddeceab0SThierry Reding value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); 398ddeceab0SThierry Reding mc_writel(mc, value, MC_EMEM_ARB_CFG); 399ddeceab0SThierry Reding 400ddeceab0SThierry Reding /* write latency allowance defaults */ 401ddeceab0SThierry Reding for (i = 0; i < mc->soc->num_clients; i++) { 402ddeceab0SThierry Reding const struct tegra_mc_client *client = &mc->soc->clients[i]; 403ddeceab0SThierry Reding u32 value; 404ddeceab0SThierry Reding 405ddeceab0SThierry Reding value = mc_readl(mc, client->regs.la.reg); 406ddeceab0SThierry Reding value &= ~(client->regs.la.mask << client->regs.la.shift); 407ddeceab0SThierry Reding value |= (client->regs.la.def & client->regs.la.mask) << client->regs.la.shift; 408ddeceab0SThierry Reding mc_writel(mc, value, client->regs.la.reg); 409ddeceab0SThierry Reding } 410ddeceab0SThierry Reding 411ddeceab0SThierry Reding /* latch new values */ 412ddeceab0SThierry Reding mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); 413ddeceab0SThierry Reding 414ddeceab0SThierry Reding return 0; 415ddeceab0SThierry Reding } 416ddeceab0SThierry Reding 4173d9dd6fdSMikko Perttunen static int load_one_timing(struct tegra_mc *mc, 4183d9dd6fdSMikko Perttunen struct tegra_mc_timing *timing, 4193d9dd6fdSMikko Perttunen struct device_node *node) 4203d9dd6fdSMikko Perttunen { 4213d9dd6fdSMikko Perttunen int err; 4223d9dd6fdSMikko Perttunen u32 tmp; 4233d9dd6fdSMikko Perttunen 4243d9dd6fdSMikko Perttunen err = of_property_read_u32(node, "clock-frequency", &tmp); 4253d9dd6fdSMikko Perttunen if (err) { 4263d9dd6fdSMikko Perttunen dev_err(mc->dev, 427c86f9854SRob Herring "timing %pOFn: failed to read rate\n", node); 4283d9dd6fdSMikko Perttunen return err; 4293d9dd6fdSMikko Perttunen } 4303d9dd6fdSMikko Perttunen 4313d9dd6fdSMikko Perttunen timing->rate = tmp; 4323d9dd6fdSMikko Perttunen timing->emem_data = devm_kcalloc(mc->dev, mc->soc->num_emem_regs, 4333d9dd6fdSMikko Perttunen sizeof(u32), GFP_KERNEL); 4343d9dd6fdSMikko Perttunen if (!timing->emem_data) 4353d9dd6fdSMikko Perttunen return -ENOMEM; 4363d9dd6fdSMikko Perttunen 4373d9dd6fdSMikko Perttunen err = of_property_read_u32_array(node, "nvidia,emem-configuration", 4383d9dd6fdSMikko Perttunen timing->emem_data, 4393d9dd6fdSMikko Perttunen mc->soc->num_emem_regs); 4403d9dd6fdSMikko Perttunen if (err) { 4413d9dd6fdSMikko Perttunen dev_err(mc->dev, 442c86f9854SRob Herring "timing %pOFn: failed to read EMEM configuration\n", 443c86f9854SRob Herring node); 4443d9dd6fdSMikko Perttunen return err; 4453d9dd6fdSMikko Perttunen } 4463d9dd6fdSMikko Perttunen 4473d9dd6fdSMikko Perttunen return 0; 4483d9dd6fdSMikko Perttunen } 4493d9dd6fdSMikko Perttunen 4503d9dd6fdSMikko Perttunen static int load_timings(struct tegra_mc *mc, struct device_node *node) 4513d9dd6fdSMikko Perttunen { 4523d9dd6fdSMikko Perttunen struct device_node *child; 4533d9dd6fdSMikko Perttunen struct tegra_mc_timing *timing; 4543d9dd6fdSMikko Perttunen int child_count = of_get_child_count(node); 4553d9dd6fdSMikko Perttunen int i = 0, err; 4563d9dd6fdSMikko Perttunen 4573d9dd6fdSMikko Perttunen mc->timings = devm_kcalloc(mc->dev, child_count, sizeof(*timing), 4583d9dd6fdSMikko Perttunen GFP_KERNEL); 4593d9dd6fdSMikko Perttunen if (!mc->timings) 4603d9dd6fdSMikko Perttunen return -ENOMEM; 4613d9dd6fdSMikko Perttunen 4623d9dd6fdSMikko Perttunen mc->num_timings = child_count; 4633d9dd6fdSMikko Perttunen 4643d9dd6fdSMikko Perttunen for_each_child_of_node(node, child) { 4653d9dd6fdSMikko Perttunen timing = &mc->timings[i++]; 4663d9dd6fdSMikko Perttunen 4673d9dd6fdSMikko Perttunen err = load_one_timing(mc, timing, child); 46855bb1d83SAmitoj Kaur Chawla if (err) { 46955bb1d83SAmitoj Kaur Chawla of_node_put(child); 4703d9dd6fdSMikko Perttunen return err; 4713d9dd6fdSMikko Perttunen } 47255bb1d83SAmitoj Kaur Chawla } 4733d9dd6fdSMikko Perttunen 4743d9dd6fdSMikko Perttunen return 0; 4753d9dd6fdSMikko Perttunen } 4763d9dd6fdSMikko Perttunen 4773d9dd6fdSMikko Perttunen static int tegra_mc_setup_timings(struct tegra_mc *mc) 4783d9dd6fdSMikko Perttunen { 4793d9dd6fdSMikko Perttunen struct device_node *node; 4803d9dd6fdSMikko Perttunen u32 ram_code, node_ram_code; 4813d9dd6fdSMikko Perttunen int err; 4823d9dd6fdSMikko Perttunen 4833d9dd6fdSMikko Perttunen ram_code = tegra_read_ram_code(); 4843d9dd6fdSMikko Perttunen 4853d9dd6fdSMikko Perttunen mc->num_timings = 0; 4863d9dd6fdSMikko Perttunen 4873d9dd6fdSMikko Perttunen for_each_child_of_node(mc->dev->of_node, node) { 4883d9dd6fdSMikko Perttunen err = of_property_read_u32(node, "nvidia,ram-code", 4893d9dd6fdSMikko Perttunen &node_ram_code); 490d1122e4bSJulia Lawall if (err || (node_ram_code != ram_code)) 4913d9dd6fdSMikko Perttunen continue; 4923d9dd6fdSMikko Perttunen 4933d9dd6fdSMikko Perttunen err = load_timings(mc, node); 49455bb1d83SAmitoj Kaur Chawla of_node_put(node); 4953d9dd6fdSMikko Perttunen if (err) 4963d9dd6fdSMikko Perttunen return err; 4973d9dd6fdSMikko Perttunen break; 4983d9dd6fdSMikko Perttunen } 4993d9dd6fdSMikko Perttunen 5003d9dd6fdSMikko Perttunen if (mc->num_timings == 0) 5013d9dd6fdSMikko Perttunen dev_warn(mc->dev, 5023d9dd6fdSMikko Perttunen "no memory timings for RAM code %u registered\n", 5033d9dd6fdSMikko Perttunen ram_code); 5043d9dd6fdSMikko Perttunen 5053d9dd6fdSMikko Perttunen return 0; 5063d9dd6fdSMikko Perttunen } 5073d9dd6fdSMikko Perttunen 508ddeceab0SThierry Reding int tegra30_mc_probe(struct tegra_mc *mc) 509ddeceab0SThierry Reding { 510ddeceab0SThierry Reding int err; 511ddeceab0SThierry Reding 512ddeceab0SThierry Reding mc->clk = devm_clk_get_optional(mc->dev, "mc"); 513ddeceab0SThierry Reding if (IS_ERR(mc->clk)) { 514ddeceab0SThierry Reding dev_err(mc->dev, "failed to get MC clock: %ld\n", PTR_ERR(mc->clk)); 515ddeceab0SThierry Reding return PTR_ERR(mc->clk); 516ddeceab0SThierry Reding } 517ddeceab0SThierry Reding 518ddeceab0SThierry Reding /* ensure that debug features are disabled */ 519ddeceab0SThierry Reding mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG); 520ddeceab0SThierry Reding 521ddeceab0SThierry Reding err = tegra_mc_setup_latency_allowance(mc); 522ddeceab0SThierry Reding if (err < 0) { 523ddeceab0SThierry Reding dev_err(mc->dev, "failed to setup latency allowance: %d\n", err); 524ddeceab0SThierry Reding return err; 525ddeceab0SThierry Reding } 526ddeceab0SThierry Reding 527ddeceab0SThierry Reding err = tegra_mc_setup_timings(mc); 528ddeceab0SThierry Reding if (err < 0) { 529ddeceab0SThierry Reding dev_err(mc->dev, "failed to setup timings: %d\n", err); 530ddeceab0SThierry Reding return err; 531ddeceab0SThierry Reding } 532ddeceab0SThierry Reding 533ddeceab0SThierry Reding return 0; 534ddeceab0SThierry Reding } 535ddeceab0SThierry Reding 53654a85e09SAshish Mhetre const struct tegra_mc_ops tegra30_mc_ops = { 53754a85e09SAshish Mhetre .probe = tegra30_mc_probe, 53854a85e09SAshish Mhetre .handle_irq = tegra30_mc_handle_irq, 53954a85e09SAshish Mhetre }; 54054a85e09SAshish Mhetre #endif 54154a85e09SAshish Mhetre 54254a85e09SAshish Mhetre static int mc_global_intstatus_to_channel(const struct tegra_mc *mc, u32 status, 54354a85e09SAshish Mhetre unsigned int *mc_channel) 54454a85e09SAshish Mhetre { 54554a85e09SAshish Mhetre if ((status & mc->soc->ch_intmask) == 0) 54654a85e09SAshish Mhetre return -EINVAL; 54754a85e09SAshish Mhetre 54854a85e09SAshish Mhetre *mc_channel = __ffs((status & mc->soc->ch_intmask) >> 54954a85e09SAshish Mhetre mc->soc->global_intstatus_channel_shift); 55054a85e09SAshish Mhetre 55154a85e09SAshish Mhetre return 0; 55254a85e09SAshish Mhetre } 55354a85e09SAshish Mhetre 55454a85e09SAshish Mhetre static u32 mc_channel_to_global_intstatus(const struct tegra_mc *mc, 55554a85e09SAshish Mhetre unsigned int channel) 55654a85e09SAshish Mhetre { 55754a85e09SAshish Mhetre return BIT(channel) << mc->soc->global_intstatus_channel_shift; 55854a85e09SAshish Mhetre } 55954a85e09SAshish Mhetre 56054a85e09SAshish Mhetre irqreturn_t tegra30_mc_handle_irq(int irq, void *data) 56189184651SThierry Reding { 56289184651SThierry Reding struct tegra_mc *mc = data; 56354a85e09SAshish Mhetre unsigned int bit, channel; 5641c74d5c0SDmitry Osipenko unsigned long status; 56554a85e09SAshish Mhetre 56654a85e09SAshish Mhetre if (mc->soc->num_channels) { 56754a85e09SAshish Mhetre u32 global_status; 56854a85e09SAshish Mhetre int err; 56954a85e09SAshish Mhetre 57054a85e09SAshish Mhetre global_status = mc_ch_readl(mc, MC_BROADCAST_CHANNEL, MC_GLOBAL_INTSTATUS); 57154a85e09SAshish Mhetre err = mc_global_intstatus_to_channel(mc, global_status, &channel); 57254a85e09SAshish Mhetre if (err < 0) { 57354a85e09SAshish Mhetre dev_err_ratelimited(mc->dev, "unknown interrupt channel 0x%08x\n", 57454a85e09SAshish Mhetre global_status); 57554a85e09SAshish Mhetre return IRQ_NONE; 57654a85e09SAshish Mhetre } 57789184651SThierry Reding 57889184651SThierry Reding /* mask all interrupts to avoid flooding */ 57954a85e09SAshish Mhetre status = mc_ch_readl(mc, channel, MC_INTSTATUS) & mc->soc->intmask; 58054a85e09SAshish Mhetre } else { 5811c74d5c0SDmitry Osipenko status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmask; 58254a85e09SAshish Mhetre } 58354a85e09SAshish Mhetre 584bf3fbdfbSDmitry Osipenko if (!status) 585bf3fbdfbSDmitry Osipenko return IRQ_NONE; 58689184651SThierry Reding 58789184651SThierry Reding for_each_set_bit(bit, &status, 32) { 5881079a66bSThierry Reding const char *error = tegra_mc_status_names[bit] ?: "unknown"; 58989184651SThierry Reding const char *client = "unknown", *desc; 59089184651SThierry Reding const char *direction, *secure; 59154a85e09SAshish Mhetre u32 status_reg, addr_reg; 59254a85e09SAshish Mhetre u32 intmask = BIT(bit); 59389184651SThierry Reding phys_addr_t addr = 0; 59454a85e09SAshish Mhetre #ifdef CONFIG_PHYS_ADDR_T_64BIT 59554a85e09SAshish Mhetre u32 addr_hi_reg = 0; 59654a85e09SAshish Mhetre #endif 59789184651SThierry Reding unsigned int i; 59889184651SThierry Reding char perm[7]; 59989184651SThierry Reding u8 id, type; 60089184651SThierry Reding u32 value; 60189184651SThierry Reding 60254a85e09SAshish Mhetre switch (intmask) { 60354a85e09SAshish Mhetre case MC_INT_DECERR_VPR: 60454a85e09SAshish Mhetre status_reg = MC_ERR_VPR_STATUS; 60554a85e09SAshish Mhetre addr_reg = MC_ERR_VPR_ADR; 60654a85e09SAshish Mhetre break; 60754a85e09SAshish Mhetre 60854a85e09SAshish Mhetre case MC_INT_SECERR_SEC: 60954a85e09SAshish Mhetre status_reg = MC_ERR_SEC_STATUS; 61054a85e09SAshish Mhetre addr_reg = MC_ERR_SEC_ADR; 61154a85e09SAshish Mhetre break; 61254a85e09SAshish Mhetre 61354a85e09SAshish Mhetre case MC_INT_DECERR_MTS: 61454a85e09SAshish Mhetre status_reg = MC_ERR_MTS_STATUS; 61554a85e09SAshish Mhetre addr_reg = MC_ERR_MTS_ADR; 61654a85e09SAshish Mhetre break; 61754a85e09SAshish Mhetre 61854a85e09SAshish Mhetre case MC_INT_DECERR_GENERALIZED_CARVEOUT: 61954a85e09SAshish Mhetre status_reg = MC_ERR_GENERALIZED_CARVEOUT_STATUS; 62054a85e09SAshish Mhetre addr_reg = MC_ERR_GENERALIZED_CARVEOUT_ADR; 62154a85e09SAshish Mhetre break; 62254a85e09SAshish Mhetre 62354a85e09SAshish Mhetre case MC_INT_DECERR_ROUTE_SANITY: 62454a85e09SAshish Mhetre status_reg = MC_ERR_ROUTE_SANITY_STATUS; 62554a85e09SAshish Mhetre addr_reg = MC_ERR_ROUTE_SANITY_ADR; 62654a85e09SAshish Mhetre break; 62754a85e09SAshish Mhetre 62854a85e09SAshish Mhetre default: 62954a85e09SAshish Mhetre status_reg = MC_ERR_STATUS; 63054a85e09SAshish Mhetre addr_reg = MC_ERR_ADR; 63154a85e09SAshish Mhetre 63254a85e09SAshish Mhetre #ifdef CONFIG_PHYS_ADDR_T_64BIT 63354a85e09SAshish Mhetre if (mc->soc->has_addr_hi_reg) 63454a85e09SAshish Mhetre addr_hi_reg = MC_ERR_ADR_HI; 63554a85e09SAshish Mhetre #endif 63654a85e09SAshish Mhetre break; 63754a85e09SAshish Mhetre } 63854a85e09SAshish Mhetre 63954a85e09SAshish Mhetre if (mc->soc->num_channels) 64054a85e09SAshish Mhetre value = mc_ch_readl(mc, channel, status_reg); 64154a85e09SAshish Mhetre else 64254a85e09SAshish Mhetre value = mc_readl(mc, status_reg); 64389184651SThierry Reding 64489184651SThierry Reding #ifdef CONFIG_PHYS_ADDR_T_64BIT 64589184651SThierry Reding if (mc->soc->num_address_bits > 32) { 64654a85e09SAshish Mhetre if (addr_hi_reg) { 64754a85e09SAshish Mhetre if (mc->soc->num_channels) 64854a85e09SAshish Mhetre addr = mc_ch_readl(mc, channel, addr_hi_reg); 64954a85e09SAshish Mhetre else 65054a85e09SAshish Mhetre addr = mc_readl(mc, addr_hi_reg); 65154a85e09SAshish Mhetre } else { 65289184651SThierry Reding addr = ((value >> MC_ERR_STATUS_ADR_HI_SHIFT) & 65389184651SThierry Reding MC_ERR_STATUS_ADR_HI_MASK); 65454a85e09SAshish Mhetre } 65589184651SThierry Reding addr <<= 32; 65689184651SThierry Reding } 65789184651SThierry Reding #endif 65889184651SThierry Reding 65989184651SThierry Reding if (value & MC_ERR_STATUS_RW) 66089184651SThierry Reding direction = "write"; 66189184651SThierry Reding else 66289184651SThierry Reding direction = "read"; 66389184651SThierry Reding 66489184651SThierry Reding if (value & MC_ERR_STATUS_SECURITY) 66589184651SThierry Reding secure = "secure "; 66689184651SThierry Reding else 66789184651SThierry Reding secure = ""; 66889184651SThierry Reding 6693c01cf3bSPaul Walmsley id = value & mc->soc->client_id_mask; 67089184651SThierry Reding 67189184651SThierry Reding for (i = 0; i < mc->soc->num_clients; i++) { 67289184651SThierry Reding if (mc->soc->clients[i].id == id) { 67389184651SThierry Reding client = mc->soc->clients[i].name; 67489184651SThierry Reding break; 67589184651SThierry Reding } 67689184651SThierry Reding } 67789184651SThierry Reding 67889184651SThierry Reding type = (value & MC_ERR_STATUS_TYPE_MASK) >> 67989184651SThierry Reding MC_ERR_STATUS_TYPE_SHIFT; 6801079a66bSThierry Reding desc = tegra_mc_error_names[type]; 68189184651SThierry Reding 68289184651SThierry Reding switch (value & MC_ERR_STATUS_TYPE_MASK) { 68389184651SThierry Reding case MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE: 68489184651SThierry Reding perm[0] = ' '; 68589184651SThierry Reding perm[1] = '['; 68689184651SThierry Reding 68789184651SThierry Reding if (value & MC_ERR_STATUS_READABLE) 68889184651SThierry Reding perm[2] = 'R'; 68989184651SThierry Reding else 69089184651SThierry Reding perm[2] = '-'; 69189184651SThierry Reding 69289184651SThierry Reding if (value & MC_ERR_STATUS_WRITABLE) 69389184651SThierry Reding perm[3] = 'W'; 69489184651SThierry Reding else 69589184651SThierry Reding perm[3] = '-'; 69689184651SThierry Reding 69789184651SThierry Reding if (value & MC_ERR_STATUS_NONSECURE) 69889184651SThierry Reding perm[4] = '-'; 69989184651SThierry Reding else 70089184651SThierry Reding perm[4] = 'S'; 70189184651SThierry Reding 70289184651SThierry Reding perm[5] = ']'; 70389184651SThierry Reding perm[6] = '\0'; 70489184651SThierry Reding break; 70589184651SThierry Reding 70689184651SThierry Reding default: 70789184651SThierry Reding perm[0] = '\0'; 70889184651SThierry Reding break; 70989184651SThierry Reding } 71089184651SThierry Reding 71154a85e09SAshish Mhetre if (mc->soc->num_channels) 71254a85e09SAshish Mhetre value = mc_ch_readl(mc, channel, addr_reg); 71354a85e09SAshish Mhetre else 71454a85e09SAshish Mhetre value = mc_readl(mc, addr_reg); 71589184651SThierry Reding addr |= value; 71689184651SThierry Reding 71789184651SThierry Reding dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s%s)\n", 71889184651SThierry Reding client, secure, direction, &addr, error, 71989184651SThierry Reding desc, perm); 72089184651SThierry Reding } 72189184651SThierry Reding 72289184651SThierry Reding /* clear interrupts */ 72354a85e09SAshish Mhetre if (mc->soc->num_channels) { 72454a85e09SAshish Mhetre mc_ch_writel(mc, channel, status, MC_INTSTATUS); 72554a85e09SAshish Mhetre mc_ch_writel(mc, MC_BROADCAST_CHANNEL, 72654a85e09SAshish Mhetre mc_channel_to_global_intstatus(mc, channel), 72754a85e09SAshish Mhetre MC_GLOBAL_INTSTATUS); 72854a85e09SAshish Mhetre } else { 72989184651SThierry Reding mc_writel(mc, status, MC_INTSTATUS); 73054a85e09SAshish Mhetre } 73189184651SThierry Reding 73289184651SThierry Reding return IRQ_HANDLED; 73389184651SThierry Reding } 73489184651SThierry Reding 7351079a66bSThierry Reding const char *const tegra_mc_status_names[32] = { 7361079a66bSThierry Reding [ 1] = "External interrupt", 7371079a66bSThierry Reding [ 6] = "EMEM address decode error", 7381079a66bSThierry Reding [ 7] = "GART page fault", 7391079a66bSThierry Reding [ 8] = "Security violation", 7401079a66bSThierry Reding [ 9] = "EMEM arbitration error", 7411079a66bSThierry Reding [10] = "Page fault", 7421079a66bSThierry Reding [11] = "Invalid APB ASID update", 7431079a66bSThierry Reding [12] = "VPR violation", 7441079a66bSThierry Reding [13] = "Secure carveout violation", 7451079a66bSThierry Reding [16] = "MTS carveout violation", 74654a85e09SAshish Mhetre [17] = "Generalized carveout violation", 74754a85e09SAshish Mhetre [20] = "Route Sanity error", 7481079a66bSThierry Reding }; 749a8d502fdSDmitry Osipenko 7501079a66bSThierry Reding const char *const tegra_mc_error_names[8] = { 7511079a66bSThierry Reding [2] = "EMEM decode error", 7521079a66bSThierry Reding [3] = "TrustZone violation", 7531079a66bSThierry Reding [4] = "Carveout violation", 7541079a66bSThierry Reding [6] = "SMMU translation error", 7551079a66bSThierry Reding }; 756a8d502fdSDmitry Osipenko 75706f07981SDmitry Osipenko /* 75806f07981SDmitry Osipenko * Memory Controller (MC) has few Memory Clients that are issuing memory 75906f07981SDmitry Osipenko * bandwidth allocation requests to the MC interconnect provider. The MC 76006f07981SDmitry Osipenko * provider aggregates the requests and then sends the aggregated request 76106f07981SDmitry Osipenko * up to the External Memory Controller (EMC) interconnect provider which 76206f07981SDmitry Osipenko * re-configures hardware interface to External Memory (EMEM) in accordance 76306f07981SDmitry Osipenko * to the required bandwidth. Each MC interconnect node represents an 76406f07981SDmitry Osipenko * individual Memory Client. 76506f07981SDmitry Osipenko * 76606f07981SDmitry Osipenko * Memory interconnect topology: 76706f07981SDmitry Osipenko * 76806f07981SDmitry Osipenko * +----+ 76906f07981SDmitry Osipenko * +--------+ | | 77006f07981SDmitry Osipenko * | TEXSRD +--->+ | 77106f07981SDmitry Osipenko * +--------+ | | 77206f07981SDmitry Osipenko * | | +-----+ +------+ 77306f07981SDmitry Osipenko * ... | MC +--->+ EMC +--->+ EMEM | 77406f07981SDmitry Osipenko * | | +-----+ +------+ 77506f07981SDmitry Osipenko * +--------+ | | 77606f07981SDmitry Osipenko * | DISP.. +--->+ | 77706f07981SDmitry Osipenko * +--------+ | | 77806f07981SDmitry Osipenko * +----+ 77906f07981SDmitry Osipenko */ 78006f07981SDmitry Osipenko static int tegra_mc_interconnect_setup(struct tegra_mc *mc) 78106f07981SDmitry Osipenko { 78206f07981SDmitry Osipenko struct icc_node *node; 78306f07981SDmitry Osipenko unsigned int i; 78406f07981SDmitry Osipenko int err; 78506f07981SDmitry Osipenko 78606f07981SDmitry Osipenko /* older device-trees don't have interconnect properties */ 78706f07981SDmitry Osipenko if (!device_property_present(mc->dev, "#interconnect-cells") || 78806f07981SDmitry Osipenko !mc->soc->icc_ops) 78906f07981SDmitry Osipenko return 0; 79006f07981SDmitry Osipenko 79106f07981SDmitry Osipenko mc->provider.dev = mc->dev; 79206f07981SDmitry Osipenko mc->provider.data = &mc->provider; 79306f07981SDmitry Osipenko mc->provider.set = mc->soc->icc_ops->set; 79406f07981SDmitry Osipenko mc->provider.aggregate = mc->soc->icc_ops->aggregate; 79506f07981SDmitry Osipenko mc->provider.xlate_extended = mc->soc->icc_ops->xlate_extended; 79606f07981SDmitry Osipenko 79706f07981SDmitry Osipenko err = icc_provider_add(&mc->provider); 79806f07981SDmitry Osipenko if (err) 79906f07981SDmitry Osipenko return err; 80006f07981SDmitry Osipenko 80106f07981SDmitry Osipenko /* create Memory Controller node */ 80206f07981SDmitry Osipenko node = icc_node_create(TEGRA_ICC_MC); 80306f07981SDmitry Osipenko if (IS_ERR(node)) { 80406f07981SDmitry Osipenko err = PTR_ERR(node); 80506f07981SDmitry Osipenko goto del_provider; 80606f07981SDmitry Osipenko } 80706f07981SDmitry Osipenko 80806f07981SDmitry Osipenko node->name = "Memory Controller"; 80906f07981SDmitry Osipenko icc_node_add(node, &mc->provider); 81006f07981SDmitry Osipenko 81106f07981SDmitry Osipenko /* link Memory Controller to External Memory Controller */ 81206f07981SDmitry Osipenko err = icc_link_create(node, TEGRA_ICC_EMC); 81306f07981SDmitry Osipenko if (err) 81406f07981SDmitry Osipenko goto remove_nodes; 81506f07981SDmitry Osipenko 81606f07981SDmitry Osipenko for (i = 0; i < mc->soc->num_clients; i++) { 81706f07981SDmitry Osipenko /* create MC client node */ 81806f07981SDmitry Osipenko node = icc_node_create(mc->soc->clients[i].id); 81906f07981SDmitry Osipenko if (IS_ERR(node)) { 82006f07981SDmitry Osipenko err = PTR_ERR(node); 82106f07981SDmitry Osipenko goto remove_nodes; 82206f07981SDmitry Osipenko } 82306f07981SDmitry Osipenko 82406f07981SDmitry Osipenko node->name = mc->soc->clients[i].name; 82506f07981SDmitry Osipenko icc_node_add(node, &mc->provider); 82606f07981SDmitry Osipenko 82706f07981SDmitry Osipenko /* link Memory Client to Memory Controller */ 82806f07981SDmitry Osipenko err = icc_link_create(node, TEGRA_ICC_MC); 82906f07981SDmitry Osipenko if (err) 83006f07981SDmitry Osipenko goto remove_nodes; 83106f07981SDmitry Osipenko } 83206f07981SDmitry Osipenko 83306f07981SDmitry Osipenko return 0; 83406f07981SDmitry Osipenko 83506f07981SDmitry Osipenko remove_nodes: 83606f07981SDmitry Osipenko icc_nodes_remove(&mc->provider); 83706f07981SDmitry Osipenko del_provider: 83806f07981SDmitry Osipenko icc_provider_del(&mc->provider); 83906f07981SDmitry Osipenko 84006f07981SDmitry Osipenko return err; 84106f07981SDmitry Osipenko } 84206f07981SDmitry Osipenko 84389184651SThierry Reding static int tegra_mc_probe(struct platform_device *pdev) 84489184651SThierry Reding { 84589184651SThierry Reding struct tegra_mc *mc; 846c4c21f22SThierry Reding u64 mask; 84789184651SThierry Reding int err; 84889184651SThierry Reding 84989184651SThierry Reding mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 85089184651SThierry Reding if (!mc) 85189184651SThierry Reding return -ENOMEM; 85289184651SThierry Reding 85389184651SThierry Reding platform_set_drvdata(pdev, mc); 85420e92462SDmitry Osipenko spin_lock_init(&mc->lock); 85559cd046fSDmitry Osipenko mc->soc = of_device_get_match_data(&pdev->dev); 85689184651SThierry Reding mc->dev = &pdev->dev; 85789184651SThierry Reding 858c4c21f22SThierry Reding mask = DMA_BIT_MASK(mc->soc->num_address_bits); 859c4c21f22SThierry Reding 860c4c21f22SThierry Reding err = dma_coerce_mask_and_coherent(&pdev->dev, mask); 861c4c21f22SThierry Reding if (err < 0) { 862c4c21f22SThierry Reding dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); 863c4c21f22SThierry Reding return err; 864c4c21f22SThierry Reding } 865c4c21f22SThierry Reding 86689184651SThierry Reding /* length of MC tick in nanoseconds */ 86789184651SThierry Reding mc->tick = 30; 86889184651SThierry Reding 869dab022f2SKrzysztof Kozlowski mc->regs = devm_platform_ioremap_resource(pdev, 0); 87089184651SThierry Reding if (IS_ERR(mc->regs)) 87189184651SThierry Reding return PTR_ERR(mc->regs); 87289184651SThierry Reding 873c64738e9SThierry Reding mc->debugfs.root = debugfs_create_dir("mc", NULL); 874c64738e9SThierry Reding 875c64738e9SThierry Reding if (mc->soc->ops && mc->soc->ops->probe) { 876c64738e9SThierry Reding err = mc->soc->ops->probe(mc); 877c64738e9SThierry Reding if (err < 0) 878c64738e9SThierry Reding return err; 879c64738e9SThierry Reding } 880c64738e9SThierry Reding 881e474b3a1SThierry Reding if (mc->soc->ops && mc->soc->ops->handle_irq) { 88289184651SThierry Reding mc->irq = platform_get_irq(pdev, 0); 883162641a6SDmitry Osipenko if (mc->irq < 0) 88489184651SThierry Reding return mc->irq; 88589184651SThierry Reding 886f2dcded1SDmitry Osipenko WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); 8873c01cf3bSPaul Walmsley 88854a85e09SAshish Mhetre if (mc->soc->num_channels) 88954a85e09SAshish Mhetre mc_ch_writel(mc, MC_BROADCAST_CHANNEL, mc->soc->intmask, 89054a85e09SAshish Mhetre MC_INTMASK); 89154a85e09SAshish Mhetre else 8921c74d5c0SDmitry Osipenko mc_writel(mc, mc->soc->intmask, MC_INTMASK); 89389184651SThierry Reding 8941079a66bSThierry Reding err = devm_request_irq(&pdev->dev, mc->irq, mc->soc->ops->handle_irq, 0, 895db4a9c19SDmitry Osipenko dev_name(&pdev->dev), mc); 896db4a9c19SDmitry Osipenko if (err < 0) { 897db4a9c19SDmitry Osipenko dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, 898db4a9c19SDmitry Osipenko err); 899db4a9c19SDmitry Osipenko return err; 900db4a9c19SDmitry Osipenko } 901e474b3a1SThierry Reding } 902db4a9c19SDmitry Osipenko 9030de93c69SThierry Reding if (mc->soc->reset_ops) { 9041662dd64SDmitry Osipenko err = tegra_mc_reset_setup(mc); 9051662dd64SDmitry Osipenko if (err < 0) 9060de93c69SThierry Reding dev_err(&pdev->dev, "failed to register reset controller: %d\n", err); 9070de93c69SThierry Reding } 9081662dd64SDmitry Osipenko 90906f07981SDmitry Osipenko err = tegra_mc_interconnect_setup(mc); 91006f07981SDmitry Osipenko if (err < 0) 91106f07981SDmitry Osipenko dev_err(&pdev->dev, "failed to initialize interconnect: %d\n", 91206f07981SDmitry Osipenko err); 91306f07981SDmitry Osipenko 914568ece5bSDmitry Osipenko if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { 91545a81df0SDmitry Osipenko mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); 916568ece5bSDmitry Osipenko if (IS_ERR(mc->smmu)) { 91745a81df0SDmitry Osipenko dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", 91845a81df0SDmitry Osipenko PTR_ERR(mc->smmu)); 919568ece5bSDmitry Osipenko mc->smmu = NULL; 920568ece5bSDmitry Osipenko } 92145a81df0SDmitry Osipenko } 92245a81df0SDmitry Osipenko 923ce2785a7SDmitry Osipenko if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) { 924ce2785a7SDmitry Osipenko mc->gart = tegra_gart_probe(&pdev->dev, mc); 925ce2785a7SDmitry Osipenko if (IS_ERR(mc->gart)) { 926ce2785a7SDmitry Osipenko dev_err(&pdev->dev, "failed to probe GART: %ld\n", 927ce2785a7SDmitry Osipenko PTR_ERR(mc->gart)); 928ce2785a7SDmitry Osipenko mc->gart = NULL; 929ce2785a7SDmitry Osipenko } 930ce2785a7SDmitry Osipenko } 931ce2785a7SDmitry Osipenko 93289184651SThierry Reding return 0; 93389184651SThierry Reding } 93489184651SThierry Reding 9355c9016f0SThierry Reding static int __maybe_unused tegra_mc_suspend(struct device *dev) 936ce2785a7SDmitry Osipenko { 937ce2785a7SDmitry Osipenko struct tegra_mc *mc = dev_get_drvdata(dev); 938ce2785a7SDmitry Osipenko 9395c9016f0SThierry Reding if (mc->soc->ops && mc->soc->ops->suspend) 9405c9016f0SThierry Reding return mc->soc->ops->suspend(mc); 941ce2785a7SDmitry Osipenko 942ce2785a7SDmitry Osipenko return 0; 943ce2785a7SDmitry Osipenko } 944ce2785a7SDmitry Osipenko 9455c9016f0SThierry Reding static int __maybe_unused tegra_mc_resume(struct device *dev) 946ce2785a7SDmitry Osipenko { 947ce2785a7SDmitry Osipenko struct tegra_mc *mc = dev_get_drvdata(dev); 948ce2785a7SDmitry Osipenko 9495c9016f0SThierry Reding if (mc->soc->ops && mc->soc->ops->resume) 9505c9016f0SThierry Reding return mc->soc->ops->resume(mc); 951ce2785a7SDmitry Osipenko 952ce2785a7SDmitry Osipenko return 0; 953ce2785a7SDmitry Osipenko } 954ce2785a7SDmitry Osipenko 95577b14c9dSDmitry Osipenko static void tegra_mc_sync_state(struct device *dev) 95677b14c9dSDmitry Osipenko { 95777b14c9dSDmitry Osipenko struct tegra_mc *mc = dev_get_drvdata(dev); 95877b14c9dSDmitry Osipenko 95977b14c9dSDmitry Osipenko /* check whether ICC provider is registered */ 96077b14c9dSDmitry Osipenko if (mc->provider.dev == dev) 96177b14c9dSDmitry Osipenko icc_sync_state(dev); 96277b14c9dSDmitry Osipenko } 96377b14c9dSDmitry Osipenko 964ce2785a7SDmitry Osipenko static const struct dev_pm_ops tegra_mc_pm_ops = { 9655c9016f0SThierry Reding SET_SYSTEM_SLEEP_PM_OPS(tegra_mc_suspend, tegra_mc_resume) 966ce2785a7SDmitry Osipenko }; 967ce2785a7SDmitry Osipenko 96889184651SThierry Reding static struct platform_driver tegra_mc_driver = { 96989184651SThierry Reding .driver = { 97089184651SThierry Reding .name = "tegra-mc", 97189184651SThierry Reding .of_match_table = tegra_mc_of_match, 972ce2785a7SDmitry Osipenko .pm = &tegra_mc_pm_ops, 97389184651SThierry Reding .suppress_bind_attrs = true, 97477b14c9dSDmitry Osipenko .sync_state = tegra_mc_sync_state, 97589184651SThierry Reding }, 97689184651SThierry Reding .prevent_deferred_probe = true, 97789184651SThierry Reding .probe = tegra_mc_probe, 97889184651SThierry Reding }; 97989184651SThierry Reding 98089184651SThierry Reding static int tegra_mc_init(void) 98189184651SThierry Reding { 98289184651SThierry Reding return platform_driver_register(&tegra_mc_driver); 98389184651SThierry Reding } 98489184651SThierry Reding arch_initcall(tegra_mc_init); 98589184651SThierry Reding 98689184651SThierry Reding MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 98789184651SThierry Reding MODULE_DESCRIPTION("NVIDIA Tegra Memory Controller driver"); 98889184651SThierry Reding MODULE_LICENSE("GPL v2"); 989