picoxcell-rng.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. * Copyright (c) 2010-2011 Picochip Ltd., Jamie Iles
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * All enquiries to support@picochip.com
  9. */
  10. #include <linux/clk.h>
  11. #include <linux/delay.h>
  12. #include <linux/err.h>
  13. #include <linux/hw_random.h>
  14. #include <linux/io.h>
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/platform_device.h>
  18. #define DATA_REG_OFFSET 0x0200
  19. #define CSR_REG_OFFSET 0x0278
  20. #define CSR_OUT_EMPTY_MASK (1 << 24)
  21. #define CSR_FAULT_MASK (1 << 1)
  22. #define TRNG_BLOCK_RESET_MASK (1 << 0)
  23. #define TAI_REG_OFFSET 0x0380
  24. /*
  25. * The maximum amount of time in microseconds to spend waiting for data if the
  26. * core wants us to wait. The TRNG should generate 32 bits every 320ns so a
  27. * timeout of 20us seems reasonable. The TRNG does builtin tests of the data
  28. * for randomness so we can't always assume there is data present.
  29. */
  30. #define PICO_TRNG_TIMEOUT 20
  31. static void __iomem *rng_base;
  32. static struct clk *rng_clk;
  33. struct device *rng_dev;
  34. static inline u32 picoxcell_trng_read_csr(void)
  35. {
  36. return __raw_readl(rng_base + CSR_REG_OFFSET);
  37. }
  38. static inline bool picoxcell_trng_is_empty(void)
  39. {
  40. return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK;
  41. }
  42. /*
  43. * Take the random number generator out of reset and make sure the interrupts
  44. * are masked. We shouldn't need to get large amounts of random bytes so just
  45. * poll the status register. The hardware generates 32 bits every 320ns so we
  46. * shouldn't have to wait long enough to warrant waiting for an IRQ.
  47. */
  48. static void picoxcell_trng_start(void)
  49. {
  50. __raw_writel(0, rng_base + TAI_REG_OFFSET);
  51. __raw_writel(0, rng_base + CSR_REG_OFFSET);
  52. }
  53. static void picoxcell_trng_reset(void)
  54. {
  55. __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET);
  56. __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET);
  57. picoxcell_trng_start();
  58. }
  59. /*
  60. * Get some random data from the random number generator. The hw_random core
  61. * layer provides us with locking.
  62. */
  63. static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max,
  64. bool wait)
  65. {
  66. int i;
  67. /* Wait for some data to become available. */
  68. for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) {
  69. if (!wait)
  70. return 0;
  71. udelay(1);
  72. }
  73. if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) {
  74. dev_err(rng_dev, "fault detected, resetting TRNG\n");
  75. picoxcell_trng_reset();
  76. return -EIO;
  77. }
  78. if (i == PICO_TRNG_TIMEOUT)
  79. return 0;
  80. *(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET);
  81. return sizeof(u32);
  82. }
  83. static struct hwrng picoxcell_trng = {
  84. .name = "picoxcell",
  85. .read = picoxcell_trng_read,
  86. };
  87. static int picoxcell_trng_probe(struct platform_device *pdev)
  88. {
  89. int ret;
  90. struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  91. if (!mem) {
  92. dev_warn(&pdev->dev, "no memory resource\n");
  93. return -ENOMEM;
  94. }
  95. if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
  96. "picoxcell_trng")) {
  97. dev_warn(&pdev->dev, "unable to request io mem\n");
  98. return -EBUSY;
  99. }
  100. rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
  101. if (!rng_base) {
  102. dev_warn(&pdev->dev, "unable to remap io mem\n");
  103. return -ENOMEM;
  104. }
  105. rng_clk = clk_get(&pdev->dev, NULL);
  106. if (IS_ERR(rng_clk)) {
  107. dev_warn(&pdev->dev, "no clk\n");
  108. return PTR_ERR(rng_clk);
  109. }
  110. ret = clk_enable(rng_clk);
  111. if (ret) {
  112. dev_warn(&pdev->dev, "unable to enable clk\n");
  113. goto err_enable;
  114. }
  115. picoxcell_trng_start();
  116. ret = hwrng_register(&picoxcell_trng);
  117. if (ret)
  118. goto err_register;
  119. rng_dev = &pdev->dev;
  120. dev_info(&pdev->dev, "pixoxcell random number generator active\n");
  121. return 0;
  122. err_register:
  123. clk_disable(rng_clk);
  124. err_enable:
  125. clk_put(rng_clk);
  126. return ret;
  127. }
  128. static int __devexit picoxcell_trng_remove(struct platform_device *pdev)
  129. {
  130. hwrng_unregister(&picoxcell_trng);
  131. clk_disable(rng_clk);
  132. clk_put(rng_clk);
  133. return 0;
  134. }
  135. #ifdef CONFIG_PM
  136. static int picoxcell_trng_suspend(struct device *dev)
  137. {
  138. clk_disable(rng_clk);
  139. return 0;
  140. }
  141. static int picoxcell_trng_resume(struct device *dev)
  142. {
  143. return clk_enable(rng_clk);
  144. }
  145. static const struct dev_pm_ops picoxcell_trng_pm_ops = {
  146. .suspend = picoxcell_trng_suspend,
  147. .resume = picoxcell_trng_resume,
  148. };
  149. #endif /* CONFIG_PM */
  150. static struct platform_driver picoxcell_trng_driver = {
  151. .probe = picoxcell_trng_probe,
  152. .remove = __devexit_p(picoxcell_trng_remove),
  153. .driver = {
  154. .name = "picoxcell-trng",
  155. .owner = THIS_MODULE,
  156. #ifdef CONFIG_PM
  157. .pm = &picoxcell_trng_pm_ops,
  158. #endif /* CONFIG_PM */
  159. },
  160. };
  161. module_platform_driver(picoxcell_trng_driver);
  162. MODULE_LICENSE("GPL");
  163. MODULE_AUTHOR("Jamie Iles");
  164. MODULE_DESCRIPTION("Picochip picoXcell TRNG driver");