mirror of
https://xff.cz/git/u-boot/
synced 2025-09-01 08:42:12 +02:00
ARM: tegra: Support TZ-only access to PMC
Some devices may restrict access to the PMC to TrustZone software only. Non-TZ software can detect this and use SMC calls to the firmware that runs in the TrustZone to perform accesses to PMC registers. Note that this also fixes reset_cpu() and the enterrcm command on Tegra186 where they were previously trying to access the PMC at a wrong physical address. Based on work by Kalyani Chidambaram <kalyanic@nvidia.com> and Tom Warren <twarren@nvidia.com>. Signed-off-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Tom Warren <twarren@nvidia.com>
This commit is contained in:
committed by
Tom Warren
parent
147fac6aef
commit
f9ec2ec850
@@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
/*
|
/*
|
||||||
* (C) Copyright 2010-2015
|
* (C) Copyright 2010-2019
|
||||||
* NVIDIA Corporation <www.nvidia.com>
|
* NVIDIA Corporation <www.nvidia.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -388,4 +388,22 @@ struct pmc_ctlr {
|
|||||||
/* APBDEV_PMC_CNTRL2_0 0x440 */
|
/* APBDEV_PMC_CNTRL2_0 0x440 */
|
||||||
#define HOLD_CKE_LOW_EN (1 << 12)
|
#define HOLD_CKE_LOW_EN (1 << 12)
|
||||||
|
|
||||||
|
/* PMC read/write functions */
|
||||||
|
u32 tegra_pmc_readl(unsigned long offset);
|
||||||
|
void tegra_pmc_writel(u32 value, unsigned long offset);
|
||||||
|
|
||||||
|
#define PMC_CNTRL 0x0
|
||||||
|
#define PMC_CNTRL_MAIN_RST BIT(4)
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_TEGRA186)
|
||||||
|
# define PMC_SCRATCH0 0x32000
|
||||||
|
#else
|
||||||
|
# define PMC_SCRATCH0 0x00050
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* for secure PMC */
|
||||||
|
#define TEGRA_SMC_PMC 0xc2fffe00
|
||||||
|
#define TEGRA_SMC_PMC_READ 0xaa
|
||||||
|
#define TEGRA_SMC_PMC_WRITE 0xbb
|
||||||
|
|
||||||
#endif /* PMC_H */
|
#endif /* PMC_H */
|
||||||
|
@@ -30,7 +30,13 @@
|
|||||||
#define NV_PA_SLINK5_BASE (NV_PA_APB_MISC_BASE + 0xDC00)
|
#define NV_PA_SLINK5_BASE (NV_PA_APB_MISC_BASE + 0xDC00)
|
||||||
#define NV_PA_SLINK6_BASE (NV_PA_APB_MISC_BASE + 0xDE00)
|
#define NV_PA_SLINK6_BASE (NV_PA_APB_MISC_BASE + 0xDE00)
|
||||||
#define TEGRA_DVC_BASE (NV_PA_APB_MISC_BASE + 0xD000)
|
#define TEGRA_DVC_BASE (NV_PA_APB_MISC_BASE + 0xD000)
|
||||||
|
#if defined(CONFIG_TEGRA20) || defined(CONFIG_TEGRA30) || \
|
||||||
|
defined(CONFIG_TEGRA114) || defined(CONFIG_TEGRA124) || \
|
||||||
|
defined(CONFIG_TEGRA132) || defined(CONFIG_TEGRA210)
|
||||||
#define NV_PA_PMC_BASE (NV_PA_APB_MISC_BASE + 0xE400)
|
#define NV_PA_PMC_BASE (NV_PA_APB_MISC_BASE + 0xE400)
|
||||||
|
#else
|
||||||
|
#define NV_PA_PMC_BASE 0xc360000
|
||||||
|
#endif
|
||||||
#define NV_PA_EMC_BASE (NV_PA_APB_MISC_BASE + 0xF400)
|
#define NV_PA_EMC_BASE (NV_PA_APB_MISC_BASE + 0xF400)
|
||||||
#define NV_PA_FUSE_BASE (NV_PA_APB_MISC_BASE + 0xF800)
|
#define NV_PA_FUSE_BASE (NV_PA_APB_MISC_BASE + 0xF800)
|
||||||
#if defined(CONFIG_TEGRA20) || defined(CONFIG_TEGRA30) || \
|
#if defined(CONFIG_TEGRA20) || defined(CONFIG_TEGRA30) || \
|
||||||
|
@@ -35,6 +35,10 @@ config TEGRA_PINCTRL
|
|||||||
config TEGRA_PMC
|
config TEGRA_PMC
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config TEGRA_PMC_SECURE
|
||||||
|
bool
|
||||||
|
depends on TEGRA_PMC
|
||||||
|
|
||||||
config TEGRA_COMMON
|
config TEGRA_COMMON
|
||||||
bool "Tegra common options"
|
bool "Tegra common options"
|
||||||
select BINMAN
|
select BINMAN
|
||||||
@@ -127,6 +131,7 @@ config TEGRA210
|
|||||||
select TEGRA_NO_BPMP
|
select TEGRA_NO_BPMP
|
||||||
select TEGRA_PINCTRL
|
select TEGRA_PINCTRL
|
||||||
select TEGRA_PMC
|
select TEGRA_PMC
|
||||||
|
select TEGRA_PMC_SECURE
|
||||||
|
|
||||||
config TEGRA186
|
config TEGRA186
|
||||||
bool "Tegra186 family"
|
bool "Tegra186 family"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
#
|
#
|
||||||
# (C) Copyright 2010-2015 Nvidia Corporation.
|
# (C) Copyright 2010-2019 Nvidia Corporation.
|
||||||
#
|
#
|
||||||
# (C) Copyright 2000-2008
|
# (C) Copyright 2000-2008
|
||||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||||
@@ -27,11 +27,11 @@ obj-y += dt-setup.o
|
|||||||
obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o
|
obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o
|
||||||
obj-$(CONFIG_TEGRA_GPU) += gpu.o
|
obj-$(CONFIG_TEGRA_GPU) += gpu.o
|
||||||
obj-$(CONFIG_TEGRA_IVC) += ivc.o
|
obj-$(CONFIG_TEGRA_IVC) += ivc.o
|
||||||
obj-y += lowlevel_init.o
|
|
||||||
ifndef CONFIG_SPL_BUILD
|
ifndef CONFIG_SPL_BUILD
|
||||||
obj-$(CONFIG_ARMV7_PSCI) += psci.o
|
obj-$(CONFIG_ARMV7_PSCI) += psci.o
|
||||||
endif
|
endif
|
||||||
obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o
|
obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o
|
||||||
|
obj-y += pmc.o
|
||||||
|
|
||||||
obj-$(CONFIG_TEGRA20) += tegra20/
|
obj-$(CONFIG_TEGRA20) += tegra20/
|
||||||
obj-$(CONFIG_TEGRA30) += tegra30/
|
obj-$(CONFIG_TEGRA30) += tegra30/
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved.
|
* Copyright (c) 2010-2019, NVIDIA CORPORATION. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Tegra SoC common clock control functions */
|
/* Tegra SoC common clock control functions */
|
||||||
@@ -815,11 +815,16 @@ void tegra30_set_up_pllp(void)
|
|||||||
|
|
||||||
int clock_external_output(int clk_id)
|
int clock_external_output(int clk_id)
|
||||||
{
|
{
|
||||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
|
u32 val;
|
||||||
|
|
||||||
if (clk_id >= 1 && clk_id <= 3) {
|
if (clk_id >= 1 && clk_id <= 3) {
|
||||||
setbits_le32(&pmc->pmc_clk_out_cntrl,
|
val = tegra_pmc_readl(offsetof(struct pmc_ctlr,
|
||||||
1 << (2 + (clk_id - 1) * 8));
|
pmc_clk_out_cntrl));
|
||||||
|
val |= 1 << (2 + (clk_id - 1) * 8);
|
||||||
|
tegra_pmc_writel(val,
|
||||||
|
offsetof(struct pmc_ctlr,
|
||||||
|
pmc_clk_out_cntrl));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
printf("%s: Unknown output clock id %d\n", __func__, clk_id);
|
printf("%s: Unknown output clock id %d\n", __func__, clk_id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0+
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
* Copyright (c) 2012-2019, NVIDIA CORPORATION. All rights reserved.
|
||||||
*
|
*
|
||||||
* Derived from code (arch/arm/lib/reset.c) that is:
|
* Derived from code (arch/arm/lib/reset.c) that is:
|
||||||
*
|
*
|
||||||
@@ -31,12 +31,10 @@
|
|||||||
static int do_enterrcm(cmd_tbl_t *cmdtp, int flag, int argc,
|
static int do_enterrcm(cmd_tbl_t *cmdtp, int flag, int argc,
|
||||||
char * const argv[])
|
char * const argv[])
|
||||||
{
|
{
|
||||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
|
|
||||||
|
|
||||||
puts("Entering RCM...\n");
|
puts("Entering RCM...\n");
|
||||||
udelay(50000);
|
udelay(50000);
|
||||||
|
|
||||||
pmc->pmc_scratch0 = 2;
|
tegra_pmc_writel(2, PMC_SCRATCH0);
|
||||||
disable_interrupts();
|
disable_interrupts();
|
||||||
reset_cpu(0);
|
reset_cpu(0);
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved.
|
* Copyright (c) 2010-2019, NVIDIA CORPORATION. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
@@ -299,21 +299,19 @@ void enable_cpu_clock(int enable)
|
|||||||
|
|
||||||
static int is_cpu_powered(void)
|
static int is_cpu_powered(void)
|
||||||
{
|
{
|
||||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
|
return (tegra_pmc_readl(offsetof(struct pmc_ctlr,
|
||||||
|
pmc_pwrgate_status)) & CPU_PWRED) ? 1 : 0;
|
||||||
return (readl(&pmc->pmc_pwrgate_status) & CPU_PWRED) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_cpu_io_clamps(void)
|
static void remove_cpu_io_clamps(void)
|
||||||
{
|
{
|
||||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
|
|
||||||
u32 reg;
|
u32 reg;
|
||||||
debug("%s entry\n", __func__);
|
debug("%s entry\n", __func__);
|
||||||
|
|
||||||
/* Remove the clamps on the CPU I/O signals */
|
/* Remove the clamps on the CPU I/O signals */
|
||||||
reg = readl(&pmc->pmc_remove_clamping);
|
reg = tegra_pmc_readl(offsetof(struct pmc_ctlr, pmc_remove_clamping));
|
||||||
reg |= CPU_CLMP;
|
reg |= CPU_CLMP;
|
||||||
writel(reg, &pmc->pmc_remove_clamping);
|
tegra_pmc_writel(reg, offsetof(struct pmc_ctlr, pmc_remove_clamping));
|
||||||
|
|
||||||
/* Give I/O signals time to stabilize */
|
/* Give I/O signals time to stabilize */
|
||||||
udelay(IO_STABILIZATION_DELAY);
|
udelay(IO_STABILIZATION_DELAY);
|
||||||
@@ -321,17 +319,19 @@ static void remove_cpu_io_clamps(void)
|
|||||||
|
|
||||||
void powerup_cpu(void)
|
void powerup_cpu(void)
|
||||||
{
|
{
|
||||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
|
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int timeout = IO_STABILIZATION_DELAY;
|
int timeout = IO_STABILIZATION_DELAY;
|
||||||
debug("%s entry\n", __func__);
|
debug("%s entry\n", __func__);
|
||||||
|
|
||||||
if (!is_cpu_powered()) {
|
if (!is_cpu_powered()) {
|
||||||
/* Toggle the CPU power state (OFF -> ON) */
|
/* Toggle the CPU power state (OFF -> ON) */
|
||||||
reg = readl(&pmc->pmc_pwrgate_toggle);
|
reg = tegra_pmc_readl(offsetof(struct pmc_ctlr,
|
||||||
|
pmc_pwrgate_toggle));
|
||||||
reg &= PARTID_CP;
|
reg &= PARTID_CP;
|
||||||
reg |= START_CP;
|
reg |= START_CP;
|
||||||
writel(reg, &pmc->pmc_pwrgate_toggle);
|
tegra_pmc_writel(reg,
|
||||||
|
offsetof(struct pmc_ctlr,
|
||||||
|
pmc_pwrgate_toggle));
|
||||||
|
|
||||||
/* Wait for the power to come up */
|
/* Wait for the power to come up */
|
||||||
while (!is_cpu_powered()) {
|
while (!is_cpu_powered()) {
|
||||||
|
@@ -1,39 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
||||||
/*
|
|
||||||
* SoC-specific setup info
|
|
||||||
*
|
|
||||||
* (C) Copyright 2010,2011
|
|
||||||
* NVIDIA Corporation <www.nvidia.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <linux/linkage.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64
|
|
||||||
.align 5
|
|
||||||
ENTRY(reset_cpu)
|
|
||||||
/* get address for global reset register */
|
|
||||||
ldr x1, =PRM_RSTCTRL
|
|
||||||
ldr w3, [x1]
|
|
||||||
/* force reset */
|
|
||||||
orr w3, w3, #0x10
|
|
||||||
str w3, [x1]
|
|
||||||
mov w0, w0
|
|
||||||
1:
|
|
||||||
b 1b
|
|
||||||
ENDPROC(reset_cpu)
|
|
||||||
#else
|
|
||||||
.align 5
|
|
||||||
ENTRY(reset_cpu)
|
|
||||||
ldr r1, rstctl @ get addr for global reset
|
|
||||||
@ reg
|
|
||||||
ldr r3, [r1]
|
|
||||||
orr r3, r3, #0x10
|
|
||||||
str r3, [r1] @ force reset
|
|
||||||
mov r0, r0
|
|
||||||
_loop_forever:
|
|
||||||
b _loop_forever
|
|
||||||
rstctl:
|
|
||||||
.word PRM_RSTCTRL
|
|
||||||
ENDPROC(reset_cpu)
|
|
||||||
#endif
|
|
92
arch/arm/mach-tegra/pmc.c
Normal file
92
arch/arm/mach-tegra/pmc.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
|
||||||
|
#include <linux/arm-smccc.h>
|
||||||
|
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/arch-tegra/pmc.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_TEGRA_PMC_SECURE)
|
||||||
|
static bool tegra_pmc_detect_tz_only(void)
|
||||||
|
{
|
||||||
|
static bool initialized = false;
|
||||||
|
static bool is_tz_only = false;
|
||||||
|
u32 value, saved;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
saved = readl(NV_PA_PMC_BASE + PMC_SCRATCH0);
|
||||||
|
value = saved ^ 0xffffffff;
|
||||||
|
|
||||||
|
if (value == 0xffffffff)
|
||||||
|
value = 0xdeadbeef;
|
||||||
|
|
||||||
|
/* write pattern and read it back */
|
||||||
|
writel(value, NV_PA_PMC_BASE + PMC_SCRATCH0);
|
||||||
|
value = readl(NV_PA_PMC_BASE + PMC_SCRATCH0);
|
||||||
|
|
||||||
|
/* if we read all-zeroes, access is restricted to TZ only */
|
||||||
|
if (value == 0) {
|
||||||
|
debug("access to PMC is restricted to TZ\n");
|
||||||
|
is_tz_only = true;
|
||||||
|
} else {
|
||||||
|
/* restore original value */
|
||||||
|
writel(saved, NV_PA_PMC_BASE + PMC_SCRATCH0);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_tz_only;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t tegra_pmc_readl(unsigned long offset)
|
||||||
|
{
|
||||||
|
#if IS_ENABLED(CONFIG_TEGRA_PMC_SECURE)
|
||||||
|
if (tegra_pmc_detect_tz_only()) {
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_READ, offset, 0, 0,
|
||||||
|
0, 0, 0, &res);
|
||||||
|
if (res.a0)
|
||||||
|
printf("%s(): SMC failed: %lu\n", __func__, res.a0);
|
||||||
|
|
||||||
|
return res.a1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return readl(NV_PA_PMC_BASE + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tegra_pmc_writel(u32 value, unsigned long offset)
|
||||||
|
{
|
||||||
|
#if IS_ENABLED(CONFIG_TEGRA_PMC_SECURE)
|
||||||
|
if (tegra_pmc_detect_tz_only()) {
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_WRITE, offset,
|
||||||
|
value, 0, 0, 0, 0, &res);
|
||||||
|
if (res.a0)
|
||||||
|
printf("%s(): SMC failed: %lu\n", __func__, res.a0);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
writel(value, NV_PA_PMC_BASE + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_cpu(ulong addr)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
value = tegra_pmc_readl(PMC_CNTRL);
|
||||||
|
value |= PMC_CNTRL_MAIN_RST;
|
||||||
|
tegra_pmc_writel(value, PMC_CNTRL);
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
* Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <asm/arch/powergate.h>
|
#include <asm/arch/powergate.h>
|
||||||
#include <asm/arch/tegra.h>
|
#include <asm/arch/tegra.h>
|
||||||
|
#include <asm/arch-tegra/pmc.h>
|
||||||
|
|
||||||
#define PWRGATE_TOGGLE 0x30
|
#define PWRGATE_TOGGLE 0x30
|
||||||
#define PWRGATE_TOGGLE_START (1 << 8)
|
#define PWRGATE_TOGGLE_START (1 << 8)
|
||||||
@@ -24,18 +25,18 @@ static int tegra_powergate_set(enum tegra_powergate id, bool state)
|
|||||||
u32 value, mask = state ? (1 << id) : 0, old_mask;
|
u32 value, mask = state ? (1 << id) : 0, old_mask;
|
||||||
unsigned long start, timeout = 25;
|
unsigned long start, timeout = 25;
|
||||||
|
|
||||||
value = readl(NV_PA_PMC_BASE + PWRGATE_STATUS);
|
value = tegra_pmc_readl(PWRGATE_STATUS);
|
||||||
old_mask = value & (1 << id);
|
old_mask = value & (1 << id);
|
||||||
|
|
||||||
if (mask == old_mask)
|
if (mask == old_mask)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
writel(PWRGATE_TOGGLE_START | id, NV_PA_PMC_BASE + PWRGATE_TOGGLE);
|
tegra_pmc_writel(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
|
||||||
|
|
||||||
start = get_timer(0);
|
start = get_timer(0);
|
||||||
|
|
||||||
while (get_timer(start) < timeout) {
|
while (get_timer(start) < timeout) {
|
||||||
value = readl(NV_PA_PMC_BASE + PWRGATE_STATUS);
|
value = tegra_pmc_readl(PWRGATE_STATUS);
|
||||||
if ((value & (1 << id)) == mask)
|
if ((value & (1 << id)) == mask)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ static int tegra_powergate_remove_clamping(enum tegra_powergate id)
|
|||||||
else
|
else
|
||||||
value = 1 << id;
|
value = 1 << id;
|
||||||
|
|
||||||
writel(value, NV_PA_PMC_BASE + REMOVE_CLAMPING);
|
tegra_pmc_writel(value, REMOVE_CLAMPING);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user