18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 138c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 148c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 158c2ecf20Sopenharmony_ci#include <linux/completion.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 188c2ecf20Sopenharmony_ci#include <linux/fips.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "cctrng.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define CC_REG_LOW(name) (name ## _BIT_SHIFT) 238c2ecf20Sopenharmony_ci#define CC_REG_HIGH(name) (CC_REG_LOW(name) + name ## _BIT_SIZE - 1) 248c2ecf20Sopenharmony_ci#define CC_GENMASK(name) GENMASK(CC_REG_HIGH(name), CC_REG_LOW(name)) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define CC_REG_FLD_GET(reg_name, fld_name, reg_val) \ 278c2ecf20Sopenharmony_ci (FIELD_GET(CC_GENMASK(CC_ ## reg_name ## _ ## fld_name), reg_val)) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define CC_HW_RESET_LOOP_COUNT 10 308c2ecf20Sopenharmony_ci#define CC_TRNG_SUSPEND_TIMEOUT 3000 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* data circular buffer in words must be: 338c2ecf20Sopenharmony_ci * - of a power-of-2 size (limitation of circ_buf.h macros) 348c2ecf20Sopenharmony_ci * - at least 6, the size generated in the EHR according to HW implementation 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define CCTRNG_DATA_BUF_WORDS 32 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* The timeout for the TRNG operation should be calculated with the formula: 398c2ecf20Sopenharmony_ci * Timeout = EHR_NUM * VN_COEFF * EHR_LENGTH * SAMPLE_CNT * SCALE_VALUE 408c2ecf20Sopenharmony_ci * while: 418c2ecf20Sopenharmony_ci * - SAMPLE_CNT is input value from the characterisation process 428c2ecf20Sopenharmony_ci * - all the rest are constants 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define EHR_NUM 1 458c2ecf20Sopenharmony_ci#define VN_COEFF 4 468c2ecf20Sopenharmony_ci#define EHR_LENGTH CC_TRNG_EHR_IN_BITS 478c2ecf20Sopenharmony_ci#define SCALE_VALUE 2 488c2ecf20Sopenharmony_ci#define CCTRNG_TIMEOUT(smpl_cnt) \ 498c2ecf20Sopenharmony_ci (EHR_NUM * VN_COEFF * EHR_LENGTH * smpl_cnt * SCALE_VALUE) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct cctrng_drvdata { 528c2ecf20Sopenharmony_ci struct platform_device *pdev; 538c2ecf20Sopenharmony_ci void __iomem *cc_base; 548c2ecf20Sopenharmony_ci struct clk *clk; 558c2ecf20Sopenharmony_ci struct hwrng rng; 568c2ecf20Sopenharmony_ci u32 active_rosc; 578c2ecf20Sopenharmony_ci /* Sampling interval for each ring oscillator: 588c2ecf20Sopenharmony_ci * count of ring oscillator cycles between consecutive bits sampling. 598c2ecf20Sopenharmony_ci * Value of 0 indicates non-valid rosc 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci u32 smpl_ratio[CC_TRNG_NUM_OF_ROSCS]; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci u32 data_buf[CCTRNG_DATA_BUF_WORDS]; 648c2ecf20Sopenharmony_ci struct circ_buf circ; 658c2ecf20Sopenharmony_ci struct work_struct compwork; 668c2ecf20Sopenharmony_ci struct work_struct startwork; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* pending_hw - 1 when HW is pending, 0 when it is idle */ 698c2ecf20Sopenharmony_ci atomic_t pending_hw; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* protects against multiple concurrent consumers of data_buf */ 728c2ecf20Sopenharmony_ci spinlock_t read_lock; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* functions for write/read CC registers */ 778c2ecf20Sopenharmony_cistatic inline void cc_iowrite(struct cctrng_drvdata *drvdata, u32 reg, u32 val) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci iowrite32(val, (drvdata->cc_base + reg)); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_cistatic inline u32 cc_ioread(struct cctrng_drvdata *drvdata, u32 reg) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return ioread32(drvdata->cc_base + reg); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int cc_trng_pm_get(struct device *dev) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int rc = 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci rc = pm_runtime_get_sync(dev); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* pm_runtime_get_sync() can return 1 as a valid return code */ 948c2ecf20Sopenharmony_ci return (rc == 1 ? 0 : rc); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void cc_trng_pm_put_suspend(struct device *dev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int rc = 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 1028c2ecf20Sopenharmony_ci rc = pm_runtime_put_autosuspend(dev); 1038c2ecf20Sopenharmony_ci if (rc) 1048c2ecf20Sopenharmony_ci dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int cc_trng_pm_init(struct cctrng_drvdata *drvdata) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* must be before the enabling to avoid redundant suspending */ 1128c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, CC_TRNG_SUSPEND_TIMEOUT); 1138c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(dev); 1148c2ecf20Sopenharmony_ci /* set us as active - note we won't do PM ops until cc_trng_pm_go()! */ 1158c2ecf20Sopenharmony_ci return pm_runtime_set_active(dev); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void cc_trng_pm_go(struct cctrng_drvdata *drvdata) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* enable the PM module*/ 1238c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void cc_trng_pm_fini(struct cctrng_drvdata *drvdata) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic inline int cc_trng_parse_sampling_ratio(struct cctrng_drvdata *drvdata) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 1378c2ecf20Sopenharmony_ci struct device_node *np = drvdata->pdev->dev.of_node; 1388c2ecf20Sopenharmony_ci int rc; 1398c2ecf20Sopenharmony_ci int i; 1408c2ecf20Sopenharmony_ci /* ret will be set to 0 if at least one rosc has (sampling ratio > 0) */ 1418c2ecf20Sopenharmony_ci int ret = -EINVAL; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci rc = of_property_read_u32_array(np, "arm,rosc-ratio", 1448c2ecf20Sopenharmony_ci drvdata->smpl_ratio, 1458c2ecf20Sopenharmony_ci CC_TRNG_NUM_OF_ROSCS); 1468c2ecf20Sopenharmony_ci if (rc) { 1478c2ecf20Sopenharmony_ci /* arm,rosc-ratio was not found in device tree */ 1488c2ecf20Sopenharmony_ci return rc; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* verify that at least one rosc has (sampling ratio > 0) */ 1528c2ecf20Sopenharmony_ci for (i = 0; i < CC_TRNG_NUM_OF_ROSCS; ++i) { 1538c2ecf20Sopenharmony_ci dev_dbg(dev, "rosc %d sampling ratio %u", 1548c2ecf20Sopenharmony_ci i, drvdata->smpl_ratio[i]); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (drvdata->smpl_ratio[i] > 0) 1578c2ecf20Sopenharmony_ci ret = 0; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int cc_trng_change_rosc(struct cctrng_drvdata *drvdata) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci dev_dbg(dev, "cctrng change rosc (was %d)\n", drvdata->active_rosc); 1688c2ecf20Sopenharmony_ci drvdata->active_rosc += 1; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci while (drvdata->active_rosc < CC_TRNG_NUM_OF_ROSCS) { 1718c2ecf20Sopenharmony_ci if (drvdata->smpl_ratio[drvdata->active_rosc] > 0) 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci drvdata->active_rosc += 1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void cc_trng_enable_rnd_source(struct cctrng_drvdata *drvdata) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci u32 max_cycles; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Set watchdog threshold to maximal allowed time (in CPU cycles) */ 1858c2ecf20Sopenharmony_ci max_cycles = CCTRNG_TIMEOUT(drvdata->smpl_ratio[drvdata->active_rosc]); 1868c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_WATCHDOG_VAL_REG_OFFSET, max_cycles); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* enable the RND source */ 1898c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0x1); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* unmask RNG interrupts */ 1928c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, (u32)~CC_RNG_INT_MASK); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* increase circular data buffer index (head/tail) */ 1978c2ecf20Sopenharmony_cistatic inline void circ_idx_inc(int *idx, int bytes) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci *idx += (bytes + 3) >> 2; 2008c2ecf20Sopenharmony_ci *idx &= (CCTRNG_DATA_BUF_WORDS - 1); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline size_t circ_buf_space(struct cctrng_drvdata *drvdata) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return CIRC_SPACE(drvdata->circ.head, 2068c2ecf20Sopenharmony_ci drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int cctrng_read(struct hwrng *rng, void *data, size_t max, bool wait) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci /* current implementation ignores "wait" */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)rng->priv; 2158c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 2168c2ecf20Sopenharmony_ci u32 *buf = (u32 *)drvdata->circ.buf; 2178c2ecf20Sopenharmony_ci size_t copied = 0; 2188c2ecf20Sopenharmony_ci size_t cnt_w; 2198c2ecf20Sopenharmony_ci size_t size; 2208c2ecf20Sopenharmony_ci size_t left; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (!spin_trylock(&drvdata->read_lock)) { 2238c2ecf20Sopenharmony_ci /* concurrent consumers from data_buf cannot be served */ 2248c2ecf20Sopenharmony_ci dev_dbg_ratelimited(dev, "unable to hold lock\n"); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* copy till end of data buffer (without wrap back) */ 2298c2ecf20Sopenharmony_ci cnt_w = CIRC_CNT_TO_END(drvdata->circ.head, 2308c2ecf20Sopenharmony_ci drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); 2318c2ecf20Sopenharmony_ci size = min((cnt_w<<2), max); 2328c2ecf20Sopenharmony_ci memcpy(data, &(buf[drvdata->circ.tail]), size); 2338c2ecf20Sopenharmony_ci copied = size; 2348c2ecf20Sopenharmony_ci circ_idx_inc(&drvdata->circ.tail, size); 2358c2ecf20Sopenharmony_ci /* copy rest of data in data buffer */ 2368c2ecf20Sopenharmony_ci left = max - copied; 2378c2ecf20Sopenharmony_ci if (left > 0) { 2388c2ecf20Sopenharmony_ci cnt_w = CIRC_CNT(drvdata->circ.head, 2398c2ecf20Sopenharmony_ci drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); 2408c2ecf20Sopenharmony_ci size = min((cnt_w<<2), left); 2418c2ecf20Sopenharmony_ci memcpy(data, &(buf[drvdata->circ.tail]), size); 2428c2ecf20Sopenharmony_ci copied += size; 2438c2ecf20Sopenharmony_ci circ_idx_inc(&drvdata->circ.tail, size); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci spin_unlock(&drvdata->read_lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { 2498c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) { 2508c2ecf20Sopenharmony_ci /* re-check space in buffer to avoid potential race */ 2518c2ecf20Sopenharmony_ci if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { 2528c2ecf20Sopenharmony_ci /* increment device's usage counter */ 2538c2ecf20Sopenharmony_ci int rc = cc_trng_pm_get(dev); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (rc) { 2568c2ecf20Sopenharmony_ci dev_err(dev, 2578c2ecf20Sopenharmony_ci "cc_trng_pm_get returned %x\n", 2588c2ecf20Sopenharmony_ci rc); 2598c2ecf20Sopenharmony_ci return rc; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* schedule execution of deferred work handler 2638c2ecf20Sopenharmony_ci * for filling of data buffer 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci schedule_work(&drvdata->startwork); 2668c2ecf20Sopenharmony_ci } else { 2678c2ecf20Sopenharmony_ci atomic_set(&drvdata->pending_hw, 0); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return copied; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void cc_trng_hw_trigger(struct cctrng_drvdata *drvdata) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci u32 tmp_smpl_cnt = 0; 2788c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dev_dbg(dev, "cctrng hw trigger.\n"); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* enable the HW RND clock */ 2838c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* do software reset */ 2868c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_SW_RESET_REG_OFFSET, 0x1); 2878c2ecf20Sopenharmony_ci /* in order to verify that the reset has completed, 2888c2ecf20Sopenharmony_ci * the sample count need to be verified 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci do { 2918c2ecf20Sopenharmony_ci /* enable the HW RND clock */ 2928c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* set sampling ratio (rng_clocks) between consecutive bits */ 2958c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_SAMPLE_CNT1_REG_OFFSET, 2968c2ecf20Sopenharmony_ci drvdata->smpl_ratio[drvdata->active_rosc]); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* read the sampling ratio */ 2998c2ecf20Sopenharmony_ci tmp_smpl_cnt = cc_ioread(drvdata, CC_SAMPLE_CNT1_REG_OFFSET); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci } while (tmp_smpl_cnt != drvdata->smpl_ratio[drvdata->active_rosc]); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* disable the RND source for setting new parameters in HW */ 3048c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, 0xFFFFFFFF); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_TRNG_CONFIG_REG_OFFSET, drvdata->active_rosc); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Debug Control register: set to 0 - no bypasses */ 3118c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_TRNG_DEBUG_CONTROL_REG_OFFSET, 0); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci cc_trng_enable_rnd_source(drvdata); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void cc_trng_compwork_handler(struct work_struct *w) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci u32 isr = 0; 3198c2ecf20Sopenharmony_ci u32 ehr_valid = 0; 3208c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = 3218c2ecf20Sopenharmony_ci container_of(w, struct cctrng_drvdata, compwork); 3228c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* stop DMA and the RNG source */ 3268c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_DMA_ENABLE_REG_OFFSET, 0); 3278c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* read RNG_ISR and check for errors */ 3308c2ecf20Sopenharmony_ci isr = cc_ioread(drvdata, CC_RNG_ISR_REG_OFFSET); 3318c2ecf20Sopenharmony_ci ehr_valid = CC_REG_FLD_GET(RNG_ISR, EHR_VALID, isr); 3328c2ecf20Sopenharmony_ci dev_dbg(dev, "Got RNG_ISR=0x%08X (EHR_VALID=%u)\n", isr, ehr_valid); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (fips_enabled && CC_REG_FLD_GET(RNG_ISR, CRNGT_ERR, isr)) { 3358c2ecf20Sopenharmony_ci fips_fail_notify(); 3368c2ecf20Sopenharmony_ci /* FIPS error is fatal */ 3378c2ecf20Sopenharmony_ci panic("Got HW CRNGT error while fips is enabled!\n"); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Clear all pending RNG interrupts */ 3418c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, isr); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!ehr_valid) { 3458c2ecf20Sopenharmony_ci /* in case of AUTOCORR/TIMEOUT error, try the next ROSC */ 3468c2ecf20Sopenharmony_ci if (CC_REG_FLD_GET(RNG_ISR, AUTOCORR_ERR, isr) || 3478c2ecf20Sopenharmony_ci CC_REG_FLD_GET(RNG_ISR, WATCHDOG, isr)) { 3488c2ecf20Sopenharmony_ci dev_dbg(dev, "cctrng autocorr/timeout error.\n"); 3498c2ecf20Sopenharmony_ci goto next_rosc; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* in case of VN error, ignore it */ 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* read EHR data from registers */ 3568c2ecf20Sopenharmony_ci for (i = 0; i < CC_TRNG_EHR_IN_WORDS; i++) { 3578c2ecf20Sopenharmony_ci /* calc word ptr in data_buf */ 3588c2ecf20Sopenharmony_ci u32 *buf = (u32 *)drvdata->circ.buf; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci buf[drvdata->circ.head] = cc_ioread(drvdata, 3618c2ecf20Sopenharmony_ci CC_EHR_DATA_0_REG_OFFSET + (i*sizeof(u32))); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* EHR_DATA registers are cleared on read. In case 0 value was 3648c2ecf20Sopenharmony_ci * returned, restart the entropy collection. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (buf[drvdata->circ.head] == 0) { 3678c2ecf20Sopenharmony_ci dev_dbg(dev, "Got 0 value in EHR. active_rosc %u\n", 3688c2ecf20Sopenharmony_ci drvdata->active_rosc); 3698c2ecf20Sopenharmony_ci goto next_rosc; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci circ_idx_inc(&drvdata->circ.head, 1<<2); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci atomic_set(&drvdata->pending_hw, 0); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* continue to fill data buffer if needed */ 3788c2ecf20Sopenharmony_ci if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { 3798c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) { 3808c2ecf20Sopenharmony_ci /* Re-enable rnd source */ 3818c2ecf20Sopenharmony_ci cc_trng_enable_rnd_source(drvdata); 3828c2ecf20Sopenharmony_ci return; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci cc_trng_pm_put_suspend(dev); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev_dbg(dev, "compwork handler done\n"); 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cinext_rosc: 3928c2ecf20Sopenharmony_ci if ((circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) && 3938c2ecf20Sopenharmony_ci (cc_trng_change_rosc(drvdata) == 0)) { 3948c2ecf20Sopenharmony_ci /* trigger trng hw with next rosc */ 3958c2ecf20Sopenharmony_ci cc_trng_hw_trigger(drvdata); 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci atomic_set(&drvdata->pending_hw, 0); 3988c2ecf20Sopenharmony_ci cc_trng_pm_put_suspend(dev); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic irqreturn_t cc_isr(int irq, void *dev_id) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)dev_id; 4058c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 4068c2ecf20Sopenharmony_ci u32 irr; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* if driver suspended return, probably shared interrupt */ 4098c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev)) 4108c2ecf20Sopenharmony_ci return IRQ_NONE; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* read the interrupt status */ 4138c2ecf20Sopenharmony_ci irr = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET); 4148c2ecf20Sopenharmony_ci dev_dbg(dev, "Got IRR=0x%08X\n", irr); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (irr == 0) /* Probably shared interrupt line */ 4178c2ecf20Sopenharmony_ci return IRQ_NONE; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* clear interrupt - must be before processing events */ 4208c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, irr); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* RNG interrupt - most probable */ 4238c2ecf20Sopenharmony_ci if (irr & CC_HOST_RNG_IRQ_MASK) { 4248c2ecf20Sopenharmony_ci /* Mask RNG interrupts - will be unmasked in deferred work */ 4258c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, 0xFFFFFFFF); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* We clear RNG interrupt here, 4288c2ecf20Sopenharmony_ci * to avoid it from firing as we'll unmask RNG interrupts. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, 4318c2ecf20Sopenharmony_ci CC_HOST_RNG_IRQ_MASK); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci irr &= ~CC_HOST_RNG_IRQ_MASK; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* schedule execution of deferred work handler */ 4368c2ecf20Sopenharmony_ci schedule_work(&drvdata->compwork); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (irr) { 4408c2ecf20Sopenharmony_ci dev_dbg_ratelimited(dev, 4418c2ecf20Sopenharmony_ci "IRR includes unknown cause bits (0x%08X)\n", 4428c2ecf20Sopenharmony_ci irr); 4438c2ecf20Sopenharmony_ci /* Just warning */ 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void cc_trng_startwork_handler(struct work_struct *w) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = 4528c2ecf20Sopenharmony_ci container_of(w, struct cctrng_drvdata, startwork); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci drvdata->active_rosc = 0; 4558c2ecf20Sopenharmony_ci cc_trng_hw_trigger(drvdata); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int cc_trng_clk_init(struct cctrng_drvdata *drvdata) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct clk *clk; 4628c2ecf20Sopenharmony_ci struct device *dev = &(drvdata->pdev->dev); 4638c2ecf20Sopenharmony_ci int rc = 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci clk = devm_clk_get_optional(dev, NULL); 4668c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 4678c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(clk), 4688c2ecf20Sopenharmony_ci "Error getting clock\n"); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci drvdata->clk = clk; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci rc = clk_prepare_enable(drvdata->clk); 4738c2ecf20Sopenharmony_ci if (rc) { 4748c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable clock\n"); 4758c2ecf20Sopenharmony_ci return rc; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void cc_trng_clk_fini(struct cctrng_drvdata *drvdata) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->clk); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int cctrng_probe(struct platform_device *pdev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct resource *req_mem_cc_regs = NULL; 4908c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata; 4918c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4928c2ecf20Sopenharmony_ci int rc = 0; 4938c2ecf20Sopenharmony_ci u32 val; 4948c2ecf20Sopenharmony_ci int irq; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 4978c2ecf20Sopenharmony_ci if (!drvdata) 4988c2ecf20Sopenharmony_ci return -ENOMEM; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci drvdata->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (!drvdata->rng.name) 5028c2ecf20Sopenharmony_ci return -ENOMEM; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci drvdata->rng.read = cctrng_read; 5058c2ecf20Sopenharmony_ci drvdata->rng.priv = (unsigned long)drvdata; 5068c2ecf20Sopenharmony_ci drvdata->rng.quality = CC_TRNG_QUALITY; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 5098c2ecf20Sopenharmony_ci drvdata->pdev = pdev; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci drvdata->circ.buf = (char *)drvdata->data_buf; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Get device resources */ 5148c2ecf20Sopenharmony_ci /* First CC registers space */ 5158c2ecf20Sopenharmony_ci req_mem_cc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5168c2ecf20Sopenharmony_ci /* Map registers space */ 5178c2ecf20Sopenharmony_ci drvdata->cc_base = devm_ioremap_resource(dev, req_mem_cc_regs); 5188c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->cc_base)) { 5198c2ecf20Sopenharmony_ci dev_err(dev, "Failed to ioremap registers"); 5208c2ecf20Sopenharmony_ci return PTR_ERR(drvdata->cc_base); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci dev_dbg(dev, "Got MEM resource (%s): %pR\n", req_mem_cc_regs->name, 5248c2ecf20Sopenharmony_ci req_mem_cc_regs); 5258c2ecf20Sopenharmony_ci dev_dbg(dev, "CC registers mapped from %pa to 0x%p\n", 5268c2ecf20Sopenharmony_ci &req_mem_cc_regs->start, drvdata->cc_base); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Then IRQ */ 5298c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 5308c2ecf20Sopenharmony_ci if (irq < 0) { 5318c2ecf20Sopenharmony_ci dev_err(dev, "Failed getting IRQ resource\n"); 5328c2ecf20Sopenharmony_ci return irq; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* parse sampling rate from device tree */ 5368c2ecf20Sopenharmony_ci rc = cc_trng_parse_sampling_ratio(drvdata); 5378c2ecf20Sopenharmony_ci if (rc) { 5388c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get legal sampling ratio for rosc\n"); 5398c2ecf20Sopenharmony_ci return rc; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci rc = cc_trng_clk_init(drvdata); 5438c2ecf20Sopenharmony_ci if (rc) { 5448c2ecf20Sopenharmony_ci dev_err(dev, "cc_trng_clk_init failed\n"); 5458c2ecf20Sopenharmony_ci return rc; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci INIT_WORK(&drvdata->compwork, cc_trng_compwork_handler); 5498c2ecf20Sopenharmony_ci INIT_WORK(&drvdata->startwork, cc_trng_startwork_handler); 5508c2ecf20Sopenharmony_ci spin_lock_init(&drvdata->read_lock); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* register the driver isr function */ 5538c2ecf20Sopenharmony_ci rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata); 5548c2ecf20Sopenharmony_ci if (rc) { 5558c2ecf20Sopenharmony_ci dev_err(dev, "Could not register to interrupt %d\n", irq); 5568c2ecf20Sopenharmony_ci goto post_clk_err; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci dev_dbg(dev, "Registered to IRQ: %d\n", irq); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Clear all pending interrupts */ 5618c2ecf20Sopenharmony_ci val = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET); 5628c2ecf20Sopenharmony_ci dev_dbg(dev, "IRR=0x%08X\n", val); 5638c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, val); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* unmask HOST RNG interrupt */ 5668c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET, 5678c2ecf20Sopenharmony_ci cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) & 5688c2ecf20Sopenharmony_ci ~CC_HOST_RNG_IRQ_MASK); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* init PM */ 5718c2ecf20Sopenharmony_ci rc = cc_trng_pm_init(drvdata); 5728c2ecf20Sopenharmony_ci if (rc) { 5738c2ecf20Sopenharmony_ci dev_err(dev, "cc_trng_pm_init failed\n"); 5748c2ecf20Sopenharmony_ci goto post_clk_err; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* increment device's usage counter */ 5788c2ecf20Sopenharmony_ci rc = cc_trng_pm_get(dev); 5798c2ecf20Sopenharmony_ci if (rc) { 5808c2ecf20Sopenharmony_ci dev_err(dev, "cc_trng_pm_get returned %x\n", rc); 5818c2ecf20Sopenharmony_ci goto post_pm_err; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* set pending_hw to verify that HW won't be triggered from read */ 5858c2ecf20Sopenharmony_ci atomic_set(&drvdata->pending_hw, 1); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* registration of the hwrng device */ 5888c2ecf20Sopenharmony_ci rc = hwrng_register(&drvdata->rng); 5898c2ecf20Sopenharmony_ci if (rc) { 5908c2ecf20Sopenharmony_ci dev_err(dev, "Could not register hwrng device.\n"); 5918c2ecf20Sopenharmony_ci goto post_pm_err; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* trigger HW to start generate data */ 5958c2ecf20Sopenharmony_ci drvdata->active_rosc = 0; 5968c2ecf20Sopenharmony_ci cc_trng_hw_trigger(drvdata); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* All set, we can allow auto-suspend */ 5998c2ecf20Sopenharmony_ci cc_trng_pm_go(drvdata); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci dev_info(dev, "ARM cctrng device initialized\n"); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cipost_pm_err: 6068c2ecf20Sopenharmony_ci cc_trng_pm_fini(drvdata); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cipost_clk_err: 6098c2ecf20Sopenharmony_ci cc_trng_clk_fini(drvdata); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return rc; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int cctrng_remove(struct platform_device *pdev) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = platform_get_drvdata(pdev); 6178c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci dev_dbg(dev, "Releasing cctrng resources...\n"); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci hwrng_unregister(&drvdata->rng); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci cc_trng_pm_fini(drvdata); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci cc_trng_clk_fini(drvdata); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci dev_info(dev, "ARM cctrng device terminated\n"); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int __maybe_unused cctrng_suspend(struct device *dev) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = dev_get_drvdata(dev); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci dev_dbg(dev, "set HOST_POWER_DOWN_EN\n"); 6378c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET, 6388c2ecf20Sopenharmony_ci POWER_DOWN_ENABLE); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->clk); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic bool cctrng_wait_for_reset_completion(struct cctrng_drvdata *drvdata) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci unsigned int val; 6488c2ecf20Sopenharmony_ci unsigned int i; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci for (i = 0; i < CC_HW_RESET_LOOP_COUNT; i++) { 6518c2ecf20Sopenharmony_ci /* in cc7x3 NVM_IS_IDLE indicates that CC reset is 6528c2ecf20Sopenharmony_ci * completed and device is fully functional 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci val = cc_ioread(drvdata, CC_NVM_IS_IDLE_REG_OFFSET); 6558c2ecf20Sopenharmony_ci if (val & BIT(CC_NVM_IS_IDLE_VALUE_BIT_SHIFT)) { 6568c2ecf20Sopenharmony_ci /* hw indicate reset completed */ 6578c2ecf20Sopenharmony_ci return true; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci /* allow scheduling other process on the processor */ 6608c2ecf20Sopenharmony_ci schedule(); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci /* reset not completed */ 6638c2ecf20Sopenharmony_ci return false; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic int __maybe_unused cctrng_resume(struct device *dev) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci struct cctrng_drvdata *drvdata = dev_get_drvdata(dev); 6698c2ecf20Sopenharmony_ci int rc; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci dev_dbg(dev, "unset HOST_POWER_DOWN_EN\n"); 6728c2ecf20Sopenharmony_ci /* Enables the device source clk */ 6738c2ecf20Sopenharmony_ci rc = clk_prepare_enable(drvdata->clk); 6748c2ecf20Sopenharmony_ci if (rc) { 6758c2ecf20Sopenharmony_ci dev_err(dev, "failed getting clock back on. We're toast.\n"); 6768c2ecf20Sopenharmony_ci return rc; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* wait for Cryptocell reset completion */ 6808c2ecf20Sopenharmony_ci if (!cctrng_wait_for_reset_completion(drvdata)) { 6818c2ecf20Sopenharmony_ci dev_err(dev, "Cryptocell reset not completed"); 6828c2ecf20Sopenharmony_ci return -EBUSY; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* unmask HOST RNG interrupt */ 6868c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET, 6878c2ecf20Sopenharmony_ci cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) & 6888c2ecf20Sopenharmony_ci ~CC_HOST_RNG_IRQ_MASK); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET, 6918c2ecf20Sopenharmony_ci POWER_DOWN_DISABLE); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic UNIVERSAL_DEV_PM_OPS(cctrng_pm, cctrng_suspend, cctrng_resume, NULL); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic const struct of_device_id arm_cctrng_dt_match[] = { 6998c2ecf20Sopenharmony_ci { .compatible = "arm,cryptocell-713-trng", }, 7008c2ecf20Sopenharmony_ci { .compatible = "arm,cryptocell-703-trng", }, 7018c2ecf20Sopenharmony_ci {}, 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_cctrng_dt_match); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic struct platform_driver cctrng_driver = { 7068c2ecf20Sopenharmony_ci .driver = { 7078c2ecf20Sopenharmony_ci .name = "cctrng", 7088c2ecf20Sopenharmony_ci .of_match_table = arm_cctrng_dt_match, 7098c2ecf20Sopenharmony_ci .pm = &cctrng_pm, 7108c2ecf20Sopenharmony_ci }, 7118c2ecf20Sopenharmony_ci .probe = cctrng_probe, 7128c2ecf20Sopenharmony_ci .remove = cctrng_remove, 7138c2ecf20Sopenharmony_ci}; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int __init cctrng_mod_init(void) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci /* Compile time assertion checks */ 7188c2ecf20Sopenharmony_ci BUILD_BUG_ON(CCTRNG_DATA_BUF_WORDS < 6); 7198c2ecf20Sopenharmony_ci BUILD_BUG_ON((CCTRNG_DATA_BUF_WORDS & (CCTRNG_DATA_BUF_WORDS-1)) != 0); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return platform_driver_register(&cctrng_driver); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_cimodule_init(cctrng_mod_init); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic void __exit cctrng_mod_exit(void) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci platform_driver_unregister(&cctrng_driver); 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_cimodule_exit(cctrng_mod_exit); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci/* Module description */ 7328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM CryptoCell TRNG Driver"); 7338c2ecf20Sopenharmony_ciMODULE_AUTHOR("ARM"); 7348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 735