1afc39d6eSKefeng Wang /* 2afc39d6eSKefeng Wang * Copyright (C) 2016 HiSilicon Co., Ltd. 3afc39d6eSKefeng Wang * 4afc39d6eSKefeng Wang * This program is free software; you can redistribute it and/or modify 5afc39d6eSKefeng Wang * it under the terms of the GNU General Public License version 2 as 6afc39d6eSKefeng Wang * published by the Free Software Foundation. 7afc39d6eSKefeng Wang */ 8afc39d6eSKefeng Wang 9afc39d6eSKefeng Wang #include <linux/err.h> 10afc39d6eSKefeng Wang #include <linux/kernel.h> 11afc39d6eSKefeng Wang #include <linux/hw_random.h> 12afc39d6eSKefeng Wang #include <linux/io.h> 13afc39d6eSKefeng Wang #include <linux/module.h> 14afc39d6eSKefeng Wang #include <linux/of.h> 15afc39d6eSKefeng Wang #include <linux/platform_device.h> 16afc39d6eSKefeng Wang #include <linux/random.h> 17afc39d6eSKefeng Wang 18afc39d6eSKefeng Wang #define RNG_SEED 0x0 19afc39d6eSKefeng Wang #define RNG_CTRL 0x4 20afc39d6eSKefeng Wang #define RNG_SEED_SEL BIT(2) 21afc39d6eSKefeng Wang #define RNG_RING_EN BIT(1) 22afc39d6eSKefeng Wang #define RNG_EN BIT(0) 23afc39d6eSKefeng Wang #define RNG_RAN_NUM 0x10 24afc39d6eSKefeng Wang #define RNG_PHY_SEED 0x14 25afc39d6eSKefeng Wang 26afc39d6eSKefeng Wang #define to_hisi_rng(p) container_of(p, struct hisi_rng, rng) 27afc39d6eSKefeng Wang 28afc39d6eSKefeng Wang static int seed_sel; 29afc39d6eSKefeng Wang module_param(seed_sel, int, S_IRUGO); 30afc39d6eSKefeng Wang MODULE_PARM_DESC(seed_sel, "Auto reload seed. 0, use LFSR(default); 1, use ring oscillator."); 31afc39d6eSKefeng Wang 32afc39d6eSKefeng Wang struct hisi_rng { 33afc39d6eSKefeng Wang void __iomem *base; 34afc39d6eSKefeng Wang struct hwrng rng; 35afc39d6eSKefeng Wang }; 36afc39d6eSKefeng Wang 37afc39d6eSKefeng Wang static int hisi_rng_init(struct hwrng *rng) 38afc39d6eSKefeng Wang { 39afc39d6eSKefeng Wang struct hisi_rng *hrng = to_hisi_rng(rng); 40afc39d6eSKefeng Wang int val = RNG_EN; 41afc39d6eSKefeng Wang u32 seed; 42afc39d6eSKefeng Wang 43afc39d6eSKefeng Wang /* get a random number as initial seed */ 44afc39d6eSKefeng Wang get_random_bytes(&seed, sizeof(seed)); 45afc39d6eSKefeng Wang 46afc39d6eSKefeng Wang writel_relaxed(seed, hrng->base + RNG_SEED); 47afc39d6eSKefeng Wang 48afc39d6eSKefeng Wang /** 49afc39d6eSKefeng Wang * The seed is reload periodically, there are two choice 50afc39d6eSKefeng Wang * of seeds, default seed using the value from LFSR, or 51afc39d6eSKefeng Wang * will use seed generated by ring oscillator. 52afc39d6eSKefeng Wang */ 53afc39d6eSKefeng Wang if (seed_sel == 1) 54afc39d6eSKefeng Wang val |= RNG_RING_EN | RNG_SEED_SEL; 55afc39d6eSKefeng Wang 56afc39d6eSKefeng Wang writel_relaxed(val, hrng->base + RNG_CTRL); 57afc39d6eSKefeng Wang return 0; 58afc39d6eSKefeng Wang } 59afc39d6eSKefeng Wang 60afc39d6eSKefeng Wang static void hisi_rng_cleanup(struct hwrng *rng) 61afc39d6eSKefeng Wang { 62afc39d6eSKefeng Wang struct hisi_rng *hrng = to_hisi_rng(rng); 63afc39d6eSKefeng Wang 64afc39d6eSKefeng Wang writel_relaxed(0, hrng->base + RNG_CTRL); 65afc39d6eSKefeng Wang } 66afc39d6eSKefeng Wang 67afc39d6eSKefeng Wang static int hisi_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 68afc39d6eSKefeng Wang { 69afc39d6eSKefeng Wang struct hisi_rng *hrng = to_hisi_rng(rng); 70afc39d6eSKefeng Wang u32 *data = buf; 71afc39d6eSKefeng Wang 72afc39d6eSKefeng Wang *data = readl_relaxed(hrng->base + RNG_RAN_NUM); 73afc39d6eSKefeng Wang return 4; 74afc39d6eSKefeng Wang } 75afc39d6eSKefeng Wang 76afc39d6eSKefeng Wang static int hisi_rng_probe(struct platform_device *pdev) 77afc39d6eSKefeng Wang { 78afc39d6eSKefeng Wang struct hisi_rng *rng; 79afc39d6eSKefeng Wang struct resource *res; 80afc39d6eSKefeng Wang int ret; 81afc39d6eSKefeng Wang 82afc39d6eSKefeng Wang rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); 83afc39d6eSKefeng Wang if (!rng) 84afc39d6eSKefeng Wang return -ENOMEM; 85afc39d6eSKefeng Wang 86afc39d6eSKefeng Wang platform_set_drvdata(pdev, rng); 87afc39d6eSKefeng Wang 88afc39d6eSKefeng Wang res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 89afc39d6eSKefeng Wang rng->base = devm_ioremap_resource(&pdev->dev, res); 90afc39d6eSKefeng Wang if (IS_ERR(rng->base)) 91afc39d6eSKefeng Wang return PTR_ERR(rng->base); 92afc39d6eSKefeng Wang 93afc39d6eSKefeng Wang rng->rng.name = pdev->name; 94afc39d6eSKefeng Wang rng->rng.init = hisi_rng_init; 95afc39d6eSKefeng Wang rng->rng.cleanup = hisi_rng_cleanup; 96afc39d6eSKefeng Wang rng->rng.read = hisi_rng_read; 97afc39d6eSKefeng Wang 98afc39d6eSKefeng Wang ret = devm_hwrng_register(&pdev->dev, &rng->rng); 99afc39d6eSKefeng Wang if (ret) { 100afc39d6eSKefeng Wang dev_err(&pdev->dev, "failed to register hwrng\n"); 101afc39d6eSKefeng Wang return ret; 102afc39d6eSKefeng Wang } 103afc39d6eSKefeng Wang 104afc39d6eSKefeng Wang return 0; 105afc39d6eSKefeng Wang } 106afc39d6eSKefeng Wang 107afc39d6eSKefeng Wang static const struct of_device_id hisi_rng_dt_ids[] = { 108afc39d6eSKefeng Wang { .compatible = "hisilicon,hip04-rng" }, 109afc39d6eSKefeng Wang { .compatible = "hisilicon,hip05-rng" }, 110afc39d6eSKefeng Wang { } 111afc39d6eSKefeng Wang }; 112afc39d6eSKefeng Wang MODULE_DEVICE_TABLE(of, hisi_rng_dt_ids); 113afc39d6eSKefeng Wang 114afc39d6eSKefeng Wang static struct platform_driver hisi_rng_driver = { 115afc39d6eSKefeng Wang .probe = hisi_rng_probe, 116afc39d6eSKefeng Wang .driver = { 117afc39d6eSKefeng Wang .name = "hisi-rng", 118afc39d6eSKefeng Wang .of_match_table = of_match_ptr(hisi_rng_dt_ids), 119afc39d6eSKefeng Wang }, 120afc39d6eSKefeng Wang }; 121afc39d6eSKefeng Wang 122afc39d6eSKefeng Wang module_platform_driver(hisi_rng_driver); 123afc39d6eSKefeng Wang 124afc39d6eSKefeng Wang MODULE_LICENSE("GPL"); 125afc39d6eSKefeng Wang MODULE_AUTHOR("Kefeng Wang <wangkefeng.wang@huawei>"); 126afc39d6eSKefeng Wang MODULE_DESCRIPTION("Hisilicon random number generator driver"); 127