flowctrl.c (5806B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * drivers/soc/tegra/flowctrl.c 4 * 5 * Functions and macros to control the flowcontroller 6 * 7 * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. 8 */ 9 10#include <linux/cpumask.h> 11#include <linux/init.h> 12#include <linux/io.h> 13#include <linux/kernel.h> 14#include <linux/of.h> 15#include <linux/of_address.h> 16#include <linux/platform_device.h> 17 18#include <soc/tegra/common.h> 19#include <soc/tegra/flowctrl.h> 20#include <soc/tegra/fuse.h> 21 22static u8 flowctrl_offset_halt_cpu[] = { 23 FLOW_CTRL_HALT_CPU0_EVENTS, 24 FLOW_CTRL_HALT_CPU1_EVENTS, 25 FLOW_CTRL_HALT_CPU1_EVENTS + 8, 26 FLOW_CTRL_HALT_CPU1_EVENTS + 16, 27}; 28 29static u8 flowctrl_offset_cpu_csr[] = { 30 FLOW_CTRL_CPU0_CSR, 31 FLOW_CTRL_CPU1_CSR, 32 FLOW_CTRL_CPU1_CSR + 8, 33 FLOW_CTRL_CPU1_CSR + 16, 34}; 35 36static void __iomem *tegra_flowctrl_base; 37 38static void flowctrl_update(u8 offset, u32 value) 39{ 40 if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), 41 "Tegra flowctrl not initialised!\n")) 42 return; 43 44 writel(value, tegra_flowctrl_base + offset); 45 46 /* ensure the update has reached the flow controller */ 47 wmb(); 48 readl_relaxed(tegra_flowctrl_base + offset); 49} 50 51u32 flowctrl_read_cpu_csr(unsigned int cpuid) 52{ 53 u8 offset = flowctrl_offset_cpu_csr[cpuid]; 54 55 if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base), 56 "Tegra flowctrl not initialised!\n")) 57 return 0; 58 59 return readl(tegra_flowctrl_base + offset); 60} 61 62void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) 63{ 64 return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value); 65} 66 67void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) 68{ 69 return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value); 70} 71 72void flowctrl_cpu_suspend_enter(unsigned int cpuid) 73{ 74 unsigned int reg; 75 int i; 76 77 reg = flowctrl_read_cpu_csr(cpuid); 78 switch (tegra_get_chip_id()) { 79 case TEGRA20: 80 /* clear wfe bitmap */ 81 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 82 /* clear wfi bitmap */ 83 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 84 /* pwr gating on wfe */ 85 reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; 86 break; 87 case TEGRA30: 88 case TEGRA114: 89 case TEGRA124: 90 /* clear wfe bitmap */ 91 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 92 /* clear wfi bitmap */ 93 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 94 95 if (tegra_get_chip_id() == TEGRA30) { 96 /* 97 * The wfi doesn't work well on Tegra30 because 98 * CPU hangs under some odd circumstances after 99 * power-gating (like memory running off PLLP), 100 * hence use wfe that is working perfectly fine. 101 * Note that Tegra30 TRM doc clearly stands that 102 * wfi should be used for the "Cluster Switching", 103 * while wfe for the power-gating, just like it 104 * is done on Tegra20. 105 */ 106 reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; 107 } else { 108 /* pwr gating on wfi */ 109 reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; 110 } 111 break; 112 } 113 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */ 114 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */ 115 reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */ 116 flowctrl_write_cpu_csr(cpuid, reg); 117 118 for (i = 0; i < num_possible_cpus(); i++) { 119 if (i == cpuid) 120 continue; 121 reg = flowctrl_read_cpu_csr(i); 122 reg |= FLOW_CTRL_CSR_EVENT_FLAG; 123 reg |= FLOW_CTRL_CSR_INTR_FLAG; 124 flowctrl_write_cpu_csr(i, reg); 125 } 126} 127 128void flowctrl_cpu_suspend_exit(unsigned int cpuid) 129{ 130 unsigned int reg; 131 132 /* Disable powergating via flow controller for CPU0 */ 133 reg = flowctrl_read_cpu_csr(cpuid); 134 switch (tegra_get_chip_id()) { 135 case TEGRA20: 136 /* clear wfe bitmap */ 137 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 138 /* clear wfi bitmap */ 139 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 140 break; 141 case TEGRA30: 142 case TEGRA114: 143 case TEGRA124: 144 /* clear wfe bitmap */ 145 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 146 /* clear wfi bitmap */ 147 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 148 break; 149 } 150 reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */ 151 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */ 152 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */ 153 flowctrl_write_cpu_csr(cpuid, reg); 154} 155 156static int tegra_flowctrl_probe(struct platform_device *pdev) 157{ 158 void __iomem *base = tegra_flowctrl_base; 159 struct resource *res; 160 161 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 162 tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res); 163 if (IS_ERR(tegra_flowctrl_base)) 164 return PTR_ERR(tegra_flowctrl_base); 165 166 iounmap(base); 167 168 return 0; 169} 170 171static const struct of_device_id tegra_flowctrl_match[] = { 172 { .compatible = "nvidia,tegra210-flowctrl" }, 173 { .compatible = "nvidia,tegra124-flowctrl" }, 174 { .compatible = "nvidia,tegra114-flowctrl" }, 175 { .compatible = "nvidia,tegra30-flowctrl" }, 176 { .compatible = "nvidia,tegra20-flowctrl" }, 177 { } 178}; 179 180static struct platform_driver tegra_flowctrl_driver = { 181 .driver = { 182 .name = "tegra-flowctrl", 183 .suppress_bind_attrs = true, 184 .of_match_table = tegra_flowctrl_match, 185 }, 186 .probe = tegra_flowctrl_probe, 187}; 188builtin_platform_driver(tegra_flowctrl_driver); 189 190static int __init tegra_flowctrl_init(void) 191{ 192 struct resource res; 193 struct device_node *np; 194 195 if (!soc_is_tegra()) 196 return 0; 197 198 np = of_find_matching_node(NULL, tegra_flowctrl_match); 199 if (np) { 200 if (of_address_to_resource(np, 0, &res) < 0) { 201 pr_err("failed to get flowctrl register\n"); 202 return -ENXIO; 203 } 204 of_node_put(np); 205 } else if (IS_ENABLED(CONFIG_ARM)) { 206 /* 207 * Hardcoded fallback for 32-bit Tegra 208 * devices if device tree node is missing. 209 */ 210 res.start = 0x60007000; 211 res.end = 0x60007fff; 212 res.flags = IORESOURCE_MEM; 213 } else { 214 /* 215 * At this point we're running on a Tegra, 216 * that doesn't support the flow controller 217 * (eg. Tegra186), so just return. 218 */ 219 return 0; 220 } 221 222 tegra_flowctrl_base = ioremap(res.start, resource_size(&res)); 223 if (!tegra_flowctrl_base) 224 return -ENXIO; 225 226 return 0; 227} 228early_initcall(tegra_flowctrl_init);