Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwrng: meson - add support for S4

For some Amlogic SOC's, mechanism to obtain random number
has been changed. For example, S4 now uses status bit waiting algo.

Signed-off-by: Alexey Romanov <avromanov@sberdevices.ru>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Alexey Romanov and committed by
Herbert Xu
a5de196d b42ab1c6

+77 -3
+77 -3
drivers/char/hw_random/meson-rng.c
··· 13 13 #include <linux/types.h> 14 14 #include <linux/of.h> 15 15 #include <linux/clk.h> 16 + #include <linux/iopoll.h> 16 17 17 - #define RNG_DATA 0x00 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 + }; 18 28 19 29 struct meson_rng_data { 20 30 void __iomem *base; 21 31 struct hwrng rng; 32 + struct device *dev; 22 33 }; 23 34 24 35 static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) ··· 42 31 return sizeof(u32); 43 32 } 44 33 34 + static int meson_rng_wait_status(void __iomem *cfg_addr, int bit) 35 + { 36 + u32 status = 0; 37 + int ret; 38 + 39 + ret = readl_relaxed_poll_timeout_atomic(cfg_addr, 40 + status, !(status & bit), 41 + 10, 10000); 42 + if (ret) 43 + return -EBUSY; 44 + 45 + return 0; 46 + } 47 + 48 + static int meson_s4_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 49 + { 50 + struct meson_rng_data *data = 51 + container_of(rng, struct meson_rng_data, rng); 52 + 53 + void __iomem *cfg_addr = data->base + RNG_S4_CFG; 54 + int err; 55 + 56 + writel_relaxed(readl_relaxed(cfg_addr) | SEED_READY_STS_BIT, cfg_addr); 57 + 58 + err = meson_rng_wait_status(cfg_addr, SEED_READY_STS_BIT); 59 + if (err) { 60 + dev_err(data->dev, "Seed isn't ready, try again\n"); 61 + return err; 62 + } 63 + 64 + err = meson_rng_wait_status(cfg_addr, RUN_BIT); 65 + if (err) { 66 + dev_err(data->dev, "Can't get random number, try again\n"); 67 + return err; 68 + } 69 + 70 + *(u32 *)buf = readl_relaxed(data->base + RNG_S4_DATA); 71 + 72 + return sizeof(u32); 73 + } 74 + 45 75 static int meson_rng_probe(struct platform_device *pdev) 46 76 { 47 77 struct device *dev = &pdev->dev; 48 78 struct meson_rng_data *data; 49 79 struct clk *core_clk; 80 + const struct meson_rng_priv *priv; 50 81 51 82 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 52 83 if (!data) 53 84 return -ENOMEM; 85 + 86 + priv = device_get_match_data(&pdev->dev); 87 + if (!priv) 88 + return -ENODEV; 54 89 55 90 data->base = devm_platform_ioremap_resource(pdev, 0); 56 91 if (IS_ERR(data->base)) ··· 108 51 "Failed to get core clock\n"); 109 52 110 53 data->rng.name = pdev->name; 111 - data->rng.read = meson_rng_read; 54 + data->rng.read = priv->read; 55 + 56 + data->dev = &pdev->dev; 112 57 113 58 return devm_hwrng_register(dev, &data->rng); 114 59 } 115 60 61 + static const struct meson_rng_priv meson_rng_priv = { 62 + .read = meson_rng_read, 63 + }; 64 + 65 + static const struct meson_rng_priv meson_rng_priv_s4 = { 66 + .read = meson_s4_rng_read, 67 + }; 68 + 116 69 static const struct of_device_id meson_rng_of_match[] = { 117 - { .compatible = "amlogic,meson-rng", }, 70 + { 71 + .compatible = "amlogic,meson-rng", 72 + .data = (void *)&meson_rng_priv, 73 + }, 74 + { 75 + .compatible = "amlogic,meson-s4-rng", 76 + .data = (void *)&meson_rng_priv_s4, 77 + }, 118 78 {}, 119 79 }; 120 80 MODULE_DEVICE_TABLE(of, meson_rng_of_match);