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/delay.h>
  28#include <linux/hw_random.h>
  29#include <linux/kernel.h>
  30#include <linux/module.h>
  31#include <linux/pci.h>
  32
  33#define DRV_NAME "AMD768-HWRNG"
  34
  35#define RNGDATA         0x00
  36#define RNGDONE         0x04
  37#define PMBASE_OFFSET   0xF0
  38#define PMBASE_SIZE     8
  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, 0x7443), 0, },
  50        { PCI_VDEVICE(AMD, 0x746b), 0, },
  51        { 0, }, /* terminate list */
  52};
  53MODULE_DEVICE_TABLE(pci, pci_tbl);
  54
  55struct amd768_priv {
  56        void __iomem *iobase;
  57        struct pci_dev *pcidev;
  58        u32 pmbase;
  59};
  60
  61static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
  62{
  63        u32 *data = buf;
  64        struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
  65        size_t read = 0;
  66        /* We will wait at maximum one time per read */
  67        int timeout = max / 4 + 1;
  68
  69        /*
  70         * RNG data is available when RNGDONE is set to 1
  71         * New random numbers are generated approximately 128 microseconds
  72         * after RNGDATA is read
  73         */
  74        while (read < max) {
  75                if (ioread32(priv->iobase + RNGDONE) == 0) {
  76                        if (wait) {
  77                                /* Delay given by datasheet */
  78                                usleep_range(128, 196);
  79                                if (timeout-- == 0)
  80                                        return read;
  81                        } else {
  82                                return 0;
  83                        }
  84                } else {
  85                        *data = ioread32(priv->iobase + RNGDATA);
  86                        data++;
  87                        read += 4;
  88                }
  89        }
  90
  91        return read;
  92}
  93
  94static int amd_rng_init(struct hwrng *rng)
  95{
  96        struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
  97        u8 rnen;
  98
  99        pci_read_config_byte(priv->pcidev, 0x40, &rnen);
 100        rnen |= BIT(7); /* RNG on */
 101        pci_write_config_byte(priv->pcidev, 0x40, rnen);
 102
 103        pci_read_config_byte(priv->pcidev, 0x41, &rnen);
 104        rnen |= BIT(7); /* PMIO enable */
 105        pci_write_config_byte(priv->pcidev, 0x41, rnen);
 106
 107        return 0;
 108}
 109
 110static void amd_rng_cleanup(struct hwrng *rng)
 111{
 112        struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
 113        u8 rnen;
 114
 115        pci_read_config_byte(priv->pcidev, 0x40, &rnen);
 116        rnen &= ~BIT(7);        /* RNG off */
 117        pci_write_config_byte(priv->pcidev, 0x40, rnen);
 118}
 119
 120static struct hwrng amd_rng = {
 121        .name           = "amd",
 122        .init           = amd_rng_init,
 123        .cleanup        = amd_rng_cleanup,
 124        .read           = amd_rng_read,
 125};
 126
 127static int __init amd_rng_mod_init(void)
 128{
 129        int err;
 130        struct pci_dev *pdev = NULL;
 131        const struct pci_device_id *ent;
 132        u32 pmbase;
 133        struct amd768_priv *priv;
 134
 135        for_each_pci_dev(pdev) {
 136                ent = pci_match_id(pci_tbl, pdev);
 137                if (ent)
 138                        goto found;
 139        }
 140        /* Device not found. */
 141        return -ENODEV;
 142
 143found:
 144        err = pci_read_config_dword(pdev, 0x58, &pmbase);
 145        if (err)
 146                return err;
 147
 148        pmbase &= 0x0000FF00;
 149        if (pmbase == 0)
 150                return -EIO;
 151
 152        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 153        if (!priv)
 154                return -ENOMEM;
 155
 156        if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
 157                dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
 158                        pmbase + 0xF0);
 159                err = -EBUSY;
 160                goto out;
 161        }
 162
 163        priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
 164        if (!priv->iobase) {
 165                pr_err(DRV_NAME "Cannot map ioport\n");
 166                err = -EINVAL;
 167                goto err_iomap;
 168        }
 169
 170        amd_rng.priv = (unsigned long)priv;
 171        priv->pmbase = pmbase;
 172        priv->pcidev = pdev;
 173
 174        pr_info(DRV_NAME " detected\n");
 175        err = hwrng_register(&amd_rng);
 176        if (err) {
 177                pr_err(DRV_NAME " registering failed (%d)\n", err);
 178                goto err_hwrng;
 179        }
 180        return 0;
 181
 182err_hwrng:
 183        ioport_unmap(priv->iobase);
 184err_iomap:
 185        release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
 186out:
 187        kfree(priv);
 188        return err;
 189}
 190
 191static void __exit amd_rng_mod_exit(void)
 192{
 193        struct amd768_priv *priv;
 194
 195        priv = (struct amd768_priv *)amd_rng.priv;
 196
 197        hwrng_unregister(&amd_rng);
 198
 199        ioport_unmap(priv->iobase);
 200
 201        release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
 202
 203        kfree(priv);
 204}
 205
 206module_init(amd_rng_mod_init);
 207module_exit(amd_rng_mod_exit);
 208
 209MODULE_AUTHOR("The Linux Kernel team");
 210MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
 211MODULE_LICENSE("GPL");
 212