1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (c) 2016 BayLibre, SAS. 4 * Author: Neil Armstrong <narmstrong@baylibre.com> 5 * Copyright (C) 2014 Amlogic, Inc. 6 */ 7 #include <linux/err.h> 8 #include <linux/module.h> 9 #include <linux/io.h> 10 #include <linux/platform_device.h> 11 #include <linux/hw_random.h> 12 #include <linux/slab.h> 13 #include <linux/types.h> 14 #include <linux/of.h> 15 #include <linux/clk.h> 16 #include <linux/iopoll.h> 17 18 #define RNG_DATA 0x00 19 #define RNG_S4_DATA 0x08 20 #define RNG_S4_CFG 0x00 21 22 #define RUN_BIT BIT(0) 23 #define SEED_READY_STS_BIT BIT(31) 24 25 struct meson_rng_priv { 26 int (*read)(struct hwrng *rng, void *buf, size_t max, bool wait); 27 }; 28 29 struct meson_rng_data { 30 void __iomem *base; 31 struct hwrng rng; 32 struct device *dev; 33 }; 34 35 static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 36 { 37 struct meson_rng_data *data = 38 container_of(rng, struct meson_rng_data, rng); 39 40 *(u32 *)buf = readl_relaxed(data->base + RNG_DATA); 41 42 return sizeof(u32); 43 } 44 45 static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) 46 { 47 u32 status = 0; 48 int ret; 49 50 ret = readl_relaxed_poll_timeout_atomic(cfg_addr, 51 status, !(status & bit), 52 10, 10000); 53 if (ret) 54 return -EBUSY; 55 56 return 0; 57 } 58 59 static int meson_s4_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 60 { 61 struct meson_rng_data *data = 62 container_of(rng, struct meson_rng_data, rng); 63 64 void __iomem *cfg_addr = data->base + RNG_S4_CFG; 65 int err; 66 67 writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); 68 69 err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); 70 if (err) { 71 dev_err(data->dev, "Seed isn't ready, try again\n"); 72 return err; 73 } 74 75 err = meson_rng_wait_status(cfg_addr, RUN_BIT); 76 if (err) { 77 dev_err(data->dev, "Can't get random number, try again\n"); 78 return err; 79 } 80 81 *(u32 *)buf = readl_relaxed(data->base + RNG_S4_DATA); 82 83 return sizeof(u32); 84 } 85 86 static int meson_rng_probe(struct platform_device *pdev) 87 { 88 struct device *dev = &pdev->dev; 89 struct meson_rng_data *data; 90 struct clk *core_clk; 91 const struct meson_rng_priv *priv; 92 93 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 94 if (!data) 95 return -ENOMEM; 96 97 priv = device_get_match_data(&pdev->dev); 98 if (!priv) 99 return -ENODEV; 100 101 data->base = devm_platform_ioremap_resource(pdev, 0); 102 if (IS_ERR(data->base)) 103 return PTR_ERR(data->base); 104 105 core_clk = devm_clk_get_optional_enabled(dev, "core"); 106 if (IS_ERR(core_clk)) 107 return dev_err_probe(dev, PTR_ERR(core_clk), 108 "Failed to get core clock\n"); 109 110 data->rng.name = pdev->name; 111 data->rng.read = priv->read; 112 113 data->dev = &pdev->dev; 114 115 return devm_hwrng_register(dev, &data->rng); 116 } 117 118 static const struct meson_rng_priv meson_rng_priv = { 119 .read = meson_rng_read, 120 }; 121 122 static const struct meson_rng_priv meson_rng_priv_s4 = { 123 .read = meson_s4_rng_read, 124 }; 125 126 static const struct of_device_id meson_rng_of_match[] = { 127 { 128 .compatible = "amlogic,meson-rng", 129 .data = (void *)&meson_rng_priv, 130 }, 131 { 132 .compatible = "amlogic,meson-s4-rng", 133 .data = (void *)&meson_rng_priv_s4, 134 }, 135 {}, 136 }; 137 MODULE_DEVICE_TABLE(of, meson_rng_of_match); 138 139 static struct platform_driver meson_rng_driver = { 140 .probe = meson_rng_probe, 141 .driver = { 142 .name = "meson-rng", 143 .of_match_table = meson_rng_of_match, 144 }, 145 }; 146 147 module_platform_driver(meson_rng_driver); 148 149 MODULE_DESCRIPTION("Meson H/W Random Number Generator driver"); 150 MODULE_AUTHOR("Lawrence Mok <lawrence.mok@amlogic.com>"); 151 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 152 MODULE_LICENSE("Dual BSD/GPL"); 153