amd-rng.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * RNG driver for AMD RNGs
  3. *
  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 from
  12. *
  13. * Hardware driver for the AMD 768 Random Number Generator (RNG)
  14. * (c) Copyright 2001 Red Hat Inc
  15. *
  16. * derived from
  17. *
  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 Public
  23. * License version 2. This program is licensed "as is" without any
  24. * warranty of any kind, whether express or implied.
  25. */
  26. #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>
  32. #define PFX KBUILD_MODNAME ": "
  33. /*
  34. * Data for PCI driver interface
  35. *
  36. * This data only exists for exporting the supported
  37. * PCI ids via MODULE_DEVICE_TABLE. We do not actually
  38. * register a pci_driver, because someone else might one day
  39. * want to register another driver on the same PCI id.
  40. */
  41. static const struct pci_device_id pci_tbl[] = {
  42. { PCI_VDEVICE(AMD, 0x7443), 0, },
  43. { PCI_VDEVICE(AMD, 0x746b), 0, },
  44. { 0, }, /* terminate list */
  45. };
  46. MODULE_DEVICE_TABLE(pci, pci_tbl);
  47. static struct pci_dev *amd_pdev;
  48. static int amd_rng_data_present(struct hwrng *rng, int wait)
  49. {
  50. u32 pmbase = (u32)rng->priv;
  51. int data, i;
  52. for (i = 0; i < 20; i++) {
  53. data = !!(inl(pmbase + 0xF4) & 1);
  54. if (data || !wait)
  55. break;
  56. udelay(10);
  57. }
  58. return data;
  59. }
  60. static int amd_rng_data_read(struct hwrng *rng, u32 *data)
  61. {
  62. u32 pmbase = (u32)rng->priv;
  63. *data = inl(pmbase + 0xF0);
  64. return 4;
  65. }
  66. static int amd_rng_init(struct hwrng *rng)
  67. {
  68. u8 rnen;
  69. pci_read_config_byte(amd_pdev, 0x40, &rnen);
  70. rnen |= (1 << 7); /* RNG on */
  71. pci_write_config_byte(amd_pdev, 0x40, rnen);
  72. pci_read_config_byte(amd_pdev, 0x41, &rnen);
  73. rnen |= (1 << 7); /* PMIO enable */
  74. pci_write_config_byte(amd_pdev, 0x41, rnen);
  75. return 0;
  76. }
  77. static void amd_rng_cleanup(struct hwrng *rng)
  78. {
  79. u8 rnen;
  80. pci_read_config_byte(amd_pdev, 0x40, &rnen);
  81. rnen &= ~(1 << 7); /* RNG off */
  82. pci_write_config_byte(amd_pdev, 0x40, rnen);
  83. }
  84. static struct hwrng amd_rng = {
  85. .name = "amd",
  86. .init = amd_rng_init,
  87. .cleanup = amd_rng_cleanup,
  88. .data_present = amd_rng_data_present,
  89. .data_read = amd_rng_data_read,
  90. };
  91. static int __init mod_init(void)
  92. {
  93. int err = -ENODEV;
  94. struct pci_dev *pdev = NULL;
  95. const struct pci_device_id *ent;
  96. u32 pmbase;
  97. for_each_pci_dev(pdev) {
  98. ent = pci_match_id(pci_tbl, pdev);
  99. if (ent)
  100. goto found;
  101. }
  102. /* Device not found. */
  103. goto out;
  104. found:
  105. err = pci_read_config_dword(pdev, 0x58, &pmbase);
  106. if (err)
  107. goto out;
  108. err = -EIO;
  109. pmbase &= 0x0000FF00;
  110. if (pmbase == 0)
  111. goto out;
  112. if (!request_region(pmbase + 0xF0, 8, "AMD HWRNG")) {
  113. dev_err(&pdev->dev, "AMD HWRNG region 0x%x already in use!\n",
  114. pmbase + 0xF0);
  115. err = -EBUSY;
  116. goto out;
  117. }
  118. amd_rng.priv = (unsigned long)pmbase;
  119. amd_pdev = pdev;
  120. pr_info("AMD768 RNG detected\n");
  121. err = hwrng_register(&amd_rng);
  122. if (err) {
  123. pr_err(PFX "RNG registering failed (%d)\n",
  124. err);
  125. release_region(pmbase + 0xF0, 8);
  126. goto out;
  127. }
  128. out:
  129. return err;
  130. }
  131. static void __exit mod_exit(void)
  132. {
  133. u32 pmbase = (unsigned long)amd_rng.priv;
  134. release_region(pmbase + 0xF0, 8);
  135. hwrng_unregister(&amd_rng);
  136. }
  137. module_init(mod_init);
  138. module_exit(mod_exit);
  139. MODULE_AUTHOR("The Linux Kernel team");
  140. MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
  141. MODULE_LICENSE("GPL");