162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Hardware Random Number Generator support for Cavium Networks 362306a36Sopenharmony_ci * Octeon processor family. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 662306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 762306a36Sopenharmony_ci * for more details. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2009 Cavium Networks 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/hw_random.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/gfp.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/octeon/octeon.h> 2062306a36Sopenharmony_ci#include <asm/octeon/cvmx-rnm-defs.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct octeon_rng { 2362306a36Sopenharmony_ci struct hwrng ops; 2462306a36Sopenharmony_ci void __iomem *control_status; 2562306a36Sopenharmony_ci void __iomem *result; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int octeon_rng_init(struct hwrng *rng) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci union cvmx_rnm_ctl_status ctl; 3162306a36Sopenharmony_ci struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ctl.u64 = 0; 3462306a36Sopenharmony_ci ctl.s.ent_en = 1; /* Enable the entropy source. */ 3562306a36Sopenharmony_ci ctl.s.rng_en = 1; /* Enable the RNG hardware. */ 3662306a36Sopenharmony_ci cvmx_write_csr((__force u64)p->control_status, ctl.u64); 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void octeon_rng_cleanup(struct hwrng *rng) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci union cvmx_rnm_ctl_status ctl; 4362306a36Sopenharmony_ci struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ctl.u64 = 0; 4662306a36Sopenharmony_ci /* Disable everything. */ 4762306a36Sopenharmony_ci cvmx_write_csr((__force u64)p->control_status, ctl.u64); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int octeon_rng_data_read(struct hwrng *rng, u32 *data) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci *data = cvmx_read64_uint32((__force u64)p->result); 5562306a36Sopenharmony_ci return sizeof(u32); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int octeon_rng_probe(struct platform_device *pdev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct resource *res_ports; 6162306a36Sopenharmony_ci struct resource *res_result; 6262306a36Sopenharmony_ci struct octeon_rng *rng; 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci struct hwrng ops = { 6562306a36Sopenharmony_ci .name = "octeon", 6662306a36Sopenharmony_ci .init = octeon_rng_init, 6762306a36Sopenharmony_ci .cleanup = octeon_rng_cleanup, 6862306a36Sopenharmony_ci .data_read = octeon_rng_data_read 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 7262306a36Sopenharmony_ci if (!rng) 7362306a36Sopenharmony_ci return -ENOMEM; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7662306a36Sopenharmony_ci if (!res_ports) 7762306a36Sopenharmony_ci return -ENOENT; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); 8062306a36Sopenharmony_ci if (!res_result) 8162306a36Sopenharmony_ci return -ENOENT; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci rng->control_status = devm_ioremap(&pdev->dev, 8562306a36Sopenharmony_ci res_ports->start, 8662306a36Sopenharmony_ci sizeof(u64)); 8762306a36Sopenharmony_ci if (!rng->control_status) 8862306a36Sopenharmony_ci return -ENOENT; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rng->result = devm_ioremap(&pdev->dev, 9162306a36Sopenharmony_ci res_result->start, 9262306a36Sopenharmony_ci sizeof(u64)); 9362306a36Sopenharmony_ci if (!rng->result) 9462306a36Sopenharmony_ci return -ENOENT; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci rng->ops = ops; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci platform_set_drvdata(pdev, &rng->ops); 9962306a36Sopenharmony_ci ret = devm_hwrng_register(&pdev->dev, &rng->ops); 10062306a36Sopenharmony_ci if (ret) 10162306a36Sopenharmony_ci return -ENOENT; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dev_info(&pdev->dev, "Octeon Random Number Generator\n"); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct platform_driver octeon_rng_driver = { 10962306a36Sopenharmony_ci .driver = { 11062306a36Sopenharmony_ci .name = "octeon_rng", 11162306a36Sopenharmony_ci }, 11262306a36Sopenharmony_ci .probe = octeon_rng_probe, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cimodule_platform_driver(octeon_rng_driver); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciMODULE_AUTHOR("David Daney"); 11862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 119