Path: blob/master/drivers/char/hw_random/ingenic-trng.c
29269 views
// SPDX-License-Identifier: GPL-2.01/*2* Ingenic True Random Number Generator driver3* Copyright (c) 2019 漆鹏振 (Qi Pengzhen) <[email protected]>4* Copyright (c) 2020 周琰杰 (Zhou Yanjie) <[email protected]>5*/67#include <linux/clk.h>8#include <linux/err.h>9#include <linux/kernel.h>10#include <linux/hw_random.h>11#include <linux/io.h>12#include <linux/iopoll.h>13#include <linux/mod_devicetable.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/slab.h>1718/* DTRNG register offsets */19#define TRNG_REG_CFG_OFFSET 0x0020#define TRNG_REG_RANDOMNUM_OFFSET 0x0421#define TRNG_REG_STATUS_OFFSET 0x082223/* bits within the CFG register */24#define CFG_GEN_EN BIT(0)2526/* bits within the STATUS register */27#define STATUS_RANDOM_RDY BIT(0)2829struct ingenic_trng {30void __iomem *base;31struct hwrng rng;32};3334static int ingenic_trng_init(struct hwrng *rng)35{36struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);37unsigned int ctrl;3839ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);40ctrl |= CFG_GEN_EN;41writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);4243return 0;44}4546static void ingenic_trng_cleanup(struct hwrng *rng)47{48struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);49unsigned int ctrl;5051ctrl = readl(trng->base + TRNG_REG_CFG_OFFSET);52ctrl &= ~CFG_GEN_EN;53writel(ctrl, trng->base + TRNG_REG_CFG_OFFSET);54}5556static int ingenic_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)57{58struct ingenic_trng *trng = container_of(rng, struct ingenic_trng, rng);59u32 *data = buf;60u32 status;61int ret;6263ret = readl_poll_timeout(trng->base + TRNG_REG_STATUS_OFFSET, status,64status & STATUS_RANDOM_RDY, 10, 1000);65if (ret == -ETIMEDOUT) {66pr_err("%s: Wait for DTRNG data ready timeout\n", __func__);67return ret;68}6970*data = readl(trng->base + TRNG_REG_RANDOMNUM_OFFSET);7172return 4;73}7475static int ingenic_trng_probe(struct platform_device *pdev)76{77struct ingenic_trng *trng;78struct clk *clk;79int ret;8081trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);82if (!trng)83return -ENOMEM;8485trng->base = devm_platform_ioremap_resource(pdev, 0);86if (IS_ERR(trng->base))87return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),88"%s: Failed to map DTRNG registers\n", __func__);8990clk = devm_clk_get_enabled(&pdev->dev, NULL);91if (IS_ERR(clk))92return dev_err_probe(&pdev->dev, PTR_ERR(clk),93"%s: Cannot get and enable DTRNG clock\n", __func__);9495trng->rng.name = pdev->name;96trng->rng.init = ingenic_trng_init;97trng->rng.cleanup = ingenic_trng_cleanup;98trng->rng.read = ingenic_trng_read;99100ret = devm_hwrng_register(&pdev->dev, &trng->rng);101if (ret)102return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");103104platform_set_drvdata(pdev, trng);105106dev_info(&pdev->dev, "Ingenic DTRNG driver registered\n");107return 0;108}109110static const struct of_device_id ingenic_trng_of_match[] = {111{ .compatible = "ingenic,x1830-dtrng" },112{ /* sentinel */ }113};114MODULE_DEVICE_TABLE(of, ingenic_trng_of_match);115116static struct platform_driver ingenic_trng_driver = {117.probe = ingenic_trng_probe,118.driver = {119.name = "ingenic-trng",120.of_match_table = ingenic_trng_of_match,121},122};123124module_platform_driver(ingenic_trng_driver);125126MODULE_LICENSE("GPL");127MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <[email protected]>");128MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <[email protected]>");129MODULE_DESCRIPTION("Ingenic True Random Number Generator driver");130131132