linux/drivers/char/hw_random/amd-rng.c
<<
>>
Prefs
   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 <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/module.h>
  28#include <linux/kernel.h>
  29#include <linux/pci.h>
  30#include <linux/hw_random.h>
  31#include <linux/delay.h>
  32#include <asm/io.h>
  33
  34
  35#define PFX     KBUILD_MODNAME ": "
  36
  37
  38/*
  39 * Data for PCI driver interface
  40 *
  41 * This data only exists for exporting the supported
  42 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
  43 * register a pci_driver, because someone else might one day
  44 * want to register another driver on the same PCI id.
  45 */
  46static const struct pci_device_id pci_tbl[] = {
  47        { PCI_VDEVICE(AMD, 0x7443), 0, },
  48        { PCI_VDEVICE(AMD, 0x746b), 0, },
  49        { 0, }, /* terminate list */
  50};
  51MODULE_DEVICE_TABLE(pci, pci_tbl);
  52
  53static struct pci_dev *amd_pdev;
  54
  55
  56static int amd_rng_data_present(struct hwrng *rng, int wait)
  57{
  58        u32 pmbase = (u32)rng->priv;
  59        int data, i;
  60
  61        for (i = 0; i < 20; i++) {
  62                data = !!(inl(pmbase + 0xF4) & 1);
  63                if (data || !wait)
  64                        break;
  65                udelay(10);
  66        }
  67        return data;
  68}
  69
  70static int amd_rng_data_read(struct hwrng *rng, u32 *data)
  71{
  72        u32 pmbase = (u32)rng->priv;
  73
  74        *data = inl(pmbase + 0xF0);
  75
  76        return 4;
  77}
  78
  79static int amd_rng_init(struct hwrng *rng)
  80{
  81        u8 rnen;
  82
  83        pci_read_config_byte(amd_pdev, 0x40, &rnen);
  84        rnen |= (1 << 7);       /* RNG on */
  85        pci_write_config_byte(amd_pdev, 0x40, rnen);
  86
  87        pci_read_config_byte(amd_pdev, 0x41, &rnen);
  88        rnen |= (1 << 7);       /* PMIO enable */
  89        pci_write_config_byte(amd_pdev, 0x41, rnen);
  90
  91        return 0;
  92}
  93
  94static void amd_rng_cleanup(struct hwrng *rng)
  95{
  96        u8 rnen;
  97
  98        pci_read_config_byte(amd_pdev, 0x40, &rnen);
  99        rnen &= ~(1 << 7);      /* RNG off */
 100        pci_write_config_byte(amd_pdev, 0x40, rnen);
 101}
 102
 103
 104static struct hwrng amd_rng = {
 105        .name           = "amd",
 106        .init           = amd_rng_init,
 107        .cleanup        = amd_rng_cleanup,
 108        .data_present   = amd_rng_data_present,
 109        .data_read      = amd_rng_data_read,
 110};
 111
 112
 113static int __init mod_init(void)
 114{
 115        int err = -ENODEV;
 116        struct pci_dev *pdev = NULL;
 117        const struct pci_device_id *ent;
 118        u32 pmbase;
 119
 120        for_each_pci_dev(pdev) {
 121                ent = pci_match_id(pci_tbl, pdev);
 122                if (ent)
 123                        goto found;
 124        }
 125        /* Device not found. */
 126        goto out;
 127
 128found:
 129        err = pci_read_config_dword(pdev, 0x58, &pmbase);
 130        if (err)
 131                goto out;
 132        err = -EIO;
 133        pmbase &= 0x0000FF00;
 134        if (pmbase == 0)
 135                goto out;
 136        if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) {
 137                dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n",
 138                        pmbase + 0xF0);
 139                err = -EBUSY;
 140                goto out;
 141        }
 142        amd_rng.priv = (unsigned long)pmbase;
 143        amd_pdev = pdev;
 144
 145        pr_info("AMD768 RNG detected\n");
 146        err = hwrng_register(&amd_rng);
 147        if (err) {
 148                pr_err(PFX "RNG registering failed (%d)\n",
 149                       err);
 150                release_region(pmbase + 0xF0, 8);
 151                goto out;
 152        }
 153out:
 154        return err;
 155}
 156
 157static void __exit mod_exit(void)
 158{
 159        u32 pmbase = (unsigned long)amd_rng.priv;
 160        release_region(pmbase + 0xF0, 8);
 161        hwrng_unregister(&amd_rng);
 162}
 163
 164module_init(mod_init);
 165module_exit(mod_exit);
 166
 167MODULE_AUTHOR("The Linux Kernel team");
 168MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
 169MODULE_LICENSE("GPL");
 170