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