brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 9776f4d Raw
398 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * TRNG driver for the StarFive JH7110 SoC4 *5 * Copyright (C) 2022 StarFive Technology Co.6 */7 8#include <linux/clk.h>9#include <linux/completion.h>10#include <linux/delay.h>11#include <linux/err.h>12#include <linux/hw_random.h>13#include <linux/interrupt.h>14#include <linux/io.h>15#include <linux/iopoll.h>16#include <linux/kernel.h>17#include <linux/module.h>18#include <linux/of.h>19#include <linux/platform_device.h>20#include <linux/pm_runtime.h>21#include <linux/random.h>22#include <linux/reset.h>23 24/* trng register offset */25#define STARFIVE_CTRL			0x0026#define STARFIVE_STAT			0x0427#define STARFIVE_MODE			0x0828#define STARFIVE_SMODE			0x0C29#define STARFIVE_IE			0x1030#define STARFIVE_ISTAT			0x1431#define STARFIVE_RAND0			0x2032#define STARFIVE_RAND1			0x2433#define STARFIVE_RAND2			0x2834#define STARFIVE_RAND3			0x2C35#define STARFIVE_RAND4			0x3036#define STARFIVE_RAND5			0x3437#define STARFIVE_RAND6			0x3838#define STARFIVE_RAND7			0x3C39#define STARFIVE_AUTO_RQSTS		0x6040#define STARFIVE_AUTO_AGE		0x6441 42/* CTRL CMD */43#define STARFIVE_CTRL_EXEC_NOP		0x044#define STARFIVE_CTRL_GENE_RANDNUM	0x145#define STARFIVE_CTRL_EXEC_RANDRESEED	0x246 47/* STAT */48#define STARFIVE_STAT_NONCE_MODE	BIT(2)49#define STARFIVE_STAT_R256		BIT(3)50#define STARFIVE_STAT_MISSION_MODE	BIT(8)51#define STARFIVE_STAT_SEEDED		BIT(9)52#define STARFIVE_STAT_LAST_RESEED(x)	((x) << 16)53#define STARFIVE_STAT_SRVC_RQST		BIT(27)54#define STARFIVE_STAT_RAND_GENERATING	BIT(30)55#define STARFIVE_STAT_RAND_SEEDING	BIT(31)56 57/* MODE */58#define STARFIVE_MODE_R256		BIT(3)59 60/* SMODE */61#define STARFIVE_SMODE_NONCE_MODE	BIT(2)62#define STARFIVE_SMODE_MISSION_MODE	BIT(8)63#define STARFIVE_SMODE_MAX_REJECTS(x)	((x) << 16)64 65/* IE */66#define STARFIVE_IE_RAND_RDY_EN		BIT(0)67#define STARFIVE_IE_SEED_DONE_EN	BIT(1)68#define STARFIVE_IE_LFSR_LOCKUP_EN	BIT(4)69#define STARFIVE_IE_GLBL_EN		BIT(31)70 71#define STARFIVE_IE_ALL			(STARFIVE_IE_GLBL_EN | \72					 STARFIVE_IE_RAND_RDY_EN | \73					 STARFIVE_IE_SEED_DONE_EN | \74					 STARFIVE_IE_LFSR_LOCKUP_EN)75 76/* ISTAT */77#define STARFIVE_ISTAT_RAND_RDY		BIT(0)78#define STARFIVE_ISTAT_SEED_DONE	BIT(1)79#define STARFIVE_ISTAT_LFSR_LOCKUP	BIT(4)80 81#define STARFIVE_RAND_LEN		sizeof(u32)82 83#define to_trng(p)			container_of(p, struct starfive_trng, rng)84 85enum reseed {86	RANDOM_RESEED,87	NONCE_RESEED,88};89 90enum mode {91	PRNG_128BIT,92	PRNG_256BIT,93};94 95struct starfive_trng {96	struct device		*dev;97	void __iomem		*base;98	struct clk		*hclk;99	struct clk		*ahb;100	struct reset_control	*rst;101	struct hwrng		rng;102	struct completion	random_done;103	struct completion	reseed_done;104	u32			mode;105	u32			mission;106	u32			reseed;107	/* protects against concurrent write to ctrl register */108	spinlock_t		write_lock;109};110 111static u16 autoreq;112module_param(autoreq, ushort, 0);113MODULE_PARM_DESC(autoreq, "Auto-reseeding after random number requests by host reaches specified counter:\n"114				" 0 - disable counter\n"115				" other - reload value for internal counter");116 117static u16 autoage;118module_param(autoage, ushort, 0);119MODULE_PARM_DESC(autoage, "Auto-reseeding after specified timer countdowns to 0:\n"120				" 0 - disable timer\n"121				" other - reload value for internal timer");122 123static inline int starfive_trng_wait_idle(struct starfive_trng *trng)124{125	u32 stat;126 127	return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,128					  !(stat & (STARFIVE_STAT_RAND_GENERATING |129						    STARFIVE_STAT_RAND_SEEDING)),130					  10, 100000);131}132 133static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)134{135	/* clear register: ISTAT */136	u32 data = readl(trng->base + STARFIVE_ISTAT);137 138	writel(data, trng->base + STARFIVE_ISTAT);139}140 141static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, bool wait)142{143	int wait_time = 1000;144 145	/* allow up to 40 us for wait == 0 */146	if (!wait)147		wait_time = 40;148 149	switch (cmd) {150	case STARFIVE_CTRL_GENE_RANDNUM:151		reinit_completion(&trng->random_done);152		spin_lock_irq(&trng->write_lock);153		writel(cmd, trng->base + STARFIVE_CTRL);154		spin_unlock_irq(&trng->write_lock);155		if (!wait_for_completion_timeout(&trng->random_done, usecs_to_jiffies(wait_time)))156			return -ETIMEDOUT;157		break;158	case STARFIVE_CTRL_EXEC_RANDRESEED:159		reinit_completion(&trng->reseed_done);160		spin_lock_irq(&trng->write_lock);161		writel(cmd, trng->base + STARFIVE_CTRL);162		spin_unlock_irq(&trng->write_lock);163		if (!wait_for_completion_timeout(&trng->reseed_done, usecs_to_jiffies(wait_time)))164			return -ETIMEDOUT;165		break;166	default:167		return -EINVAL;168	}169 170	return 0;171}172 173static int starfive_trng_init(struct hwrng *rng)174{175	struct starfive_trng *trng = to_trng(rng);176	u32 mode, intr = 0;177 178	/* setup Auto Request/Age register */179	writel(autoage, trng->base + STARFIVE_AUTO_AGE);180	writel(autoreq, trng->base + STARFIVE_AUTO_RQSTS);181 182	/* clear register: ISTAT */183	starfive_trng_irq_mask_clear(trng);184 185	intr |= STARFIVE_IE_ALL;186	writel(intr, trng->base + STARFIVE_IE);187 188	mode  = readl(trng->base + STARFIVE_MODE);189 190	switch (trng->mode) {191	case PRNG_128BIT:192		mode &= ~STARFIVE_MODE_R256;193		break;194	case PRNG_256BIT:195		mode |= STARFIVE_MODE_R256;196		break;197	default:198		mode |= STARFIVE_MODE_R256;199		break;200	}201 202	writel(mode, trng->base + STARFIVE_MODE);203 204	return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);205}206 207static irqreturn_t starfive_trng_irq(int irq, void *priv)208{209	u32 status;210	struct starfive_trng *trng = (struct starfive_trng *)priv;211 212	status = readl(trng->base + STARFIVE_ISTAT);213	if (status & STARFIVE_ISTAT_RAND_RDY) {214		writel(STARFIVE_ISTAT_RAND_RDY, trng->base + STARFIVE_ISTAT);215		complete(&trng->random_done);216	}217 218	if (status & STARFIVE_ISTAT_SEED_DONE) {219		writel(STARFIVE_ISTAT_SEED_DONE, trng->base + STARFIVE_ISTAT);220		complete(&trng->reseed_done);221	}222 223	if (status & STARFIVE_ISTAT_LFSR_LOCKUP) {224		writel(STARFIVE_ISTAT_LFSR_LOCKUP, trng->base + STARFIVE_ISTAT);225		/* SEU occurred, reseeding required*/226		spin_lock(&trng->write_lock);227		writel(STARFIVE_CTRL_EXEC_RANDRESEED, trng->base + STARFIVE_CTRL);228		spin_unlock(&trng->write_lock);229	}230 231	return IRQ_HANDLED;232}233 234static void starfive_trng_cleanup(struct hwrng *rng)235{236	struct starfive_trng *trng = to_trng(rng);237 238	writel(0, trng->base + STARFIVE_CTRL);239 240	reset_control_assert(trng->rst);241	clk_disable_unprepare(trng->hclk);242	clk_disable_unprepare(trng->ahb);243}244 245static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)246{247	struct starfive_trng *trng = to_trng(rng);248	int ret;249 250	pm_runtime_get_sync(trng->dev);251 252	if (trng->mode == PRNG_256BIT)253		max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));254	else255		max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));256 257	if (wait) {258		ret = starfive_trng_wait_idle(trng);259		if (ret)260			return -ETIMEDOUT;261	}262 263	ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);264	if (ret)265		return ret;266 267	memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);268 269	pm_runtime_put_sync_autosuspend(trng->dev);270 271	return max;272}273 274static int starfive_trng_probe(struct platform_device *pdev)275{276	int ret;277	int irq;278	struct starfive_trng *trng;279 280	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);281	if (!trng)282		return -ENOMEM;283 284	platform_set_drvdata(pdev, trng);285	trng->dev = &pdev->dev;286 287	trng->base = devm_platform_ioremap_resource(pdev, 0);288	if (IS_ERR(trng->base))289		return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),290				     "Error remapping memory for platform device.\n");291 292	irq = platform_get_irq(pdev, 0);293	if (irq < 0)294		return irq;295 296	init_completion(&trng->random_done);297	init_completion(&trng->reseed_done);298	spin_lock_init(&trng->write_lock);299 300	ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name,301			       (void *)trng);302	if (ret)303		return dev_err_probe(&pdev->dev, ret,304				     "Failed to register interrupt handler\n");305 306	trng->hclk = devm_clk_get(&pdev->dev, "hclk");307	if (IS_ERR(trng->hclk))308		return dev_err_probe(&pdev->dev, PTR_ERR(trng->hclk),309				     "Error getting hardware reference clock\n");310 311	trng->ahb = devm_clk_get(&pdev->dev, "ahb");312	if (IS_ERR(trng->ahb))313		return dev_err_probe(&pdev->dev, PTR_ERR(trng->ahb),314				     "Error getting ahb reference clock\n");315 316	trng->rst = devm_reset_control_get_shared(&pdev->dev, NULL);317	if (IS_ERR(trng->rst))318		return dev_err_probe(&pdev->dev, PTR_ERR(trng->rst),319				     "Error getting hardware reset line\n");320 321	clk_prepare_enable(trng->hclk);322	clk_prepare_enable(trng->ahb);323	reset_control_deassert(trng->rst);324 325	trng->rng.name = dev_driver_string(&pdev->dev);326	trng->rng.init = starfive_trng_init;327	trng->rng.cleanup = starfive_trng_cleanup;328	trng->rng.read = starfive_trng_read;329 330	trng->mode = PRNG_256BIT;331	trng->mission = 1;332	trng->reseed = RANDOM_RESEED;333 334	pm_runtime_use_autosuspend(&pdev->dev);335	pm_runtime_set_autosuspend_delay(&pdev->dev, 100);336	pm_runtime_enable(&pdev->dev);337 338	ret = devm_hwrng_register(&pdev->dev, &trng->rng);339	if (ret) {340		pm_runtime_disable(&pdev->dev);341 342		reset_control_assert(trng->rst);343		clk_disable_unprepare(trng->ahb);344		clk_disable_unprepare(trng->hclk);345 346		return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");347	}348 349	return 0;350}351 352static int __maybe_unused starfive_trng_suspend(struct device *dev)353{354	struct starfive_trng *trng = dev_get_drvdata(dev);355 356	clk_disable_unprepare(trng->hclk);357	clk_disable_unprepare(trng->ahb);358 359	return 0;360}361 362static int __maybe_unused starfive_trng_resume(struct device *dev)363{364	struct starfive_trng *trng = dev_get_drvdata(dev);365 366	clk_prepare_enable(trng->hclk);367	clk_prepare_enable(trng->ahb);368 369	return 0;370}371 372static const struct dev_pm_ops starfive_trng_pm_ops = {373	SET_SYSTEM_SLEEP_PM_OPS(starfive_trng_suspend,374				starfive_trng_resume)375	SET_RUNTIME_PM_OPS(starfive_trng_suspend,376			   starfive_trng_resume, NULL)377};378 379static const struct of_device_id trng_dt_ids[] __maybe_unused = {380	{ .compatible = "starfive,jh7110-trng" },381	{ }382};383MODULE_DEVICE_TABLE(of, trng_dt_ids);384 385static struct platform_driver starfive_trng_driver = {386	.probe	= starfive_trng_probe,387	.driver	= {388		.name		= "jh7110-trng",389		.pm		= &starfive_trng_pm_ops,390		.of_match_table	= of_match_ptr(trng_dt_ids),391	},392};393 394module_platform_driver(starfive_trng_driver);395 396MODULE_LICENSE("GPL");397MODULE_DESCRIPTION("StarFive True Random Number Generator");398