soc/tegra: Implement Tegra186 PMC support

The power management controller on Tegra186 has changed in backwards-
incompatible ways with respect to earlier generations. This implements a
new driver that supports inversion of the PMU interrupt as well as the
"recovery", "bootloader" and "forced-recovery" reboot commands.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Thierry Reding 2017-02-23 18:11:57 +01:00
parent c1ae3cfa0e
commit 5e7d4c6529
4 changed files with 218 additions and 1 deletions

View File

@ -0,0 +1,34 @@
NVIDIA Tegra Power Management Controller (PMC)
Required properties:
- compatible: Should contain one of the following:
- "nvidia,tegra186-pmc": for Tegra186
- reg: Must contain an (offset, length) pair of the register set for each
entry in reg-names.
- reg-names: Must include the following entries:
- "pmc"
- "wake"
- "aotag"
- "scratch"
Optional properties:
- nvidia,invert-interrupt: If present, inverts the PMU interrupt signal.
Example:
SoC DTSI:
pmc@c3600000 {
compatible = "nvidia,tegra186-pmc";
reg = <0 0x0c360000 0 0x10000>,
<0 0x0c370000 0 0x10000>,
<0 0x0c380000 0 0x10000>,
<0 0x0c390000 0 0x10000>;
reg-names = "pmc", "wake", "aotag", "scratch";
};
Board DTS:
pmc@c360000 {
nvidia,invert-interrupt;
};

View File

@ -12,6 +12,7 @@ config ARCH_TEGRA_2x_SOC
select PINCTRL_TEGRA20 select PINCTRL_TEGRA20
select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_727915 if CACHE_L2X0
select PL310_ERRATA_769419 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0
select SOC_TEGRA_PMC
select TEGRA_TIMER select TEGRA_TIMER
help help
Support for NVIDIA Tegra AP20 and T20 processors, based on the Support for NVIDIA Tegra AP20 and T20 processors, based on the
@ -23,6 +24,7 @@ config ARCH_TEGRA_3x_SOC
select ARM_ERRATA_764369 if SMP select ARM_ERRATA_764369 if SMP
select PINCTRL_TEGRA30 select PINCTRL_TEGRA30
select PL310_ERRATA_769419 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0
select SOC_TEGRA_PMC
select TEGRA_TIMER select TEGRA_TIMER
help help
Support for NVIDIA Tegra T30 processor family, based on the Support for NVIDIA Tegra T30 processor family, based on the
@ -33,6 +35,7 @@ config ARCH_TEGRA_114_SOC
select ARM_ERRATA_798181 if SMP select ARM_ERRATA_798181 if SMP
select HAVE_ARM_ARCH_TIMER select HAVE_ARM_ARCH_TIMER
select PINCTRL_TEGRA114 select PINCTRL_TEGRA114
select SOC_TEGRA_PMC
select TEGRA_TIMER select TEGRA_TIMER
help help
Support for NVIDIA Tegra T114 processor family, based on the Support for NVIDIA Tegra T114 processor family, based on the
@ -42,6 +45,7 @@ config ARCH_TEGRA_124_SOC
bool "Enable support for Tegra124 family" bool "Enable support for Tegra124 family"
select HAVE_ARM_ARCH_TIMER select HAVE_ARM_ARCH_TIMER
select PINCTRL_TEGRA124 select PINCTRL_TEGRA124
select SOC_TEGRA_PMC
select TEGRA_TIMER select TEGRA_TIMER
help help
Support for NVIDIA Tegra T124 processor family, based on the Support for NVIDIA Tegra T124 processor family, based on the
@ -55,6 +59,7 @@ if ARM64
config ARCH_TEGRA_132_SOC config ARCH_TEGRA_132_SOC
bool "NVIDIA Tegra132 SoC" bool "NVIDIA Tegra132 SoC"
select PINCTRL_TEGRA124 select PINCTRL_TEGRA124
select SOC_TEGRA_PMC
help help
Enable support for NVIDIA Tegra132 SoC, based on the Denver Enable support for NVIDIA Tegra132 SoC, based on the Denver
ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC, ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC,
@ -64,6 +69,7 @@ config ARCH_TEGRA_132_SOC
config ARCH_TEGRA_210_SOC config ARCH_TEGRA_210_SOC
bool "NVIDIA Tegra210 SoC" bool "NVIDIA Tegra210 SoC"
select PINCTRL_TEGRA210 select PINCTRL_TEGRA210
select SOC_TEGRA_PMC
help help
Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1,
the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53 the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53
@ -83,6 +89,7 @@ config ARCH_TEGRA_186_SOC
select TEGRA_BPMP select TEGRA_BPMP
select TEGRA_HSP_MBOX select TEGRA_HSP_MBOX
select TEGRA_IVC select TEGRA_IVC
select SOC_TEGRA_PMC_TEGRA186
help help
Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a
combination of Denver and Cortex-A57 CPU cores and a GPU based on combination of Denver and Cortex-A57 CPU cores and a GPU based on
@ -93,3 +100,9 @@ config ARCH_TEGRA_186_SOC
endif endif
endif endif
config SOC_TEGRA_PMC
bool
config SOC_TEGRA_PMC_TEGRA186
bool

