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