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