diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-10 21:04:16 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-10 21:04:16 (GMT) |
commit | 30066ce675d3af350bc5a53858991c0b518dda00 (patch) | |
tree | 75db2274cd0887b11b4e297771287f0fb4c14b81 /drivers/char/hw_random/amd-rng.c | |
parent | 6763afe4b9f39142bda2a92d69e62fe85f67251c (diff) | |
parent | c3afafa47898e34eb49828ec4ac92bcdc81c8f0c (diff) | |
download | linux-30066ce675d3af350bc5a53858991c0b518dda00.tar.xz |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
Pull crypto updates from Herbert Xu:
"Here is the crypto update for 4.9:
API:
- The crypto engine code now supports hashes.
Algorithms:
- Allow keys >= 2048 bits in FIPS mode for RSA.
Drivers:
- Memory overwrite fix for vmx ghash.
- Add support for building ARM sha1-neon in Thumb2 mode.
- Reenable ARM ghash-ce code by adding import/export.
- Reenable img-hash by adding import/export.
- Add support for multiple cores in omap-aes.
- Add little-endian support for sha1-powerpc.
- Add Cavium HWRNG driver for ThunderX SoC"
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6: (137 commits)
crypto: caam - treat SGT address pointer as u64
crypto: ccp - Make syslog errors human-readable
crypto: ccp - clean up data structure
crypto: vmx - Ensure ghash-generic is enabled
crypto: testmgr - add guard to dst buffer for ahash_export
crypto: caam - Unmap region obtained by of_iomap
crypto: sha1-powerpc - little-endian support
crypto: gcm - Fix IV buffer size in crypto_gcm_setkey
crypto: vmx - Fix memory corruption caused by p8_ghash
crypto: ghash-generic - move common definitions to a new header file
crypto: caam - fix sg dump
hwrng: omap - Only fail if pm_runtime_get_sync returns < 0
crypto: omap-sham - shrink the internal buffer size
crypto: omap-sham - add support for export/import
crypto: omap-sham - convert driver logic to use sgs for data xmit
crypto: omap-sham - change the DMA threshold value to a define
crypto: omap-sham - add support functions for sg based data handling
crypto: omap-sham - rename sgl to sgl_tmp for deprecation
crypto: omap-sham - align algorithms on word offset
crypto: omap-sham - add context export/import stubs
...
Diffstat (limited to 'drivers/char/hw_random/amd-rng.c')
-rw-r--r-- | drivers/char/hw_random/amd-rng.c | 140 |
1 files changed, 78 insertions, 62 deletions
diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c index 48f6a83..4a99ac7 100644 --- a/drivers/char/hw_random/amd-rng.c +++ b/drivers/char/hw_random/amd-rng.c @@ -24,16 +24,18 @@ * warranty of any kind, whether express or implied. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/hw_random.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/pci.h> -#include <linux/hw_random.h> -#include <linux/delay.h> -#include <asm/io.h> +#define DRV_NAME "AMD768-HWRNG" -#define PFX KBUILD_MODNAME ": " - +#define RNGDATA 0x00 +#define RNGDONE 0x04 +#define PMBASE_OFFSET 0xF0 +#define PMBASE_SIZE 8 /* * Data for PCI driver interface @@ -50,72 +52,84 @@ static const struct pci_device_id pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, pci_tbl); -static struct pci_dev *amd_pdev; - +struct amd768_priv { + void __iomem *iobase; + struct pci_dev *pcidev; +}; -static int amd_rng_data_present(struct hwrng *rng, int wait) +static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { - u32 pmbase = (u32)rng->priv; - int data, i; - - for (i = 0; i < 20; i++) { - data = !!(inl(pmbase + 0xF4) & 1); - if (data || !wait) - break; - udelay(10); + u32 *data = buf; + struct amd768_priv *priv = (struct amd768_priv *)rng->priv; + size_t read = 0; + /* We will wait at maximum one time per read */ + int timeout = max / 4 + 1; + + /* + * RNG data is available when RNGDONE is set to 1 + * New random numbers are generated approximately 128 microseconds + * after RNGDATA is read + */ + while (read < max) { + if (ioread32(priv->iobase + RNGDONE) == 0) { + if (wait) { + /* Delay given by datasheet */ + usleep_range(128, 196); + if (timeout-- == 0) + return read; + } else { + return 0; + } + } else { + *data = ioread32(priv->iobase + RNGDATA); + data++; + read += 4; + } } - return data; -} -static int amd_rng_data_read(struct hwrng *rng, u32 *data) -{ - u32 pmbase = (u32)rng->priv; - - *data = inl(pmbase + 0xF0); - - return 4; + return read; } static int amd_rng_init(struct hwrng *rng) { + struct amd768_priv *priv = (struct amd768_priv *)rng->priv; u8 rnen; - pci_read_config_byte(amd_pdev, 0x40, &rnen); - rnen |= (1 << 7); /* RNG on */ - pci_write_config_byte(amd_pdev, 0x40, rnen); + pci_read_config_byte(priv->pcidev, 0x40, &rnen); + rnen |= BIT(7); /* RNG on */ + pci_write_config_byte(priv->pcidev, 0x40, rnen); - pci_read_config_byte(amd_pdev, 0x41, &rnen); - rnen |= (1 << 7); /* PMIO enable */ - pci_write_config_byte(amd_pdev, 0x41, rnen); + pci_read_config_byte(priv->pcidev, 0x41, &rnen); + rnen |= BIT(7); /* PMIO enable */ + pci_write_config_byte(priv->pcidev, 0x41, rnen); return 0; } static void amd_rng_cleanup(struct hwrng *rng) { + struct amd768_priv *priv = (struct amd768_priv *)rng->priv; u8 rnen; - pci_read_config_byte(amd_pdev, 0x40, &rnen); - rnen &= ~(1 << 7); /* RNG off */ - pci_write_config_byte(amd_pdev, 0x40, rnen); + pci_read_config_byte(priv->pcidev, 0x40, &rnen); + rnen &= ~BIT(7); /* RNG off */ + pci_write_config_byte(priv->pcidev, 0x40, rnen); } - static struct hwrng amd_rng = { .name = "amd", .init = amd_rng_init, .cleanup = amd_rng_cleanup, - .data_present = amd_rng_data_present, - .data_read = amd_rng_data_read, + .read = amd_rng_read, }; - static int __init mod_init(void) { int err = -ENODEV; struct pci_dev *pdev = NULL; const struct pci_device_id *ent; u32 pmbase; + struct amd768_priv *priv; for_each_pci_dev(pdev) { ent = pci_match_id(pci_tbl, pdev); @@ -123,42 +137,44 @@ static int __init mod_init(void) goto found; } /* Device not found. */ - goto out; + return -ENODEV; found: err = pci_read_config_dword(pdev, 0x58, &pmbase); if (err) - goto out; - err = -EIO; + return err; + pmbase &= 0x0000FF00; if (pmbase == 0) - goto out; - if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) { - dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n", + return -EIO; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (!devm_request_region(&pdev->dev, pmbase + PMBASE_OFFSET, + PMBASE_SIZE, DRV_NAME)) { + dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n", pmbase + 0xF0); - err = -EBUSY; - goto out; + return -EBUSY; } - amd_rng.priv = (unsigned long)pmbase; - amd_pdev = pdev; - - pr_info("AMD768 RNG detected\n"); - err = hwrng_register(&amd_rng); - if (err) { - pr_err(PFX "RNG registering failed (%d)\n", - err); - release_region(pmbase + 0xF0, 8); - goto out; + + priv->iobase = devm_ioport_map(&pdev->dev, pmbase + PMBASE_OFFSET, + PMBASE_SIZE); + if (!priv->iobase) { + pr_err(DRV_NAME "Cannot map ioport\n"); + return -ENOMEM; } -out: - return err; + + amd_rng.priv = (unsigned long)priv; + priv->pcidev = pdev; + + pr_info(DRV_NAME " detected\n"); + return devm_hwrng_register(&pdev->dev, &amd_rng); } static void __exit mod_exit(void) { - u32 pmbase = (unsigned long)amd_rng.priv; - release_region(pmbase + 0xF0, 8); - hwrng_unregister(&amd_rng); } module_init(mod_init); |