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