^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * RNG driver for AMD Geode RNGs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright 2005 (c) MontaVista Software, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * with the majority of the code coming from:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * derived from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Hardware driver for the AMD 768 Random Number Generator (RNG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * (c) Copyright 2001 Red Hat Inc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * derived from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Hardware driver for Intel i810 Random Number Generator (RNG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * This file is licensed under the terms of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * License version 2. This program is licensed "as is" without any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * warranty of any kind, whether express or implied.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/hw_random.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define PFX KBUILD_MODNAME ": "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define GEODE_RNG_DATA_REG 0x50
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #define GEODE_RNG_STATUS_REG 0x54
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * Data for PCI driver interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * This data only exists for exporting the supported
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * PCI ids via MODULE_DEVICE_TABLE. We do not actually
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * register a pci_driver, because someone else might one day
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * want to register another driver on the same PCI id.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) static const struct pci_device_id pci_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) { 0, }, /* terminate list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) MODULE_DEVICE_TABLE(pci, pci_tbl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) static int geode_rng_data_read(struct hwrng *rng, u32 *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) void __iomem *mem = (void __iomem *)rng->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) *data = readl(mem + GEODE_RNG_DATA_REG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) return 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static int geode_rng_data_present(struct hwrng *rng, int wait)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) void __iomem *mem = (void __iomem *)rng->priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) int data, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) for (i = 0; i < 20; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) data = !!(readl(mem + GEODE_RNG_STATUS_REG));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (data || !wait)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) udelay(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static struct hwrng geode_rng = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .name = "geode",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) .data_present = geode_rng_data_present,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) .data_read = geode_rng_data_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) static int __init mod_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) int err = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) struct pci_dev *pdev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) const struct pci_device_id *ent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) void __iomem *mem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) unsigned long rng_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) for_each_pci_dev(pdev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) ent = pci_match_id(pci_tbl, pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) if (ent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) goto found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) /* Device not found. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) found:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) rng_base = pci_resource_start(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) if (rng_base == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) err = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) mem = ioremap(rng_base, 0x58);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (!mem)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) geode_rng.priv = (unsigned long)mem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) pr_info("AMD Geode RNG detected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) err = hwrng_register(&geode_rng);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) pr_err(PFX "RNG registering failed (%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) err);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) goto err_unmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) err_unmap:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) iounmap(mem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) static void __exit mod_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) void __iomem *mem = (void __iomem *)geode_rng.priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) hwrng_unregister(&geode_rng);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) iounmap(mem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) module_init(mod_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) module_exit(mod_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) MODULE_DESCRIPTION("H/W RNG driver for AMD Geode LX CPUs");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) MODULE_LICENSE("GPL");