Skip to content

Commit 667e142

Browse files
author
Maciej Falkowski
committed
rust: drivers: char: hw_random: add Exynos HWRNG driver
Signed-off-by: Maciej Falkowski <[email protected]>
1 parent 707f24f commit 667e142

File tree

4 files changed

+166
-2
lines changed

4 files changed

+166
-2
lines changed

drivers/char/hw_random/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,19 @@ config HW_RANDOM_EXYNOS
479479

480480
If unsure, say Y.
481481

482+
config HW_RANDOM_EXYNOS_RUST
483+
tristate "Rust version of Samsung Exynos True Random Number Generator support"
484+
depends on HAS_RUST && ARCH_EXYNOS
485+
default HW_RANDOM
486+
help
487+
This driver provides support for the True Random Number
488+
Generator available in Exynos SoCs.
489+
490+
To compile this driver as a module, choose M here: the module
491+
will be called exynos_trng_rust.
492+
493+
If unsure, say Y.
494+
482495
config HW_RANDOM_OPTEE
483496
tristate "OP-TEE based Random Number Generator support"
484497
depends on OPTEE

drivers/char/hw_random/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
1515
n2-rng-y := n2-drv.o n2-asm.o
1616
obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
1717
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-trng.o
18+
obj-$(CONFIG_HW_RANDOM_EXYNOS_RUST) += exynos_trng_rust.o
1819
obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
1920
obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
2021
obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! RNG driver for Exynos TRNGs
4+
//!
5+
//! Author: Maciej Falkowski <[email protected]>
6+
//!
7+
//! Copyright 2021 (c) Samsung Electronics Software, Inc.
8+
//!
9+
//! Based on the Exynos TRNG driver drivers/char/hw_random/exynos-rng by
10+
//! Łukasz Stelmach <[email protected]>
11+
12+
#![no_std]
13+
#![feature(allocator_api, global_asm)]
14+
15+
use core::{cmp::min, convert::TryInto, str};
16+
17+
use kernel::{
18+
c_str,
19+
clk::{self, Clk},
20+
declare_hwrng_operations,
21+
device::RawDevice,
22+
hw_random::{self, HwrngOperations},
23+
io_mem::{self, IoMem},
24+
of::ConstOfMatchTable,
25+
platdev::{self, PlatformDevice, PlatformDriver},
26+
prelude::*,
27+
};
28+
29+
module! {
30+
type: ExynosTrngDriverModule,
31+
name: b"exynos_trng_rust",
32+
author: b"Maciej Falkowski <[email protected]>",
33+
description: b"RNG driver for Exynos TRNGs",
34+
license: b"GPL v2",
35+
}
36+
37+
const EXYNOS_TRNG_CLKDIV: usize = 0x0;
38+
39+
const EXYNOS_TRNG_CTRL: usize = 0x20;
40+
const EXYNOS_TRNG_CTRL_RNGEN: u32 = 0x1 << 31; // BIT(31)
41+
42+
const EXYNOS_REG_SIZE: usize = 0x100;
43+
const EXYNOS_TRNG_POST_CTRL: usize = 0x30;
44+
const _EXYNOS_TRNG_ONLINE_CTRL: usize = 0x40;
45+
const _EXYNOS_TRNG_ONLINE_STAT: usize = 0x44;
46+
const _EXYNOS_TRNG_ONLINE_MAXCHI2: usize = 0x48;
47+
const EXYNOS_TRNG_FIFO_CTRL: usize = 0x50;
48+
const EXYNOS_TRNG_FIFO_0: usize = 0x80;
49+
const _EXYNOS_TRNG_FIFO_1: usize = 0x84;
50+
const _EXYNOS_TRNG_FIFO_2: usize = 0x88;
51+
const _EXYNOS_TRNG_FIFO_3: usize = 0x8c;
52+
const _EXYNOS_TRNG_FIFO_4: usize = 0x90;
53+
const _EXYNOS_TRNG_FIFO_5: usize = 0x94;
54+
const _EXYNOS_TRNG_FIFO_6: usize = 0x98;
55+
const _EXYNOS_TRNG_FIFO_7: usize = 0x9c;
56+
const EXYNOS_TRNG_FIFO_LEN: u32 = 8;
57+
const EXYNOS_TRNG_CLOCK_RATE: u32 = 500000;
58+
59+
struct ExynosTrngDevice {
60+
clk: Clk,
61+
mem: IoMem<EXYNOS_REG_SIZE>,
62+
}
63+
64+
impl HwrngOperations for ExynosTrngDevice {
65+
declare_hwrng_operations!(init);
66+
67+
type Data = Box<ExynosTrngDevice>;
68+
69+
fn init(trng: &Self) -> Result {
70+
let sss_rate = trng.clk.get_rate();
71+
72+
// For most TRNG circuits the clock frequency of under 500 kHz
73+
// is safe.
74+
let mut val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
75+
if val > 0x7fff {
76+
// dev_err(trng->dev, "clock divider too large: %d", val);
77+
pr_info!("rust_hwrng_init(): clock divider too large: {}\n", val);
78+
return Err(Error::ERANGE);
79+
}
80+
81+
val <<= 1;
82+
trng.mem.writel_relaxed(val, EXYNOS_TRNG_CLKDIV);
83+
84+
// Enable the generator.
85+
val = EXYNOS_TRNG_CTRL_RNGEN;
86+
trng.mem.writel_relaxed(val, EXYNOS_TRNG_CTRL);
87+
88+
// Disable post-processing. /dev/hwrng is supposed to deliver
89+
// unprocessed data.
90+
trng.mem.writel_relaxed(0, EXYNOS_TRNG_POST_CTRL);
91+
92+
Ok(())
93+
}
94+
95+
fn read(trng: &Self, data: &mut [i8], _wait: bool) -> Result<i32> {
96+
let max: u32 = min(data.len().try_into()?, EXYNOS_TRNG_FIFO_LEN * 4);
97+
98+
trng.mem.writel_relaxed(max * 8, EXYNOS_TRNG_FIFO_CTRL);
99+
100+
let _ =
101+
trng.mem
102+
.readl_poll_timeout(EXYNOS_TRNG_FIFO_CTRL, |val| *val == 0, 200, 1000000)?;
103+
104+
io_mem::try_memcpy_fromio(&trng.mem, data, EXYNOS_TRNG_FIFO_0, max.try_into()?)?;
105+
106+
Ok(max as i32)
107+
}
108+
}
109+
110+
struct ExynosTrngDriver;
111+
112+
impl PlatformDriver for ExynosTrngDriver {
113+
type DrvData = Pin<Box<hw_random::Registration<ExynosTrngDevice>>>;
114+
115+
fn probe(pdev: &mut PlatformDevice) -> Result<Self::DrvData> {
116+
let mut clk = clk::devm_clk_get(pdev, c_str!("secss"))?;
117+
clk.prepare_enable()?;
118+
119+
let mem = IoMem::<EXYNOS_REG_SIZE>::try_platform_ioremap_resource(pdev, 0)?;
120+
121+
let exynos_trng = Box::try_new(ExynosTrngDevice { clk, mem })?;
122+
123+
let drv_data = hw_random::Registration::new_pinned(pdev.name(), 0, exynos_trng)?;
124+
125+
Ok(drv_data)
126+
}
127+
128+
fn remove(_pdev: &mut PlatformDevice, _drv_data: Self::DrvData) -> Result {
129+
Ok(())
130+
}
131+
}
132+
133+
struct ExynosTrngDriverModule {
134+
_pdev: Pin<Box<platdev::Registration>>,
135+
}
136+
137+
impl KernelModule for ExynosTrngDriverModule {
138+
fn init(name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
139+
const OF_MATCH_TBL: ConstOfMatchTable<1> =
140+
ConstOfMatchTable::new_const([c_str!("samsung,exynos5250-trng")]);
141+
142+
let pdev = platdev::Registration::new_pinned::<ExynosTrngDriver>(
143+
name,
144+
Some(&OF_MATCH_TBL),
145+
module,
146+
)?;
147+
148+
Ok(ExynosTrngDriverModule { _pdev: pdev })
149+
}
150+
}

rust/kernel/hw_random.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
use alloc::{boxed::Box, slice::from_raw_parts_mut};
88

99
use crate::{
10-
bindings, c_types, from_kernel_result, str::CStr, types::PointerWrapper, Error, Result,
10+
bindings, c_types, error::from_kernel_result, str::CStr, types::PointerWrapper, Error, Result,
1111
};
1212

13-
use core::{cell::UnsafeCell, marker::PhantomData, ops::Deref, pin::Pin};
13+
use core::{cell::UnsafeCell, marker::PhantomData, pin::Pin};
1414

1515
/// This trait is implemented in order to provide callbacks to `struct hwrng`.
1616
pub trait HwrngOperations: Sized + 'static {

0 commit comments

Comments
 (0)