Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * RNG driver for AMD RNGs
0003  *
0004  * Copyright 2005 (c) MontaVista Software, Inc.
0005  *
0006  * with the majority of the code coming from:
0007  *
0008  * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
0009  * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
0010  *
0011  * derived from
0012  *
0013  * Hardware driver for the AMD 768 Random Number Generator (RNG)
0014  * (c) Copyright 2001 Red Hat Inc
0015  *
0016  * derived from
0017  *
0018  * Hardware driver for Intel i810 Random Number Generator (RNG)
0019  * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
0020  * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
0021  *
0022  * This file is licensed under  the terms of the GNU General Public
0023  * License version 2. This program is licensed "as is" without any
0024  * warranty of any kind, whether express or implied.
0025  */
0026 
0027 #include <linux/delay.h>
0028 #include <linux/hw_random.h>
0029 #include <linux/kernel.h>
0030 #include <linux/module.h>
0031 #include <linux/pci.h>
0032 
0033 #define DRV_NAME "AMD768-HWRNG"
0034 
0035 #define RNGDATA     0x00
0036 #define RNGDONE     0x04
0037 #define PMBASE_OFFSET   0xF0
0038 #define PMBASE_SIZE 8
0039 
0040 /*
0041  * Data for PCI driver interface
0042  *
0043  * This data only exists for exporting the supported
0044  * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
0045  * register a pci_driver, because someone else might one day
0046  * want to register another driver on the same PCI id.
0047  */
0048 static const struct pci_device_id pci_tbl[] = {
0049     { PCI_VDEVICE(AMD, 0x7443), 0, },
0050     { PCI_VDEVICE(AMD, 0x746b), 0, },
0051     { 0, }, /* terminate list */
0052 };
0053 MODULE_DEVICE_TABLE(pci, pci_tbl);
0054 
0055 struct amd768_priv {
0056     void __iomem *iobase;
0057     struct pci_dev *pcidev;
0058     u32 pmbase;
0059 };
0060 
0061 static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
0062 {
0063     u32 *data = buf;
0064     struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0065     size_t read = 0;
0066     /* We will wait at maximum one time per read */
0067     int timeout = max / 4 + 1;
0068 
0069     /*
0070      * RNG data is available when RNGDONE is set to 1
0071      * New random numbers are generated approximately 128 microseconds
0072      * after RNGDATA is read
0073      */
0074     while (read < max) {
0075         if (ioread32(priv->iobase + RNGDONE) == 0) {
0076             if (wait) {
0077                 /* Delay given by datasheet */
0078                 usleep_range(128, 196);
0079                 if (timeout-- == 0)
0080                     return read;
0081             } else {
0082                 return 0;
0083             }
0084         } else {
0085             *data = ioread32(priv->iobase + RNGDATA);
0086             data++;
0087             read += 4;
0088         }
0089     }
0090 
0091     return read;
0092 }
0093 
0094 static int amd_rng_init(struct hwrng *rng)
0095 {
0096     struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0097     u8 rnen;
0098 
0099     pci_read_config_byte(priv->pcidev, 0x40, &rnen);
0100     rnen |= BIT(7); /* RNG on */
0101     pci_write_config_byte(priv->pcidev, 0x40, rnen);
0102 
0103     pci_read_config_byte(priv->pcidev, 0x41, &rnen);
0104     rnen |= BIT(7); /* PMIO enable */
0105     pci_write_config_byte(priv->pcidev, 0x41, rnen);
0106 
0107     return 0;
0108 }
0109 
0110 static void amd_rng_cleanup(struct hwrng *rng)
0111 {
0112     struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
0113     u8 rnen;
0114 
0115     pci_read_config_byte(priv->pcidev, 0x40, &rnen);
0116     rnen &= ~BIT(7);    /* RNG off */
0117     pci_write_config_byte(priv->pcidev, 0x40, rnen);
0118 }
0119 
0120 static struct hwrng amd_rng = {
0121     .name       = "amd",
0122     .init       = amd_rng_init,
0123     .cleanup    = amd_rng_cleanup,
0124     .read       = amd_rng_read,
0125 };
0126 
0127 static int __init amd_rng_mod_init(void)
0128 {
0129     int err;
0130     struct pci_dev *pdev = NULL;
0131     const struct pci_device_id *ent;
0132     u32 pmbase;
0133     struct amd768_priv *priv;
0134 
0135     for_each_pci_dev(pdev) {
0136         ent = pci_match_id(pci_tbl, pdev);
0137         if (ent)
0138             goto found;
0139     }
0140     /* Device not found. */
0141     return -ENODEV;
0142 
0143 found:
0144     err = pci_read_config_dword(pdev, 0x58, &pmbase);
0145     if (err)
0146         return err;
0147 
0148     pmbase &= 0x0000FF00;
0149     if (pmbase == 0)
0150         return -EIO;
0151 
0152     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0153     if (!priv)
0154         return -ENOMEM;
0155 
0156     if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
0157         dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
0158             pmbase + 0xF0);
0159         err = -EBUSY;
0160         goto out;
0161     }
0162 
0163     priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0164     if (!priv->iobase) {
0165         pr_err(DRV_NAME "Cannot map ioport\n");
0166         err = -EINVAL;
0167         goto err_iomap;
0168     }
0169 
0170     amd_rng.priv = (unsigned long)priv;
0171     priv->pmbase = pmbase;
0172     priv->pcidev = pdev;
0173 
0174     pr_info(DRV_NAME " detected\n");
0175     err = hwrng_register(&amd_rng);
0176     if (err) {
0177         pr_err(DRV_NAME " registering failed (%d)\n", err);
0178         goto err_hwrng;
0179     }
0180     return 0;
0181 
0182 err_hwrng:
0183     ioport_unmap(priv->iobase);
0184 err_iomap:
0185     release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0186 out:
0187     kfree(priv);
0188     return err;
0189 }
0190 
0191 static void __exit amd_rng_mod_exit(void)
0192 {
0193     struct amd768_priv *priv;
0194 
0195     priv = (struct amd768_priv *)amd_rng.priv;
0196 
0197     hwrng_unregister(&amd_rng);
0198 
0199     ioport_unmap(priv->iobase);
0200 
0201     release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
0202 
0203     kfree(priv);
0204 }
0205 
0206 module_init(amd_rng_mod_init);
0207 module_exit(amd_rng_mod_exit);
0208 
0209 MODULE_AUTHOR("The Linux Kernel team");
0210 MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
0211 MODULE_LICENSE("GPL");