-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
hw_random: add Broadcom BCM63xx RNG driver
Signed-off-by: Florian Fainelli <florian@openwrt.org> Cc: linux-mips@linux-mips.org Cc: mpm@selenic.com Cc: herbert@gondor.apana.org.au Patchwork: https://patchwork.linux-mips.org/patch/3327/ Patchwork: https://patchwork.linux-mips.org/patch/4072/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
- Loading branch information
Florian Fainelli
authored and
Ralf Baechle
committed
Jul 24, 2012
1 parent
b73ab84
commit 553072b
Showing
3 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/* | ||
* 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> | ||
|
||
#include <bcm63xx_io.h> | ||
#include <bcm63xx_regs.h> | ||
|
||
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 = bcm_readl(priv->regs + RNG_CTRL); | ||
val |= RNG_EN; | ||
bcm_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 = bcm_readl(priv->regs + RNG_CTRL); | ||
val &= ~RNG_EN; | ||
bcm_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 bcm_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 = bcm_readl(priv->regs + RNG_DATA); | ||
|
||
return 4; | ||
} | ||
|
||
static int __devinit 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 = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
if (!priv) { | ||
dev_err(&pdev->dev, "no memory for private structure\n"); | ||
ret = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
rng = kzalloc(sizeof(*rng), GFP_KERNEL); | ||
if (!rng) { | ||
dev_err(&pdev->dev, "no memory for rng structure\n"); | ||
ret = -ENOMEM; | ||
goto out_free_priv; | ||
} | ||
|
||
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_free_rng; | ||
} | ||
|
||
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_free_rng; | ||
} | ||
|
||
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_free_rng; | ||
} | ||
|
||
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_free_rng: | ||
platform_set_drvdata(pdev, NULL); | ||
kfree(rng); | ||
out_free_priv: | ||
kfree(priv); | ||
out: | ||
return ret; | ||
} | ||
|
||
static int __devexit 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); | ||
kfree(priv); | ||
kfree(rng); | ||
platform_set_drvdata(pdev, NULL); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver bcm63xx_rng_driver = { | ||
.probe = bcm63xx_rng_probe, | ||
.remove = __devexit_p(bcm63xx_rng_remove), | ||
.driver = { | ||
.name = "bcm63xx-rng", | ||
.owner = THIS_MODULE, | ||
}, | ||
}; | ||
|
||
module_platform_driver(bcm63xx_rng_driver); | ||
|
||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | ||
MODULE_DESCRIPTION("Broadcom BCM63xx RNG driver"); | ||
MODULE_LICENSE("GPL"); |