162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2010 Michael Neuling IBM Corporation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for the pseries hardware RNG for POWER7+ and above
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/hw_random.h>
1362306a36Sopenharmony_ci#include <asm/vio.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	u64 buffer[PLPAR_HCALL_BUFSIZE];
1962306a36Sopenharmony_ci	int rc;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	rc = plpar_hcall(H_RANDOM, (unsigned long *)buffer);
2262306a36Sopenharmony_ci	if (rc != H_SUCCESS) {
2362306a36Sopenharmony_ci		pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
2462306a36Sopenharmony_ci		return -EIO;
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci	memcpy(data, buffer, 8);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* The hypervisor interface returns 64 bits */
2962306a36Sopenharmony_ci	return 8;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * This is a required function for a driver to operate in a CMO environment
3662306a36Sopenharmony_ci * but this device does not make use of DMA allocations, return 0.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * Return value:
3962306a36Sopenharmony_ci *	Number of bytes of IO data the driver will need to perform well -> 0
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_cistatic unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct hwrng pseries_rng = {
4762306a36Sopenharmony_ci	.name		= KBUILD_MODNAME,
4862306a36Sopenharmony_ci	.read		= pseries_rng_read,
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int pseries_rng_probe(struct vio_dev *dev,
5262306a36Sopenharmony_ci		const struct vio_device_id *id)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return hwrng_register(&pseries_rng);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void pseries_rng_remove(struct vio_dev *dev)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	hwrng_unregister(&pseries_rng);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct vio_device_id pseries_rng_driver_ids[] = {
6362306a36Sopenharmony_ci	{ "ibm,random-v1", "ibm,random"},
6462306a36Sopenharmony_ci	{ "", "" }
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct vio_driver pseries_rng_driver = {
6962306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
7062306a36Sopenharmony_ci	.probe = pseries_rng_probe,
7162306a36Sopenharmony_ci	.remove = pseries_rng_remove,
7262306a36Sopenharmony_ci	.get_desired_dma = pseries_rng_get_desired_dma,
7362306a36Sopenharmony_ci	.id_table = pseries_rng_driver_ids
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int __init rng_init(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	pr_info("Registering IBM pSeries RNG driver\n");
7962306a36Sopenharmony_ci	return vio_register_driver(&pseries_rng_driver);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cimodule_init(rng_init);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void __exit rng_exit(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	vio_unregister_driver(&pseries_rng_driver);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_cimodule_exit(rng_exit);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9162306a36Sopenharmony_ciMODULE_AUTHOR("Michael Neuling <mikey@neuling.org>");
9262306a36Sopenharmony_ciMODULE_DESCRIPTION("H/W RNG driver for IBM pSeries processors");
93