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 v4.13 183 lines 4.5 kB view raw
1/* 2 * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. 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 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14#include <linux/clk.h> 15#include <linux/err.h> 16#include <linux/hw_random.h> 17#include <linux/io.h> 18#include <linux/module.h> 19#include <linux/of.h> 20#include <linux/platform_device.h> 21 22/* Device specific register offsets */ 23#define PRNG_DATA_OUT 0x0000 24#define PRNG_STATUS 0x0004 25#define PRNG_LFSR_CFG 0x0100 26#define PRNG_CONFIG 0x0104 27 28/* Device specific register masks and config values */ 29#define PRNG_LFSR_CFG_MASK 0x0000ffff 30#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd 31#define PRNG_CONFIG_HW_ENABLE BIT(1) 32#define PRNG_STATUS_DATA_AVAIL BIT(0) 33 34#define MAX_HW_FIFO_DEPTH 16 35#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) 36#define WORD_SZ 4 37 38struct msm_rng { 39 void __iomem *base; 40 struct clk *clk; 41 struct hwrng hwrng; 42}; 43 44#define to_msm_rng(p) container_of(p, struct msm_rng, hwrng) 45 46static int msm_rng_enable(struct hwrng *hwrng, int enable) 47{ 48 struct msm_rng *rng = to_msm_rng(hwrng); 49 u32 val; 50 int ret; 51 52 ret = clk_prepare_enable(rng->clk); 53 if (ret) 54 return ret; 55 56 if (enable) { 57 /* Enable PRNG only if it is not already enabled */ 58 val = readl_relaxed(rng->base + PRNG_CONFIG); 59 if (val & PRNG_CONFIG_HW_ENABLE) 60 goto already_enabled; 61 62 val = readl_relaxed(rng->base + PRNG_LFSR_CFG); 63 val &= ~PRNG_LFSR_CFG_MASK; 64 val |= PRNG_LFSR_CFG_CLOCKS; 65 writel(val, rng->base + PRNG_LFSR_CFG); 66 67 val = readl_relaxed(rng->base + PRNG_CONFIG); 68 val |= PRNG_CONFIG_HW_ENABLE; 69 writel(val, rng->base + PRNG_CONFIG); 70 } else { 71 val = readl_relaxed(rng->base + PRNG_CONFIG); 72 val &= ~PRNG_CONFIG_HW_ENABLE; 73 writel(val, rng->base + PRNG_CONFIG); 74 } 75 76already_enabled: 77 clk_disable_unprepare(rng->clk); 78 return 0; 79} 80 81static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) 82{ 83 struct msm_rng *rng = to_msm_rng(hwrng); 84 size_t currsize = 0; 85 u32 *retdata = data; 86 size_t maxsize; 87 int ret; 88 u32 val; 89 90 /* calculate max size bytes to transfer back to caller */ 91 maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max); 92 93 ret = clk_prepare_enable(rng->clk); 94 if (ret) 95 return ret; 96 97 /* read random data from hardware */ 98 do { 99 val = readl_relaxed(rng->base + PRNG_STATUS); 100 if (!(val & PRNG_STATUS_DATA_AVAIL)) 101 break; 102 103 val = readl_relaxed(rng->base + PRNG_DATA_OUT); 104 if (!val) 105 break; 106 107 *retdata++ = val; 108 currsize += WORD_SZ; 109 110 /* make sure we stay on 32bit boundary */ 111 if ((maxsize - currsize) < WORD_SZ) 112 break; 113 } while (currsize < maxsize); 114 115 clk_disable_unprepare(rng->clk); 116 117 return currsize; 118} 119 120static int msm_rng_init(struct hwrng *hwrng) 121{ 122 return msm_rng_enable(hwrng, 1); 123} 124 125static void msm_rng_cleanup(struct hwrng *hwrng) 126{ 127 msm_rng_enable(hwrng, 0); 128} 129 130static int msm_rng_probe(struct platform_device *pdev) 131{ 132 struct resource *res; 133 struct msm_rng *rng; 134 int ret; 135 136 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 137 if (!rng) 138 return -ENOMEM; 139 140 platform_set_drvdata(pdev, rng); 141 142 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 143 rng->base = devm_ioremap_resource(&pdev->dev, res); 144 if (IS_ERR(rng->base)) 145 return PTR_ERR(rng->base); 146 147 rng->clk = devm_clk_get(&pdev->dev, "core"); 148 if (IS_ERR(rng->clk)) 149 return PTR_ERR(rng->clk); 150 151 rng->hwrng.name = KBUILD_MODNAME, 152 rng->hwrng.init = msm_rng_init, 153 rng->hwrng.cleanup = msm_rng_cleanup, 154 rng->hwrng.read = msm_rng_read, 155 156 ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); 157 if (ret) { 158 dev_err(&pdev->dev, "failed to register hwrng\n"); 159 return ret; 160 } 161 162 return 0; 163} 164 165static const struct of_device_id msm_rng_of_match[] = { 166 { .compatible = "qcom,prng", }, 167 {} 168}; 169MODULE_DEVICE_TABLE(of, msm_rng_of_match); 170 171static struct platform_driver msm_rng_driver = { 172 .probe = msm_rng_probe, 173 .driver = { 174 .name = KBUILD_MODNAME, 175 .of_match_table = of_match_ptr(msm_rng_of_match), 176 } 177}; 178module_platform_driver(msm_rng_driver); 179 180MODULE_ALIAS("platform:" KBUILD_MODNAME); 181MODULE_AUTHOR("The Linux Foundation"); 182MODULE_DESCRIPTION("Qualcomm MSM random number generator driver"); 183MODULE_LICENSE("GPL v2");