18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Google, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: 78c2ecf20Sopenharmony_ci * Jay Cheng <jacheng@nvidia.com> 88c2ecf20Sopenharmony_ci * James Wylder <james.wylder@motorola.com> 98c2ecf20Sopenharmony_ci * Benoit Goby <benoit@android.com> 108c2ecf20Sopenharmony_ci * Colin Cross <ccross@android.com> 118c2ecf20Sopenharmony_ci * Hiroshi DOYU <hdoyu@nvidia.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <soc/tegra/ahb.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRV_NAME "tegra-ahb" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define AHB_ARBITRATION_DISABLE 0x04 268c2ecf20Sopenharmony_ci#define AHB_ARBITRATION_PRIORITY_CTRL 0x08 278c2ecf20Sopenharmony_ci#define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29) 288c2ecf20Sopenharmony_ci#define PRIORITY_SELECT_USB BIT(6) 298c2ecf20Sopenharmony_ci#define PRIORITY_SELECT_USB2 BIT(18) 308c2ecf20Sopenharmony_ci#define PRIORITY_SELECT_USB3 BIT(17) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define AHB_GIZMO_AHB_MEM 0x10 338c2ecf20Sopenharmony_ci#define ENB_FAST_REARBITRATE BIT(2) 348c2ecf20Sopenharmony_ci#define DONT_SPLIT_AHB_WR BIT(7) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define AHB_GIZMO_APB_DMA 0x14 378c2ecf20Sopenharmony_ci#define AHB_GIZMO_IDE 0x1c 388c2ecf20Sopenharmony_ci#define AHB_GIZMO_USB 0x20 398c2ecf20Sopenharmony_ci#define AHB_GIZMO_AHB_XBAR_BRIDGE 0x24 408c2ecf20Sopenharmony_ci#define AHB_GIZMO_CPU_AHB_BRIDGE 0x28 418c2ecf20Sopenharmony_ci#define AHB_GIZMO_COP_AHB_BRIDGE 0x2c 428c2ecf20Sopenharmony_ci#define AHB_GIZMO_XBAR_APB_CTLR 0x30 438c2ecf20Sopenharmony_ci#define AHB_GIZMO_VCP_AHB_BRIDGE 0x34 448c2ecf20Sopenharmony_ci#define AHB_GIZMO_NAND 0x40 458c2ecf20Sopenharmony_ci#define AHB_GIZMO_SDMMC4 0x48 468c2ecf20Sopenharmony_ci#define AHB_GIZMO_XIO 0x4c 478c2ecf20Sopenharmony_ci#define AHB_GIZMO_BSEV 0x64 488c2ecf20Sopenharmony_ci#define AHB_GIZMO_BSEA 0x74 498c2ecf20Sopenharmony_ci#define AHB_GIZMO_NOR 0x78 508c2ecf20Sopenharmony_ci#define AHB_GIZMO_USB2 0x7c 518c2ecf20Sopenharmony_ci#define AHB_GIZMO_USB3 0x80 528c2ecf20Sopenharmony_ci#define IMMEDIATE BIT(18) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define AHB_GIZMO_SDMMC1 0x84 558c2ecf20Sopenharmony_ci#define AHB_GIZMO_SDMMC2 0x88 568c2ecf20Sopenharmony_ci#define AHB_GIZMO_SDMMC3 0x8c 578c2ecf20Sopenharmony_ci#define AHB_MEM_PREFETCH_CFG_X 0xdc 588c2ecf20Sopenharmony_ci#define AHB_ARBITRATION_XBAR_CTRL 0xe0 598c2ecf20Sopenharmony_ci#define AHB_MEM_PREFETCH_CFG3 0xe4 608c2ecf20Sopenharmony_ci#define AHB_MEM_PREFETCH_CFG4 0xe8 618c2ecf20Sopenharmony_ci#define AHB_MEM_PREFETCH_CFG1 0xf0 628c2ecf20Sopenharmony_ci#define AHB_MEM_PREFETCH_CFG2 0xf4 638c2ecf20Sopenharmony_ci#define PREFETCH_ENB BIT(31) 648c2ecf20Sopenharmony_ci#define MST_ID(x) (((x) & 0x1f) << 26) 658c2ecf20Sopenharmony_ci#define AHBDMA_MST_ID MST_ID(5) 668c2ecf20Sopenharmony_ci#define USB_MST_ID MST_ID(6) 678c2ecf20Sopenharmony_ci#define USB2_MST_ID MST_ID(18) 688c2ecf20Sopenharmony_ci#define USB3_MST_ID MST_ID(17) 698c2ecf20Sopenharmony_ci#define ADDR_BNDRY(x) (((x) & 0xf) << 21) 708c2ecf20Sopenharmony_ci#define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID 0xfc 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE BIT(17) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * INCORRECT_BASE_ADDR_LOW_BYTE: Legacy kernel DT files for Tegra SoCs 788c2ecf20Sopenharmony_ci * prior to Tegra124 generally use a physical base address ending in 798c2ecf20Sopenharmony_ci * 0x4 for the AHB IP block. According to the TRM, the low byte 808c2ecf20Sopenharmony_ci * should be 0x0. During device probing, this macro is used to detect 818c2ecf20Sopenharmony_ci * whether the passed-in physical address is incorrect, and if so, to 828c2ecf20Sopenharmony_ci * correct it. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci#define INCORRECT_BASE_ADDR_LOW_BYTE 0x4 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic struct platform_driver tegra_ahb_driver; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const u32 tegra_ahb_gizmo[] = { 898c2ecf20Sopenharmony_ci AHB_ARBITRATION_DISABLE, 908c2ecf20Sopenharmony_ci AHB_ARBITRATION_PRIORITY_CTRL, 918c2ecf20Sopenharmony_ci AHB_GIZMO_AHB_MEM, 928c2ecf20Sopenharmony_ci AHB_GIZMO_APB_DMA, 938c2ecf20Sopenharmony_ci AHB_GIZMO_IDE, 948c2ecf20Sopenharmony_ci AHB_GIZMO_USB, 958c2ecf20Sopenharmony_ci AHB_GIZMO_AHB_XBAR_BRIDGE, 968c2ecf20Sopenharmony_ci AHB_GIZMO_CPU_AHB_BRIDGE, 978c2ecf20Sopenharmony_ci AHB_GIZMO_COP_AHB_BRIDGE, 988c2ecf20Sopenharmony_ci AHB_GIZMO_XBAR_APB_CTLR, 998c2ecf20Sopenharmony_ci AHB_GIZMO_VCP_AHB_BRIDGE, 1008c2ecf20Sopenharmony_ci AHB_GIZMO_NAND, 1018c2ecf20Sopenharmony_ci AHB_GIZMO_SDMMC4, 1028c2ecf20Sopenharmony_ci AHB_GIZMO_XIO, 1038c2ecf20Sopenharmony_ci AHB_GIZMO_BSEV, 1048c2ecf20Sopenharmony_ci AHB_GIZMO_BSEA, 1058c2ecf20Sopenharmony_ci AHB_GIZMO_NOR, 1068c2ecf20Sopenharmony_ci AHB_GIZMO_USB2, 1078c2ecf20Sopenharmony_ci AHB_GIZMO_USB3, 1088c2ecf20Sopenharmony_ci AHB_GIZMO_SDMMC1, 1098c2ecf20Sopenharmony_ci AHB_GIZMO_SDMMC2, 1108c2ecf20Sopenharmony_ci AHB_GIZMO_SDMMC3, 1118c2ecf20Sopenharmony_ci AHB_MEM_PREFETCH_CFG_X, 1128c2ecf20Sopenharmony_ci AHB_ARBITRATION_XBAR_CTRL, 1138c2ecf20Sopenharmony_ci AHB_MEM_PREFETCH_CFG3, 1148c2ecf20Sopenharmony_ci AHB_MEM_PREFETCH_CFG4, 1158c2ecf20Sopenharmony_ci AHB_MEM_PREFETCH_CFG1, 1168c2ecf20Sopenharmony_ci AHB_MEM_PREFETCH_CFG2, 1178c2ecf20Sopenharmony_ci AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct tegra_ahb { 1218c2ecf20Sopenharmony_ci void __iomem *regs; 1228c2ecf20Sopenharmony_ci struct device *dev; 1238c2ecf20Sopenharmony_ci u32 ctx[]; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline u32 gizmo_readl(struct tegra_ahb *ahb, u32 offset) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return readl(ahb->regs + offset); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void gizmo_writel(struct tegra_ahb *ahb, u32 value, u32 offset) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci writel(value, ahb->regs + offset); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#ifdef CONFIG_TEGRA_IOMMU_SMMU 1378c2ecf20Sopenharmony_ciint tegra_ahb_enable_smmu(struct device_node *dn) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct device *dev; 1408c2ecf20Sopenharmony_ci u32 val; 1418c2ecf20Sopenharmony_ci struct tegra_ahb *ahb; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci dev = driver_find_device_by_of_node(&tegra_ahb_driver.driver, dn); 1448c2ecf20Sopenharmony_ci if (!dev) 1458c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1468c2ecf20Sopenharmony_ci ahb = dev_get_drvdata(dev); 1478c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_ARBITRATION_XBAR_CTRL); 1488c2ecf20Sopenharmony_ci val |= AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE; 1498c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_ARBITRATION_XBAR_CTRL); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_ahb_enable_smmu); 1538c2ecf20Sopenharmony_ci#endif 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_ahb_suspend(struct device *dev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int i; 1588c2ecf20Sopenharmony_ci struct tegra_ahb *ahb = dev_get_drvdata(dev); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++) 1618c2ecf20Sopenharmony_ci ahb->ctx[i] = gizmo_readl(ahb, tegra_ahb_gizmo[i]); 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_ahb_resume(struct device *dev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int i; 1688c2ecf20Sopenharmony_ci struct tegra_ahb *ahb = dev_get_drvdata(dev); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++) 1718c2ecf20Sopenharmony_ci gizmo_writel(ahb, ahb->ctx[i], tegra_ahb_gizmo[i]); 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic UNIVERSAL_DEV_PM_OPS(tegra_ahb_pm, 1768c2ecf20Sopenharmony_ci tegra_ahb_suspend, 1778c2ecf20Sopenharmony_ci tegra_ahb_resume, NULL); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void tegra_ahb_gizmo_init(struct tegra_ahb *ahb) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci u32 val; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_GIZMO_AHB_MEM); 1848c2ecf20Sopenharmony_ci val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR; 1858c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_GIZMO_AHB_MEM); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_GIZMO_USB); 1888c2ecf20Sopenharmony_ci val |= IMMEDIATE; 1898c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_GIZMO_USB); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_GIZMO_USB2); 1928c2ecf20Sopenharmony_ci val |= IMMEDIATE; 1938c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_GIZMO_USB2); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_GIZMO_USB3); 1968c2ecf20Sopenharmony_ci val |= IMMEDIATE; 1978c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_GIZMO_USB3); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_ARBITRATION_PRIORITY_CTRL); 2008c2ecf20Sopenharmony_ci val |= PRIORITY_SELECT_USB | 2018c2ecf20Sopenharmony_ci PRIORITY_SELECT_USB2 | 2028c2ecf20Sopenharmony_ci PRIORITY_SELECT_USB3 | 2038c2ecf20Sopenharmony_ci AHB_PRIORITY_WEIGHT(7); 2048c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_ARBITRATION_PRIORITY_CTRL); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG1); 2078c2ecf20Sopenharmony_ci val &= ~MST_ID(~0); 2088c2ecf20Sopenharmony_ci val |= PREFETCH_ENB | 2098c2ecf20Sopenharmony_ci AHBDMA_MST_ID | 2108c2ecf20Sopenharmony_ci ADDR_BNDRY(0xc) | 2118c2ecf20Sopenharmony_ci INACTIVITY_TIMEOUT(0x1000); 2128c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG1); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG2); 2158c2ecf20Sopenharmony_ci val &= ~MST_ID(~0); 2168c2ecf20Sopenharmony_ci val |= PREFETCH_ENB | 2178c2ecf20Sopenharmony_ci USB_MST_ID | 2188c2ecf20Sopenharmony_ci ADDR_BNDRY(0xc) | 2198c2ecf20Sopenharmony_ci INACTIVITY_TIMEOUT(0x1000); 2208c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG2); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG3); 2238c2ecf20Sopenharmony_ci val &= ~MST_ID(~0); 2248c2ecf20Sopenharmony_ci val |= PREFETCH_ENB | 2258c2ecf20Sopenharmony_ci USB3_MST_ID | 2268c2ecf20Sopenharmony_ci ADDR_BNDRY(0xc) | 2278c2ecf20Sopenharmony_ci INACTIVITY_TIMEOUT(0x1000); 2288c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG3); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG4); 2318c2ecf20Sopenharmony_ci val &= ~MST_ID(~0); 2328c2ecf20Sopenharmony_ci val |= PREFETCH_ENB | 2338c2ecf20Sopenharmony_ci USB2_MST_ID | 2348c2ecf20Sopenharmony_ci ADDR_BNDRY(0xc) | 2358c2ecf20Sopenharmony_ci INACTIVITY_TIMEOUT(0x1000); 2368c2ecf20Sopenharmony_ci gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG4); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int tegra_ahb_probe(struct platform_device *pdev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct resource *res; 2428c2ecf20Sopenharmony_ci struct tegra_ahb *ahb; 2438c2ecf20Sopenharmony_ci size_t bytes; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci bytes = sizeof(*ahb) + sizeof(u32) * ARRAY_SIZE(tegra_ahb_gizmo); 2468c2ecf20Sopenharmony_ci ahb = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL); 2478c2ecf20Sopenharmony_ci if (!ahb) 2488c2ecf20Sopenharmony_ci return -ENOMEM; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Correct the IP block base address if necessary */ 2538c2ecf20Sopenharmony_ci if (res && 2548c2ecf20Sopenharmony_ci (res->start & INCORRECT_BASE_ADDR_LOW_BYTE) == 2558c2ecf20Sopenharmony_ci INCORRECT_BASE_ADDR_LOW_BYTE) { 2568c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "incorrect AHB base address in DT data - enabling workaround\n"); 2578c2ecf20Sopenharmony_ci res->start -= INCORRECT_BASE_ADDR_LOW_BYTE; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ahb->regs = devm_ioremap_resource(&pdev->dev, res); 2618c2ecf20Sopenharmony_ci if (IS_ERR(ahb->regs)) 2628c2ecf20Sopenharmony_ci return PTR_ERR(ahb->regs); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ahb->dev = &pdev->dev; 2658c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ahb); 2668c2ecf20Sopenharmony_ci tegra_ahb_gizmo_init(ahb); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_ahb_of_match[] = { 2718c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-ahb", }, 2728c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-ahb", }, 2738c2ecf20Sopenharmony_ci {}, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic struct platform_driver tegra_ahb_driver = { 2778c2ecf20Sopenharmony_ci .probe = tegra_ahb_probe, 2788c2ecf20Sopenharmony_ci .driver = { 2798c2ecf20Sopenharmony_ci .name = DRV_NAME, 2808c2ecf20Sopenharmony_ci .of_match_table = tegra_ahb_of_match, 2818c2ecf20Sopenharmony_ci .pm = &tegra_ahb_pm, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_cimodule_platform_driver(tegra_ahb_driver); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); 2878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra AHB driver"); 2888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2898c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 290