Skip to content

Commit

Permalink
general-legacy-rockchip-hwrng(:1)
Browse files Browse the repository at this point in the history
Original-Subject: [ARCHEOLOGY] Restored Hardware Random Number Generator from legacy (4.4) kernel (#4286)
> X-Git-Archeology: > recovered message: > so boot no longer starves for entropy, delaying for up to 15 seconds
> X-Git-Archeology: > recovered message: > Advances kernel to v5.19.15
> X-Git-Archeology: - Revision f6f3f1b8b034bf7c1f069e831fc42a46940d2239: armbian/build@f6f3f1b
> X-Git-Archeology:   Date: Sat, 15 Oct 2022 10:46:04 +0200
> X-Git-Archeology:   From: brentr <brent@mbari.org>
> X-Git-Archeology:   Subject: Restored Hardware Random Number Generator from legacy (4.4) kernel (#4286)
> X-Git-Archeology: 
> X-Git-Archeology: - Revision 92f1a22d76b987afa7ba555d5b509adc51d689e7: armbian/build@92f1a22
> X-Git-Archeology:   Date: Fri, 16 Dec 2022 13:38:13 +0100
> X-Git-Archeology:   From: Igor Pecovnik <igorpecovnik@users.noreply.github.com>
> X-Git-Archeology:   Subject: Re-add rockchip64 6.0 patches (#4575)
> X-Git-Archeology: 
X-Armbian: Patch-File: general-legacy-rockchip-hwrng
X-Armbian: Patch-File-Counter: 1
X-Armbian: Patch-Rel-Directory: patch/kernel/archive/rockchip64-6.1
X-Armbian: Patch-Type: kernel
X-Armbian: Patch-Root-Type: core
X-Armbian: Patch-Sub-Type: common
X-Armbian: Original-Subject: [ARCHEOLOGY] Restored Hardware Random Number Generator from legacy (4.4) kernel (#4286)
  • Loading branch information
brentr authored and Armbian AutoPatcher committed Oct 15, 2022
1 parent 3c837e5 commit 315a5d2
Show file tree
Hide file tree
Showing 4 changed files with 359 additions and 0 deletions.
15 changes: 15 additions & 0 deletions arch/arm64/boot/dts/rockchip/rk3308.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,21 @@
};
};

rng: rng@ff2f0000 {
compatible = "rockchip,cryptov2-rng";
reg = <0x0 0xff2f0000 0x0 0x4000>;
clocks = <&cru SCLK_CRYPTO>, <&cru SCLK_CRYPTO_APK>,
<&cru ACLK_CRYPTO>, <&cru HCLK_CRYPTO>;
clock-names = "clk_crypto", "clk_crypto_apk",
"aclk_crypto", "hclk_crypto";
assigned-clocks = <&cru SCLK_CRYPTO>, <&cru SCLK_CRYPTO_APK>,
<&cru ACLK_CRYPTO>, <&cru HCLK_CRYPTO>;
assigned-clock-rates = <150000000>, <150000000>,
<200000000>, <100000000>;
resets = <&cru SRST_CRYPTO>;
reset-names = "reset";
};

tsadc: tsadc@ff1f0000 {
compatible = "rockchip,rk3308-tsadc";
reg = <0x0 0xff1f0000 0x0 0x100>;
Expand Down
13 changes: 13 additions & 0 deletions drivers/char/hw_random/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,19 @@ config HW_RANDOM_STM32

If unsure, say N.

config HW_RANDOM_ROCKCHIP
tristate "Rockchip Random Number Generator support"
depends on ARCH_ROCKCHIP
default HW_RANDOM
help
This driver provides kernel-side support for the Random Number
Generator hardware found on Rockchip cpus.

To compile this driver as a module, choose M here: the
module will be called rockchip-rng.

If unsure, say Y.

config HW_RANDOM_PIC32
tristate "Microchip PIC32 Random Number Generator support"
depends on HW_RANDOM && MACH_PIC32
Expand Down
1 change: 1 addition & 0 deletions drivers/char/hw_random/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o
obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o
obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-rng.o
obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o
obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
Expand Down
330 changes: 330 additions & 0 deletions drivers/char/hw_random/rockchip-rng.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
// SPDX-License-Identifier: GPL-2.0
/*
* rockchip-rng.c Random Number Generator driver for the Rockchip
*
* Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
* Author: Lin Jinhan <troy.lin@rock-chips.com>
*
*/
#include <linux/clk.h>
#include <linux/hw_random.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#define _SBF(s, v) ((v) << (s))
#define HIWORD_UPDATE(val, mask, shift) \
((val) << (shift) | (mask) << ((shift) + 16))

#define ROCKCHIP_AUTOSUSPEND_DELAY 100
#define ROCKCHIP_POLL_PERIOD_US 100
#define ROCKCHIP_POLL_TIMEOUT_US 10000
#define RK_MAX_RNG_BYTE (32)

#define CRYPTO_V1_CTRL 0x0008
#define CRYPTO_V1_RNG_START BIT(8)
#define CRYPTO_V1_RNG_FLUSH BIT(9)
#define CRYPTO_V1_TRNG_CTRL 0x0200
#define CRYPTO_V1_OSC_ENABLE BIT(16)
#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x) (x)
#define CRYPTO_V1_TRNG_DOUT_0 0x0204

#define CRYPTO_V2_RNG_CTL 0x0400
#define CRYPTO_V2_RNG_64_BIT_LEN _SBF(4, 0x00)
#define CRYPTO_V2_RNG_128_BIT_LEN _SBF(4, 0x01)
#define CRYPTO_V2_RNG_192_BIT_LEN _SBF(4, 0x02)
#define CRYPTO_V2_RNG_256_BIT_LEN _SBF(4, 0x03)
#define CRYPTO_V2_RNG_FATESY_SOC_RING _SBF(2, 0x00)
#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0 _SBF(2, 0x01)
#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1 _SBF(2, 0x02)
#define CRYPTO_V2_RNG_SLOWEST_SOC_RING _SBF(2, 0x03)
#define CRYPTO_V2_RNG_ENABLE BIT(1)
#define CRYPTO_V2_RNG_START BIT(0)
#define CRYPTO_V2_RNG_SAMPLE_CNT 0x0404
#define CRYPTO_V2_RNG_DOUT_0 0x0410

struct rk_rng_soc_data {
const char * const *clks;
int clks_num;
int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait);
};

struct rk_rng {
struct device *dev;
struct hwrng rng;
void __iomem *mem;
struct rk_rng_soc_data *soc_data;
u32 clk_num;
struct clk_bulk_data *clk_bulks;
};

static const char * const rk_rng_v1_clks[] = {
"hclk_crypto",
"clk_crypto",
};

static const char * const rk_rng_v2_clks[] = {
"hclk_crypto",
"aclk_crypto",
"clk_crypto",
"clk_crypto_apk",
};

static void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset)
{
__raw_writel(val, rng->mem + offset);
}

static u32 rk_rng_readl(struct rk_rng *rng, u32 offset)
{
return __raw_readl(rng->mem + offset);
}

static int rk_rng_init(struct hwrng *rng)
{
int ret;
struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);

ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks);
if (ret < 0) {
dev_err(rk_rng->dev, "failed to enable clks %d\n", ret);
return ret;
}

