xref: /openbmc/linux/drivers/soc/tegra/flowctrl.c (revision 42c67aa2)
146fe7771SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27e10cf74SJon Hunter /*
37e10cf74SJon Hunter  * drivers/soc/tegra/flowctrl.c
47e10cf74SJon Hunter  *
57e10cf74SJon Hunter  * Functions and macros to control the flowcontroller
67e10cf74SJon Hunter  *
77e10cf74SJon Hunter  * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
87e10cf74SJon Hunter  */
97e10cf74SJon Hunter 
107e10cf74SJon Hunter #include <linux/cpumask.h>
117e10cf74SJon Hunter #include <linux/init.h>
127e10cf74SJon Hunter #include <linux/io.h>
137e10cf74SJon Hunter #include <linux/kernel.h>
147e10cf74SJon Hunter #include <linux/of.h>
157e10cf74SJon Hunter #include <linux/of_address.h>
16841fd94cSJon Hunter #include <linux/platform_device.h>
177e10cf74SJon Hunter 
187e10cf74SJon Hunter #include <soc/tegra/common.h>
197e10cf74SJon Hunter #include <soc/tegra/flowctrl.h>
207e10cf74SJon Hunter #include <soc/tegra/fuse.h>
217e10cf74SJon Hunter 
227e10cf74SJon Hunter static u8 flowctrl_offset_halt_cpu[] = {
237e10cf74SJon Hunter 	FLOW_CTRL_HALT_CPU0_EVENTS,
247e10cf74SJon Hunter 	FLOW_CTRL_HALT_CPU1_EVENTS,
257e10cf74SJon Hunter 	FLOW_CTRL_HALT_CPU1_EVENTS + 8,
267e10cf74SJon Hunter 	FLOW_CTRL_HALT_CPU1_EVENTS + 16,
277e10cf74SJon Hunter };
287e10cf74SJon Hunter 
297e10cf74SJon Hunter static u8 flowctrl_offset_cpu_csr[] = {
307e10cf74SJon Hunter 	FLOW_CTRL_CPU0_CSR,
317e10cf74SJon Hunter 	FLOW_CTRL_CPU1_CSR,
327e10cf74SJon Hunter 	FLOW_CTRL_CPU1_CSR + 8,
337e10cf74SJon Hunter 	FLOW_CTRL_CPU1_CSR + 16,
347e10cf74SJon Hunter };
357e10cf74SJon Hunter 
367e10cf74SJon Hunter static void __iomem *tegra_flowctrl_base;
377e10cf74SJon Hunter 
flowctrl_update(u8 offset,u32 value)387e10cf74SJon Hunter static void flowctrl_update(u8 offset, u32 value)
397e10cf74SJon Hunter {
40841fd94cSJon Hunter 	if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),
417e10cf74SJon Hunter 		      "Tegra flowctrl not initialised!\n"))
427e10cf74SJon Hunter 		return;
437e10cf74SJon Hunter 
447e10cf74SJon Hunter 	writel(value, tegra_flowctrl_base + offset);
457e10cf74SJon Hunter 
467e10cf74SJon Hunter 	/* ensure the update has reached the flow controller */
477e10cf74SJon Hunter 	wmb();
487e10cf74SJon Hunter 	readl_relaxed(tegra_flowctrl_base + offset);
497e10cf74SJon Hunter }
507e10cf74SJon Hunter 
flowctrl_read_cpu_csr(unsigned int cpuid)517e10cf74SJon Hunter u32 flowctrl_read_cpu_csr(unsigned int cpuid)
527e10cf74SJon Hunter {
537e10cf74SJon Hunter 	u8 offset = flowctrl_offset_cpu_csr[cpuid];
547e10cf74SJon Hunter 
55841fd94cSJon Hunter 	if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),
567e10cf74SJon Hunter 		      "Tegra flowctrl not initialised!\n"))
577e10cf74SJon Hunter 		return 0;
587e10cf74SJon Hunter 
597e10cf74SJon Hunter 	return readl(tegra_flowctrl_base + offset);
607e10cf74SJon Hunter }
617e10cf74SJon Hunter 
flowctrl_write_cpu_csr(unsigned int cpuid,u32 value)627e10cf74SJon Hunter void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value)
637e10cf74SJon Hunter {
647e10cf74SJon Hunter 	return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);
657e10cf74SJon Hunter }
667e10cf74SJon Hunter 
flowctrl_write_cpu_halt(unsigned int cpuid,u32 value)677e10cf74SJon Hunter void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value)
687e10cf74SJon Hunter {
697e10cf74SJon Hunter 	return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);
707e10cf74SJon Hunter }
717e10cf74SJon Hunter 
flowctrl_cpu_suspend_enter(unsigned int cpuid)727e10cf74SJon Hunter void flowctrl_cpu_suspend_enter(unsigned int cpuid)
737e10cf74SJon Hunter {
747e10cf74SJon Hunter 	unsigned int reg;
757e10cf74SJon Hunter 	int i;
767e10cf74SJon Hunter 
777e10cf74SJon Hunter 	reg = flowctrl_read_cpu_csr(cpuid);
787e10cf74SJon Hunter 	switch (tegra_get_chip_id()) {
797e10cf74SJon Hunter 	case TEGRA20:
807e10cf74SJon Hunter 		/* clear wfe bitmap */
817e10cf74SJon Hunter 		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
827e10cf74SJon Hunter 		/* clear wfi bitmap */
837e10cf74SJon Hunter 		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
847e10cf74SJon Hunter 		/* pwr gating on wfe */
857e10cf74SJon Hunter 		reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid;
867e10cf74SJon Hunter 		break;
877e10cf74SJon Hunter 	case TEGRA30:
887e10cf74SJon Hunter 	case TEGRA114:
897e10cf74SJon Hunter 	case TEGRA124:
907e10cf74SJon Hunter 		/* clear wfe bitmap */
917e10cf74SJon Hunter 		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
927e10cf74SJon Hunter 		/* clear wfi bitmap */
937e10cf74SJon Hunter 		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
9491d7ff5aSDmitry Osipenko 
9591d7ff5aSDmitry Osipenko 		if (tegra_get_chip_id() == TEGRA30) {
9691d7ff5aSDmitry Osipenko 			/*
9791d7ff5aSDmitry Osipenko 			 * The wfi doesn't work well on Tegra30 because
9891d7ff5aSDmitry Osipenko 			 * CPU hangs under some odd circumstances after
9991d7ff5aSDmitry Osipenko 			 * power-gating (like memory running off PLLP),
10091d7ff5aSDmitry Osipenko 			 * hence use wfe that is working perfectly fine.
10191d7ff5aSDmitry Osipenko 			 * Note that Tegra30 TRM doc clearly stands that
10291d7ff5aSDmitry Osipenko 			 * wfi should be used for the "Cluster Switching",
10391d7ff5aSDmitry Osipenko 			 * while wfe for the power-gating, just like it
10491d7ff5aSDmitry Osipenko 			 * is done on Tegra20.
10591d7ff5aSDmitry Osipenko 			 */
10691d7ff5aSDmitry Osipenko 			reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid;
10791d7ff5aSDmitry Osipenko 		} else {
1087e10cf74SJon Hunter 			/* pwr gating on wfi */
1097e10cf74SJon Hunter 			reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid;
11091d7ff5aSDmitry Osipenko 		}
1117e10cf74SJon Hunter 		break;
1127e10cf74SJon Hunter 	}
1137e10cf74SJon Hunter 	reg |= FLOW_CTRL_CSR_INTR_FLAG;			/* clear intr flag */
1147e10cf74SJon Hunter 	reg |= FLOW_CTRL_CSR_EVENT_FLAG;		/* clear event flag */
1157e10cf74SJon Hunter 	reg |= FLOW_CTRL_CSR_ENABLE;			/* pwr gating */
1167e10cf74SJon Hunter 	flowctrl_write_cpu_csr(cpuid, reg);
1177e10cf74SJon Hunter 
1187e10cf74SJon Hunter 	for (i = 0; i < num_possible_cpus(); i++) {
1197e10cf74SJon Hunter 		if (i == cpuid)
1207e10cf74SJon Hunter 			continue;
1217e10cf74SJon Hunter 		reg = flowctrl_read_cpu_csr(i);
1227e10cf74SJon Hunter 		reg |= FLOW_CTRL_CSR_EVENT_FLAG;
1237e10cf74SJon Hunter 		reg |= FLOW_CTRL_CSR_INTR_FLAG;
1247e10cf74SJon Hunter 		flowctrl_write_cpu_csr(i, reg);
1257e10cf74SJon Hunter 	}
1267e10cf74SJon Hunter }
1277e10cf74SJon Hunter 
flowctrl_cpu_suspend_exit(unsigned int cpuid)1287e10cf74SJon Hunter void flowctrl_cpu_suspend_exit(unsigned int cpuid)
1297e10cf74SJon Hunter {
1307e10cf74SJon Hunter 	unsigned int reg;
1317e10cf74SJon Hunter 
1327e10cf74SJon Hunter 	/* Disable powergating via flow controller for CPU0 */
1337e10cf74SJon Hunter 	reg = flowctrl_read_cpu_csr(cpuid);
1347e10cf74SJon Hunter 	switch (tegra_get_chip_id()) {
1357e10cf74SJon Hunter 	case TEGRA20:
1367e10cf74SJon Hunter 		/* clear wfe bitmap */
1377e10cf74SJon Hunter 		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
1387e10cf74SJon Hunter 		/* clear wfi bitmap */
1397e10cf74SJon Hunter 		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
1407e10cf74SJon Hunter 		break;
1417e10cf74SJon Hunter 	case TEGRA30:
1427e10cf74SJon Hunter 	case TEGRA114:
1437e10cf74SJon Hunter 	case TEGRA124:
1447e10cf74SJon Hunter 		/* clear wfe bitmap */
1457e10cf74SJon Hunter 		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;
1467e10cf74SJon Hunter 		/* clear wfi bitmap */
1477e10cf74SJon Hunter 		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
1487e10cf74SJon Hunter 		break;
1497e10cf74SJon Hunter 	}
1507e10cf74SJon Hunter 	reg &= ~FLOW_CTRL_CSR_ENABLE;			/* clear enable */
1517e10cf74SJon Hunter 	reg |= FLOW_CTRL_CSR_INTR_FLAG;			/* clear intr */
1527e10cf74SJon Hunter 	reg |= FLOW_CTRL_CSR_EVENT_FLAG;		/* clear event */
1537e10cf74SJon Hunter 	flowctrl_write_cpu_csr(cpuid, reg);
1547e10cf74SJon Hunter }
1557e10cf74SJon Hunter 
tegra_flowctrl_probe(struct platform_device * pdev)156841fd94cSJon Hunter static int tegra_flowctrl_probe(struct platform_device *pdev)
157841fd94cSJon Hunter {
158841fd94cSJon Hunter 	void __iomem *base = tegra_flowctrl_base;
159841fd94cSJon Hunter 
160*42c67aa2SYe Xingchen 	tegra_flowctrl_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
161841fd94cSJon Hunter 	if (IS_ERR(tegra_flowctrl_base))
162da1dbec1SChristophe Jaillet 		return PTR_ERR(tegra_flowctrl_base);
163841fd94cSJon Hunter 
164841fd94cSJon Hunter 	iounmap(base);
165841fd94cSJon Hunter 
166841fd94cSJon Hunter 	return 0;
167841fd94cSJon Hunter }
168841fd94cSJon Hunter 
169841fd94cSJon Hunter static const struct of_device_id tegra_flowctrl_match[] = {
1701fd09e5dSJon Hunter 	{ .compatible = "nvidia,tegra210-flowctrl" },
1717e10cf74SJon Hunter 	{ .compatible = "nvidia,tegra124-flowctrl" },
1727e10cf74SJon Hunter 	{ .compatible = "nvidia,tegra114-flowctrl" },
1737e10cf74SJon Hunter 	{ .compatible = "nvidia,tegra30-flowctrl" },
1747e10cf74SJon Hunter 	{ .compatible = "nvidia,tegra20-flowctrl" },
1757e10cf74SJon Hunter 	{ }
1767e10cf74SJon Hunter };
1777e10cf74SJon Hunter 
178841fd94cSJon Hunter static struct platform_driver tegra_flowctrl_driver = {
179841fd94cSJon Hunter 	.driver = {
180841fd94cSJon Hunter 		.name = "tegra-flowctrl",
181841fd94cSJon Hunter 		.suppress_bind_attrs = true,
182841fd94cSJon Hunter 		.of_match_table = tegra_flowctrl_match,
183841fd94cSJon Hunter 	},
184841fd94cSJon Hunter 	.probe = tegra_flowctrl_probe,
185841fd94cSJon Hunter };
186841fd94cSJon Hunter builtin_platform_driver(tegra_flowctrl_driver);
187841fd94cSJon Hunter 
tegra_flowctrl_init(void)1887e10cf74SJon Hunter static int __init tegra_flowctrl_init(void)
1897e10cf74SJon Hunter {
1901fd09e5dSJon Hunter 	struct resource res;
1917e10cf74SJon Hunter 	struct device_node *np;
1927e10cf74SJon Hunter 
1937e10cf74SJon Hunter 	if (!soc_is_tegra())
1947e10cf74SJon Hunter 		return 0;
1957e10cf74SJon Hunter 
196841fd94cSJon Hunter 	np = of_find_matching_node(NULL, tegra_flowctrl_match);
1977e10cf74SJon Hunter 	if (np) {
1981fd09e5dSJon Hunter 		if (of_address_to_resource(np, 0, &res) < 0) {
1991fd09e5dSJon Hunter 			pr_err("failed to get flowctrl register\n");
2001fd09e5dSJon Hunter 			return -ENXIO;
2017e10cf74SJon Hunter 		}
2027e10cf74SJon Hunter 		of_node_put(np);
2031fd09e5dSJon Hunter 	} else if (IS_ENABLED(CONFIG_ARM)) {
2041fd09e5dSJon Hunter 		/*
2051fd09e5dSJon Hunter 		 * Hardcoded fallback for 32-bit Tegra
2061fd09e5dSJon Hunter 		 * devices if device tree node is missing.
2071fd09e5dSJon Hunter 		 */
2081fd09e5dSJon Hunter 		res.start = 0x60007000;
2091fd09e5dSJon Hunter 		res.end = 0x60007fff;
2101fd09e5dSJon Hunter 		res.flags = IORESOURCE_MEM;
2111fd09e5dSJon Hunter 	} else {
2121fd09e5dSJon Hunter 		/*
2131fd09e5dSJon Hunter 		 * At this point we're running on a Tegra,
2141fd09e5dSJon Hunter 		 * that doesn't support the flow controller
2151fd09e5dSJon Hunter 		 * (eg. Tegra186), so just return.
2161fd09e5dSJon Hunter 		 */
2171fd09e5dSJon Hunter 		return 0;
2187e10cf74SJon Hunter 	}
2197e10cf74SJon Hunter 
2204bdc0d67SChristoph Hellwig 	tegra_flowctrl_base = ioremap(res.start, resource_size(&res));
2217e10cf74SJon Hunter 	if (!tegra_flowctrl_base)
2227e10cf74SJon Hunter 		return -ENXIO;
2237e10cf74SJon Hunter 
2247e10cf74SJon Hunter 	return 0;
2257e10cf74SJon Hunter }
2267e10cf74SJon Hunter early_initcall(tegra_flowctrl_init);
227