1 /* 2 * Driver for Mediatek Hardware Random Number Generator 3 * 4 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 #define MTK_RNG_DEV KBUILD_MODNAME 17 18 #include <linux/clk.h> 19 #include <linux/delay.h> 20 #include <linux/err.h> 21 #include <linux/hw_random.h> 22 #include <linux/io.h> 23 #include <linux/iopoll.h> 24 #include <linux/kernel.h> 25 #include <linux/module.h> 26 #include <linux/of.h> 27 #include <linux/platform_device.h> 28 29 #define USEC_POLL 2 30 #define TIMEOUT_POLL 20 31 32 #define RNG_CTRL 0x00 33 #define RNG_EN BIT(0) 34 #define RNG_READY BIT(31) 35 36 #define RNG_DATA 0x08 37 38 #define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) 39 40 struct mtk_rng { 41 void __iomem *base; 42 struct clk *clk; 43 struct hwrng rng; 44 }; 45 46 static int mtk_rng_init(struct hwrng *rng) 47 { 48 struct mtk_rng *priv = to_mtk_rng(rng); 49 u32 val; 50 int err; 51 52 err = clk_prepare_enable(priv->clk); 53 if (err) 54 return err; 55 56 val = readl(priv->base + RNG_CTRL); 57 val |= RNG_EN; 58 writel(val, priv->base + RNG_CTRL); 59 60 return 0; 61 } 62 63 static void mtk_rng_cleanup(struct hwrng *rng) 64 { 65 struct mtk_rng *priv = to_mtk_rng(rng); 66 u32 val; 67 68 val = readl(priv->base + RNG_CTRL); 69 val &= ~RNG_EN; 70 writel(val, priv->base + RNG_CTRL); 71 72 clk_disable_unprepare(priv->clk); 73 } 74 75 static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait) 76 { 77 struct mtk_rng *priv = to_mtk_rng(rng); 78 int ready; 79 80 ready = readl(priv->base + RNG_CTRL) & RNG_READY; 81 if (!ready && wait) 82 readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready, 83 ready & RNG_READY, USEC_POLL, 84 TIMEOUT_POLL); 85 return !!ready; 86 } 87 88 static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 89 { 90 struct mtk_rng *priv = to_mtk_rng(rng); 91 int retval = 0; 92 93 while (max >= sizeof(u32)) { 94 if (!mtk_rng_wait_ready(rng, wait)) 95 break; 96 97 *(u32 *)buf = readl(priv->base + RNG_DATA); 98 retval += sizeof(u32); 99 buf += sizeof(u32); 100 max -= sizeof(u32); 101 } 102 103 return retval || !wait ? retval : -EIO; 104 } 105 106 static int mtk_rng_probe(struct platform_device *pdev) 107 { 108 struct resource *res; 109 int ret; 110 struct mtk_rng *priv; 111 112 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 113 if (!res) { 114 dev_err(&pdev->dev, "no iomem resource\n"); 115 return -ENXIO; 116 } 117 118 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 119 if (!priv) 120 return -ENOMEM; 121 122 priv->rng.name = pdev->name; 123 priv->rng.init = mtk_rng_init; 124 priv->rng.cleanup = mtk_rng_cleanup; 125 priv->rng.read = mtk_rng_read; 126 127 priv->clk = devm_clk_get(&pdev->dev, "rng"); 128 if (IS_ERR(priv->clk)) { 129 ret = PTR_ERR(priv->clk); 130 dev_err(&pdev->dev, "no clock for device: %d\n", ret); 131 return ret; 132 } 133 134 priv->base = devm_ioremap_resource(&pdev->dev, res); 135 if (IS_ERR(priv->base)) 136 return PTR_ERR(priv->base); 137 138 ret = devm_hwrng_register(&pdev->dev, &priv->rng); 139 if (ret) { 140 dev_err(&pdev->dev, "failed to register rng device: %d\n", 141 ret); 142 return ret; 143 } 144 145 dev_info(&pdev->dev, "registered RNG driver\n"); 146 147 return 0; 148 } 149 150 static const struct of_device_id mtk_rng_match[] = { 151 { .compatible = "mediatek,mt7623-rng" }, 152 {}, 153 }; 154 MODULE_DEVICE_TABLE(of, mtk_rng_match); 155 156 static struct platform_driver mtk_rng_driver = { 157 .probe = mtk_rng_probe, 158 .driver = { 159 .name = MTK_RNG_DEV, 160 .of_match_table = mtk_rng_match, 161 }, 162 }; 163 164 module_platform_driver(mtk_rng_driver); 165 166 MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); 167 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 168 MODULE_LICENSE("GPL"); 169