return 0;
}

static void rk_rng_cleanup(struct hwrng *rng)
{
struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);

clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
}

static void rk_rng_read_regs(struct rk_rng *rng, u32 offset, void *buf,
size_t size)
{
u32 i, sample;

for (i = 0; i < size; i += 4) {
sample = rk_rng_readl(rng, offset + i);
memcpy(buf + i, &sample, sizeof(sample));
}
}

static int rk_rng_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
int ret = 0;
u32 reg_ctrl = 0;
struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);

ret = pm_runtime_get_sync(rk_rng->dev);
if (ret < 0) {
pm_runtime_put_noidle(rk_rng->dev);
return ret;
}

/* enable osc_ring to get entropy, sample period is set as 100 */
reg_ctrl = CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100);
rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_TRNG_CTRL);

reg_ctrl = HIWORD_UPDATE(CRYPTO_V1_RNG_START, CRYPTO_V1_RNG_START, 0);

rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_CTRL);

ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V1_CTRL, reg_ctrl,
!(reg_ctrl & CRYPTO_V1_RNG_START),
ROCKCHIP_POLL_PERIOD_US,
ROCKCHIP_POLL_TIMEOUT_US);
if (ret < 0)
goto out;

ret = min_t(size_t, max, RK_MAX_RNG_BYTE);

rk_rng_read_regs(rk_rng, CRYPTO_V1_TRNG_DOUT_0, buf, ret);

out:
/* close TRNG */
rk_rng_writel(rk_rng, HIWORD_UPDATE(0, CRYPTO_V1_RNG_START, 0),
CRYPTO_V1_CTRL);

pm_runtime_mark_last_busy(rk_rng->dev);
pm_runtime_put_sync_autosuspend(rk_rng->dev);

return ret;
}

static int rk_rng_v2_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
int ret = 0;
u32 reg_ctrl = 0;
struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);

ret = pm_runtime_get_sync(rk_rng->dev);
if (ret < 0) {
pm_runtime_put_noidle(rk_rng->dev);
return ret;
}

/* enable osc_ring to get entropy, sample period is set as 100 */
rk_rng_writel(rk_rng, 100, CRYPTO_V2_RNG_SAMPLE_CNT);

