hw_random: Add msm_rng driver

This is a combination of 13 commits.

 commit 59a27ca89fb515caeac2b97059832515566f70d9
 Author: John Howe <jhowe@codeaurora.org>
 Date:   Tue Jan 4 17:18:54 2011 -0500

    hw_random: Add Qualcomm MSM random number driver

    This driver uses the hardware random number generator on MSM chips.

    Change-Id: I13b4d831fb9b5487356af466536a9005107ff723
    Signed-off-by: John Howe <jhowe@codeaurora.org>

 commit 09cae33a80706e0aa77bdc4e1b493ef2a36dbbde
 Author: John Howe <jhowe@codeaurora.org>
 Date:   Fri Jan 14 10:21:05 2011 -0500

    hw_random: add PRNG clock control

    Change-Id: I6d9dd2ee7c50020fb8d41242b655e172e4c9eff2
    Signed-off-by: John Howe <jhowe@codeaurora.org>

 commit 0573eda3ce19c01825ded4fe850362c37460dc20
 Author: John Howe <jhowe@codeaurora.org>
 Date:   Mon Jan 24 14:56:06 2011 -0500

    hw_random: remove clk frequency control

    The clock only supports one rate, 64MHz, and is set to that
    rate by default.

    Change-Id: I820c74e44f4ebdec7b891a4c89d681cb5c3dcee6
    Signed-off-by: John Howe <jhowe@codeaurora.org>

 commit fab0a4d40e18be4a31a4720acafb128893c620e8
 Author: John Howe <jhowe@codeaurora.org>
 Date:   Tue Apr 12 14:48:51 2011 -0400

    hw_random: Use memory barriers

    Change-Id: Ie216141d7f6997266d8c5ed4bbd436f1be6551b8
    Signed-off-by: John Howe <jhowe@codeaurora.org>

 commit f07f1ba547790155db4c508ccfa58b64c967fe8e
 Author: Raj Kushwaha <rajk@codeaurora.org>
 Date:   Tue Jul 26 14:35:28 2011 -0700

    hw_random: msm: Remove PRNG hardware configuration

    LSFR and CONFIG registers are configured by secure domain
    code and XPU protected.

    CRs-Fixed: 284133
    Signed-off-by: Mona Hossain <mhossain@codeaurora.org>
    (cherry picked from commit a4f802b0f5d0faee9e83020c599b2f4b0b27de5f)

    Change-Id: Ie73cc3a95d1e2fff970e4d71f15fc1cbb4ead11c
    Signed-off-by: Raj Kushwaha <rajk@codeaurora.org>

 commit 26cef0c9d572e55df0f17dc4a5de380595eddaeb
 Author: Matt Wagantall <mattw@codeaurora.org>
 Date:   Thu Aug 11 17:19:31 2011 -0700

    msm: clock: Use device names to distinguish between PRNG clocks

    Drivers should now use their device names to distinguish between
    clocks of the same type rather than the clock name. This allows
    the clock names to be updated to match the new naming convention.

    Change-Id: Ibf00fd3d406adb04299e3e79e379d4fefe70f2b4
    Signed-off-by: Matt Wagantall <mattw@codeaurora.org>

 commit 5ad373931744ae98289ec680334d7cc0bee1c0b6
 Author: Ramesh Masavarapu <rameshm@codeaurora.org>
 Date:   Mon Oct 10 10:44:10 2011 -0700

    msm: Add PRNG to MSM9615 device.

    Add configuration parameters for MSM9615 device.
    PRNG hardware registers initialization in msm_rng.c is done
    currently for MSM9615.

    Change-Id: I2a05e9e582ce94a25bec71e1acaee95d62cd9469
    Signed-off-by: Ramesh Masavarapu <rameshm@codeaurora.org>

 commit 2f963dbc78c6c0703f51466b2b242287a2df5487
 Author: Ramesh Masavarapu <rameshm@codeaurora.org>
 Date:   Thu Oct 20 15:33:50 2011 -0700

    msm: Removed target specific changes for enabling PRNG h/w.

    The driver checks if the PRNG h/w is enabled. If it is not ON, it
    enables the PRNG h/w.

    Change-Id: I7c73eba7ba47f4fca116cfe0884758e6dd130ed0
    Signed-off-by: Ramesh Masavarapu <rameshm@codeaurora.org>

 commit 46af59fb8f4bce302ad4d787b63b25e46548df6a
 Author: Stepan Moskovchenko <stepanm@codeaurora.org>
 Date:   Tue Feb 7 14:38:59 2012 -0800

    msm: rng: Disable RNG init on APQ8064

    Change-Id: Ic42a85c51faea8a17b02eb4987d0f5db732716c2
    Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>

 commit a54263b0056b15cad1336a33d57bb1db013e8abb
 Author: Ramesh Masavarapu <rameshm@codeaurora.org>
 Date:   Wed Feb 1 22:49:01 2012 -0800

    msm: Removed XPU violations.

    During initialization, there is a violation in writing to
    "read only" registers for targets that support trust zone.
    Trustzone marks certain registers as read-only.
    This change fixes the issue by writing to registers only
    on targets that do not support trust zone.

    Change-Id: I69bb0f1bad199201aa3dd8b378ca1683dfa81c86
    Signed-off-by: Ramesh Masavarapu <rameshm@codeaurora.org>

 commit a99fcc24e94481134ca22f9778e88ed31646d6e3
 Author: Ramesh Masavarapu <rameshm@codeaurora.org>
 Date:   Wed Feb 22 14:30:32 2012 -0800

    Revert "msm: rng: Disable RNG init on APQ8064"

    This reverts commit 46af59fb8f4bce302ad4d787b63b25e46548df6a.
    The RNG driver was initially disabled because of missing clock
    changes and this driver was causing boot up issues.
    Now with the clock changes checked in the RNG driver works.

    Change-Id: I127f25c8be6b715510c1fb16b274a814416d8a8a
    Signed-off-by: Ramesh Masavarapu <rameshm@codeaurora.org>

 commit 801c392ab9ee2893c6f3ca506e43afa62798890b
 Author: Ramesh Masavarapu <rameshm@codeaurora.org>
 Date:   Tue Apr 24 16:28:00 2012 -0700

    prng: Replace clk_enable and clk_disable APIs.

    The clk driver has introduced new clock APIs that replace
    the existing clk_enable and clk_disable.
     -clk_enable() APIs is replaced with clk_prepare_enable().
     -clk_disable() API is replaced with clk_disable_unprepare().

    Change-Id: Ib6c452e7dc3f357497eae5a9302a7352a19fcb18
    Signed-off-by: Ramesh Masavarapu <rameshm@codeaurora.org>

 commit de991f08a8738dc66e65488aebed472de65ce237
 Author: Hariprasad Dhalinarasimha <hnamgund@codeaurora.org>
 Date:   Thu May 31 13:15:51 2012 -0700

    PRNG: Device tree entry for qrng device.

    Cleanup platorm device entry & add device tree entry

    Change-Id: I5bde944d63276a3aaf00b7415066963027f11249
    Signed-off-by: Hariprasad Dhalinarasimha <hnamgund@codeaurora.org>

 commit a4100a4a6e2a1843645d3eff22dabb387d4cbb61
 Author: Stephen Boyd <sboyd@codeaurora.org>
 Date:   Mon Jun 25 15:48:37 2012 -0700

    msm-rng: Fix PRNG_LFSR_CFG setup

    Changes to only configure the LFSR on devices that don't have the
    prng hardware already setup mistakenly removed the LFSR
    configuration. Instead, the change is ORing in 1s into the top 16
    bits of the register (they're marked as reserved).

    Restore the original code by masking off the lower 16 bits of the
    register and filling them with values from the
    PRNG_LFSR_CFG_CLOCKS define.

    Change-Id: Idd0df7b49175c211eec5ea778733ae81f5bc8188
    Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>

commit 0bef6ede4b779c19091234e20ecfbb76a44edac0
Author: Hariprasad Dhalinarasimha <hnamgund@codeaurora.org>
Date:   Wed Nov 7 19:47:21 2012 -0800

    msm: rng: Add support for iface clk

    Currently the driver supports only enabling core_clk,but on
    certain targets, iface_clk is used for the hardware RNG
    block. This fix adds compatibility to targets that have iface
    clock instead of the core clock.

    Change-Id: I480c3c7070e09f945439ea48e6877c7170ceeeb9
    Signed-off-by: Hariprasad Dhalinarasimha <hnamgund@codeaurora.org>

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
John Howe 2011-01-04 17:18:54 -05:00 committed by Stephen Boyd
parent 8b8455d5e6
commit 3440df24e7
5 changed files with 365 additions and 0 deletions

View file

@ -0,0 +1,75 @@
Introduction:
=============
The msm_rng device driver handles random number generation
using hardware present in MSM chipsets.
Hardware description:
=====================
The supported hardware is a macro block within a system-on-a-chip (SoC).
The hardware is pseudo random number generator (PRNG) with four oscillators
setup with a linear feedback shift register (LFSR).
The hardware must be initially configured once for normal operation and
a 32bit FIFO is read to obtain hardware generated pseudo random numbers.
Currently the driver configures the hardware registers during initialization
and the future plan is to have the boot loader configure these registers and
write lock them so only host OS can read them and the driver writes will be
ignored.
Software description
====================
The driver is based on the platform_driver model. It registers an entry,
exit and probe functions. Once the probe function is called, the driver
registers a callback function with the hwrng (Hardware Random Number Generator)
subsystem that is called when the hardware device (i.e. /dev/hw_random) is
requesting random data from this device.
Once the callback is issued from the hwrng subsystem, the driver checks to
make sure the hardware has random data available and determines the maximum
data it can return and returns that much data back.
Power Management
================
Initially, no services are provided in the area of power management.
SMP/multi-core
==============
The locking mechanism for the hwrng operations is taken care of by the hwrng
framework. There are no SMP situations within the driver that need addressing.
Driver parameters
=================
This driver is built and statically linked into the kernel; therefore,
there are no module parameters supported by this driver.
There are no kernel command line parameters supported by this driver.
Config options
==============
This driver is enabled by the kernel config option CONFIG_HW_RANDOM_MSM.
The option CONFIG_HW_RANDOM_MSM depends on HW_RANDOM && ARCH_MSM.
Dependencies:
=============
This driver depends on the HW_RANDOM subsystem to register with and get
callbacks to request random data.
User space utilities:
=====================
The driver alone does not feed random numbers into kernel but just provides a
method to get random numbers to a known device (i.e. /dev/hw_random). A user-
space utility is required to monitor the /dev/random device entropy pool and
feed it from the /dev/hw_random device. This application also must perform some
sort of sanity checking on the returned data to make sure the data is not all
the same.
There is currently a GPL v2 tool called rng-tools that has a daemon called,
"rngd" that performs this functionality. There is also a test tool in this
package that tests the whole random subsystem.

View file

@ -0,0 +1,12 @@
* RNG (Random Number Generator)
Required properties:
- compatible : Should be "qcom,msm-rng"
- reg : Offset and length of the register set for the device
Example:
qcom,msm-rng@f9bff000 {
compatible = "qcom,msm-rng";
reg = <0xf9bff000 0x200>;
};

View file

@ -313,4 +313,15 @@ config HW_RANDOM_TPM
To compile this driver as a module, choose M here: the
module will be called tpm-rng.
config HW_RANDOM_MSM
tristate "Qualcomm MSM Random Number Generator support"
depends on HW_RANDOM && ARCH_MSM
default n
---help---
This driver provides kernel-side support for the Random Number
Generator hardware found on Qualcomm MSM SoCs.
To compile this driver as a module, choose M here: the
module will be called msm_rng.
If unsure, say Y.

View file

@ -25,5 +25,6 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o

View file

@ -0,0 +1,266 @@
/*
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/types.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#define DRIVER_NAME "msm_rng"
/* Device specific register offsets */
#define PRNG_DATA_OUT_OFFSET 0x0000
#define PRNG_STATUS_OFFSET 0x0004
#define PRNG_LFSR_CFG_OFFSET 0x0100
#define PRNG_CONFIG_OFFSET 0x0104
/* Device specific register masks and config values */
#define PRNG_LFSR_CFG_MASK 0xFFFF0000
#define PRNG_LFSR_CFG_CLOCKS 0x0000DDDD
#define PRNG_CONFIG_MASK 0xFFFFFFFD
#define PRNG_HW_ENABLE 0x00000002
#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
struct msm_rng_device {
struct platform_device *pdev;
void __iomem *base;
struct clk *prng_clk;
};
static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct msm_rng_device *msm_rng_dev;
struct platform_device *pdev;
void __iomem *base;
size_t maxsize;
size_t currsize = 0;
unsigned long val;
unsigned long *retdata = data;
int ret;
msm_rng_dev = (struct msm_rng_device *)rng->priv;
pdev = msm_rng_dev->pdev;
base = msm_rng_dev->base;
/* calculate max size bytes to transfer back to caller */
maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
/* no room for word data */
if (maxsize < 4)
return 0;
/* enable PRNG clock */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock in callback\n");
return 0;
}
/* read random data from h/w */
do {
/* check status bit if data is available */
if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001))
break; /* no data to read so just bail */
/* read FIFO */
val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
if (!val)
break; /* no data to read so just bail */
/* write data back to callers pointer */
*(retdata++) = val;
currsize += 4;
/* make sure we stay on 32bit boundary */
if ((maxsize - currsize) < 4)
break;
} while (currsize < maxsize);
/* vote to turn off clock */
clk_disable_unprepare(msm_rng_dev->prng_clk);
return currsize;
}
static struct hwrng msm_rng = {
.name = DRIVER_NAME,
.read = msm_rng_read,
};
static int msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
{
unsigned long val = 0;
unsigned long reg_val = 0;
int ret = 0;
/* Enable the PRNG CLK */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
dev_err(&(msm_rng_dev->pdev)->dev,
"failed to enable clock in probe\n");
return -EPERM;
}
/* Enable PRNG h/w only if it is NOT ON */
val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET) &
PRNG_HW_ENABLE;
/* PRNG H/W is not ON */
if (val != PRNG_HW_ENABLE) {
val = readl_relaxed(msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET) &
PRNG_LFSR_CFG_MASK;
val |= PRNG_LFSR_CFG_MASK;
writel_relaxed(val, msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
/* The PRNG CONFIG register should be first written */
mb();
reg_val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET)
& PRNG_CONFIG_MASK;
reg_val |= PRNG_HW_ENABLE;
writel_relaxed(reg_val, msm_rng_dev->base + PRNG_CONFIG_OFFSET);
/* The PRNG clk should be disabled only after we enable the
* PRNG h/w by writing to the PRNG CONFIG register.
*/
mb();
}
clk_disable_unprepare(msm_rng_dev->prng_clk);
return 0;
}
static int msm_rng_probe(struct platform_device *pdev)
{
struct resource *res;
struct msm_rng_device *msm_rng_dev = NULL;
void __iomem *base = NULL;
int error = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "invalid address\n");
error = -EFAULT;
goto err_exit;
}
msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
if (!msm_rng_dev) {
dev_err(&pdev->dev, "cannot allocate memory\n");
error = -ENOMEM;
goto err_exit;
}
base = ioremap(res->start, resource_size(res));
if (!base) {
dev_err(&pdev->dev, "ioremap failed\n");
error = -ENOMEM;
goto err_iomap;
}
msm_rng_dev->base = base;
/* create a handle for clock control */
msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm_rng_dev->prng_clk)) {
dev_err(&pdev->dev, "failed to register clock source\n");
error = -EPERM;
goto err_clk_get;
}
/* save away pdev and register driver data */
msm_rng_dev->pdev = pdev;
platform_set_drvdata(pdev, msm_rng_dev);
/* Enable rng h/w */
error = msm_rng_enable_hw(msm_rng_dev);
if (error)
goto rollback_clk;
/* register with hwrng framework */
msm_rng.priv = (unsigned long) msm_rng_dev;
error = hwrng_register(&msm_rng);
if (error) {
dev_err(&pdev->dev, "failed to register hwrng\n");
error = -EPERM;
goto rollback_clk;
}
return 0;
rollback_clk:
clk_put(msm_rng_dev->prng_clk);
err_clk_get:
iounmap(msm_rng_dev->base);
err_iomap:
kfree(msm_rng_dev);
err_exit:
return error;
}
static int msm_rng_remove(struct platform_device *pdev)
{
struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
hwrng_unregister(&msm_rng);
clk_put(msm_rng_dev->prng_clk);
iounmap(msm_rng_dev->base);
platform_set_drvdata(pdev, NULL);
kfree(msm_rng_dev);
return 0;
}
static struct of_device_id qrng_match[] = {
{ .compatible = "qcom,msm-rng",
},
{}
};
static struct platform_driver rng_driver = {
.probe = msm_rng_probe,
.remove = msm_rng_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = qrng_match,
}
};
static int __init msm_rng_init(void)
{
return platform_driver_register(&rng_driver);
}
module_init(msm_rng_init);
static void __exit msm_rng_exit(void)
{
platform_driver_unregister(&rng_driver);
}
module_exit(msm_rng_exit);
MODULE_AUTHOR("The Linux Foundation");
MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
MODULE_LICENSE("GPL v2");