18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RNG driver for Freescale RNGC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Copyright (C) 2017 Martin Kaiser <martin@kaiser.cx> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 188c2ecf20Sopenharmony_ci#include <linux/completion.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define RNGC_VER_ID 0x0000 228c2ecf20Sopenharmony_ci#define RNGC_COMMAND 0x0004 238c2ecf20Sopenharmony_ci#define RNGC_CONTROL 0x0008 248c2ecf20Sopenharmony_ci#define RNGC_STATUS 0x000C 258c2ecf20Sopenharmony_ci#define RNGC_ERROR 0x0010 268c2ecf20Sopenharmony_ci#define RNGC_FIFO 0x0014 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* the fields in the ver id register */ 298c2ecf20Sopenharmony_ci#define RNGC_TYPE_SHIFT 28 308c2ecf20Sopenharmony_ci#define RNGC_VER_MAJ_SHIFT 8 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* the rng_type field */ 338c2ecf20Sopenharmony_ci#define RNGC_TYPE_RNGB 0x1 348c2ecf20Sopenharmony_ci#define RNGC_TYPE_RNGC 0x2 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define RNGC_CMD_CLR_ERR 0x00000020 388c2ecf20Sopenharmony_ci#define RNGC_CMD_CLR_INT 0x00000010 398c2ecf20Sopenharmony_ci#define RNGC_CMD_SEED 0x00000002 408c2ecf20Sopenharmony_ci#define RNGC_CMD_SELF_TEST 0x00000001 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define RNGC_CTRL_MASK_ERROR 0x00000040 438c2ecf20Sopenharmony_ci#define RNGC_CTRL_MASK_DONE 0x00000020 448c2ecf20Sopenharmony_ci#define RNGC_CTRL_AUTO_SEED 0x00000010 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define RNGC_STATUS_ERROR 0x00010000 478c2ecf20Sopenharmony_ci#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00 488c2ecf20Sopenharmony_ci#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8 498c2ecf20Sopenharmony_ci#define RNGC_STATUS_SEED_DONE 0x00000020 508c2ecf20Sopenharmony_ci#define RNGC_STATUS_ST_DONE 0x00000010 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define RNGC_TIMEOUT 3000 /* 3 sec */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic bool self_test = true; 588c2ecf20Sopenharmony_cimodule_param(self_test, bool, 0); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct imx_rngc { 618c2ecf20Sopenharmony_ci struct device *dev; 628c2ecf20Sopenharmony_ci struct clk *clk; 638c2ecf20Sopenharmony_ci void __iomem *base; 648c2ecf20Sopenharmony_ci struct hwrng rng; 658c2ecf20Sopenharmony_ci struct completion rng_op_done; 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * err_reg is written only by the irq handler and read only 688c2ecf20Sopenharmony_ci * when interrupts are masked, we need no spinlock 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci u32 err_reg; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline void imx_rngc_irq_mask_clear(struct imx_rngc *rngc) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u32 ctrl, cmd; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* mask interrupts */ 798c2ecf20Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 808c2ecf20Sopenharmony_ci ctrl |= RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR; 818c2ecf20Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * CLR_INT clears the interrupt only if there's no error 858c2ecf20Sopenharmony_ci * CLR_ERR clear the interrupt and the error register if there 868c2ecf20Sopenharmony_ci * is an error 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 898c2ecf20Sopenharmony_ci cmd |= RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR; 908c2ecf20Sopenharmony_ci writel(cmd, rngc->base + RNGC_COMMAND); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic inline void imx_rngc_irq_unmask(struct imx_rngc *rngc) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u32 ctrl; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 988c2ecf20Sopenharmony_ci ctrl &= ~(RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR); 998c2ecf20Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int imx_rngc_self_test(struct imx_rngc *rngc) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u32 cmd; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci imx_rngc_irq_unmask(rngc); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* run self test */ 1108c2ecf20Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 1118c2ecf20Sopenharmony_ci writel(cmd | RNGC_CMD_SELF_TEST, rngc->base + RNGC_COMMAND); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); 1148c2ecf20Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 1158c2ecf20Sopenharmony_ci if (!ret) 1168c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return rngc->err_reg ? -EIO : 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 1248c2ecf20Sopenharmony_ci unsigned int status; 1258c2ecf20Sopenharmony_ci unsigned int level; 1268c2ecf20Sopenharmony_ci int retval = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci while (max >= sizeof(u32)) { 1298c2ecf20Sopenharmony_ci status = readl(rngc->base + RNGC_STATUS); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* is there some error while reading this random number? */ 1328c2ecf20Sopenharmony_ci if (status & RNGC_STATUS_ERROR) 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* how many random numbers are in FIFO? [0-16] */ 1368c2ecf20Sopenharmony_ci level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >> 1378c2ecf20Sopenharmony_ci RNGC_STATUS_FIFO_LEVEL_SHIFT; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (level) { 1408c2ecf20Sopenharmony_ci /* retrieve a random number from FIFO */ 1418c2ecf20Sopenharmony_ci *(u32 *)data = readl(rngc->base + RNGC_FIFO); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci retval += sizeof(u32); 1448c2ecf20Sopenharmony_ci data += sizeof(u32); 1458c2ecf20Sopenharmony_ci max -= sizeof(u32); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return retval ? retval : -EIO; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic irqreturn_t imx_rngc_irq(int irq, void *priv) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct imx_rngc *rngc = (struct imx_rngc *)priv; 1558c2ecf20Sopenharmony_ci u32 status; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * clearing the interrupt will also clear the error register 1598c2ecf20Sopenharmony_ci * read error and status before clearing 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci status = readl(rngc->base + RNGC_STATUS); 1628c2ecf20Sopenharmony_ci rngc->err_reg = readl(rngc->base + RNGC_ERROR); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE)) 1678c2ecf20Sopenharmony_ci complete(&rngc->rng_op_done); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int imx_rngc_init(struct hwrng *rng) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 1758c2ecf20Sopenharmony_ci u32 cmd, ctrl; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* clear error */ 1798c2ecf20Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 1808c2ecf20Sopenharmony_ci writel(cmd | RNGC_CMD_CLR_ERR, rngc->base + RNGC_COMMAND); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci imx_rngc_irq_unmask(rngc); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* create seed, repeat while there is some statistical error */ 1858c2ecf20Sopenharmony_ci do { 1868c2ecf20Sopenharmony_ci /* seed creation */ 1878c2ecf20Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 1888c2ecf20Sopenharmony_ci writel(cmd | RNGC_CMD_SEED, rngc->base + RNGC_COMMAND); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); 1918c2ecf20Sopenharmony_ci if (!ret) { 1928c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 1938c2ecf20Sopenharmony_ci goto err; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (rngc->err_reg) { 1998c2ecf20Sopenharmony_ci ret = -EIO; 2008c2ecf20Sopenharmony_ci goto err; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * enable automatic seeding, the rngc creates a new seed automatically 2058c2ecf20Sopenharmony_ci * after serving 2^20 random 160-bit words 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 2088c2ecf20Sopenharmony_ci ctrl |= RNGC_CTRL_AUTO_SEED; 2098c2ecf20Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * if initialisation was successful, we keep the interrupt 2138c2ecf20Sopenharmony_ci * unmasked until imx_rngc_cleanup is called 2148c2ecf20Sopenharmony_ci * we mask the interrupt ourselves if we return an error 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cierr: 2198c2ecf20Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void imx_rngc_cleanup(struct hwrng *rng) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int imx_rngc_probe(struct platform_device *pdev) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct imx_rngc *rngc; 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci int irq; 2358c2ecf20Sopenharmony_ci u32 ver_id; 2368c2ecf20Sopenharmony_ci u8 rng_type; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rngc = devm_kzalloc(&pdev->dev, sizeof(*rngc), GFP_KERNEL); 2398c2ecf20Sopenharmony_ci if (!rngc) 2408c2ecf20Sopenharmony_ci return -ENOMEM; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci rngc->base = devm_platform_ioremap_resource(pdev, 0); 2438c2ecf20Sopenharmony_ci if (IS_ERR(rngc->base)) 2448c2ecf20Sopenharmony_ci return PTR_ERR(rngc->base); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci rngc->clk = devm_clk_get(&pdev->dev, NULL); 2478c2ecf20Sopenharmony_ci if (IS_ERR(rngc->clk)) { 2488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can not get rng_clk\n"); 2498c2ecf20Sopenharmony_ci return PTR_ERR(rngc->clk); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 2538c2ecf20Sopenharmony_ci if (irq <= 0) { 2548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't get irq %d\n", irq); 2558c2ecf20Sopenharmony_ci return irq; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ret = clk_prepare_enable(rngc->clk); 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ver_id = readl(rngc->base + RNGC_VER_ID); 2638c2ecf20Sopenharmony_ci rng_type = ver_id >> RNGC_TYPE_SHIFT; 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * This driver supports only RNGC and RNGB. (There's a different 2668c2ecf20Sopenharmony_ci * driver for RNGA.) 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) { 2698c2ecf20Sopenharmony_ci ret = -ENODEV; 2708c2ecf20Sopenharmony_ci goto err; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci init_completion(&rngc->rng_op_done); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci rngc->rng.name = pdev->name; 2768c2ecf20Sopenharmony_ci rngc->rng.init = imx_rngc_init; 2778c2ecf20Sopenharmony_ci rngc->rng.read = imx_rngc_read; 2788c2ecf20Sopenharmony_ci rngc->rng.cleanup = imx_rngc_cleanup; 2798c2ecf20Sopenharmony_ci rngc->rng.quality = 19; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci rngc->dev = &pdev->dev; 2828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rngc); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, 2878c2ecf20Sopenharmony_ci irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); 2888c2ecf20Sopenharmony_ci if (ret) { 2898c2ecf20Sopenharmony_ci dev_err(rngc->dev, "Can't get interrupt working.\n"); 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (self_test) { 2948c2ecf20Sopenharmony_ci ret = imx_rngc_self_test(rngc); 2958c2ecf20Sopenharmony_ci if (ret) { 2968c2ecf20Sopenharmony_ci dev_err(rngc->dev, "self test failed\n"); 2978c2ecf20Sopenharmony_ci goto err; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = hwrng_register(&rngc->rng); 3028c2ecf20Sopenharmony_ci if (ret) { 3038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "hwrng registration failed\n"); 3048c2ecf20Sopenharmony_ci goto err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 3088c2ecf20Sopenharmony_ci "Freescale RNG%c registered (HW revision %d.%02d)\n", 3098c2ecf20Sopenharmony_ci rng_type == RNGC_TYPE_RNGB ? 'B' : 'C', 3108c2ecf20Sopenharmony_ci (ver_id >> RNGC_VER_MAJ_SHIFT) & 0xff, ver_id & 0xff); 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cierr: 3148c2ecf20Sopenharmony_ci clk_disable_unprepare(rngc->clk); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int __exit imx_rngc_remove(struct platform_device *pdev) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct imx_rngc *rngc = platform_get_drvdata(pdev); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci hwrng_unregister(&rngc->rng); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci clk_disable_unprepare(rngc->clk); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int __maybe_unused imx_rngc_suspend(struct device *dev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct imx_rngc *rngc = dev_get_drvdata(dev); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci clk_disable_unprepare(rngc->clk); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int __maybe_unused imx_rngc_resume(struct device *dev) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct imx_rngc *rngc = dev_get_drvdata(dev); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci clk_prepare_enable(rngc->clk); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic const struct of_device_id imx_rngc_dt_ids[] = { 3518c2ecf20Sopenharmony_ci { .compatible = "fsl,imx25-rngb", .data = NULL, }, 3528c2ecf20Sopenharmony_ci { /* sentinel */ } 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_rngc_dt_ids); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic struct platform_driver imx_rngc_driver = { 3578c2ecf20Sopenharmony_ci .driver = { 3588c2ecf20Sopenharmony_ci .name = "imx_rngc", 3598c2ecf20Sopenharmony_ci .pm = &imx_rngc_pm_ops, 3608c2ecf20Sopenharmony_ci .of_match_table = imx_rngc_dt_ids, 3618c2ecf20Sopenharmony_ci }, 3628c2ecf20Sopenharmony_ci .remove = __exit_p(imx_rngc_remove), 3638c2ecf20Sopenharmony_ci}; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cimodule_platform_driver_probe(imx_rngc_driver, imx_rngc_probe); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 3688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("H/W RNGC driver for i.MX"); 3698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 370