linux-stable/drivers/char/hw_random/bcm63xx-rng.c
Florian Fainelli 0052a65413 hwrng: bcm63xx - use devm_* helpers
Simplify the driver's probe function and error handling by using the
device managed allocators, while at it, drop the redundant "out of
memory" messages since these are already printed by the allocator.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2015-03-01 23:02:26 +13:00

171 lines
3.5 KiB
C

/*
* Broadcom BCM63xx Random Number Generator support
*
* Copyright (C) 2011, Florian Fainelli <florian@openwrt.org>
* Copyright (C) 2009, Broadcom Corporation
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#define RNG_CTRL 0x00
#define RNG_EN (1 << 0)
#define RNG_STAT 0x04
#define RNG_AVAIL_MASK (0xff000000)
#define RNG_DATA 0x08
#define RNG_THRES 0x0c
#define RNG_MASK 0x10
struct bcm63xx_rng_priv {
struct clk *clk;
void __iomem *regs;
};
#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv)
static int bcm63xx_rng_init(struct hwrng *rng)
{
struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
u32 val;
val = __raw_readl(priv->regs + RNG_CTRL);
val |= RNG_EN;
__raw_writel(val, priv->regs + RNG_CTRL);
return 0;
}
static void bcm63xx_rng_cleanup(struct hwrng *rng)
{
struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
u32 val;
val = __raw_readl(priv->regs + RNG_CTRL);
val &= ~RNG_EN;
__raw_writel(val, priv->regs + RNG_CTRL);
}
static int bcm63xx_rng_data_present(struct hwrng *rng, int wait)
{
struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
return __raw_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK;
}
static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data)
{
struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
*data = __raw_readl(priv->regs + RNG_DATA);
return 4;
}
static int bcm63xx_rng_probe(struct platform_device *pdev)
{
struct resource *r;
struct clk *clk;
int ret;
struct bcm63xx_rng_priv *priv;
struct hwrng *rng;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no iomem resource\n");
ret = -ENXIO;
goto out;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out;
}
rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
if (!rng) {
ret = -ENOMEM;
goto out;
}
platform_set_drvdata(pdev, rng);
rng->priv = (unsigned long)priv;
rng->name = pdev->name;
rng->init = bcm63xx_rng_init;
rng->cleanup = bcm63xx_rng_cleanup;
rng->data_present = bcm63xx_rng_data_present;
rng->data_read = bcm63xx_rng_data_read;
clk = clk_get(&pdev->dev, "ipsec");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "no clock for device\n");
ret = PTR_ERR(clk);
goto out;
}
priv->clk = clk;
if (!devm_request_mem_region(&pdev->dev, r->start,
resource_size(r), pdev->name)) {
dev_err(&pdev->dev, "request mem failed");
ret = -ENOMEM;
goto out;
}
priv->regs = devm_ioremap_nocache(&pdev->dev, r->start,
resource_size(r));
if (!priv->regs) {
dev_err(&pdev->dev, "ioremap failed");
ret = -ENOMEM;
goto out;
}
clk_enable(clk);
ret = hwrng_register(rng);
if (ret) {
dev_err(&pdev->dev, "failed to register rng device\n");
goto out_clk_disable;
}
dev_info(&pdev->dev, "registered RNG driver\n");
return 0;
out_clk_disable:
clk_disable(clk);
out:
return ret;
}
static int bcm63xx_rng_remove(struct platform_device *pdev)
{
struct hwrng *rng = platform_get_drvdata(pdev);
struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
hwrng_unregister(rng);
clk_disable(priv->clk);
return 0;
}
static struct platform_driver bcm63xx_rng_driver = {
.probe = bcm63xx_rng_probe,
.remove = bcm63xx_rng_remove,
.driver = {
.name = "bcm63xx-rng",
},
};
module_platform_driver(bcm63xx_rng_driver);
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver");
MODULE_LICENSE("GPL");