Commit f29cd5bb authored by Om Prakash Singh's avatar Om Prakash Singh Committed by Herbert Xu
Browse files

crypto: qcom-rng - Add hw_random interface support



Add hw_random interface support in qcom-rng driver as new IP block
in Qualcomm SoC has inbuilt NIST SP800 90B compliant entropic source
to generate true random number.

Keeping current rng_alg interface as well for random number generation
using Kernel Crypto API.

Signed-off-by: default avatarOm Prakash Singh <quic_omprsing@quicinc.com>
Reviewed-by: default avatarBjorn Andersson <quic_bjorande@quicinc.com>
Signed-off-by: default avatarNeil Armstrong <neil.armstrong@linaro.org>
Acked-by: default avatarOm Prakash Singh <quic_omprsing@quicinc.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent a4d7e9ec
Loading
Loading
Loading
Loading
+58 −7
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/crypto.h>
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -28,17 +29,25 @@

#define WORD_SZ			4

#define QCOM_TRNG_QUALITY	1024

struct qcom_rng {
	struct mutex lock;
	void __iomem *base;
	struct clk *clk;
	unsigned int skip_init;
	struct hwrng hwrng;
	struct qcom_rng_of_data *of_data;
};

struct qcom_rng_ctx {
	struct qcom_rng *rng;
};

struct qcom_rng_of_data {
	bool skip_init;
	bool hwrng_support;
};

static struct qcom_rng *qcom_rng_dev;

static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
@@ -66,11 +75,11 @@ static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max)
		} else {
			/* copy only remaining bytes */
			memcpy(data, &val, max - currsize);
			break;
			currsize = max;
		}
	} while (currsize < max);

	return 0;
	return currsize;
}

static int qcom_rng_generate(struct crypto_rng *tfm,
@@ -92,6 +101,9 @@ static int qcom_rng_generate(struct crypto_rng *tfm,
	mutex_unlock(&rng->lock);
	clk_disable_unprepare(rng->clk);

	if (ret >= 0)
		ret = 0;

	return ret;
}

@@ -101,6 +113,13 @@ static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed,
	return 0;
}

static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wait)
{
	struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng);

	return qcom_rng_read(qrng, data, max);
}

static int qcom_rng_enable(struct qcom_rng *rng)
{
	u32 val;
@@ -136,7 +155,7 @@ static int qcom_rng_init(struct crypto_tfm *tfm)

	ctx->rng = qcom_rng_dev;

	if (!ctx->rng->skip_init)
	if (!ctx->rng->of_data->skip_init)
		return qcom_rng_enable(ctx->rng);

	return 0;
@@ -177,15 +196,31 @@ static int qcom_rng_probe(struct platform_device *pdev)
	if (IS_ERR(rng->clk))
		return PTR_ERR(rng->clk);

	rng->skip_init = (unsigned long)device_get_match_data(&pdev->dev);
	rng->of_data = (struct qcom_rng_of_data *)of_device_get_match_data(&pdev->dev);

	qcom_rng_dev = rng;
	ret = crypto_register_rng(&qcom_rng_alg);
	if (ret) {
		dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret);
		qcom_rng_dev = NULL;
		return ret;
	}

	if (rng->of_data->hwrng_support) {
		rng->hwrng.name = "qcom_hwrng";
		rng->hwrng.read = qcom_hwrng_read;
		rng->hwrng.quality = QCOM_TRNG_QUALITY;
		ret = devm_hwrng_register(&pdev->dev, &rng->hwrng);
		if (ret) {
			dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret);
			qcom_rng_dev = NULL;
			goto fail;
		}
	}

	return ret;
fail:
	crypto_unregister_rng(&qcom_rng_alg);
	return ret;
}

@@ -198,6 +233,21 @@ static int qcom_rng_remove(struct platform_device *pdev)
	return 0;
}

static struct qcom_rng_of_data qcom_prng_of_data = {
	.skip_init = false,
	.hwrng_support = false,
};

static struct qcom_rng_of_data qcom_prng_ee_of_data = {
	.skip_init = true,
	.hwrng_support = false,
};

static struct qcom_rng_of_data qcom_trng_of_data = {
	.skip_init = true,
	.hwrng_support = true,
};

static const struct acpi_device_id __maybe_unused qcom_rng_acpi_match[] = {
	{ .id = "QCOM8160", .driver_data = 1 },
	{}
@@ -205,8 +255,9 @@ static const struct acpi_device_id __maybe_unused qcom_rng_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, qcom_rng_acpi_match);

static const struct of_device_id __maybe_unused qcom_rng_of_match[] = {
	{ .compatible = "qcom,prng", .data = (void *)0},
	{ .compatible = "qcom,prng-ee", .data = (void *)1},
	{ .compatible = "qcom,prng", .data = &qcom_prng_of_data },
	{ .compatible = "qcom,prng-ee", .data = &qcom_prng_ee_of_data },
	{ .compatible = "qcom,trng", .data = &qcom_trng_of_data },
	{}
};
MODULE_DEVICE_TABLE(of, qcom_rng_of_match);