linux/drivers/char/hw_random/geode-rng.c
<<
>>
Prefs
   1/*
   2 * RNG driver for AMD Geode 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 <jgarzik@redhat.com>
  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 <jgarzik@pobox.com>
  20 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
  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
  35#define PFX     KBUILD_MODNAME ": "
  36
  37#define GEODE_RNG_DATA_REG   0x50
  38#define GEODE_RNG_STATUS_REG 0x54
  39
  40/*
  41 * Data for PCI driver interface
  42 *
  43 * This data only exists for exporting the supported
  44 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
  45 * register a pci_driver, because someone else might one day
  46 * want to register another driver on the same PCI id.
  47 */
  48static const struct pci_device_id pci_tbl[] = {
  49        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_LX_AES), 0, },
  50        { 0, }, /* terminate list */
  51};
  52MODULE_DEVICE_TABLE(pci, pci_tbl);
  53
  54
  55static int geode_rng_data_read(struct hwrng *rng, u32 *data)
  56{
  57        void __iomem *mem = (void __iomem *)rng->priv;
  58
  59        *data = readl(mem + GEODE_RNG_DATA_REG);
  60
  61        return 4;
  62}
  63
  64static int geode_rng_data_present(struct hwrng *rng, int wait)
  65{
  66        void __iomem *mem = (void __iomem *)rng->priv;
  67        int data, i;
  68
  69        for (i = 0; i < 20; i++) {
  70                data = !!(readl(mem + GEODE_RNG_STATUS_REG));
  71                if (data || !wait)
  72                        break;
  73                udelay(10);
  74        }
  75        return data;
  76}
  77
  78
  79static struct hwrng geode_rng = {
  80        .name           = "geode",
  81        .data_present   = geode_rng_data_present,
  82        .data_read      = geode_rng_data_read,
  83};
  84
  85
  86static int __init geode_rng_init(void)
  87{
  88        int err = -ENODEV;
  89        struct pci_dev *pdev = NULL;
  90        const struct pci_device_id *ent;
  91        void __iomem *mem;
  92        unsigned long rng_base;
  93
  94        for_each_pci_dev(pdev) {
  95                ent = pci_match_id(pci_tbl, pdev);
  96                if (ent)
  97                        goto found;
  98        }
  99        /* Device not found. */
 100        goto out;
 101
 102found:
 103        rng_base = pci_resource_start(pdev, 0);
 104        if (rng_base == 0)
 105                goto out;
 106        err = -ENOMEM;
 107        mem = ioremap(rng_base, 0x58);
 108        if (!mem)
 109                goto out;
 110        geode_rng.priv = (unsigned long)mem;
 111
 112        pr_info("AMD Geode RNG detected\n");
 113        err = hwrng_register(&geode_rng);
 114        if (err) {
 115                pr_err(PFX "RNG registering failed (%d)\n",
 116                       err);
 117                goto err_unmap;
 118        }
 119out:
 120        return err;
 121
 122err_unmap:
 123        iounmap(mem);
 124        goto out;
 125}
 126
 127static void __exit geode_rng_exit(void)
 128{
 129        void __iomem *mem = (void __iomem *)geode_rng.priv;
 130
 131        hwrng_unregister(&geode_rng);
 132        iounmap(mem);
 133}
 134
 135module_init(geode_rng_init);
 136module_exit(geode_rng_exit);
 137
 138MODULE_DESCRIPTION("H/W RNG driver for AMD Geode LX CPUs");
 139MODULE_LICENSE("GPL");
 140