brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · 9a24d19 Raw
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