Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+218 -1
+34
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra186-pmc.txt
··· 1 + NVIDIA Tegra Power Management Controller (PMC) 2 + 3 + Required properties: 4 + - compatible: Should contain one of the following: 5 + - "nvidia,tegra186-pmc": for Tegra186 6 + - reg: Must contain an (offset, length) pair of the register set for each 7 + entry in reg-names. 8 + - reg-names: Must include the following entries: 9 + - "pmc" 10 + - "wake" 11 + - "aotag" 12 + - "scratch" 13 + 14 + Optional properties: 15 + - nvidia,invert-interrupt: If present, inverts the PMU interrupt signal. 16 + 17 + Example: 18 + 19 + SoC DTSI: 20 + 21 + pmc@c3600000 { 22 + compatible = "nvidia,tegra186-pmc"; 23 + reg = <0 0x0c360000 0 0x10000>, 24 + <0 0x0c370000 0 0x10000>, 25 + <0 0x0c380000 0 0x10000>, 26 + <0 0x0c390000 0 0x10000>; 27 + reg-names = "pmc", "wake", "aotag", "scratch"; 28 + }; 29 + 30 + Board DTS: 31 + 32 + pmc@c360000 { 33 + nvidia,invert-interrupt; 34 + };
+13
drivers/soc/tegra/Kconfig
··· 12 12 select PINCTRL_TEGRA20 13 13 select PL310_ERRATA_727915 if CACHE_L2X0 14 14 select PL310_ERRATA_769419 if CACHE_L2X0 15 + select SOC_TEGRA_PMC 15 16 select TEGRA_TIMER 16 17 help 17 18 Support for NVIDIA Tegra AP20 and T20 processors, based on the ··· 24 23 select ARM_ERRATA_764369 if SMP 25 24 select PINCTRL_TEGRA30 26 25 select PL310_ERRATA_769419 if CACHE_L2X0 26 + select SOC_TEGRA_PMC 27 27 select TEGRA_TIMER 28 28 help 29 29 Support for NVIDIA Tegra T30 processor family, based on the ··· 35 33 select ARM_ERRATA_798181 if SMP 36 34 select HAVE_ARM_ARCH_TIMER 37 35 select PINCTRL_TEGRA114 36 + select SOC_TEGRA_PMC 38 37 select TEGRA_TIMER 39 38 help 40 39 Support for NVIDIA Tegra T114 processor family, based on the ··· 45 42 bool "Enable support for Tegra124 family" 46 43 select HAVE_ARM_ARCH_TIMER 47 44 select PINCTRL_TEGRA124 45 + select SOC_TEGRA_PMC 48 46 select TEGRA_TIMER 49 47 help 50 48 Support for NVIDIA Tegra T124 processor family, based on the ··· 59 55 config ARCH_TEGRA_132_SOC 60 56 bool "NVIDIA Tegra132 SoC" 61 57 select PINCTRL_TEGRA124 58 + select SOC_TEGRA_PMC 62 59 help 63 60 Enable support for NVIDIA Tegra132 SoC, based on the Denver 64 61 ARMv8 CPU. The Tegra132 SoC is similar to the Tegra124 SoC, ··· 69 64 config ARCH_TEGRA_210_SOC 70 65 bool "NVIDIA Tegra210 SoC" 71 66 select PINCTRL_TEGRA210 67 + select SOC_TEGRA_PMC 72 68 help 73 69 Enable support for the NVIDIA Tegra210 SoC. Also known as Tegra X1, 74 70 the Tegra210 has four Cortex-A57 cores paired with four Cortex-A53 ··· 89 83 select TEGRA_BPMP 90 84 select TEGRA_HSP_MBOX 91 85 select TEGRA_IVC 86 + select SOC_TEGRA_PMC_TEGRA186 92 87 help 93 88 Enable support for the NVIDIA Tegar186 SoC. The Tegra186 features a 94 89 combination of Denver and Cortex-A57 CPU cores and a GPU based on ··· 100 93 101 94 endif 102 95 endif 96 + 97 + config SOC_TEGRA_PMC 98 + bool 99 + 100 + config SOC_TEGRA_PMC_TEGRA186 101 + bool
+2 -1
drivers/soc/tegra/Makefile
··· 1 1 obj-y += fuse/ 2 2 3 3 obj-y += common.o 4 - obj-y += pmc.o 4 + obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o 5 + obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
+169
drivers/soc/tegra/pmc-tegra186.c
··· 1 + /* 2 + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify it 5 + * under the terms and conditions of the GNU General Public License, 6 + * version 2, as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope it will be useful, but WITHOUT 9 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 + * more details. 12 + */ 13 + 14 + #define pr_fmt(fmt) "tegra-pmc: " fmt 15 + 16 + #include <linux/io.h> 17 + #include <linux/module.h> 18 + #include <linux/of.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/reboot.h> 21 + 22 + #include <asm/system_misc.h> 23 + 24 + #define PMC_CNTRL 0x000 25 + #define PMC_CNTRL_MAIN_RST BIT(4) 26 + 27 + #define PMC_RST_STATUS 0x070 28 + 29 + #define WAKE_AOWAKE_CTRL 0x4f4 30 + #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) 31 + 32 + #define SCRATCH_SCRATCH0 0x2000 33 + #define SCRATCH_SCRATCH0_MODE_RECOVERY BIT(31) 34 + #define SCRATCH_SCRATCH0_MODE_BOOTLOADER BIT(30) 35 + #define SCRATCH_SCRATCH0_MODE_RCM BIT(1) 36 + #define SCRATCH_SCRATCH0_MODE_MASK (SCRATCH_SCRATCH0_MODE_RECOVERY | \ 37 + SCRATCH_SCRATCH0_MODE_BOOTLOADER | \ 38 + SCRATCH_SCRATCH0_MODE_RCM) 39 + 40 + struct tegra_pmc { 41 + struct device *dev; 42 + void __iomem *regs; 43 + void __iomem *wake; 44 + void __iomem *aotag; 45 + void __iomem *scratch; 46 + 47 + void (*system_restart)(enum reboot_mode mode, const char *cmd); 48 + struct notifier_block restart; 49 + }; 50 + 51 + static int tegra186_pmc_restart_notify(struct notifier_block *nb, 52 + unsigned long action, 53 + void *data) 54 + { 55 + struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, restart); 56 + const char *cmd = data; 57 + u32 value; 58 + 59 + value = readl(pmc->scratch + SCRATCH_SCRATCH0); 60 + value &= ~SCRATCH_SCRATCH0_MODE_MASK; 61 + 62 + if (cmd) { 63 + if (strcmp(cmd, "recovery") == 0) 64 + value |= SCRATCH_SCRATCH0_MODE_RECOVERY; 65 + 66 + if (strcmp(cmd, "bootloader") == 0) 67 + value |= SCRATCH_SCRATCH0_MODE_BOOTLOADER; 68 + 69 + if (strcmp(cmd, "forced-recovery") == 0) 70 + value |= SCRATCH_SCRATCH0_MODE_RCM; 71 + } 72 + 73 + writel(value, pmc->scratch + SCRATCH_SCRATCH0); 74 + 75 + /* 76 + * If available, call the system restart implementation that was 77 + * registered earlier (typically PSCI). 78 + */ 79 + if (pmc->system_restart) { 80 + pmc->system_restart(reboot_mode, cmd); 81 + return NOTIFY_DONE; 82 + } 83 + 84 + /* reset everything but SCRATCH0_SCRATCH0 and PMC_RST_STATUS */ 85 + value = readl(pmc->regs + PMC_CNTRL); 86 + value |= PMC_CNTRL_MAIN_RST; 87 + writel(value, pmc->regs + PMC_CNTRL); 88 + 89 + return NOTIFY_DONE; 90 + } 91 + 92 + static int tegra186_pmc_setup(struct tegra_pmc *pmc) 93 + { 94 + struct device_node *np = pmc->dev->of_node; 95 + bool invert; 96 + u32 value; 97 + 98 + invert = of_property_read_bool(np, "nvidia,invert-interrupt"); 99 + 100 + value = readl(pmc->wake + WAKE_AOWAKE_CTRL); 101 + 102 + if (invert) 103 + value |= WAKE_AOWAKE_CTRL_INTR_POLARITY; 104 + else 105 + value &= ~WAKE_AOWAKE_CTRL_INTR_POLARITY; 106 + 107 + writel(value, pmc->wake + WAKE_AOWAKE_CTRL); 108 + 109 + /* 110 + * We need to hook any system restart implementation registered 111 + * previously so we can write SCRATCH_SCRATCH0 before reset. 112 + */ 113 + pmc->system_restart = arm_pm_restart; 114 + arm_pm_restart = NULL; 115 + 116 + pmc->restart.notifier_call = tegra186_pmc_restart_notify; 117 + pmc->restart.priority = 128; 118 + 119 + return register_restart_handler(&pmc->restart); 120 + } 121 + 122 + static int tegra186_pmc_probe(struct platform_device *pdev) 123 + { 124 + struct tegra_pmc *pmc; 125 + struct resource *res; 126 + 127 + pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); 128 + if (!pmc) 129 + return -ENOMEM; 130 + 131 + pmc->dev = &pdev->dev; 132 + 133 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmc"); 134 + pmc->regs = devm_ioremap_resource(&pdev->dev, res); 135 + if (IS_ERR(pmc->regs)) 136 + return PTR_ERR(pmc->regs); 137 + 138 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake"); 139 + pmc->wake = devm_ioremap_resource(&pdev->dev, res); 140 + if (IS_ERR(pmc->wake)) 141 + return PTR_ERR(pmc->wake); 142 + 143 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag"); 144 + pmc->aotag = devm_ioremap_resource(&pdev->dev, res); 145 + if (IS_ERR(pmc->aotag)) 146 + return PTR_ERR(pmc->aotag); 147 + 148 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); 149 + pmc->scratch = devm_ioremap_resource(&pdev->dev, res); 150 + if (IS_ERR(pmc->scratch)) 151 + return PTR_ERR(pmc->scratch); 152 + 153 + return tegra186_pmc_setup(pmc); 154 + } 155 + 156 + static const struct of_device_id tegra186_pmc_of_match[] = { 157 + { .compatible = "nvidia,tegra186-pmc" }, 158 + { /* sentinel */ } 159 + }; 160 + MODULE_DEVICE_TABLE(of, tegra186_pmc_of_match); 161 + 162 + static struct platform_driver tegra186_pmc_driver = { 163 + .driver = { 164 + .name = "tegra186-pmc", 165 + .of_match_table = tegra186_pmc_of_match, 166 + }, 167 + .probe = tegra186_pmc_probe, 168 + }; 169 + builtin_platform_driver(tegra186_pmc_driver);