153 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*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 0x0019#define RNG_S4_DATA 0x0820#define RNG_S4_CFG 0x0021 22#define RUN_BIT BIT(0)23#define SEED_READY_STS_BIT BIT(31)24 25struct meson_rng_priv {26 int (*read)(struct hwrng *rng, void *buf, size_t max, bool wait);27};28 29struct meson_rng_data {30 void __iomem *base;31 struct hwrng rng;32 struct device *dev;33};34 35static 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 45static 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 59static 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 86static 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 118static const struct meson_rng_priv meson_rng_priv = {119 .read = meson_rng_read,120};121 122static const struct meson_rng_priv meson_rng_priv_s4 = {123 .read = meson_s4_rng_read,124};125 126static 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};137MODULE_DEVICE_TABLE(of, meson_rng_of_match);138 139static 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 147module_platform_driver(meson_rng_driver);148 149MODULE_DESCRIPTION("Meson H/W Random Number Generator driver");150MODULE_AUTHOR("Lawrence Mok <lawrence.mok@amlogic.com>");151MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");152MODULE_LICENSE("Dual BSD/GPL");153