diff options
| author | Mikko Perttunen <mperttunen@nvidia.com> | 2019-01-25 05:22:55 -0500 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2019-01-25 10:18:24 -0500 |
| commit | e247deae1a55089cb04cc25c91faeba083d0c39c (patch) | |
| tree | 2c85583a47f879f93b7751e54fe668fbfb528a31 | |
| parent | fa3bc04ef8ccccfe47db1f4030a7a43569956402 (diff) | |
soc/tegra: pmc: Support systems where PMC is marked secure
On Tegra210 systems with new enough boot software, direct register
accesses to PMC register space from the non-secure world are not
allowed. Instead a monitor call may be used to read and write PMC
registers.
Add code to detect such a system by attempting to write a scratch
register and detecting if the write happened or not. If not, we switch
to doing all register accesses through the monitor call.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
| -rw-r--r-- | drivers/soc/tegra/pmc.c | 100 |
1 files changed, 97 insertions, 3 deletions
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index dba89fc81a04..0df258518693 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | #define pr_fmt(fmt) "tegra-pmc: " fmt | 21 | #define pr_fmt(fmt) "tegra-pmc: " fmt |
| 22 | 22 | ||
| 23 | #include <linux/arm-smccc.h> | ||
| 23 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
| 24 | #include <linux/clk/tegra.h> | 25 | #include <linux/clk/tegra.h> |
| 25 | #include <linux/debugfs.h> | 26 | #include <linux/debugfs.h> |
| @@ -145,6 +146,11 @@ | |||
| 145 | #define WAKE_AOWAKE_CTRL 0x4f4 | 146 | #define WAKE_AOWAKE_CTRL 0x4f4 |
| 146 | #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) | 147 | #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) |
| 147 | 148 | ||
| 149 | /* for secure PMC */ | ||
| 150 | #define TEGRA_SMC_PMC 0xc2fffe00 | ||
| 151 | #define TEGRA_SMC_PMC_READ 0xaa | ||
| 152 | #define TEGRA_SMC_PMC_WRITE 0xbb | ||
| 153 | |||
| 148 | struct tegra_powergate { | 154 | struct tegra_powergate { |
| 149 | struct generic_pm_domain genpd; | 155 | struct generic_pm_domain genpd; |
| 150 | struct tegra_pmc *pmc; | 156 | struct tegra_pmc *pmc; |
| @@ -216,6 +222,7 @@ struct tegra_pmc_soc { | |||
| 216 | bool has_gpu_clamps; | 222 | bool has_gpu_clamps; |
| 217 | bool needs_mbist_war; | 223 | bool needs_mbist_war; |
| 218 | bool has_impl_33v_pwr; | 224 | bool has_impl_33v_pwr; |
| 225 | bool maybe_tz_only; | ||
| 219 | 226 | ||
| 220 | const struct tegra_io_pad_soc *io_pads; | 227 | const struct tegra_io_pad_soc *io_pads; |
| 221 | unsigned int num_io_pads; | 228 | unsigned int num_io_pads; |
| @@ -278,6 +285,7 @@ static const char * const tegra30_reset_sources[] = { | |||
| 278 | * @scratch: pointer to I/O remapped region for scratch registers | 285 | * @scratch: pointer to I/O remapped region for scratch registers |
| 279 | * @clk: pointer to pclk clock | 286 | * @clk: pointer to pclk clock |
| 280 | * @soc: pointer to SoC data structure | 287 | * @soc: pointer to SoC data structure |
| 288 | * @tz_only: flag specifying if the PMC can only be accessed via TrustZone | ||
| 281 | * @debugfs: pointer to debugfs entry | 289 | * @debugfs: pointer to debugfs entry |
| 282 | * @rate: currently configured rate of pclk | 290 | * @rate: currently configured rate of pclk |
| 283 | * @suspend_mode: lowest suspend mode available | 291 | * @suspend_mode: lowest suspend mode available |
| @@ -308,6 +316,7 @@ struct tegra_pmc { | |||
| 308 | struct dentry *debugfs; | 316 | struct dentry *debugfs; |
| 309 | 317 | ||
| 310 | const struct tegra_pmc_soc *soc; | 318 | const struct tegra_pmc_soc *soc; |
| 319 | bool tz_only; | ||
| 311 | 320 | ||
| 312 | unsigned long rate; | 321 | unsigned long rate; |
| 313 | 322 | ||
| @@ -346,13 +355,62 @@ to_powergate(struct generic_pm_domain *domain) | |||
| 346 | 355 | ||
| 347 | static u32 tegra_pmc_readl(struct tegra_pmc *pmc, unsigned long offset) | 356 | static u32 tegra_pmc_readl(struct tegra_pmc *pmc, unsigned long offset) |
| 348 | { | 357 | { |
| 358 | struct arm_smccc_res res; | ||
| 359 | |||
| 360 | if (pmc->tz_only) { | ||
| 361 | arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_READ, offset, 0, 0, | ||
| 362 | 0, 0, 0, &res); | ||
| 363 | if (res.a0) { | ||
| 364 | if (pmc->dev) | ||
| 365 | dev_warn(pmc->dev, "%s(): SMC failed: %lu\n", | ||
| 366 | __func__, res.a0); | ||
| 367 | else | ||
| 368 | pr_warn("%s(): SMC failed: %lu\n", __func__, | ||
| 369 | res.a0); | ||
| 370 | } | ||
| 371 | |||
| 372 | return res.a1; | ||
| 373 | } | ||
| 374 | |||
| 349 | return readl(pmc->base + offset); | 375 | return readl(pmc->base + offset); |
| 350 | } | 376 | } |
| 351 | 377 | ||
| 352 | static void tegra_pmc_writel(struct tegra_pmc *pmc, u32 value, | 378 | static void tegra_pmc_writel(struct tegra_pmc *pmc, u32 value, |
| 353 | unsigned long offset) | 379 | unsigned long offset) |
| 354 | { | 380 | { |
| 355 | writel(value, pmc->base + offset); | 381 | struct arm_smccc_res res; |
| 382 | |||
| 383 | if (pmc->tz_only) { | ||
| 384 | arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_WRITE, offset, | ||
| 385 | value, 0, 0, 0, 0, &res); | ||
| 386 | if (res.a0) { | ||
| 387 | if (pmc->dev) | ||
| 388 | dev_warn(pmc->dev, "%s(): SMC failed: %lu\n", | ||
| 389 | __func__, res.a0); | ||
| 390 | else | ||
| 391 | pr_warn("%s(): SMC failed: %lu\n", __func__, | ||
| 392 | res.a0); | ||
| 393 | } | ||
| 394 | } else { | ||
| 395 | writel(value, pmc->base + offset); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | static u32 tegra_pmc_scratch_readl(struct tegra_pmc *pmc, unsigned long offset) | ||
| 400 | { | ||
| 401 | if (pmc->tz_only) | ||
| 402 | return tegra_pmc_readl(pmc, offset); | ||
| 403 | |||
| 404 | return readl(pmc->scratch + offset); | ||
| 405 | } | ||
| 406 | |||
| 407 | static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value, | ||
| 408 | unsigned long offset) | ||
| 409 | { | ||
| 410 | if (pmc->tz_only) | ||
| 411 | tegra_pmc_writel(pmc, value, offset); | ||
| 412 | else | ||
| 413 | writel(value, pmc->scratch + offset); | ||
| 356 | } | 414 | } |
| 357 | 415 | ||
| 358 | /* | 416 | /* |
| @@ -776,7 +834,7 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, | |||
| 776 | const char *cmd = data; | 834 | const char *cmd = data; |
| 777 | u32 value; | 835 | u32 value; |
| 778 | 836 | ||
| 779 | value = readl(pmc->scratch + pmc->soc->regs->scratch0); | 837 | value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->scratch0); |
| 780 | value &= ~PMC_SCRATCH0_MODE_MASK; | 838 | value &= ~PMC_SCRATCH0_MODE_MASK; |
| 781 | 839 | ||
| 782 | if (cmd) { | 840 | if (cmd) { |
| @@ -790,7 +848,7 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, | |||
| 790 | value |= PMC_SCRATCH0_MODE_RCM; | 848 | value |= PMC_SCRATCH0_MODE_RCM; |
| 791 | } | 849 | } |
| 792 | 850 | ||
| 793 | writel(value, pmc->scratch + pmc->soc->regs->scratch0); | 851 | tegra_pmc_scratch_writel(pmc, value, pmc->soc->regs->scratch0); |
| 794 | 852 | ||
| 795 | /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ | 853 | /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ |
| 796 | value = tegra_pmc_readl(pmc, PMC_CNTRL); | 854 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
| @@ -2071,6 +2129,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { | |||
| 2071 | .has_gpu_clamps = false, | 2129 | .has_gpu_clamps = false, |
| 2072 | .needs_mbist_war = false, | 2130 | .needs_mbist_war = false, |
| 2073 | .has_impl_33v_pwr = false, | 2131 | .has_impl_33v_pwr = false, |
| 2132 | .maybe_tz_only = false, | ||
| 2074 | .num_io_pads = 0, | 2133 | .num_io_pads = 0, |
| 2075 | .io_pads = NULL, | 2134 | .io_pads = NULL, |
| 2076 | .num_pin_descs = 0, | 2135 | .num_pin_descs = 0, |
| @@ -2117,6 +2176,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { | |||
| 2117 | .has_gpu_clamps = false, | 2176 | .has_gpu_clamps = false, |
| 2118 | .needs_mbist_war = false, | 2177 | .needs_mbist_war = false, |
| 2119 | .has_impl_33v_pwr = false, | 2178 | .has_impl_33v_pwr = false, |
| 2179 | .maybe_tz_only = false, | ||
| 2120 | .num_io_pads = 0, | 2180 | .num_io_pads = 0, |
| 2121 | .io_pads = NULL, | 2181 | .io_pads = NULL, |
| 2122 | .num_pin_descs = 0, | 2182 | .num_pin_descs = 0, |
| @@ -2167,6 +2227,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { | |||
| 2167 | .has_gpu_clamps = false, | 2227 | .has_gpu_clamps = false, |
| 2168 | .needs_mbist_war = false, | 2228 | .needs_mbist_war = false, |
| 2169 | .has_impl_33v_pwr = false, | 2229 | .has_impl_33v_pwr = false, |
| 2230 | .maybe_tz_only = false, | ||
| 2170 | .num_io_pads = 0, | 2231 | .num_io_pads = 0, |
| 2171 | .io_pads = NULL, | 2232 | .io_pads = NULL, |
| 2172 | .num_pin_descs = 0, | 2233 | .num_pin_descs = 0, |
| @@ -2277,6 +2338,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { | |||
| 2277 | .has_gpu_clamps = true, | 2338 | .has_gpu_clamps = true, |
| 2278 | .needs_mbist_war = false, | 2339 | .needs_mbist_war = false, |
| 2279 | .has_impl_33v_pwr = false, | 2340 | .has_impl_33v_pwr = false, |
| 2341 | .maybe_tz_only = false, | ||
| 2280 | .num_io_pads = ARRAY_SIZE(tegra124_io_pads), | 2342 | .num_io_pads = ARRAY_SIZE(tegra124_io_pads), |
| 2281 | .io_pads = tegra124_io_pads, | 2343 | .io_pads = tegra124_io_pads, |
| 2282 | .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), | 2344 | .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), |
| @@ -2382,6 +2444,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { | |||
| 2382 | .has_gpu_clamps = true, | 2444 | .has_gpu_clamps = true, |
| 2383 | .needs_mbist_war = true, | 2445 | .needs_mbist_war = true, |
| 2384 | .has_impl_33v_pwr = false, | 2446 | .has_impl_33v_pwr = false, |
| 2447 | .maybe_tz_only = true, | ||
| 2385 | .num_io_pads = ARRAY_SIZE(tegra210_io_pads), | 2448 | .num_io_pads = ARRAY_SIZE(tegra210_io_pads), |
| 2386 | .io_pads = tegra210_io_pads, | 2449 | .io_pads = tegra210_io_pads, |
| 2387 | .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), | 2450 | .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), |
| @@ -2506,6 +2569,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { | |||
| 2506 | .has_gpu_clamps = false, | 2569 | .has_gpu_clamps = false, |
| 2507 | .needs_mbist_war = false, | 2570 | .needs_mbist_war = false, |
| 2508 | .has_impl_33v_pwr = true, | 2571 | .has_impl_33v_pwr = true, |
| 2572 | .maybe_tz_only = false, | ||
| 2509 | .num_io_pads = ARRAY_SIZE(tegra186_io_pads), | 2573 | .num_io_pads = ARRAY_SIZE(tegra186_io_pads), |
| 2510 | .io_pads = tegra186_io_pads, | 2574 | .io_pads = tegra186_io_pads, |
| 2511 | .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), | 2575 | .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), |
| @@ -2585,6 +2649,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { | |||
| 2585 | .has_gpu_clamps = false, | 2649 | .has_gpu_clamps = false, |
| 2586 | .needs_mbist_war = false, | 2650 | .needs_mbist_war = false, |
| 2587 | .has_impl_33v_pwr = false, | 2651 | .has_impl_33v_pwr = false, |
| 2652 | .maybe_tz_only = false, | ||
| 2588 | .num_io_pads = ARRAY_SIZE(tegra194_io_pads), | 2653 | .num_io_pads = ARRAY_SIZE(tegra194_io_pads), |
| 2589 | .io_pads = tegra194_io_pads, | 2654 | .io_pads = tegra194_io_pads, |
| 2590 | .regs = &tegra186_pmc_regs, | 2655 | .regs = &tegra186_pmc_regs, |
| @@ -2619,6 +2684,32 @@ static struct platform_driver tegra_pmc_driver = { | |||
| 2619 | }; | 2684 | }; |
| 2620 | builtin_platform_driver(tegra_pmc_driver); | 2685 | builtin_platform_driver(tegra_pmc_driver); |
| 2621 | 2686 | ||
| 2687 | static bool __init tegra_pmc_detect_tz_only(struct tegra_pmc *pmc) | ||
| 2688 | { | ||
| 2689 | u32 value, saved; | ||
| 2690 | |||
| 2691 | saved = readl(pmc->base + pmc->soc->regs->scratch0); | ||
| 2692 | value = saved ^ 0xffffffff; | ||
| 2693 | |||
| 2694 | if (value == 0xffffffff) | ||
| 2695 | value = 0xdeadbeef; | ||
| 2696 | |||
| 2697 | /* write pattern and read it back */ | ||
| 2698 | writel(value, pmc->base + pmc->soc->regs->scratch0); | ||
| 2699 | value = readl(pmc->base + pmc->soc->regs->scratch0); | ||
| 2700 | |||
| 2701 | /* if we read all-zeroes, access is restricted to TZ only */ | ||
| 2702 | if (value == 0) { | ||
| 2703 | pr_info("access to PMC is restricted to TZ\n"); | ||
| 2704 | return true; | ||
| 2705 | } | ||
| 2706 | |||
| 2707 | /* restore original value */ | ||
| 2708 | writel(saved, pmc->base + pmc->soc->regs->scratch0); | ||
| 2709 | |||
| 2710 | return false; | ||
| 2711 | } | ||
| 2712 | |||
| 2622 | /* | 2713 | /* |
| 2623 | * Early initialization to allow access to registers in the very early boot | 2714 | * Early initialization to allow access to registers in the very early boot |
| 2624 | * process. | 2715 | * process. |
| @@ -2681,6 +2772,9 @@ static int __init tegra_pmc_early_init(void) | |||
| 2681 | if (np) { | 2772 | if (np) { |
| 2682 | pmc->soc = match->data; | 2773 | pmc->soc = match->data; |
| 2683 | 2774 | ||
| 2775 | if (pmc->soc->maybe_tz_only) | ||
| 2776 | pmc->tz_only = tegra_pmc_detect_tz_only(pmc); | ||
| 2777 | |||
| 2684 | tegra_powergate_init(pmc, np); | 2778 | tegra_powergate_init(pmc, np); |
| 2685 | 2779 | ||
| 2686 | /* | 2780 | /* |