reg_ctrl |= CRYPTO_V2_RNG_256_BIT_LEN;
reg_ctrl |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
reg_ctrl |= CRYPTO_V2_RNG_ENABLE;
reg_ctrl |= CRYPTO_V2_RNG_START;

rk_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0),
CRYPTO_V2_RNG_CTL);

ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V2_RNG_CTL, reg_ctrl,
!(reg_ctrl & CRYPTO_V2_RNG_START),
ROCKCHIP_POLL_PERIOD_US,
ROCKCHIP_POLL_TIMEOUT_US);
if (ret < 0)
goto out;

ret = min_t(size_t, max, RK_MAX_RNG_BYTE);

rk_rng_read_regs(rk_rng, CRYPTO_V2_RNG_DOUT_0, buf, ret);

out:
/* close TRNG */
rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), CRYPTO_V2_RNG_CTL);

pm_runtime_mark_last_busy(rk_rng->dev);
pm_runtime_put_sync_autosuspend(rk_rng->dev);

return ret;
}

static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
.clks_num = ARRAY_SIZE(rk_rng_v1_clks),
.clks = rk_rng_v1_clks,
.rk_rng_read = rk_rng_v1_read,
};

static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
.clks_num = ARRAY_SIZE(rk_rng_v2_clks),
.clks = rk_rng_v2_clks,
.rk_rng_read = rk_rng_v2_read,
};

static const struct of_device_id rk_rng_dt_match[] = {
{
.compatible = "rockchip,cryptov1-rng",
.data = (void *)&rk_rng_v1_soc_data,
},
{
.compatible = "rockchip,cryptov2-rng",
.data = (void *)&rk_rng_v2_soc_data,
},
{ },
};

MODULE_DEVICE_TABLE(of, rk_rng_dt_match);

static int rk_rng_probe(struct platform_device *pdev)
{
int i;
int ret;
struct rk_rng *rk_rng;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;

rk_rng = devm_kzalloc(&pdev->dev, sizeof(struct rk_rng), GFP_KERNEL);
if (!rk_rng)
return -ENOMEM;

match = of_match_node(rk_rng_dt_match, np);
rk_rng->soc_data = (struct rk_rng_soc_data *)match->data;

rk_rng->dev = &pdev->dev;
rk_rng->rng.name = "rockchip";
#ifndef CONFIG_PM
rk_rng->rng.init = rk_rng_init;
rk_rng->rng.cleanup = rk_rng_cleanup,
#endif
rk_rng->rng.read = rk_rng->soc_data->rk_rng_read;
rk_rng->rng.quality = 1024;

rk_rng->clk_bulks =
devm_kzalloc(&pdev->dev, sizeof(*rk_rng->clk_bulks) *
rk_rng->soc_data->clks_num, GFP_KERNEL);

rk_rng->clk_num = rk_rng->soc_data->clks_num;

for (i = 0; i < rk_rng->soc_data->clks_num; i++)
rk_rng->clk_bulks[i].id = rk_rng->soc_data->clks[i];

rk_rng->mem = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
if (IS_ERR(rk_rng->mem))
return PTR_ERR(rk_rng->mem);

ret = devm_clk_bulk_get(&pdev->dev, rk_rng->clk_num,
rk_rng->clk_bulks);
if (ret) {
dev_err(&pdev->dev, "failed to get clks property\n");
return ret;
}

platform_set_drvdata(pdev, rk_rng);

pm_runtime_set_autosuspend_delay(&pdev->dev,
ROCKCHIP_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);

ret = devm_hwrng_register(&pdev->dev, &rk_rng->rng);
if (ret) {
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}

return ret;
}

#ifdef CONFIG_PM
static int rk_rng_runtime_suspend(struct device *dev)
{
struct rk_rng *rk_rng = dev_get_drvdata(dev);

rk_rng_cleanup(&rk_rng->rng);

return 0;
}

static int rk_rng_runtime_resume(struct device *dev)
{
struct rk_rng *rk_rng = dev_get_drvdata(dev);

return rk_rng_init(&rk_rng->rng);
}

static const struct dev_pm_ops rk_rng_pm_ops = {
SET_RUNTIME_PM_OPS(rk_rng_runtime_suspend,
rk_rng_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
#endif

static struct platform_driver rk_rng_driver = {
.driver = {
.name = "rockchip-rng",
#ifdef CONFIG_PM
.pm = &rk_rng_pm_ops,
#endif
.of_match_table = rk_rng_dt_match,
},
.probe = rk_rng_probe,
};

module_platform_driver(rk_rng_driver);

MODULE_DESCRIPTION("ROCKCHIP H/W Random Number Generator driver");
MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>");
MODULE_LICENSE("GPL v2");

0 comments on commit 315a5d2

Please sign in to comment.