1f7557dc8SDavid Daney /* 2f7557dc8SDavid Daney * Hardware Random Number Generator support for Cavium Networks 3f7557dc8SDavid Daney * Octeon processor family. 4f7557dc8SDavid Daney * 5f7557dc8SDavid Daney * This file is subject to the terms and conditions of the GNU General Public 6f7557dc8SDavid Daney * License. See the file "COPYING" in the main directory of this archive 7f7557dc8SDavid Daney * for more details. 8f7557dc8SDavid Daney * 9f7557dc8SDavid Daney * Copyright (C) 2009 Cavium Networks 10f7557dc8SDavid Daney */ 11f7557dc8SDavid Daney 12f7557dc8SDavid Daney #include <linux/module.h> 13f7557dc8SDavid Daney #include <linux/init.h> 14f7557dc8SDavid Daney #include <linux/platform_device.h> 15f7557dc8SDavid Daney #include <linux/device.h> 16f7557dc8SDavid Daney #include <linux/hw_random.h> 17f7557dc8SDavid Daney #include <linux/io.h> 185a0e3ad6STejun Heo #include <linux/gfp.h> 19f7557dc8SDavid Daney 20f7557dc8SDavid Daney #include <asm/octeon/octeon.h> 21f7557dc8SDavid Daney #include <asm/octeon/cvmx-rnm-defs.h> 22f7557dc8SDavid Daney 23f7557dc8SDavid Daney struct octeon_rng { 24f7557dc8SDavid Daney struct hwrng ops; 25f7557dc8SDavid Daney void __iomem *control_status; 26f7557dc8SDavid Daney void __iomem *result; 27f7557dc8SDavid Daney }; 28f7557dc8SDavid Daney 29f7557dc8SDavid Daney static int octeon_rng_init(struct hwrng *rng) 30f7557dc8SDavid Daney { 31f7557dc8SDavid Daney union cvmx_rnm_ctl_status ctl; 32f7557dc8SDavid Daney struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 33f7557dc8SDavid Daney 34f7557dc8SDavid Daney ctl.u64 = 0; 35f7557dc8SDavid Daney ctl.s.ent_en = 1; /* Enable the entropy source. */ 36f7557dc8SDavid Daney ctl.s.rng_en = 1; /* Enable the RNG hardware. */ 37f7557dc8SDavid Daney cvmx_write_csr((u64)p->control_status, ctl.u64); 38f7557dc8SDavid Daney return 0; 39f7557dc8SDavid Daney } 40f7557dc8SDavid Daney 41f7557dc8SDavid Daney static void octeon_rng_cleanup(struct hwrng *rng) 42f7557dc8SDavid Daney { 43f7557dc8SDavid Daney union cvmx_rnm_ctl_status ctl; 44f7557dc8SDavid Daney struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 45f7557dc8SDavid Daney 46f7557dc8SDavid Daney ctl.u64 = 0; 47f7557dc8SDavid Daney /* Disable everything. */ 48f7557dc8SDavid Daney cvmx_write_csr((u64)p->control_status, ctl.u64); 49f7557dc8SDavid Daney } 50f7557dc8SDavid Daney 51f7557dc8SDavid Daney static int octeon_rng_data_read(struct hwrng *rng, u32 *data) 52f7557dc8SDavid Daney { 53f7557dc8SDavid Daney struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); 54f7557dc8SDavid Daney 55f7557dc8SDavid Daney *data = cvmx_read64_uint32((u64)p->result); 56f7557dc8SDavid Daney return sizeof(u32); 57f7557dc8SDavid Daney } 58f7557dc8SDavid Daney 59f7557dc8SDavid Daney static int __devinit octeon_rng_probe(struct platform_device *pdev) 60f7557dc8SDavid Daney { 61f7557dc8SDavid Daney struct resource *res_ports; 62f7557dc8SDavid Daney struct resource *res_result; 63f7557dc8SDavid Daney struct octeon_rng *rng; 64f7557dc8SDavid Daney int ret; 65f7557dc8SDavid Daney struct hwrng ops = { 66f7557dc8SDavid Daney .name = "octeon", 67f7557dc8SDavid Daney .init = octeon_rng_init, 68f7557dc8SDavid Daney .cleanup = octeon_rng_cleanup, 69f7557dc8SDavid Daney .data_read = octeon_rng_data_read 70f7557dc8SDavid Daney }; 71f7557dc8SDavid Daney 72f7557dc8SDavid Daney rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 73f7557dc8SDavid Daney if (!rng) 74f7557dc8SDavid Daney return -ENOMEM; 75f7557dc8SDavid Daney 76f7557dc8SDavid Daney res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); 77f7557dc8SDavid Daney if (!res_ports) 78f7557dc8SDavid Daney goto err_ports; 79f7557dc8SDavid Daney 80f7557dc8SDavid Daney res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); 81f7557dc8SDavid Daney if (!res_result) 82f7557dc8SDavid Daney goto err_ports; 83f7557dc8SDavid Daney 84f7557dc8SDavid Daney 85f7557dc8SDavid Daney rng->control_status = devm_ioremap_nocache(&pdev->dev, 86f7557dc8SDavid Daney res_ports->start, 87f7557dc8SDavid Daney sizeof(u64)); 88f7557dc8SDavid Daney if (!rng->control_status) 89f7557dc8SDavid Daney goto err_ports; 90f7557dc8SDavid Daney 91f7557dc8SDavid Daney rng->result = devm_ioremap_nocache(&pdev->dev, 92f7557dc8SDavid Daney res_result->start, 93f7557dc8SDavid Daney sizeof(u64)); 94f7557dc8SDavid Daney if (!rng->result) 95f7557dc8SDavid Daney goto err_r; 96f7557dc8SDavid Daney 97f7557dc8SDavid Daney rng->ops = ops; 98f7557dc8SDavid Daney 99f7557dc8SDavid Daney dev_set_drvdata(&pdev->dev, &rng->ops); 100f7557dc8SDavid Daney ret = hwrng_register(&rng->ops); 101f7557dc8SDavid Daney if (ret) 102f7557dc8SDavid Daney goto err; 103f7557dc8SDavid Daney 104f7557dc8SDavid Daney dev_info(&pdev->dev, "Octeon Random Number Generator\n"); 105f7557dc8SDavid Daney 106f7557dc8SDavid Daney return 0; 107f7557dc8SDavid Daney err: 108f7557dc8SDavid Daney devm_iounmap(&pdev->dev, rng->control_status); 109f7557dc8SDavid Daney err_r: 110f7557dc8SDavid Daney devm_iounmap(&pdev->dev, rng->result); 111f7557dc8SDavid Daney err_ports: 112f7557dc8SDavid Daney devm_kfree(&pdev->dev, rng); 113f7557dc8SDavid Daney return -ENOENT; 114f7557dc8SDavid Daney } 115f7557dc8SDavid Daney 116f7557dc8SDavid Daney static int __exit octeon_rng_remove(struct platform_device *pdev) 117f7557dc8SDavid Daney { 118f7557dc8SDavid Daney struct hwrng *rng = dev_get_drvdata(&pdev->dev); 119f7557dc8SDavid Daney 120f7557dc8SDavid Daney hwrng_unregister(rng); 121f7557dc8SDavid Daney 122f7557dc8SDavid Daney return 0; 123f7557dc8SDavid Daney } 124f7557dc8SDavid Daney 125f7557dc8SDavid Daney static struct platform_driver octeon_rng_driver = { 126f7557dc8SDavid Daney .driver = { 127f7557dc8SDavid Daney .name = "octeon_rng", 128f7557dc8SDavid Daney .owner = THIS_MODULE, 129f7557dc8SDavid Daney }, 130f7557dc8SDavid Daney .probe = octeon_rng_probe, 131f7557dc8SDavid Daney .remove = __exit_p(octeon_rng_remove), 132f7557dc8SDavid Daney }; 133f7557dc8SDavid Daney 134f7557dc8SDavid Daney static int __init octeon_rng_mod_init(void) 135f7557dc8SDavid Daney { 136f7557dc8SDavid Daney return platform_driver_register(&octeon_rng_driver); 137f7557dc8SDavid Daney } 138f7557dc8SDavid Daney 139f7557dc8SDavid Daney static void __exit octeon_rng_mod_exit(void) 140f7557dc8SDavid Daney { 141f7557dc8SDavid Daney platform_driver_unregister(&octeon_rng_driver); 142f7557dc8SDavid Daney } 143f7557dc8SDavid Daney 144f7557dc8SDavid Daney module_init(octeon_rng_mod_init); 145f7557dc8SDavid Daney module_exit(octeon_rng_mod_exit); 146f7557dc8SDavid Daney 147f7557dc8SDavid Daney MODULE_AUTHOR("David Daney"); 148f7557dc8SDavid Daney MODULE_LICENSE("GPL"); 149