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