Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.12 198 lines 4.8 kB view raw
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 19#define DATA_REG_OFFSET 0x0200 20#define CSR_REG_OFFSET 0x0278 21#define CSR_OUT_EMPTY_MASK (1 << 24) 22#define CSR_FAULT_MASK (1 << 1) 23#define TRNG_BLOCK_RESET_MASK (1 << 0) 24#define TAI_REG_OFFSET 0x0380 25 26/* 27 * The maximum amount of time in microseconds to spend waiting for data if the 28 * core wants us to wait. The TRNG should generate 32 bits every 320ns so a 29 * timeout of 20us seems reasonable. The TRNG does builtin tests of the data 30 * for randomness so we can't always assume there is data present. 31 */ 32#define PICO_TRNG_TIMEOUT 20 33 34static void __iomem *rng_base; 35static struct clk *rng_clk; 36static struct device *rng_dev; 37 38static inline u32 picoxcell_trng_read_csr(void) 39{ 40 return __raw_readl(rng_base + CSR_REG_OFFSET); 41} 42 43static inline bool picoxcell_trng_is_empty(void) 44{ 45 return picoxcell_trng_read_csr() & CSR_OUT_EMPTY_MASK; 46} 47 48/* 49 * Take the random number generator out of reset and make sure the interrupts 50 * are masked. We shouldn't need to get large amounts of random bytes so just 51 * poll the status register. The hardware generates 32 bits every 320ns so we 52 * shouldn't have to wait long enough to warrant waiting for an IRQ. 53 */ 54static void picoxcell_trng_start(void) 55{ 56 __raw_writel(0, rng_base + TAI_REG_OFFSET); 57 __raw_writel(0, rng_base + CSR_REG_OFFSET); 58} 59 60static void picoxcell_trng_reset(void) 61{ 62 __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + CSR_REG_OFFSET); 63 __raw_writel(TRNG_BLOCK_RESET_MASK, rng_base + TAI_REG_OFFSET); 64 picoxcell_trng_start(); 65} 66 67/* 68 * Get some random data from the random number generator. The hw_random core 69 * layer provides us with locking. 70 */ 71static int picoxcell_trng_read(struct hwrng *rng, void *buf, size_t max, 72 bool wait) 73{ 74 int i; 75 76 /* Wait for some data to become available. */ 77 for (i = 0; i < PICO_TRNG_TIMEOUT && picoxcell_trng_is_empty(); ++i) { 78 if (!wait) 79 return 0; 80 81 udelay(1); 82 } 83 84 if (picoxcell_trng_read_csr() & CSR_FAULT_MASK) { 85 dev_err(rng_dev, "fault detected, resetting TRNG\n"); 86 picoxcell_trng_reset(); 87 return -EIO; 88 } 89 90 if (i == PICO_TRNG_TIMEOUT) 91 return 0; 92 93 *(u32 *)buf = __raw_readl(rng_base + DATA_REG_OFFSET); 94 return sizeof(u32); 95} 96 97static struct hwrng picoxcell_trng = { 98 .name = "picoxcell", 99 .read = picoxcell_trng_read, 100}; 101 102static int picoxcell_trng_probe(struct platform_device *pdev) 103{ 104 int ret; 105 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 106 107 if (!mem) { 108 dev_warn(&pdev->dev, "no memory resource\n"); 109 return -ENOMEM; 110 } 111 112 if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), 113 "picoxcell_trng")) { 114 dev_warn(&pdev->dev, "unable to request io mem\n"); 115 return -EBUSY; 116 } 117 118 rng_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); 119 if (!rng_base) { 120 dev_warn(&pdev->dev, "unable to remap io mem\n"); 121 return -ENOMEM; 122 } 123 124 rng_clk = clk_get(&pdev->dev, NULL); 125 if (IS_ERR(rng_clk)) { 126 dev_warn(&pdev->dev, "no clk\n"); 127 return PTR_ERR(rng_clk); 128 } 129 130 ret = clk_enable(rng_clk); 131 if (ret) { 132 dev_warn(&pdev->dev, "unable to enable clk\n"); 133 goto err_enable; 134 } 135 136 picoxcell_trng_start(); 137 ret = hwrng_register(&picoxcell_trng); 138 if (ret) 139 goto err_register; 140 141 rng_dev = &pdev->dev; 142 dev_info(&pdev->dev, "pixoxcell random number generator active\n"); 143 144 return 0; 145 146err_register: 147 clk_disable(rng_clk); 148err_enable: 149 clk_put(rng_clk); 150 151 return ret; 152} 153 154static int picoxcell_trng_remove(struct platform_device *pdev) 155{ 156 hwrng_unregister(&picoxcell_trng); 157 clk_disable(rng_clk); 158 clk_put(rng_clk); 159 160 return 0; 161} 162 163#ifdef CONFIG_PM 164static int picoxcell_trng_suspend(struct device *dev) 165{ 166 clk_disable(rng_clk); 167 168 return 0; 169} 170 171static int picoxcell_trng_resume(struct device *dev) 172{ 173 return clk_enable(rng_clk); 174} 175 176static const struct dev_pm_ops picoxcell_trng_pm_ops = { 177 .suspend = picoxcell_trng_suspend, 178 .resume = picoxcell_trng_resume, 179}; 180#endif /* CONFIG_PM */ 181 182static struct platform_driver picoxcell_trng_driver = { 183 .probe = picoxcell_trng_probe, 184 .remove = picoxcell_trng_remove, 185 .driver = { 186 .name = "picoxcell-trng", 187 .owner = THIS_MODULE, 188#ifdef CONFIG_PM 189 .pm = &picoxcell_trng_pm_ops, 190#endif /* CONFIG_PM */ 191 }, 192}; 193 194module_platform_driver(picoxcell_trng_driver); 195 196MODULE_LICENSE("GPL"); 197MODULE_AUTHOR("Jamie Iles"); 198MODULE_DESCRIPTION("Picochip picoXcell TRNG driver");