223 lines · c
1/*2 * RNG driver for AMD RNGs3 *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 from12 *13 * Hardware driver for the AMD 768 Random Number Generator (RNG)14 * (c) Copyright 2001 Red Hat Inc15 *16 * derived from17 *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 Public23 * License version 2. This program is licensed "as is" without any24 * 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#define DRV_NAME "AMD768-HWRNG"35 36#define RNGDATA 0x0037#define RNGDONE 0x0438#define PMBASE_OFFSET 0xF039#define PMBASE_SIZE 840 41/*42 * Data for PCI driver interface43 *44 * This data only exists for exporting the supported45 * PCI ids via MODULE_DEVICE_TABLE. We do not actually46 * register a pci_driver, because someone else might one day47 * want to register another driver on the same PCI id.48 */49static const struct pci_device_id pci_tbl[] = {50 { PCI_VDEVICE(AMD, 0x7443), 0, },51 { PCI_VDEVICE(AMD, 0x746b), 0, },52 { 0, }, /* terminate list */53};54MODULE_DEVICE_TABLE(pci, pci_tbl);55 56struct amd768_priv {57 void __iomem *iobase;58 struct pci_dev *pcidev;59 u32 pmbase;60};61 62static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)63{64 u32 *data = buf;65 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;66 size_t read = 0;67 /* We will wait at maximum one time per read */68 int timeout = max / 4 + 1;69 70 /*71 * RNG data is available when RNGDONE is set to 172 * New random numbers are generated approximately 128 microseconds73 * after RNGDATA is read74 */75 while (read < max) {76 if (ioread32(priv->iobase + RNGDONE) == 0) {77 if (wait) {78 /* Delay given by datasheet */79 usleep_range(128, 196);80 if (timeout-- == 0)81 return read;82 } else {83 return 0;84 }85 } else {86 *data = ioread32(priv->iobase + RNGDATA);87 data++;88 read += 4;89 }90 }91 92 return read;93}94 95static int amd_rng_init(struct hwrng *rng)96{97 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;98 u8 rnen;99 100 pci_read_config_byte(priv->pcidev, 0x40, &rnen);101 rnen |= BIT(7); /* RNG on */102 pci_write_config_byte(priv->pcidev, 0x40, rnen);103 104 pci_read_config_byte(priv->pcidev, 0x41, &rnen);105 rnen |= BIT(7); /* PMIO enable */106 pci_write_config_byte(priv->pcidev, 0x41, rnen);107 108 return 0;109}110 111static void amd_rng_cleanup(struct hwrng *rng)112{113 struct amd768_priv *priv = (struct amd768_priv *)rng->priv;114 u8 rnen;115 116 pci_read_config_byte(priv->pcidev, 0x40, &rnen);117 rnen &= ~BIT(7); /* RNG off */118 pci_write_config_byte(priv->pcidev, 0x40, rnen);119}120 121static struct hwrng amd_rng = {122 .name = "amd",123 .init = amd_rng_init,124 .cleanup = amd_rng_cleanup,125 .read = amd_rng_read,126};127 128static int __init amd_rng_mod_init(void)129{130 int err;131 struct pci_dev *pdev = NULL;132 const struct pci_device_id *ent;133 u32 pmbase;134 struct amd768_priv *priv;135 136 for_each_pci_dev(pdev) {137 ent = pci_match_id(pci_tbl, pdev);138 if (ent)139 goto found;140 }141 /* Device not found. */142 return -ENODEV;143 144found:145 err = pci_read_config_dword(pdev, 0x58, &pmbase);146 if (err) {147 err = pcibios_err_to_errno(err);148 goto put_dev;149 }150 151 pmbase &= 0x0000FF00;152 if (pmbase == 0) {153 err = -EIO;154 goto put_dev;155 }156 157 priv = kzalloc(sizeof(*priv), GFP_KERNEL);158 if (!priv) {159 err = -ENOMEM;160 goto put_dev;161 }162 163 if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {164 dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",165 pmbase + 0xF0);166 err = -EBUSY;167 goto out;168 }169 170 priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);171 if (!priv->iobase) {172 pr_err(DRV_NAME "Cannot map ioport\n");173 err = -EINVAL;174 goto err_iomap;175 }176 177 amd_rng.priv = (unsigned long)priv;178 priv->pmbase = pmbase;179 priv->pcidev = pdev;180 181 pr_info(DRV_NAME " detected\n");182 err = hwrng_register(&amd_rng);183 if (err) {184 pr_err(DRV_NAME " registering failed (%d)\n", err);185 goto err_hwrng;186 }187 return 0;188 189err_hwrng:190 ioport_unmap(priv->iobase);191err_iomap:192 release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);193out:194 kfree(priv);195put_dev:196 pci_dev_put(pdev);197 return err;198}199 200static void __exit amd_rng_mod_exit(void)201{202 struct amd768_priv *priv;203 204 priv = (struct amd768_priv *)amd_rng.priv;205 206 hwrng_unregister(&amd_rng);207 208 ioport_unmap(priv->iobase);209 210 release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);211 212 pci_dev_put(priv->pcidev);213 214 kfree(priv);215}216 217module_init(amd_rng_mod_init);218module_exit(amd_rng_mod_exit);219 220MODULE_AUTHOR("The Linux Kernel team");221MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");222MODULE_LICENSE("GPL");223