mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-23 13:56:20 +00:00
Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-samsung
This commit is contained in:
commit
2d4925a096
14 changed files with 506 additions and 6 deletions
|
@ -3,6 +3,17 @@
|
||||||
* Copyright (c) 2023 Linaro Ltd.
|
* Copyright (c) 2023 Linaro Ltd.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
&soc {
|
||||||
|
/* TODO: Remove this node once it appears in upstream dts */
|
||||||
|
trng: rng@12081400 {
|
||||||
|
compatible = "samsung,exynos850-trng";
|
||||||
|
reg = <0x12081400 0x100>;
|
||||||
|
clocks = <&cmu_core CLK_GOUT_SSS_ACLK>,
|
||||||
|
<&cmu_core CLK_GOUT_SSS_PCLK>;
|
||||||
|
clock-names = "secss", "pclk";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
&pmu_system_controller {
|
&pmu_system_controller {
|
||||||
bootph-all;
|
bootph-all;
|
||||||
samsung,uart-debug-1;
|
samsung,uart-debug-1;
|
||||||
|
|
|
@ -250,6 +250,8 @@ config TARGET_E850_96
|
||||||
select PINCTRL
|
select PINCTRL
|
||||||
select PINCTRL_EXYNOS850
|
select PINCTRL_EXYNOS850
|
||||||
imply OF_UPSTREAM
|
imply OF_UPSTREAM
|
||||||
|
imply DM_RNG
|
||||||
|
imply RNG_EXYNOS
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
#
|
#
|
||||||
# Copyright (C) 2020, Linaro Limited
|
# Copyright (C) 2024, Linaro Limited
|
||||||
# Sam Protsenko <semen.protsenko@linaro.org>
|
# Sam Protsenko <semen.protsenko@linaro.org>
|
||||||
|
|
||||||
obj-y := e850-96.o
|
obj-y := e850-96.o fw.o
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0+
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2020, Linaro Limited
|
* Copyright (c) 2024, Linaro Ltd.
|
||||||
* Sam Protsenko <semen.protsenko@linaro.org>
|
* Author: Sam Protsenko <semen.protsenko@linaro.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
|
#include "fw.h"
|
||||||
|
|
||||||
int dram_init(void)
|
int dram_init(void)
|
||||||
{
|
{
|
||||||
|
@ -18,5 +19,6 @@ int dram_init_banksize(void)
|
||||||
|
|
||||||
int board_init(void)
|
int board_init(void)
|
||||||
{
|
{
|
||||||
|
load_ldfw();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
26
board/samsung/e850-96/e850-96.env
Normal file
26
board/samsung/e850-96/e850-96.env
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
partitions=
|
||||||
|
uuid_disk=${uuid_gpt_disk};
|
||||||
|
name=efs,start=512K,size=20M,uuid=${uuid_gpt_efs};
|
||||||
|
name=env,size=16K,uuid=${uuid_gpt_env};
|
||||||
|
name=kernel,size=30M,uuid=${uuid_gpt_kernel};
|
||||||
|
name=ramdisk,size=26M,uuid=${uuid_gpt_ramdisk};
|
||||||
|
name=dtbo,size=1M,uuid=${uuid_gpt_dtbo};
|
||||||
|
name=ldfw,size=4016K,uuid=${uuid_gpt_ldfw};
|
||||||
|
name=keystorage,size=8K,uuid=${uuid_gpt_keystorage};
|
||||||
|
name=tzsw,size=1M,uuid=${uuid_gpt_tzsw};
|
||||||
|
name=harx,size=2M,uuid=${uuid_gpt_harx};
|
||||||
|
name=harx_rkp,size=2M,uuid=${uuid_gpt_harx_rkp};
|
||||||
|
name=logo,size=40M,uuid=${uuid_gpt_logo};
|
||||||
|
name=super,size=3600M,uuid=${uuid_gpt_super};
|
||||||
|
name=cache,size=300M,uuid=${uuid_gpt_cache};
|
||||||
|
name=modem,size=100M,uuid=${uuid_gpt_modem};
|
||||||
|
name=boot,size=100M,uuid=${uuid_gpt_boot};
|
||||||
|
name=persist,size=30M,uuid=${uuid_gpt_persist};
|
||||||
|
name=recovery,size=40M,uuid=${uuid_gpt_recovery};
|
||||||
|
name=misc,size=40M,uuid=${uuid_gpt_misc};
|
||||||
|
name=mnv,size=20M,uuid=${uuid_gpt_mnv};
|
||||||
|
name=frp,size=512K,uuid=${uuid_gpt_frp};
|
||||||
|
name=vbmeta,size=64K,uuid=${uuid_gpt_vbmeta};
|
||||||
|
name=metadata,size=16M,uuid=${uuid_gpt_metadata};
|
||||||
|
name=dtb,size=1M,uuid=${uuid_gpt_dtb};
|
||||||
|
name=userdata,size=-,uuid=${uuid_gpt_userdata}
|
131
board/samsung/e850-96/fw.c
Normal file
131
board/samsung/e850-96/fw.c
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Linaro Ltd.
|
||||||
|
* Author: Sam Protsenko <semen.protsenko@linaro.org>
|
||||||
|
*
|
||||||
|
* Firmware loading code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <part.h>
|
||||||
|
#include <linux/arm-smccc.h>
|
||||||
|
#include "fw.h"
|
||||||
|
|
||||||
|
#define EMMC_IFACE "mmc"
|
||||||
|
#define EMMC_DEV_NUM 0
|
||||||
|
|
||||||
|
/* LDFW constants */
|
||||||
|
#define LDFW_PART_NAME "ldfw"
|
||||||
|
#define LDFW_NWD_ADDR 0x88000000
|
||||||
|
#define LDFW_MAGIC 0x10adab1e
|
||||||
|
#define SMC_CMD_LOAD_LDFW -0x500
|
||||||
|
#define SDM_HW_RESET_STATUS 0x1230
|
||||||
|
#define SDM_SW_RESET_STATUS 0x1231
|
||||||
|
#define SB_ERROR_PREFIX 0xfdaa0000
|
||||||
|
|
||||||
|
struct ldfw_header {
|
||||||
|
u32 magic;
|
||||||
|
u32 size;
|
||||||
|
u32 init_entry;
|
||||||
|
u32 entry_point;
|
||||||
|
u32 suspend_entry;
|
||||||
|
u32 resume_entry;
|
||||||
|
u32 start_smc_id;
|
||||||
|
u32 version;
|
||||||
|
u32 set_runtime_entry;
|
||||||
|
u32 reserved[3];
|
||||||
|
char fw_name[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int read_fw(const char *part_name, void *buf)
|
||||||
|
{
|
||||||
|
struct blk_desc *blk_desc;
|
||||||
|
struct disk_partition part;
|
||||||
|
unsigned long cnt;
|
||||||
|
int part_num;
|
||||||
|
|
||||||
|
blk_desc = blk_get_dev(EMMC_IFACE, EMMC_DEV_NUM);
|
||||||
|
if (!blk_desc) {
|
||||||
|
debug("%s: Can't get eMMC device\n", __func__);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
part_num = part_get_info_by_name(blk_desc, part_name, &part);
|
||||||
|
if (part_num < 0) {
|
||||||
|
debug("%s: Can't get LDWF partition\n", __func__);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt = blk_dread(blk_desc, part.start, part.size, buf);
|
||||||
|
if (cnt != part.size) {
|
||||||
|
debug("%s: Can't read LDFW partition\n", __func__);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_ldfw(void)
|
||||||
|
{
|
||||||
|
const phys_addr_t addr = (phys_addr_t)LDFW_NWD_ADDR;
|
||||||
|
struct ldfw_header *hdr;
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
void *buf = (void *)addr;
|
||||||
|
u64 size = 0;
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
/* Load LDFW from the block device partition into RAM buffer */
|
||||||
|
err = read_fw(LDFW_PART_NAME, buf);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Validate LDFW by magic number in its header */
|
||||||
|
hdr = buf;
|
||||||
|
if (hdr->magic != LDFW_MAGIC) {
|
||||||
|
debug("%s: Wrong LDFW magic; is LDFW flashed?\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate actual total size of all LDFW blobs */
|
||||||
|
for (i = 0; hdr->magic == LDFW_MAGIC; ++i) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
char name[17] = { 0 };
|
||||||
|
|
||||||
|
strncpy(name, hdr->fw_name, 16);
|
||||||
|
debug("%s: ldfw #%d: version = 0x%x, name = %s\n", __func__, i,
|
||||||
|
hdr->version, name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size += (u64)hdr->size;
|
||||||
|
hdr = (struct ldfw_header *)((u64)hdr + (u64)hdr->size);
|
||||||
|
}
|
||||||
|
debug("%s: The whole size of all LDFWs: 0x%llx\n", __func__, size);
|
||||||
|
|
||||||
|
/* Load LDFW firmware to SWD (Secure World) memory via EL3 monitor */
|
||||||
|
arm_smccc_smc(SMC_CMD_LOAD_LDFW, addr, size, 0, 0, 0, 0, 0, &res);
|
||||||
|
err = (int)res.a0;
|
||||||
|
if (err == -1 || err == SDM_HW_RESET_STATUS) {
|
||||||
|
debug("%s: Can't load LDFW in dump_gpr state\n", __func__);
|
||||||
|
return -EIO;
|
||||||
|
} else if (err == SDM_SW_RESET_STATUS) {
|
||||||
|
debug("%s: Can't load LDFW in kernel panic (SW RESET) state\n",
|
||||||
|
__func__);
|
||||||
|
return -EIO;
|
||||||
|
} else if (err < 0 && (err & 0xffff0000) == SB_ERROR_PREFIX) {
|
||||||
|
debug("%s: LDFW signature is corrupted! ret=0x%x\n", __func__,
|
||||||
|
(u32)err);
|
||||||
|
return -EIO;
|
||||||
|
} else if (err == 0) {
|
||||||
|
debug("%s: No LDFW is inited\n", __func__);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
u32 tried = res.a0 & 0xffff;
|
||||||
|
u32 failed = (res.a0 >> 16) & 0xffff;
|
||||||
|
|
||||||
|
debug("%s: %d/%d LDFWs have been loaded successfully\n", __func__,
|
||||||
|
tried - failed, tried);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
12
board/samsung/e850-96/fw.h
Normal file
12
board/samsung/e850-96/fw.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Linaro Ltd.
|
||||||
|
* Sam Protsenko <semen.protsenko@linaro.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __E850_96_FW_H
|
||||||
|
#define __E850_96_FW_H
|
||||||
|
|
||||||
|
int load_ldfw(void);
|
||||||
|
|
||||||
|
#endif /* __E850_96_FW_H */
|
|
@ -3,4 +3,4 @@
|
||||||
# Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
|
# Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
|
||||||
# Przemyslaw Marczak <p.marczak@samsung.com>
|
# Przemyslaw Marczak <p.marczak@samsung.com>
|
||||||
|
|
||||||
obj-y := odroid.o
|
obj-$(CONFIG_TARGET_ODROID) := odroid.o
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
if TARGET_ODROID_XU3
|
if TARGET_ODROID_XU3
|
||||||
|
|
||||||
config SYS_BOARD
|
config SYS_BOARD
|
||||||
default "smdk5420"
|
default "odroid"
|
||||||
|
|
||||||
config SYS_VENDOR
|
config SYS_VENDOR
|
||||||
default "samsung"
|
default "samsung"
|
||||||
|
|
|
@ -11,6 +11,7 @@ CONFIG_DEFAULT_DEVICE_TREE="exynos/exynos850-e850-96"
|
||||||
CONFIG_SYS_LOAD_ADDR=0x80000000
|
CONFIG_SYS_LOAD_ADDR=0x80000000
|
||||||
# CONFIG_AUTOBOOT is not set
|
# CONFIG_AUTOBOOT is not set
|
||||||
# CONFIG_DISPLAY_CPUINFO is not set
|
# CONFIG_DISPLAY_CPUINFO is not set
|
||||||
|
CONFIG_CMD_RNG=y
|
||||||
# CONFIG_NET is not set
|
# CONFIG_NET is not set
|
||||||
CONFIG_CLK_EXYNOS850=y
|
CONFIG_CLK_EXYNOS850=y
|
||||||
# CONFIG_MMC is not set
|
# CONFIG_MMC is not set
|
||||||
|
|
|
@ -323,14 +323,18 @@ U_BOOT_DRIVER(exynos850_cmu_peri) = {
|
||||||
/* Register Offset definitions for CMU_CORE (0x12000000) */
|
/* Register Offset definitions for CMU_CORE (0x12000000) */
|
||||||
#define PLL_CON0_MUX_CLKCMU_CORE_BUS_USER 0x0600
|
#define PLL_CON0_MUX_CLKCMU_CORE_BUS_USER 0x0600
|
||||||
#define PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER 0x0620
|
#define PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER 0x0620
|
||||||
|
#define PLL_CON0_MUX_CLKCMU_CORE_SSS_USER 0x0630
|
||||||
#define CLK_CON_DIV_DIV_CLK_CORE_BUSP 0x1800
|
#define CLK_CON_DIV_DIV_CLK_CORE_BUSP 0x1800
|
||||||
#define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_I_ACLK 0x20e8
|
#define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_I_ACLK 0x20e8
|
||||||
#define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN 0x20ec
|
#define CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN 0x20ec
|
||||||
|
#define CLK_CON_GAT_GOUT_CORE_SSS_I_ACLK 0x2128
|
||||||
|
#define CLK_CON_GAT_GOUT_CORE_SSS_I_PCLK 0x212c
|
||||||
|
|
||||||
/* List of parent clocks for Muxes in CMU_CORE */
|
/* List of parent clocks for Muxes in CMU_CORE */
|
||||||
PNAME(mout_core_bus_user_p) = { "clock-oscclk", "dout_core_bus" };
|
PNAME(mout_core_bus_user_p) = { "clock-oscclk", "dout_core_bus" };
|
||||||
PNAME(mout_core_mmc_embd_user_p) = { "clock-oscclk",
|
PNAME(mout_core_mmc_embd_user_p) = { "clock-oscclk",
|
||||||
"dout_core_mmc_embd" };
|
"dout_core_mmc_embd" };
|
||||||
|
PNAME(mout_core_sss_user_p) = { "clock-oscclk", "dout_core_sss" };
|
||||||
|
|
||||||
static const struct samsung_mux_clock core_mux_clks[] = {
|
static const struct samsung_mux_clock core_mux_clks[] = {
|
||||||
MUX(CLK_MOUT_CORE_BUS_USER, "mout_core_bus_user", mout_core_bus_user_p,
|
MUX(CLK_MOUT_CORE_BUS_USER, "mout_core_bus_user", mout_core_bus_user_p,
|
||||||
|
@ -338,6 +342,8 @@ static const struct samsung_mux_clock core_mux_clks[] = {
|
||||||
MUX_F(CLK_MOUT_CORE_MMC_EMBD_USER, "mout_core_mmc_embd_user",
|
MUX_F(CLK_MOUT_CORE_MMC_EMBD_USER, "mout_core_mmc_embd_user",
|
||||||
mout_core_mmc_embd_user_p, PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER,
|
mout_core_mmc_embd_user_p, PLL_CON0_MUX_CLKCMU_CORE_MMC_EMBD_USER,
|
||||||
4, 1, CLK_SET_RATE_PARENT, 0),
|
4, 1, CLK_SET_RATE_PARENT, 0),
|
||||||
|
MUX(CLK_MOUT_CORE_SSS_USER, "mout_core_sss_user", mout_core_sss_user_p,
|
||||||
|
PLL_CON0_MUX_CLKCMU_CORE_SSS_USER, 4, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_div_clock core_div_clks[] = {
|
static const struct samsung_div_clock core_div_clks[] = {
|
||||||
|
@ -351,6 +357,10 @@ static const struct samsung_gate_clock core_gate_clks[] = {
|
||||||
GATE(CLK_GOUT_MMC_EMBD_SDCLKIN, "gout_mmc_embd_sdclkin",
|
GATE(CLK_GOUT_MMC_EMBD_SDCLKIN, "gout_mmc_embd_sdclkin",
|
||||||
"mout_core_mmc_embd_user", CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN,
|
"mout_core_mmc_embd_user", CLK_CON_GAT_GOUT_CORE_MMC_EMBD_SDCLKIN,
|
||||||
21, CLK_SET_RATE_PARENT, 0),
|
21, CLK_SET_RATE_PARENT, 0),
|
||||||
|
GATE(CLK_GOUT_SSS_ACLK, "gout_sss_aclk", "mout_core_sss_user",
|
||||||
|
CLK_CON_GAT_GOUT_CORE_SSS_I_ACLK, 21, 0, 0),
|
||||||
|
GATE(CLK_GOUT_SSS_PCLK, "gout_sss_pclk", "dout_core_busp",
|
||||||
|
CLK_CON_GAT_GOUT_CORE_SSS_I_PCLK, 21, 0, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct samsung_clk_group core_cmu_clks[] = {
|
static const struct samsung_clk_group core_cmu_clks[] = {
|
||||||
|
|
|
@ -120,4 +120,17 @@ config RNG_TURRIS_RWTM
|
||||||
on other Armada-3700 devices (like EspressoBin) if Secure
|
on other Armada-3700 devices (like EspressoBin) if Secure
|
||||||
Firmware from CZ.NIC is used.
|
Firmware from CZ.NIC is used.
|
||||||
|
|
||||||
|
config RNG_EXYNOS
|
||||||
|
bool "Samsung Exynos True Random Number Generator support"
|
||||||
|
depends on DM_RNG
|
||||||
|
help
|
||||||
|
Enable support for True Random Number Generator (TRNG) available on
|
||||||
|
Exynos SoCs.
|
||||||
|
|
||||||
|
On some chips (like Exynos850) TRNG registers are protected with TZPC
|
||||||
|
(TrustZone Protection Control). For such chips the driver provides an
|
||||||
|
implementation based on SMC calls to EL3 monitor program. In that
|
||||||
|
case the LDFW (Loadable Firmware) has to be loaded first, as it
|
||||||
|
actually implements TRNG SMC calls.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -18,3 +18,4 @@ obj-$(CONFIG_RNG_ARM_RNDR) += arm_rndr.o
|
||||||
obj-$(CONFIG_TPM_RNG) += tpm_rng.o
|
obj-$(CONFIG_TPM_RNG) += tpm_rng.o
|
||||||
obj-$(CONFIG_RNG_JH7110) += jh7110_rng.o
|
obj-$(CONFIG_RNG_JH7110) += jh7110_rng.o
|
||||||
obj-$(CONFIG_RNG_TURRIS_RWTM) += turris_rwtm_rng.o
|
obj-$(CONFIG_RNG_TURRIS_RWTM) += turris_rwtm_rng.o
|
||||||
|
obj-$(CONFIG_RNG_EXYNOS) += exynos-trng.o
|
||||||
|
|
291
drivers/rng/exynos-trng.c
Normal file
291
drivers/rng/exynos-trng.c
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Linaro Ltd.
|
||||||
|
* Author: Sam Protsenko <semen.protsenko@linaro.org>
|
||||||
|
*
|
||||||
|
* Samsung Exynos TRNG driver (True Random Number Generator).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <clk.h>
|
||||||
|
#include <dm.h>
|
||||||
|
#include <rng.h>
|
||||||
|
#include <dm/device.h>
|
||||||
|
#include <dm/device_compat.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <linux/arm-smccc.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
|
||||||
|
#define EXYNOS_TRNG_CLKDIV 0x0
|
||||||
|
#define EXYNOS_TRNG_CLKDIV_MASK GENMASK(15, 0)
|
||||||
|
#define EXYNOS_TRNG_CLOCK_RATE 500000
|
||||||
|
|
||||||
|
#define EXYNOS_TRNG_CTRL 0x20
|
||||||
|
#define EXYNOS_TRNG_CTRL_RNGEN BIT(31)
|
||||||
|
|
||||||
|
#define EXYNOS_TRNG_POST_CTRL 0x30
|
||||||
|
#define EXYNOS_TRNG_ONLINE_CTRL 0x40
|
||||||
|
#define EXYNOS_TRNG_ONLINE_STAT 0x44
|
||||||
|
#define EXYNOS_TRNG_ONLINE_MAXCHI2 0x48
|
||||||
|
#define EXYNOS_TRNG_FIFO_CTRL 0x50
|
||||||
|
#define EXYNOS_TRNG_FIFO_0 0x80
|
||||||
|
#define EXYNOS_TRNG_FIFO_1 0x84
|
||||||
|
#define EXYNOS_TRNG_FIFO_2 0x88
|
||||||
|
#define EXYNOS_TRNG_FIFO_3 0x8c
|
||||||
|
#define EXYNOS_TRNG_FIFO_4 0x90
|
||||||
|
#define EXYNOS_TRNG_FIFO_5 0x94
|
||||||
|
#define EXYNOS_TRNG_FIFO_6 0x98
|
||||||
|
#define EXYNOS_TRNG_FIFO_7 0x9c
|
||||||
|
#define EXYNOS_TRNG_FIFO_LEN 8
|
||||||
|
#define EXYNOS_TRNG_FIFO_TIMEOUT (1 * USEC_PER_SEC)
|
||||||
|
|
||||||
|
#define EXYNOS_SMC_CALL_VAL(func_num) \
|
||||||
|
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||||
|
ARM_SMCCC_SMC_32, \
|
||||||
|
ARM_SMCCC_OWNER_SIP, \
|
||||||
|
func_num)
|
||||||
|
|
||||||
|
/* SMC command for DTRNG access */
|
||||||
|
#define SMC_CMD_RANDOM EXYNOS_SMC_CALL_VAL(0x1012)
|
||||||
|
|
||||||
|
/* SMC_CMD_RANDOM: arguments */
|
||||||
|
#define HWRNG_INIT 0x0
|
||||||
|
#define HWRNG_EXIT 0x1
|
||||||
|
#define HWRNG_GET_DATA 0x2
|
||||||
|
|
||||||
|
/* SMC_CMD_RANDOM: return values */
|
||||||
|
#define HWRNG_RET_OK 0x0
|
||||||
|
#define HWRNG_RET_RETRY_ERROR 0x2
|
||||||
|
|
||||||
|
#define HWRNG_MAX_TRIES 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct exynos_trng_variant - Chip specific data
|
||||||
|
*
|
||||||
|
* @smc: Set "true" if TRNG block has to be accessed via SMC calls
|
||||||
|
* @init: (Optional) TRNG initialization function to call on probe
|
||||||
|
* @exit: (Optional) TRNG deinitialization function to call on remove
|
||||||
|
* @read: Function to read the random data from TRNG block
|
||||||
|
*/
|
||||||
|
struct exynos_trng_variant {
|
||||||
|
bool smc;
|
||||||
|
int (*init)(struct udevice *dev);
|
||||||
|
void (*exit)(struct udevice *dev);
|
||||||
|
int (*read)(struct udevice *dev, void *data, size_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct exynos_trng_priv - Driver's private data
|
||||||
|
*
|
||||||
|
* @base: Base address of MMIO registers of TRNG block
|
||||||
|
* @clk: Operating clock (needed for TRNG block functioning)
|
||||||
|
* @pclk: Bus clock (needed for interfacing the TRNG block registers)
|
||||||
|
* @data: Chip specific data
|
||||||
|
*/
|
||||||
|
struct exynos_trng_priv {
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
struct clk *pclk;
|
||||||
|
const struct exynos_trng_variant *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int exynos_trng_read_reg(struct udevice *dev, void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
int val;
|
||||||
|
|
||||||
|
len = min_t(size_t, len, EXYNOS_TRNG_FIFO_LEN * 4);
|
||||||
|
writel_relaxed(len * 8, trng->base + EXYNOS_TRNG_FIFO_CTRL);
|
||||||
|
val = readl_poll_timeout(trng->base + EXYNOS_TRNG_FIFO_CTRL, val,
|
||||||
|
val == 0, EXYNOS_TRNG_FIFO_TIMEOUT);
|
||||||
|
if (val < 0)
|
||||||
|
return val;
|
||||||
|
|
||||||
|
memcpy_fromio(data, trng->base + EXYNOS_TRNG_FIFO_0, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_read_smc(struct udevice *dev, void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
unsigned int copied = 0;
|
||||||
|
u32 *buf = data;
|
||||||
|
int tries = 0;
|
||||||
|
|
||||||
|
while (copied < len) {
|
||||||
|
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
|
||||||
|
&res);
|
||||||
|
switch (res.a0) {
|
||||||
|
case HWRNG_RET_OK:
|
||||||
|
*buf++ = res.a2;
|
||||||
|
*buf++ = res.a3;
|
||||||
|
copied += 8;
|
||||||
|
tries = 0;
|
||||||
|
break;
|
||||||
|
case HWRNG_RET_RETRY_ERROR:
|
||||||
|
if (++tries >= HWRNG_MAX_TRIES)
|
||||||
|
return -EIO;
|
||||||
|
udelay(10);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_init_reg(struct udevice *dev)
|
||||||
|
{
|
||||||
|
const u32 max_div = EXYNOS_TRNG_CLKDIV_MASK;
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
unsigned long sss_rate;
|
||||||
|
u32 div;
|
||||||
|
|
||||||
|
sss_rate = clk_get_rate(trng->clk);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For most TRNG circuits the clock frequency of under 500 kHz is safe.
|
||||||
|
* The clock divider should be an even number.
|
||||||
|
*/
|
||||||
|
div = sss_rate / EXYNOS_TRNG_CLOCK_RATE;
|
||||||
|
div -= div % 2; /* make sure it's even */
|
||||||
|
if (div > max_div) {
|
||||||
|
dev_err(dev, "Clock divider too large: %u", div);
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
writel_relaxed(div, trng->base + EXYNOS_TRNG_CLKDIV);
|
||||||
|
|
||||||
|
/* Enable the generator */
|
||||||
|
writel_relaxed(EXYNOS_TRNG_CTRL_RNGEN, trng->base + EXYNOS_TRNG_CTRL);
|
||||||
|
|
||||||
|
/* Disable post-processing */
|
||||||
|
writel_relaxed(0, trng->base + EXYNOS_TRNG_POST_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_init_smc(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0, &res);
|
||||||
|
if (res.a0 != HWRNG_RET_OK) {
|
||||||
|
dev_err(dev, "SMC command for TRNG init failed (%d)\n",
|
||||||
|
(int)res.a0);
|
||||||
|
ret = -EIO;
|
||||||
|
}
|
||||||
|
if ((int)res.a0 == -1)
|
||||||
|
dev_info(dev, "Make sure LDFW is loaded\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exynos_trng_exit_smc(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0, &res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_read(struct udevice *dev, void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
|
||||||
|
return trng->data->read(dev, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_of_to_plat(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
|
||||||
|
trng->data = (struct exynos_trng_variant *)dev_get_driver_data(dev);
|
||||||
|
if (!trng->data->smc) {
|
||||||
|
trng->base = dev_read_addr_ptr(dev);
|
||||||
|
if (!trng->base)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
trng->clk = devm_clk_get(dev, "secss");
|
||||||
|
if (IS_ERR(trng->clk))
|
||||||
|
return PTR_ERR(trng->clk);
|
||||||
|
|
||||||
|
trng->pclk = devm_clk_get_optional(dev, "pclk");
|
||||||
|
if (IS_ERR(trng->pclk))
|
||||||
|
return PTR_ERR(trng->pclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_probe(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_enable(trng->pclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_enable(trng->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (trng->data->init)
|
||||||
|
ret = trng->data->init(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos_trng_remove(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct exynos_trng_priv *trng = dev_get_priv(dev);
|
||||||
|
|
||||||
|
if (trng->data->exit)
|
||||||
|
trng->data->exit(dev);
|
||||||
|
|
||||||
|
/* Keep SSS clocks enabled, they are needed for EL3_MON and kernel */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dm_rng_ops exynos_trng_ops = {
|
||||||
|
.read = exynos_trng_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct exynos_trng_variant exynos5250_trng_data = {
|
||||||
|
.init = exynos_trng_init_reg,
|
||||||
|
.read = exynos_trng_read_reg,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct exynos_trng_variant exynos850_trng_data = {
|
||||||
|
.smc = true,
|
||||||
|
.init = exynos_trng_init_smc,
|
||||||
|
.exit = exynos_trng_exit_smc,
|
||||||
|
.read = exynos_trng_read_smc,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct udevice_id exynos_trng_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "samsung,exynos5250-trng",
|
||||||
|
.data = (ulong)&exynos5250_trng_data,
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,exynos850-trng",
|
||||||
|
.data = (ulong)&exynos850_trng_data,
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
U_BOOT_DRIVER(exynos_trng) = {
|
||||||
|
.name = "exynos-trng",
|
||||||
|
.id = UCLASS_RNG,
|
||||||
|
.of_match = exynos_trng_match,
|
||||||
|
.of_to_plat = exynos_trng_of_to_plat,
|
||||||
|
.probe = exynos_trng_probe,
|
||||||
|
.remove = exynos_trng_remove,
|
||||||
|
.ops = &exynos_trng_ops,
|
||||||
|
.priv_auto = sizeof(struct exynos_trng_priv),
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue