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/io.h>
0030 #include <linux/kernel.h>
0031 #include <linux/module.h>
0032 #include <linux/pci.h>
0033
0034
0035 #define PFX KBUILD_MODNAME ": "
0036
0037 #define GEODE_RNG_DATA_REG 0x50
0038 #define GEODE_RNG_STATUS_REG 0x54
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 static const struct pci_device_id pci_tbl[] = {
0049 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, },
0050 { 0, },
0051 };
0052 MODULE_DEVICE_TABLE(pci, pci_tbl);
0053
0054
0055 static int geode_rng_data_read(struct hwrng *rng, u32 *data)
0056 {
0057 void __iomem *mem = (void __iomem *)rng->priv;
0058
0059 *data = readl(mem + GEODE_RNG_DATA_REG);
0060
0061 return 4;
0062 }
0063
0064 static int geode_rng_data_present(struct hwrng *rng, int wait)
0065 {
0066 void __iomem *mem = (void __iomem *)rng->priv;
0067 int data, i;
0068
0069 for (i = 0; i < 20; i++) {
0070 data = !!(readl(mem + GEODE_RNG_STATUS_REG));
0071 if (data || !wait)
0072 break;
0073 udelay(10);
0074 }
0075 return data;
0076 }
0077
0078
0079 static struct hwrng geode_rng = {
0080 .name = "geode",
0081 .data_present = geode_rng_data_present,
0082 .data_read = geode_rng_data_read,
0083 };
0084
0085
0086 static int __init geode_rng_init(void)
0087 {
0088 int err = -ENODEV;
0089 struct pci_dev *pdev = NULL;
0090 const struct pci_device_id *ent;
0091 void __iomem *mem;
0092 unsigned long rng_base;
0093
0094 for_each_pci_dev(pdev) {
0095 ent = pci_match_id(pci_tbl, pdev);
0096 if (ent)
0097 goto found;
0098 }
0099
0100 goto out;
0101
0102 found:
0103 rng_base = pci_resource_start(pdev, 0);
0104 if (rng_base == 0)
0105 goto out;
0106 err = -ENOMEM;
0107 mem = ioremap(rng_base, 0x58);
0108 if (!mem)
0109 goto out;
0110 geode_rng.priv = (unsigned long)mem;
0111
0112 pr_info("AMD Geode RNG detected\n");
0113 err = hwrng_register(&geode_rng);
0114 if (err) {
0115 pr_err(PFX "RNG registering failed (%d)\n",
0116 err);
0117 goto err_unmap;
0118 }
0119 out:
0120 return err;
0121
0122 err_unmap:
0123 iounmap(mem);
0124 goto out;
0125 }
0126
0127 static void __exit geode_rng_exit(void)
0128 {
0129 void __iomem *mem = (void __iomem *)geode_rng.priv;
0130
0131 hwrng_unregister(&geode_rng);
0132 iounmap(mem);
0133 }
0134
0135 module_init(geode_rng_init);
0136 module_exit(geode_rng_exit);
0137
0138 MODULE_DESCRIPTION("H/W RNG driver for AMD Geode LX CPUs");
0139 MODULE_LICENSE("GPL");