brintos

brintos / linux-shallow public Read only

0
0
Text · 3.4 KiB · 75225eb Raw
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