18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generic PowerPC 44x RNG driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 IBM Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "crypto4xx_core.h"
198c2ecf20Sopenharmony_ci#include "crypto4xx_trng.h"
208c2ecf20Sopenharmony_ci#include "crypto4xx_reg_def.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define PPC4XX_TRNG_CTRL	0x0008
238c2ecf20Sopenharmony_ci#define PPC4XX_TRNG_CTRL_DALM	0x20
248c2ecf20Sopenharmony_ci#define PPC4XX_TRNG_STAT	0x0004
258c2ecf20Sopenharmony_ci#define PPC4XX_TRNG_STAT_B	0x1
268c2ecf20Sopenharmony_ci#define PPC4XX_TRNG_DATA	0x0000
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int ppc4xx_trng_data_present(struct hwrng *rng, int wait)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct crypto4xx_device *dev = (void *)rng->priv;
318c2ecf20Sopenharmony_ci	int busy, i, present = 0;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	for (i = 0; i < 20; i++) {
348c2ecf20Sopenharmony_ci		busy = (in_le32(dev->trng_base + PPC4XX_TRNG_STAT) &
358c2ecf20Sopenharmony_ci			PPC4XX_TRNG_STAT_B);
368c2ecf20Sopenharmony_ci		if (!busy || !wait) {
378c2ecf20Sopenharmony_ci			present = 1;
388c2ecf20Sopenharmony_ci			break;
398c2ecf20Sopenharmony_ci		}
408c2ecf20Sopenharmony_ci		udelay(10);
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	return present;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int ppc4xx_trng_data_read(struct hwrng *rng, u32 *data)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct crypto4xx_device *dev = (void *)rng->priv;
488c2ecf20Sopenharmony_ci	*data = in_le32(dev->trng_base + PPC4XX_TRNG_DATA);
498c2ecf20Sopenharmony_ci	return 4;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void ppc4xx_trng_enable(struct crypto4xx_device *dev, bool enable)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	u32 device_ctrl;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	device_ctrl = readl(dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
578c2ecf20Sopenharmony_ci	if (enable)
588c2ecf20Sopenharmony_ci		device_ctrl |= PPC4XX_TRNG_EN;
598c2ecf20Sopenharmony_ci	else
608c2ecf20Sopenharmony_ci		device_ctrl &= ~PPC4XX_TRNG_EN;
618c2ecf20Sopenharmony_ci	writel(device_ctrl, dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct of_device_id ppc4xx_trng_match[] = {
658c2ecf20Sopenharmony_ci	{ .compatible = "ppc4xx-rng", },
668c2ecf20Sopenharmony_ci	{ .compatible = "amcc,ppc460ex-rng", },
678c2ecf20Sopenharmony_ci	{ .compatible = "amcc,ppc440epx-rng", },
688c2ecf20Sopenharmony_ci	{},
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_civoid ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct crypto4xx_device *dev = core_dev->dev;
748c2ecf20Sopenharmony_ci	struct device_node *trng = NULL;
758c2ecf20Sopenharmony_ci	struct hwrng *rng = NULL;
768c2ecf20Sopenharmony_ci	int err;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Find the TRNG device node and map it */
798c2ecf20Sopenharmony_ci	trng = of_find_matching_node(NULL, ppc4xx_trng_match);
808c2ecf20Sopenharmony_ci	if (!trng || !of_device_is_available(trng)) {
818c2ecf20Sopenharmony_ci		of_node_put(trng);
828c2ecf20Sopenharmony_ci		return;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	dev->trng_base = of_iomap(trng, 0);
868c2ecf20Sopenharmony_ci	of_node_put(trng);
878c2ecf20Sopenharmony_ci	if (!dev->trng_base)
888c2ecf20Sopenharmony_ci		goto err_out;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	rng = kzalloc(sizeof(*rng), GFP_KERNEL);
918c2ecf20Sopenharmony_ci	if (!rng)
928c2ecf20Sopenharmony_ci		goto err_out;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rng->name = KBUILD_MODNAME;
958c2ecf20Sopenharmony_ci	rng->data_present = ppc4xx_trng_data_present;
968c2ecf20Sopenharmony_ci	rng->data_read = ppc4xx_trng_data_read;
978c2ecf20Sopenharmony_ci	rng->priv = (unsigned long) dev;
988c2ecf20Sopenharmony_ci	core_dev->trng = rng;
998c2ecf20Sopenharmony_ci	ppc4xx_trng_enable(dev, true);
1008c2ecf20Sopenharmony_ci	out_le32(dev->trng_base + PPC4XX_TRNG_CTRL, PPC4XX_TRNG_CTRL_DALM);
1018c2ecf20Sopenharmony_ci	err = devm_hwrng_register(core_dev->device, core_dev->trng);
1028c2ecf20Sopenharmony_ci	if (err) {
1038c2ecf20Sopenharmony_ci		ppc4xx_trng_enable(dev, false);
1048c2ecf20Sopenharmony_ci		dev_err(core_dev->device, "failed to register hwrng (%d).\n",
1058c2ecf20Sopenharmony_ci			err);
1068c2ecf20Sopenharmony_ci		goto err_out;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	return;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cierr_out:
1118c2ecf20Sopenharmony_ci	iounmap(dev->trng_base);
1128c2ecf20Sopenharmony_ci	kfree(rng);
1138c2ecf20Sopenharmony_ci	dev->trng_base = NULL;
1148c2ecf20Sopenharmony_ci	core_dev->trng = NULL;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_civoid ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	if (core_dev && core_dev->trng) {
1208c2ecf20Sopenharmony_ci		struct crypto4xx_device *dev = core_dev->dev;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		devm_hwrng_unregister(core_dev->device, core_dev->trng);
1238c2ecf20Sopenharmony_ci		ppc4xx_trng_enable(dev, false);
1248c2ecf20Sopenharmony_ci		iounmap(dev->trng_base);
1258c2ecf20Sopenharmony_ci		kfree(core_dev->trng);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciMODULE_ALIAS("ppc4xx_rng");
130