View File

@ -1,4 +1,5 @@
obj-y += fuse/ obj-y += fuse/
obj-y += common.o obj-y += common.o
obj-y += pmc.o obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#define pr_fmt(fmt) "tegra-pmc: " fmt
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <asm/system_misc.h>
#define PMC_CNTRL 0x000
#define PMC_CNTRL_MAIN_RST BIT(4)
#define PMC_RST_STATUS 0x070
#define WAKE_AOWAKE_CTRL 0x4f4
#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0)
#define SCRATCH_SCRATCH0 0x2000
#define SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31)
#define SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30)
#define SCRATCH_SCRATCH0_MODE_RCM BIT(1)
#define SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \
SCRATCH_SCRATCH0_MODE_BOOTLOADER | \
SCRATCH_SCRATCH0_MODE_RCM)
struct tegra_pmc {
struct device *dev;
void __iomem *regs;
void __iomem *wake;
void __iomem *aotag;
void __iomem *scratch;
void (*system_restart)(enum reboot_mode mode, const char *cmd);
struct notifier_block restart;
};
static int tegra186_pmc_restart_notify(struct notifier_block *nb,
unsigned long action,
void *data)
{
struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart);
const char *cmd = data;
u32 value;
value = readl(pmc->scratch + SCRATCH_SCRATCH0);
value &= ~SCRATCH_SCRATCH0_MODE_MASK;
if (cmd) {
if (strcmp(cmd, "recovery") == 0)
value |= SCRATCH_SCRATCH0_MODE_RECOVERY;
if (strcmp(cmd, "bootloader") == 0)
value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER;
if (strcmp(cmd, "forced-recovery") == 0)
value |= SCRATCH_SCRATCH0_MODE_RCM;
}
writel(value, pmc->scratch + SCRATCH_SCRATCH0);
/*
* If available, call the system restart implementation that was
* registered earlier (typically PSCI).
*/
if (pmc->system_restart) {
pmc->system_restart(reboot_mode, cmd);
return NOTIFY_DONE;
}
/* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */
value = readl(pmc->regs + PMC_CNTRL);
value |= PMC_CNTRL_MAIN_RST;
writel(value, pmc->regs + PMC_CNTRL);
return NOTIFY_DONE;
}
static int tegra186_pmc_setup(struct tegra_pmc *pmc)
{
struct device_node *np = pmc->dev->of_node;
bool invert;
u32 value;
invert = of_property_read_bool(np, "nvidia,invert-interrupt");
value = readl(pmc->wake + WAKE_AOWAKE_CTRL);
if (invert)
value |= WAKE_AOWAKE_CTRL_INTR_POLARITY;
else
value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY;
writel(value, pmc->wake + WAKE_AOWAKE_CTRL);
/*
* We need to hook any system restart implementation registered
* previously so we can write SCRATCH_SCRATCH0 before reset.
*/
pmc->system_restart = arm_pm_restart;
arm_pm_restart = NULL;
pmc->restart.notifier_call = tegra186_pmc_restart_notify;
pmc->restart.priority = 128;
return register_restart_handler(&pmc->restart);
}
static int tegra186_pmc_probe(struct platform_device *pdev)
{
struct tegra_pmc *pmc;
struct resource *res;
pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return -ENOMEM;
pmc->dev = &pdev->dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc");
pmc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->regs))
return PTR_ERR(pmc->regs);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
pmc->wake = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->wake))
return PTR_ERR(pmc->wake);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->aotag))
return PTR_ERR(pmc->aotag);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->scratch))
return PTR_ERR(pmc->scratch);
return tegra186_pmc_setup(pmc);
}
static const struct of_device_id tegra186_pmc_of_match[] = {
{ .compatible = "nvidia,tegra186-pmc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match);
static struct platform_driver tegra186_pmc_driver = {
.driver = {
.name = "tegra186-pmc",
.of_match_table = tegra186_pmc_of_match,
},
.probe = tegra186_pmc_probe,
};
builtin_platform_driver(tegra186_pmc_driver);