Path: blob/master/drivers/char/hw_random/amd-rng.c
15111 views
/*1* RNG driver for AMD RNGs2*3* Copyright 2005 (c) MontaVista Software, Inc.4*5* with the majority of the code coming from:6*7* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)8* (c) Copyright 2003 Red Hat Inc <[email protected]>9*10* derived from11*12* Hardware driver for the AMD 768 Random Number Generator (RNG)13* (c) Copyright 2001 Red Hat Inc14*15* derived from16*17* Hardware driver for Intel i810 Random Number Generator (RNG)18* Copyright 2000,2001 Jeff Garzik <[email protected]>19* Copyright 2000,2001 Philipp Rumpf <[email protected]>20*21* This file is licensed under the terms of the GNU General Public22* License version 2. This program is licensed "as is" without any23* warranty of any kind, whether express or implied.24*/2526#include <linux/module.h>27#include <linux/kernel.h>28#include <linux/pci.h>29#include <linux/hw_random.h>30#include <linux/delay.h>31#include <asm/io.h>323334#define PFX KBUILD_MODNAME ": "353637/*38* Data for PCI driver interface39*40* This data only exists for exporting the supported41* PCI ids via MODULE_DEVICE_TABLE. We do not actually42* register a pci_driver, because someone else might one day43* want to register another driver on the same PCI id.44*/45static const struct pci_device_id pci_tbl[] = {46{ PCI_VDEVICE(AMD, 0x7443), 0, },47{ PCI_VDEVICE(AMD, 0x746b), 0, },48{ 0, }, /* terminate list */49};50MODULE_DEVICE_TABLE(pci, pci_tbl);5152static struct pci_dev *amd_pdev;535455static int amd_rng_data_present(struct hwrng *rng, int wait)56{57u32 pmbase = (u32)rng->priv;58int data, i;5960for (i = 0; i < 20; i++) {61data = !!(inl(pmbase + 0xF4) & 1);62if (data || !wait)63break;64udelay(10);65}66return data;67}6869static int amd_rng_data_read(struct hwrng *rng, u32 *data)70{71u32 pmbase = (u32)rng->priv;7273*data = inl(pmbase + 0xF0);7475return 4;76}7778static int amd_rng_init(struct hwrng *rng)79{80u8 rnen;8182pci_read_config_byte(amd_pdev, 0x40, &rnen);83rnen |= (1 << 7); /* RNG on */84pci_write_config_byte(amd_pdev, 0x40, rnen);8586pci_read_config_byte(amd_pdev, 0x41, &rnen);87rnen |= (1 << 7); /* PMIO enable */88pci_write_config_byte(amd_pdev, 0x41, rnen);8990return 0;91}9293static void amd_rng_cleanup(struct hwrng *rng)94{95u8 rnen;9697pci_read_config_byte(amd_pdev, 0x40, &rnen);98rnen &= ~(1 << 7); /* RNG off */99pci_write_config_byte(amd_pdev, 0x40, rnen);100}101102103static struct hwrng amd_rng = {104.name = "amd",105.init = amd_rng_init,106.cleanup = amd_rng_cleanup,107.data_present = amd_rng_data_present,108.data_read = amd_rng_data_read,109};110111112static int __init mod_init(void)113{114int err = -ENODEV;115struct pci_dev *pdev = NULL;116const struct pci_device_id *ent;117u32 pmbase;118119for_each_pci_dev(pdev) {120ent = pci_match_id(pci_tbl, pdev);121if (ent)122goto found;123}124/* Device not found. */125goto out;126127found:128err = pci_read_config_dword(pdev, 0x58, &pmbase);129if (err)130goto out;131err = -EIO;132pmbase &= 0x0000FF00;133if (pmbase == 0)134goto out;135if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) {136dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n",137pmbase + 0xF0);138err = -EBUSY;139goto out;140}141amd_rng.priv = (unsigned long)pmbase;142amd_pdev = pdev;143144printk(KERN_INFO "AMD768 RNG detected\n");145err = hwrng_register(&amd_rng);146if (err) {147printk(KERN_ERR PFX "RNG registering failed (%d)\n",148err);149release_region(pmbase + 0xF0, 8);150goto out;151}152out:153return err;154}155156static void __exit mod_exit(void)157{158u32 pmbase = (unsigned long)amd_rng.priv;159release_region(pmbase + 0xF0, 8);160hwrng_unregister(&amd_rng);161}162163module_init(mod_init);164module_exit(mod_exit);165166MODULE_AUTHOR("The Linux Kernel team");167MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");168MODULE_LICENSE("GPL");169170171