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