18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/soc/tegra/flowctrl.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Functions and macros to control the flowcontroller 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <soc/tegra/common.h> 198c2ecf20Sopenharmony_ci#include <soc/tegra/flowctrl.h> 208c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic u8 flowctrl_offset_halt_cpu[] = { 238c2ecf20Sopenharmony_ci FLOW_CTRL_HALT_CPU0_EVENTS, 248c2ecf20Sopenharmony_ci FLOW_CTRL_HALT_CPU1_EVENTS, 258c2ecf20Sopenharmony_ci FLOW_CTRL_HALT_CPU1_EVENTS + 8, 268c2ecf20Sopenharmony_ci FLOW_CTRL_HALT_CPU1_EVENTS + 16, 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic u8 flowctrl_offset_cpu_csr[] = { 308c2ecf20Sopenharmony_ci FLOW_CTRL_CPU0_CSR, 318c2ecf20Sopenharmony_ci FLOW_CTRL_CPU1_CSR, 328c2ecf20Sopenharmony_ci FLOW_CTRL_CPU1_CSR + 8, 338c2ecf20Sopenharmony_ci FLOW_CTRL_CPU1_CSR + 16, 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void __iomem *tegra_flowctrl_base; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void flowctrl_update(u8 offset, u32 value) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), 418c2ecf20Sopenharmony_ci "Tegra flowctrl not initialised!\n")) 428c2ecf20Sopenharmony_ci return; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci writel(value, tegra_flowctrl_base + offset); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* ensure the update has reached the flow controller */ 478c2ecf20Sopenharmony_ci wmb(); 488c2ecf20Sopenharmony_ci readl_relaxed(tegra_flowctrl_base + offset); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciu32 flowctrl_read_cpu_csr(unsigned int cpuid) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci u8 offset = flowctrl_offset_cpu_csr[cpuid]; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), 568c2ecf20Sopenharmony_ci "Tegra flowctrl not initialised!\n")) 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return readl(tegra_flowctrl_base + offset); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_civoid flowctrl_cpu_suspend_enter(unsigned int cpuid) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned int reg; 758c2ecf20Sopenharmony_ci int i; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci reg = flowctrl_read_cpu_csr(cpuid); 788c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 798c2ecf20Sopenharmony_ci case TEGRA20: 808c2ecf20Sopenharmony_ci /* clear wfe bitmap */ 818c2ecf20Sopenharmony_ci reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 828c2ecf20Sopenharmony_ci /* clear wfi bitmap */ 838c2ecf20Sopenharmony_ci reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 848c2ecf20Sopenharmony_ci /* pwr gating on wfe */ 858c2ecf20Sopenharmony_ci reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case TEGRA30: 888c2ecf20Sopenharmony_ci case TEGRA114: 898c2ecf20Sopenharmony_ci case TEGRA124: 908c2ecf20Sopenharmony_ci /* clear wfe bitmap */ 918c2ecf20Sopenharmony_ci reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 928c2ecf20Sopenharmony_ci /* clear wfi bitmap */ 938c2ecf20Sopenharmony_ci reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (tegra_get_chip_id() == TEGRA30) { 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * The wfi doesn't work well on Tegra30 because 988c2ecf20Sopenharmony_ci * CPU hangs under some odd circumstances after 998c2ecf20Sopenharmony_ci * power-gating (like memory running off PLLP), 1008c2ecf20Sopenharmony_ci * hence use wfe that is working perfectly fine. 1018c2ecf20Sopenharmony_ci * Note that Tegra30 TRM doc clearly stands that 1028c2ecf20Sopenharmony_ci * wfi should be used for the "Cluster Switching", 1038c2ecf20Sopenharmony_ci * while wfe for the power-gating, just like it 1048c2ecf20Sopenharmony_ci * is done on Tegra20. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; 1078c2ecf20Sopenharmony_ci } else { 1088c2ecf20Sopenharmony_ci /* pwr gating on wfi */ 1098c2ecf20Sopenharmony_ci reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */ 1148c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */ 1158c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */ 1168c2ecf20Sopenharmony_ci flowctrl_write_cpu_csr(cpuid, reg); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci for (i = 0; i < num_possible_cpus(); i++) { 1198c2ecf20Sopenharmony_ci if (i == cpuid) 1208c2ecf20Sopenharmony_ci continue; 1218c2ecf20Sopenharmony_ci reg = flowctrl_read_cpu_csr(i); 1228c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_EVENT_FLAG; 1238c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_INTR_FLAG; 1248c2ecf20Sopenharmony_ci flowctrl_write_cpu_csr(i, reg); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_civoid flowctrl_cpu_suspend_exit(unsigned int cpuid) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned int reg; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Disable powergating via flow controller for CPU0 */ 1338c2ecf20Sopenharmony_ci reg = flowctrl_read_cpu_csr(cpuid); 1348c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 1358c2ecf20Sopenharmony_ci case TEGRA20: 1368c2ecf20Sopenharmony_ci /* clear wfe bitmap */ 1378c2ecf20Sopenharmony_ci reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 1388c2ecf20Sopenharmony_ci /* clear wfi bitmap */ 1398c2ecf20Sopenharmony_ci reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case TEGRA30: 1428c2ecf20Sopenharmony_ci case TEGRA114: 1438c2ecf20Sopenharmony_ci case TEGRA124: 1448c2ecf20Sopenharmony_ci /* clear wfe bitmap */ 1458c2ecf20Sopenharmony_ci reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 1468c2ecf20Sopenharmony_ci /* clear wfi bitmap */ 1478c2ecf20Sopenharmony_ci reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */ 1518c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */ 1528c2ecf20Sopenharmony_ci reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */ 1538c2ecf20Sopenharmony_ci flowctrl_write_cpu_csr(cpuid, reg); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int tegra_flowctrl_probe(struct platform_device *pdev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci void __iomem *base = tegra_flowctrl_base; 1598c2ecf20Sopenharmony_ci struct resource *res; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1628c2ecf20Sopenharmony_ci tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res); 1638c2ecf20Sopenharmony_ci if (IS_ERR(tegra_flowctrl_base)) 1648c2ecf20Sopenharmony_ci return PTR_ERR(tegra_flowctrl_base); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci iounmap(base); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_flowctrl_match[] = { 1728c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra210-flowctrl" }, 1738c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra124-flowctrl" }, 1748c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra114-flowctrl" }, 1758c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-flowctrl" }, 1768c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-flowctrl" }, 1778c2ecf20Sopenharmony_ci { } 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct platform_driver tegra_flowctrl_driver = { 1818c2ecf20Sopenharmony_ci .driver = { 1828c2ecf20Sopenharmony_ci .name = "tegra-flowctrl", 1838c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 1848c2ecf20Sopenharmony_ci .of_match_table = tegra_flowctrl_match, 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci .probe = tegra_flowctrl_probe, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_cibuiltin_platform_driver(tegra_flowctrl_driver); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int __init tegra_flowctrl_init(void) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct resource res; 1938c2ecf20Sopenharmony_ci struct device_node *np; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!soc_is_tegra()) 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, tegra_flowctrl_match); 1998c2ecf20Sopenharmony_ci if (np) { 2008c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, &res) < 0) { 2018c2ecf20Sopenharmony_ci pr_err("failed to get flowctrl register\n"); 2028c2ecf20Sopenharmony_ci return -ENXIO; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci of_node_put(np); 2058c2ecf20Sopenharmony_ci } else if (IS_ENABLED(CONFIG_ARM)) { 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Hardcoded fallback for 32-bit Tegra 2088c2ecf20Sopenharmony_ci * devices if device tree node is missing. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci res.start = 0x60007000; 2118c2ecf20Sopenharmony_ci res.end = 0x60007fff; 2128c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * At this point we're running on a Tegra, 2168c2ecf20Sopenharmony_ci * that doesn't support the flow controller 2178c2ecf20Sopenharmony_ci * (eg. Tegra186), so just return. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci tegra_flowctrl_base = ioremap(res.start, resource_size(&res)); 2238c2ecf20Sopenharmony_ci if (!tegra_flowctrl_base) 2248c2ecf20Sopenharmony_ci return -ENXIO; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ciearly_initcall(tegra_flowctrl_init); 229