1/* 2 * arch/arm/mach-tegra/flowctrl.c 3 * 4 * functions and macros to control the flowcontroller 5 * 6 * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include <linux/cpumask.h> 22#include <linux/init.h> 23#include <linux/io.h> 24#include <linux/kernel.h> 25#include <linux/of.h> 26#include <linux/of_address.h> 27 28#include <soc/tegra/fuse.h> 29 30#include "flowctrl.h" 31 32static u8 flowctrl_offset_halt_cpu[] = { 33 FLOW_CTRL_HALT_CPU0_EVENTS, 34 FLOW_CTRL_HALT_CPU1_EVENTS, 35 FLOW_CTRL_HALT_CPU1_EVENTS + 8, 36 FLOW_CTRL_HALT_CPU1_EVENTS + 16, 37}; 38 39static u8 flowctrl_offset_cpu_csr[] = { 40 FLOW_CTRL_CPU0_CSR, 41 FLOW_CTRL_CPU1_CSR, 42 FLOW_CTRL_CPU1_CSR + 8, 43 FLOW_CTRL_CPU1_CSR + 16, 44}; 45 46static void __iomem *tegra_flowctrl_base; 47 48static void flowctrl_update(u8 offset, u32 value) 49{ 50 writel(value, tegra_flowctrl_base + offset); 51 52 /* ensure the update has reached the flow controller */ 53 wmb(); 54 readl_relaxed(tegra_flowctrl_base + offset); 55} 56 57u32 flowctrl_read_cpu_csr(unsigned int cpuid) 58{ 59 u8 offset = flowctrl_offset_cpu_csr[cpuid]; 60 61 return readl(tegra_flowctrl_base + offset); 62} 63 64void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) 65{ 66 return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value); 67} 68 69void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) 70{ 71 return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value); 72} 73 74void flowctrl_cpu_suspend_enter(unsigned int cpuid) 75{ 76 unsigned int reg; 77 int i; 78 79 reg = flowctrl_read_cpu_csr(cpuid); 80 switch (tegra_get_chip_id()) { 81 case TEGRA20: 82 /* clear wfe bitmap */ 83 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 84 /* clear wfi bitmap */ 85 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 86 /* pwr gating on wfe */ 87 reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; 88 break; 89 case TEGRA30: 90 case TEGRA114: 91 case TEGRA124: 92 /* clear wfe bitmap */ 93 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 94 /* clear wfi bitmap */ 95 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 96 /* pwr gating on wfi */ 97 reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; 98 break; 99 } 100 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */ 101 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */ 102 reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */ 103 flowctrl_write_cpu_csr(cpuid, reg); 104 105 for (i = 0; i < num_possible_cpus(); i++) { 106 if (i == cpuid) 107 continue; 108 reg = flowctrl_read_cpu_csr(i); 109 reg |= FLOW_CTRL_CSR_EVENT_FLAG; 110 reg |= FLOW_CTRL_CSR_INTR_FLAG; 111 flowctrl_write_cpu_csr(i, reg); 112 } 113} 114 115void flowctrl_cpu_suspend_exit(unsigned int cpuid) 116{ 117 unsigned int reg; 118 119 /* Disable powergating via flow controller for CPU0 */ 120 reg = flowctrl_read_cpu_csr(cpuid); 121 switch (tegra_get_chip_id()) { 122 case TEGRA20: 123 /* clear wfe bitmap */ 124 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; 125 /* clear wfi bitmap */ 126 reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; 127 break; 128 case TEGRA30: 129 case TEGRA114: 130 case TEGRA124: 131 /* clear wfe bitmap */ 132 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; 133 /* clear wfi bitmap */ 134 reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; 135 break; 136 } 137 reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */ 138 reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */ 139 reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */ 140 flowctrl_write_cpu_csr(cpuid, reg); 141} 142 143static const struct of_device_id matches[] __initconst = { 144 { .compatible = "nvidia,tegra124-flowctrl" }, 145 { .compatible = "nvidia,tegra114-flowctrl" }, 146 { .compatible = "nvidia,tegra30-flowctrl" }, 147 { .compatible = "nvidia,tegra20-flowctrl" }, 148 { } 149}; 150 151void __init tegra_flowctrl_init(void) 152{ 153 /* hardcoded fallback if device tree node is missing */ 154 unsigned long base = 0x60007000; 155 unsigned long size = SZ_4K; 156 struct device_node *np; 157 158 np = of_find_matching_node(NULL, matches); 159 if (np) { 160 struct resource res; 161 162 if (of_address_to_resource(np, 0, &res) == 0) { 163 size = resource_size(&res); 164 base = res.start; 165 } 166 167 of_node_put(np); 168 } 169 170 tegra_flowctrl_base = ioremap_nocache(base, size); 171} 172