summaryrefslogtreecommitdiffstats
path: root/drivers/char/hw_random/octeon-rng.c
blob: 0943edc782a16fcf789df1d2c7f12e4ffe54659e (plain)
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");