0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027 #include <linux/delay.h>
0028 #include <linux/hw_random.h>
0029 #include <linux/kernel.h>
0030 #include <linux/module.h>
0031 #include <linux/pci.h>
0032
0033 #define DRV_NAME "AMD768-HWRNG"
0034
0035 #define RNGDATA 0x00
0036 #define RNGDONE 0x04
0037 #define PMBASE_OFFSET 0xF0
0038 #define PMBASE_SIZE 8
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 static const struct pci_device_id pci_tbl[] = {
0049 { PCI_VDEVICE(AMD, 0x7443), 0, },
0050 { PCI_VDEVICE(AMD, 0x746b), 0, },
0051 { 0, },
0052 };
0053 MODULE_DEVICE_TABLE(pci, pci_tbl);
0054
0055 struct amd768_priv {
0056 void __iomem *iobase;
0057 struct pci_dev *pcidev;
0058 u32 pmbase;
0059 };
0060
0061 static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
0062 {
0063 u32 *data = buf;
0064 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0065 size_t read = 0;
0066
0067 int timeout = max / 4 + 1;
0068
0069
0070
0071
0072
0073
0074 while (read < max) {
0075 if (ioread32(priv->iobase + RNGDONE) == 0) {
0076 if (wait) {
0077
0078 usleep_range(128, 196);
0079 if (timeout-- == 0)
0080 return read;
0081 } else {
0082 return 0;
0083 }
0084 } else {
0085 *data = ioread32(priv->iobase + RNGDATA);
0086 data++;
0087 read += 4;
0088 }
0089 }
0090
0091 return read;
0092 }
0093
0094 static int amd_rng_init(struct hwrng *rng)
0095 {
0096 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0097 u8 rnen;
0098
0099 pci_read_config_byte(priv->pcidev, 0x40, &rnen);
0100 rnen |= BIT(7);
0101 pci_write_config_byte(priv->pcidev, 0x40, rnen);
0102
0103 pci_read_config_byte(priv->pcidev, 0x41, &rnen);
0104 rnen |= BIT(7);
0105 pci_write_config_byte(priv->pcidev, 0x41, rnen);
0106
0107 return 0;
0108 }
0109
0110 static void amd_rng_cleanup(struct hwrng *rng)
0111 {
0112 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0113 u8 rnen;
0114
0115 pci_read_config_byte(priv->pcidev, 0x40, &rnen);
0116 rnen &= ~BIT(7);
0117 pci_write_config_byte(priv->pcidev, 0x40, rnen);
0118 }
0119
0120 static struct hwrng amd_rng = {
0121 .name = "amd",
0122 .init = amd_rng_init,
0123 .cleanup = amd_rng_cleanup,
0124 .read = amd_rng_read,
0125 };
0126
0127 static int __init amd_rng_mod_init(void)
0128 {
0129 int err;
0130 struct pci_dev *pdev = NULL;
0131 const struct pci_device_id *ent;
0132 u32 pmbase;
0133 struct amd768_priv *priv;
0134
0135 for_each_pci_dev(pdev) {
0136 ent = pci_match_id(pci_tbl, pdev);
0137 if (ent)
0138 goto found;
0139 }
0140
0141 return -ENODEV;
0142
0143 found:
0144 err = pci_read_config_dword(pdev, 0x58, &pmbase);
0145 if (err)
0146 return err;
0147
0148 pmbase &= 0x0000FF00;
0149 if (pmbase == 0)
0150 return -EIO;
0151
0152 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0153 if (!priv)
0154 return -ENOMEM;
0155
0156 if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
0157 dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
0158 pmbase + 0xF0);
0159 err = -EBUSY;
0160 goto out;
0161 }
0162
0163 priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0164 if (!priv->iobase) {
0165 pr_err(DRV_NAME "Cannot map ioport\n");
0166 err = -EINVAL;
0167 goto err_iomap;
0168 }
0169
0170 amd_rng.priv = (unsigned long)priv;
0171 priv->pmbase = pmbase;
0172 priv->pcidev = pdev;
0173
0174 pr_info(DRV_NAME " detected\n");
0175 err = hwrng_register(&amd_rng);
0176 if (err) {
0177 pr_err(DRV_NAME " registering failed (%d)\n", err);
0178 goto err_hwrng;
0179 }
0180 return 0;
0181
0182 err_hwrng:
0183 ioport_unmap(priv->iobase);
0184 err_iomap:
0185 release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0186 out:
0187 kfree(priv);
0188 return err;
0189 }
0190
0191 static void __exit amd_rng_mod_exit(void)
0192 {
0193 struct amd768_priv *priv;
0194
0195 priv = (struct amd768_priv *)amd_rng.priv;
0196
0197 hwrng_unregister(&amd_rng);
0198
0199 ioport_unmap(priv->iobase);
0200
0201 release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0202
0203 kfree(priv);
0204 }
0205
0206 module_init(amd_rng_mod_init);
0207 module_exit(amd_rng_mod_exit);
0208
0209 MODULE_AUTHOR("The Linux Kernel team");
0210 MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
0211 MODULE_LICENSE("GPL");