Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/hw_random/amd-rng.c
29269 views
1
/*
2
* RNG driver for AMD RNGs
3
*
4
* Copyright 2005 (c) MontaVista Software, Inc.
5
*
6
* with the majority of the code coming from:
7
*
8
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
9
* (c) Copyright 2003 Red Hat Inc <[email protected]>
10
*
11
* derived from
12
*
13
* Hardware driver for the AMD 768 Random Number Generator (RNG)
14
* (c) Copyright 2001 Red Hat Inc
15
*
16
* derived from
17
*
18
* Hardware driver for Intel i810 Random Number Generator (RNG)
19
* Copyright 2000,2001 Jeff Garzik <[email protected]>
20
* Copyright 2000,2001 Philipp Rumpf <[email protected]>
21
*
22
* This file is licensed under the terms of the GNU General Public
23
* License version 2. This program is licensed "as is" without any
24
* warranty of any kind, whether express or implied.
25
*/
26
27
#include <linux/delay.h>
28
#include <linux/hw_random.h>
29
#include <linux/io.h>
30
#include <linux/kernel.h>
31
#include <linux/module.h>
32
#include <linux/pci.h>
33
34
#define DRV_NAME "AMD768-HWRNG"
35
36
#define RNGDATA 0x00
37
#define RNGDONE 0x04
38
#define PMBASE_OFFSET 0xF0
39
#define PMBASE_SIZE 8
40
41
/*
42
* Data for PCI driver interface
43
*
44
* This data only exists for exporting the supported
45
* PCI ids via MODULE_DEVICE_TABLE. We do not actually
46
* register a pci_driver, because someone else might one day
47
* want to register another driver on the same PCI id.
48
*/
49
static const struct pci_device_id pci_tbl[] = {
50
{ PCI_VDEVICE(AMD, 0x7443), 0, },
51
{ PCI_VDEVICE(AMD, 0x746b), 0, },
52
{ 0, }, /* terminate list */
53
};
54
MODULE_DEVICE_TABLE(pci, pci_tbl);
55
56
struct amd768_priv {
57
void __iomem *iobase;
58
struct pci_dev *pcidev;
59
u32 pmbase;
60
};
61
62
static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
63
{
64
u32 *data = buf;
65
struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
66
size_t read = 0;
67
/* We will wait at maximum one time per read */
68
int timeout = max / 4 + 1;
69
70
/*
71
* RNG data is available when RNGDONE is set to 1
72
* New random numbers are generated approximately 128 microseconds
73
* after RNGDATA is read
74
*/
75
while (read < max) {
76
if (ioread32(priv->iobase + RNGDONE) == 0) {
77
if (wait) {
78
/* Delay given by datasheet */
79
usleep_range(128, 196);
80
if (timeout-- == 0)
81
return read;
82
} else {
83
return 0;
84
}
85
} else {
86
*data = ioread32(priv->iobase + RNGDATA);
87
data++;
88
read += 4;
89
}
90
}
91
92
return read;
93
}
94
95
static int amd_rng_init(struct hwrng *rng)
96
{
97
struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
98
u8 rnen;
99
100
pci_read_config_byte(priv->pcidev, 0x40, &rnen);
101
rnen |= BIT(7); /* RNG on */
102
pci_write_config_byte(priv->pcidev, 0x40, rnen);
103
104
pci_read_config_byte(priv->pcidev, 0x41, &rnen);
105
rnen |= BIT(7); /* PMIO enable */
106
pci_write_config_byte(priv->pcidev, 0x41, rnen);
107
108
return 0;
109
}
110
111
static void amd_rng_cleanup(struct hwrng *rng)
112
{
113
struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
114
u8 rnen;
115
116
pci_read_config_byte(priv->pcidev, 0x40, &rnen);
117
rnen &= ~BIT(7); /* RNG off */
118
pci_write_config_byte(priv->pcidev, 0x40, rnen);
119
}
120
121
static struct hwrng amd_rng = {
122
.name = "amd",
123
.init = amd_rng_init,
124
.cleanup = amd_rng_cleanup,
125
.read = amd_rng_read,
126
};
127
128
static int __init amd_rng_mod_init(void)
129
{
130
int err;
131
struct pci_dev *pdev = NULL;
132
const struct pci_device_id *ent;
133
u32 pmbase;
134
struct amd768_priv *priv;
135
136
for_each_pci_dev(pdev) {
137
ent = pci_match_id(pci_tbl, pdev);
138
if (ent)
139
goto found;
140
}
141
/* Device not found. */
142
return -ENODEV;
143
144
found:
145
err = pci_read_config_dword(pdev, 0x58, &pmbase);
146
if (err) {
147
err = pcibios_err_to_errno(err);
148
goto put_dev;
149
}
150
151
pmbase &= 0x0000FF00;
152
if (pmbase == 0) {
153
err = -EIO;
154
goto put_dev;
155
}
156
157
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
158
if (!priv) {
159
err = -ENOMEM;
160
goto put_dev;
161
}
162
163
if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
164
dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
165
pmbase + 0xF0);
166
err = -EBUSY;
167
goto out;
168
}
169
170
priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
171
if (!priv->iobase) {
172
pr_err(DRV_NAME "Cannot map ioport\n");
173
err = -EINVAL;
174
goto err_iomap;
175
}
176
177
amd_rng.priv = (unsigned long)priv;
178
priv->pmbase = pmbase;
179
priv->pcidev = pdev;
180
181
pr_info(DRV_NAME " detected\n");
182
err = hwrng_register(&amd_rng);
183
if (err) {
184
pr_err(DRV_NAME " registering failed (%d)\n", err);
185
goto err_hwrng;
186
}
187
return 0;
188
189
err_hwrng:
190
ioport_unmap(priv->iobase);
191
err_iomap:
192
release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
193
out:
194
kfree(priv);
195
put_dev:
196
pci_dev_put(pdev);
197
return err;
198
}
199
200
static void __exit amd_rng_mod_exit(void)
201
{
202
struct amd768_priv *priv;
203
204
priv = (struct amd768_priv *)amd_rng.priv;
205
206
hwrng_unregister(&amd_rng);
207
208
ioport_unmap(priv->iobase);
209
210
release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
211
212
pci_dev_put(priv->pcidev);
213
214
kfree(priv);
215
}
216
217
module_init(amd_rng_mod_init);
218
module_exit(amd_rng_mod_exit);
219
220
MODULE_AUTHOR("The Linux Kernel team");
221
MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
222
MODULE_LICENSE("GPL");
223
224