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 /* no room for word data */ 94 if (maxsize < WORD_SZ) 95 return 0; 96 97 ret = clk_prepare_enable(rng->clk); 98 if (ret) 99 return ret; 100 101 /* read random data from hardware */ 102 do { 103 val = readl_relaxed(rng->base + PRNG_STATUS); 104 if (!(val & PRNG_STATUS_DATA_AVAIL)) 105 break; 106 107 val = readl_relaxed(rng->base + PRNG_DATA_OUT); 108 if (!val) 109 break; 110 111 *retdata++ = val; 112 currsize += WORD_SZ; 113 114 /* make sure we stay on 32bit boundary */ 115 if ((maxsize - currsize) < WORD_SZ) 116 break; 117 } while (currsize < maxsize); 118 119 clk_disable_unprepare(rng->clk); 120 121 return currsize; 122} 123 124static int msm_rng_init(struct hwrng *hwrng) 125{ 126 return msm_rng_enable(hwrng, 1); 127} 128 129static void msm_rng_cleanup(struct hwrng *hwrng) 130{ 131 msm_rng_enable(hwrng, 0); 132} 133 134static int msm_rng_probe(struct platform_device *pdev) 135{ 136 struct resource *res; 137 struct msm_rng *rng; 138 int ret; 139 140 rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 141 if (!rng) 142 return -ENOMEM; 143 144 platform_set_drvdata(pdev, rng); 145 146 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 147 rng->base = devm_ioremap_resource(&pdev->dev, res); 148 if (IS_ERR(rng->base)) 149 return PTR_ERR(rng->base); 150 151 rng->clk = devm_clk_get(&pdev->dev, "core"); 152 if (IS_ERR(rng->clk)) 153 return PTR_ERR(rng->clk); 154 155 rng->hwrng.name = KBUILD_MODNAME, 156 rng->hwrng.init = msm_rng_init, 157 rng->hwrng.cleanup = msm_rng_cleanup, 158 rng->hwrng.read = msm_rng_read, 159 160 ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); 161 if (ret) { 162 dev_err(&pdev->dev, "failed to register hwrng\n"); 163 return ret; 164 } 165 166 return 0; 167} 168 169static const struct of_device_id msm_rng_of_match[] = { 170 { .compatible = "qcom,prng", }, 171 {} 172}; 173MODULE_DEVICE_TABLE(of, msm_rng_of_match); 174 175static struct platform_driver msm_rng_driver = { 176 .probe = msm_rng_probe, 177 .driver = { 178 .name = KBUILD_MODNAME, 179 .of_match_table = of_match_ptr(msm_rng_of_match), 180 } 181}; 182module_platform_driver(msm_rng_driver); 183 184MODULE_ALIAS("platform:" KBUILD_MODNAME); 185MODULE_AUTHOR("The Linux Foundation"); 186MODULE_DESCRIPTION("Qualcomm MSM random number generator driver"); 187MODULE_LICENSE("GPL v2"); 188