18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Random Number Generator driver for the Keystone SOC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:	Sandeep Nair
88c2ecf20Sopenharmony_ci *		Vitaly Andrianov
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/regmap.h>
208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/timekeeping.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define SA_CMD_STATUS_OFS			0x8
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* TRNG enable control in SA System module*/
298c2ecf20Sopenharmony_ci#define SA_CMD_STATUS_REG_TRNG_ENABLE		BIT(3)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* TRNG start control in TRNG module */
328c2ecf20Sopenharmony_ci#define TRNG_CNTL_REG_TRNG_ENABLE		BIT(10)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Data ready indicator in STATUS register */
358c2ecf20Sopenharmony_ci#define TRNG_STATUS_REG_READY			BIT(0)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Data ready clear control in INTACK register */
388c2ecf20Sopenharmony_ci#define TRNG_INTACK_REG_READY			BIT(0)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Number of samples taken to gather entropy during startup.
428c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else
438c2ecf20Sopenharmony_ci * equals value times 2^8.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci#define TRNG_DEF_STARTUP_CYCLES			0
468c2ecf20Sopenharmony_ci#define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT	16
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Minimum number of samples taken to regenerate entropy
508c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else
518c2ecf20Sopenharmony_ci * equals value times 2^6.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci#define TRNG_DEF_MIN_REFILL_CYCLES		1
548c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT	0
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Maximum number of samples taken to regenerate entropy
588c2ecf20Sopenharmony_ci * If value is 0, the number of samples is 2^24 else
598c2ecf20Sopenharmony_ci * equals value times 2^8.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ci#define TRNG_DEF_MAX_REFILL_CYCLES		0
628c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT	16
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Number of CLK input cycles between samples */
658c2ecf20Sopenharmony_ci#define TRNG_DEF_CLK_DIV_CYCLES			0
668c2ecf20Sopenharmony_ci#define TRNG_CFG_REG_SAMPLE_DIV_SHIFT		8
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Maximum retries to get rng data */
698c2ecf20Sopenharmony_ci#define SA_MAX_RNG_DATA_RETRIES			5
708c2ecf20Sopenharmony_ci/* Delay between retries (in usecs) */
718c2ecf20Sopenharmony_ci#define SA_RNG_DATA_RETRY_DELAY			5
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct trng_regs {
748c2ecf20Sopenharmony_ci	u32	output_l;
758c2ecf20Sopenharmony_ci	u32	output_h;
768c2ecf20Sopenharmony_ci	u32	status;
778c2ecf20Sopenharmony_ci	u32	intmask;
788c2ecf20Sopenharmony_ci	u32	intack;
798c2ecf20Sopenharmony_ci	u32	control;
808c2ecf20Sopenharmony_ci	u32	config;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct ks_sa_rng {
848c2ecf20Sopenharmony_ci	struct device	*dev;
858c2ecf20Sopenharmony_ci	struct hwrng	rng;
868c2ecf20Sopenharmony_ci	struct clk	*clk;
878c2ecf20Sopenharmony_ci	struct regmap	*regmap_cfg;
888c2ecf20Sopenharmony_ci	struct trng_regs __iomem *reg_rng;
898c2ecf20Sopenharmony_ci	u64 ready_ts;
908c2ecf20Sopenharmony_ci	unsigned int refill_delay_ns;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic unsigned int cycles_to_ns(unsigned long clk_rate, unsigned int cycles)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	return DIV_ROUND_UP_ULL((TRNG_DEF_CLK_DIV_CYCLES + 1) * 1000000000ull *
968c2ecf20Sopenharmony_ci				cycles, clk_rate);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic unsigned int startup_delay_ns(unsigned long clk_rate)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	if (!TRNG_DEF_STARTUP_CYCLES)
1028c2ecf20Sopenharmony_ci		return cycles_to_ns(clk_rate, BIT(24));
1038c2ecf20Sopenharmony_ci	return cycles_to_ns(clk_rate, 256 * TRNG_DEF_STARTUP_CYCLES);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic unsigned int refill_delay_ns(unsigned long clk_rate)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	if (!TRNG_DEF_MAX_REFILL_CYCLES)
1098c2ecf20Sopenharmony_ci		return cycles_to_ns(clk_rate, BIT(24));
1108c2ecf20Sopenharmony_ci	return cycles_to_ns(clk_rate, 256 * TRNG_DEF_MAX_REFILL_CYCLES);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int ks_sa_rng_init(struct hwrng *rng)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u32 value;
1168c2ecf20Sopenharmony_ci	struct device *dev = (struct device *)rng->priv;
1178c2ecf20Sopenharmony_ci	struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
1188c2ecf20Sopenharmony_ci	unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Enable RNG module */
1218c2ecf20Sopenharmony_ci	regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS,
1228c2ecf20Sopenharmony_ci			  SA_CMD_STATUS_REG_TRNG_ENABLE,
1238c2ecf20Sopenharmony_ci			  SA_CMD_STATUS_REG_TRNG_ENABLE);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Configure RNG module */
1268c2ecf20Sopenharmony_ci	writel(0, &ks_sa_rng->reg_rng->control);
1278c2ecf20Sopenharmony_ci	value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT;
1288c2ecf20Sopenharmony_ci	writel(value, &ks_sa_rng->reg_rng->control);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	value =	(TRNG_DEF_MIN_REFILL_CYCLES <<
1318c2ecf20Sopenharmony_ci		 TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) |
1328c2ecf20Sopenharmony_ci		(TRNG_DEF_MAX_REFILL_CYCLES <<
1338c2ecf20Sopenharmony_ci		 TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) |
1348c2ecf20Sopenharmony_ci		(TRNG_DEF_CLK_DIV_CYCLES <<
1358c2ecf20Sopenharmony_ci		 TRNG_CFG_REG_SAMPLE_DIV_SHIFT);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	writel(value, &ks_sa_rng->reg_rng->config);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Disable all interrupts from TRNG */
1408c2ecf20Sopenharmony_ci	writel(0, &ks_sa_rng->reg_rng->intmask);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Enable RNG */
1438c2ecf20Sopenharmony_ci	value = readl(&ks_sa_rng->reg_rng->control);
1448c2ecf20Sopenharmony_ci	value |= TRNG_CNTL_REG_TRNG_ENABLE;
1458c2ecf20Sopenharmony_ci	writel(value, &ks_sa_rng->reg_rng->control);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	ks_sa_rng->refill_delay_ns = refill_delay_ns(clk_rate);
1488c2ecf20Sopenharmony_ci	ks_sa_rng->ready_ts = ktime_get_ns() +
1498c2ecf20Sopenharmony_ci			      startup_delay_ns(clk_rate);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void ks_sa_rng_cleanup(struct hwrng *rng)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct device *dev = (struct device *)rng->priv;
1578c2ecf20Sopenharmony_ci	struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Disable RNG */
1608c2ecf20Sopenharmony_ci	writel(0, &ks_sa_rng->reg_rng->control);
1618c2ecf20Sopenharmony_ci	regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS,
1628c2ecf20Sopenharmony_ci			  SA_CMD_STATUS_REG_TRNG_ENABLE, 0);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int ks_sa_rng_data_read(struct hwrng *rng, u32 *data)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct device *dev = (struct device *)rng->priv;
1688c2ecf20Sopenharmony_ci	struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Read random data */
1718c2ecf20Sopenharmony_ci	data[0] = readl(&ks_sa_rng->reg_rng->output_l);
1728c2ecf20Sopenharmony_ci	data[1] = readl(&ks_sa_rng->reg_rng->output_h);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack);
1758c2ecf20Sopenharmony_ci	ks_sa_rng->ready_ts = ktime_get_ns() + ks_sa_rng->refill_delay_ns;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return sizeof(u32) * 2;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int ks_sa_rng_data_present(struct hwrng *rng, int wait)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct device *dev = (struct device *)rng->priv;
1838c2ecf20Sopenharmony_ci	struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev);
1848c2ecf20Sopenharmony_ci	u64 now = ktime_get_ns();
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	u32	ready;
1878c2ecf20Sopenharmony_ci	int	j;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (wait && now < ks_sa_rng->ready_ts) {
1908c2ecf20Sopenharmony_ci		/* Max delay expected here is 81920000 ns */
1918c2ecf20Sopenharmony_ci		unsigned long min_delay =
1928c2ecf20Sopenharmony_ci			DIV_ROUND_UP((u32)(ks_sa_rng->ready_ts - now), 1000);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		usleep_range(min_delay, min_delay + SA_RNG_DATA_RETRY_DELAY);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) {
1988c2ecf20Sopenharmony_ci		ready = readl(&ks_sa_rng->reg_rng->status);
1998c2ecf20Sopenharmony_ci		ready &= TRNG_STATUS_REG_READY;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		if (ready || !wait)
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		udelay(SA_RNG_DATA_RETRY_DELAY);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return ready;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int ks_sa_rng_probe(struct platform_device *pdev)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct ks_sa_rng	*ks_sa_rng;
2138c2ecf20Sopenharmony_ci	struct device		*dev = &pdev->dev;
2148c2ecf20Sopenharmony_ci	int			ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (!ks_sa_rng)
2188c2ecf20Sopenharmony_ci		return -ENOMEM;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ks_sa_rng->dev = dev;
2218c2ecf20Sopenharmony_ci	ks_sa_rng->rng = (struct hwrng) {
2228c2ecf20Sopenharmony_ci		.name = "ks_sa_hwrng",
2238c2ecf20Sopenharmony_ci		.init = ks_sa_rng_init,
2248c2ecf20Sopenharmony_ci		.data_read = ks_sa_rng_data_read,
2258c2ecf20Sopenharmony_ci		.data_present = ks_sa_rng_data_present,
2268c2ecf20Sopenharmony_ci		.cleanup = ks_sa_rng_cleanup,
2278c2ecf20Sopenharmony_ci	};
2288c2ecf20Sopenharmony_ci	ks_sa_rng->rng.priv = (unsigned long)dev;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0);
2318c2ecf20Sopenharmony_ci	if (IS_ERR(ks_sa_rng->reg_rng))
2328c2ecf20Sopenharmony_ci		return PTR_ERR(ks_sa_rng->reg_rng);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	ks_sa_rng->regmap_cfg =
2358c2ecf20Sopenharmony_ci		syscon_regmap_lookup_by_phandle(dev->of_node,
2368c2ecf20Sopenharmony_ci						"ti,syscon-sa-cfg");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (IS_ERR(ks_sa_rng->regmap_cfg)) {
2398c2ecf20Sopenharmony_ci		dev_err(dev, "syscon_node_to_regmap failed\n");
2408c2ecf20Sopenharmony_ci		return -EINVAL;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
2448c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(dev);
2458c2ecf20Sopenharmony_ci	if (ret < 0) {
2468c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable SA power-domain\n");
2478c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(dev);
2488c2ecf20Sopenharmony_ci		pm_runtime_disable(dev);
2498c2ecf20Sopenharmony_ci		return ret;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ks_sa_rng);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int ks_sa_rng_remove(struct platform_device *pdev)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
2608c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic const struct of_device_id ks_sa_rng_dt_match[] = {
2668c2ecf20Sopenharmony_ci	{
2678c2ecf20Sopenharmony_ci		.compatible = "ti,keystone-rng",
2688c2ecf20Sopenharmony_ci	},
2698c2ecf20Sopenharmony_ci	{ },
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic struct platform_driver ks_sa_rng_driver = {
2748c2ecf20Sopenharmony_ci	.driver		= {
2758c2ecf20Sopenharmony_ci		.name	= "ks-sa-rng",
2768c2ecf20Sopenharmony_ci		.of_match_table = ks_sa_rng_dt_match,
2778c2ecf20Sopenharmony_ci	},
2788c2ecf20Sopenharmony_ci	.probe		= ks_sa_rng_probe,
2798c2ecf20Sopenharmony_ci	.remove		= ks_sa_rng_remove,
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cimodule_platform_driver(ks_sa_rng_driver);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver");
2858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>");
2868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
287