2007-11-18 21:32:54

by Patrick McHardy

[permalink] [raw]
Subject: [HIFN 00/03]: RNG support v2

These patches add support for using the HIFN rng.

The first patch changes the hwrng API to move waiting for availability
of new random into the drivers. This allows to use driver-specific
delays instead of the constant 10us delay used previously, increasing
the HIFN speed from 2.5mbit to almost 15mbit, which is nearly the
theoretical maximum.

The second patch improves the PLL initialization by making the reference
clock configurable and its speed known to the driver, which is needed
to calculate the amount of time to wait between two RNG reads. Since
there is no way to find out the frequency reliably (especially for the
external clock), it adds some sane looking defaults and a module
parameter to override it. It also changes the PLL initialization to
follow the procedure described in the HIFN documentation.

The third patch adds hw_random support. To use the rng, the HW_RANDOM
config option has to be enabled.

Running rngtest on the random number generator indicates that it works
properly, with an average failure ratio of about 1:1000 at ~15mbit:

rngtest: bits received from input: 3155000032
rngtest: FIPS 140-2 successes: 157621
rngtest: FIPS 140-2 failures: 129
rngtest: FIPS 140-2(2001-10-10) Monobit: 20
rngtest: FIPS 140-2(2001-10-10) Poker: 13
rngtest: FIPS 140-2(2001-10-10) Runs: 54
rngtest: FIPS 140-2(2001-10-10) Long run: 43
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=441.005; avg=14730.378; max=21510.187)Kibits/s
rngtest: FIPS tests speed: (min=1.228; avg=55.967; max=62.742)Mibits/s
rngtest: Program run time: 263373905 microseconds

Changes this last posting:

- follow PLL initialization procedure as described in the documentation
- allow to specify clock source without a frequency
- rename pll_ref module parameter to hifn_pll_ref
- discard first rng value after initialization as suggested by the
documentation
- use ktime instead of get_cycles for delaying rng reads
- adapt to the hwrng API changes
- fix cleanup in initialization error path


Michael, if you're OK with the RNG changes, I'd suggest that all three
patches go through Herbert since the HIFN patches depend on that part.


drivers/char/hw_random/amd-rng.c | 12 ++-
drivers/char/hw_random/core.c | 24 +----
drivers/char/hw_random/geode-rng.c | 12 ++-
drivers/char/hw_random/intel-rng.c | 15 ++-
drivers/char/hw_random/omap-rng.c | 13 ++-
drivers/char/hw_random/pasemi-rng.c | 14 ++-
drivers/char/hw_random/via-rng.c | 19 ++--
drivers/crypto/hifn_795x.c | 192 ++++++++++++++++++++++++++++++++++-
include/linux/hw_random.h | 2 +-
9 files changed, 262 insertions(+), 41 deletions(-)

Patrick McHardy (3):
[HWRNG]: move status polling loop to data_present callbacks
[HIFN]: Improve PLL initialization
[HIFN]: Add support for using the random number generator


2007-11-18 21:32:55

by Patrick McHardy

[permalink] [raw]
Subject: [HWRNG 01/03]: move status polling loop to data_present callbacks

[HWRNG]: move status polling loop to data_present callbacks

Handle waiting for new random within the drivers themselves, this allows to
use better suited timeouts for the individual rngs.

Signed-off-by: Patrick McHardy <[email protected]>

---
commit 5632554998aafc5605635f842bca50d5353cd9d4
tree 161324874febaf01d70ab2dd0b434db962108265
parent 92704884669e1af381feb364bc83985a63934c56
author Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:05 +0100
committer Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:05 +0100

drivers/char/hw_random/amd-rng.c | 12 ++++++++++--
drivers/char/hw_random/core.c | 24 ++++++------------------
drivers/char/hw_random/geode-rng.c | 12 ++++++++++--
drivers/char/hw_random/intel-rng.c | 15 ++++++++++++---
drivers/char/hw_random/omap-rng.c | 13 +++++++++++--
drivers/char/hw_random/pasemi-rng.c | 14 +++++++++++---
drivers/char/hw_random/via-rng.c | 19 ++++++++++++-------
include/linux/hw_random.h | 2 +-
8 files changed, 73 insertions(+), 38 deletions(-)

diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c
index 556fd81..c422e87 100644
--- a/drivers/char/hw_random/amd-rng.c
+++ b/drivers/char/hw_random/amd-rng.c
@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>


@@ -52,11 +53,18 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct pci_dev *amd_pdev;


-static int amd_rng_data_present(struct hwrng *rng)
+static int amd_rng_data_present(struct hwrng *rng, int wait)
{
u32 pmbase = (u32)rng->priv;
+ int data, i;

- return !!(inl(pmbase + 0xF4) & 1);
+ for (i = 0; i < 20; i++) {
+ data = !!(inl(pmbase + 0xF4) & 1);
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}

static int amd_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 26a860a..0118b98 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -66,11 +66,11 @@ static inline void hwrng_cleanup(struct hwrng *rng)
rng->cleanup(rng);
}

-static inline int hwrng_data_present(struct hwrng *rng)
+static inline int hwrng_data_present(struct hwrng *rng, int wait)
{
if (!rng->data_present)
return 1;
- return rng->data_present(rng);
+ return rng->data_present(rng, wait);
}

static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
@@ -94,8 +94,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
{
u32 data;
ssize_t ret = 0;
- int i, err = 0;
- int data_present;
+ int err = 0;
int bytes_read;

while (size) {
@@ -107,21 +106,10 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
err = -ENODEV;
goto out;
}
- if (filp->f_flags & O_NONBLOCK) {
- data_present = hwrng_data_present(current_rng);
- } else {
- /* Some RNG require some time between data_reads to gather
- * new entropy. Poll it.
- */
- for (i = 0; i < 20; i++) {
- data_present = hwrng_data_present(current_rng);
- if (data_present)
- break;
- udelay(10);
- }
- }
+
bytes_read = 0;
- if (data_present)
+ if (hwrng_data_present(current_rng,
+ !(filp->f_flags & O_NONBLOCK)))
bytes_read = hwrng_data_read(current_rng, &data);
mutex_unlock(&rng_mutex);

diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c
index 8e8658d..fed4ef5 100644
--- a/drivers/char/hw_random/geode-rng.c
+++ b/drivers/char/hw_random/geode-rng.c
@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>


@@ -61,11 +62,18 @@ static int geode_rng_data_read(struct hwrng *rng, u32 *data)
return 4;
}

-static int geode_rng_data_present(struct hwrng *rng)
+static int geode_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
+ int data, i;

- return !!(readl(mem + GEODE_RNG_STATUS_REG));
+ for (i = 0; i < 20; i++) {
+ data = !!(readl(mem + GEODE_RNG_STATUS_REG));
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}


diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c
index 753f460..5cc651e 100644
--- a/drivers/char/hw_random/intel-rng.c
+++ b/drivers/char/hw_random/intel-rng.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/stop_machine.h>
+#include <linux/delay.h>
#include <asm/io.h>


@@ -162,11 +163,19 @@ static inline u8 hwstatus_set(void __iomem *mem,
return hwstatus_get(mem);
}

-static int intel_rng_data_present(struct hwrng *rng)
+static int intel_rng_data_present(struct hwrng *rng, int wait)
{
void __iomem *mem = (void __iomem *)rng->priv;
-
- return !!(readb(mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT);
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = !!(readb(mem + INTEL_RNG_STATUS) &
+ INTEL_RNG_DATA_PRESENT);
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}

static int intel_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 3f35a1c..7e31995 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -29,6 +29,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>

#include <asm/io.h>

@@ -65,9 +66,17 @@ static void omap_rng_write_reg(int reg, u32 val)
}

/* REVISIT: Does the status bit really work on 16xx? */
-static int omap_rng_data_present(struct hwrng *rng)
+static int omap_rng_data_present(struct hwrng *rng, int wait)
{
- return omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}

static int omap_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index fa6040b..621adf2 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/of_platform.h>
#include <asm/io.h>

@@ -44,9 +45,16 @@
static int pasemi_rng_data_present(struct hwrng *rng)
{
void __iomem *rng_regs = (void __iomem *)rng->priv;
-
- return (in_le32(rng_regs + SDCRNG_CTL_REG)
- & SDCRNG_CTL_FVLD_M) ? 1 : 0;
+ int data, i;
+
+ for (i = 0; i < 20; i++) {
+ data = (in_le32(rng_regs + SDCRNG_CTL_REG)
+ & SDCRNG_CTL_FVLD_M) ? 1 : 0;
+ if (data || !wait)
+ break;
+ udelay(10);
+ }
+ return data;
}

static int pasemi_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index ec435cb..868e39f 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hw_random.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/cpufeature.h>
@@ -77,10 +78,11 @@ static inline u32 xstore(u32 *addr, u32 edx_in)
return eax_out;
}

-static int via_rng_data_present(struct hwrng *rng)
+static int via_rng_data_present(struct hwrng *rng, int wait)
{
u32 bytes_out;
u32 *via_rng_datum = (u32 *)(&rng->priv);
+ int i;

/* We choose the recommended 1-byte-per-instruction RNG rate,
* for greater randomness at the expense of speed. Larger
@@ -95,12 +97,15 @@ static int via_rng_data_present(struct hwrng *rng)
* completes.
*/

- *via_rng_datum = 0; /* paranoia, not really necessary */
- bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
- bytes_out &= VIA_XSTORE_CNT_MASK;
- if (bytes_out == 0)
- return 0;
- return 1;
+ for (i = 0; i < 20; i++) {
+ *via_rng_datum = 0; /* paranoia, not really necessary */
+ bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
+ bytes_out &= VIA_XSTORE_CNT_MASK;
+ if (bytes_out || !wait)
+ break;
+ udelay(10);
+ }
+ return bytes_out ? 1 : 0;
}

static int via_rng_data_read(struct hwrng *rng, u32 *data)
diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h
index 21ea761..85d1191 100644
--- a/include/linux/hw_random.h
+++ b/include/linux/hw_random.h
@@ -33,7 +33,7 @@ struct hwrng {
const char *name;
int (*init)(struct hwrng *rng);
void (*cleanup)(struct hwrng *rng);
- int (*data_present)(struct hwrng *rng);
+ int (*data_present)(struct hwrng *rng, int wait);
int (*data_read)(struct hwrng *rng, u32 *data);
unsigned long priv;


2007-11-18 21:32:57

by Patrick McHardy

[permalink] [raw]
Subject: [HIFN 02/03]: Improve PLL initialization

[HIFN]: Improve PLL initialization

The current PLL initalization has a number of deficiencies:

- uses fixed multiplier of 8, which overclocks the chip when using a
reference clock that operates at frequencies above 33MHz. According
to a comment in the BSD source, this is true for the external clock
on almost all every board.

- writes to a reserved bit

- doesn't follow the initialization procedure specified in chapter
6.11.1 of the HIFN hardware users guide

- doesn't allow to use the PCI clock

This patch adds a module parameter to specify the reference clock
(pci or external) and its frequency and uses that to calculate the
optimum multiplier to reach the maximal speed. By default it uses
the external clock and assumes a speed of 66MHz, which effectively
halfs the frequency currently used.

Signed-off-by: Patrick McHardy <[email protected]>

---
commit 70467fae3a656562f86adefdfe6d54e3ca20feeb
tree 9fc347d453e04e21f243693ede743f6a8e280ef2
parent 5632554998aafc5605635f842bca50d5353cd9d4
author Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:17 +0100
committer Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:17 +0100

drivers/crypto/hifn_795x.c | 110 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index bf817d4..de594bc 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -19,6 +19,7 @@

#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/mod_devicetable.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -47,6 +48,11 @@
#define dprintk(f, a...) do {} while (0)
#endif

+static char hifn_pll_ref[sizeof("extNNN")] = "ext";
+module_param_string(hifn_pll_ref, hifn_pll_ref, sizeof(hifn_pll_ref), 0444);
+MODULE_PARM_DESC(hifn_pll_ref,
+ "PLL reference clock (pci[freq] or ext[freq], default ext)");
+
static atomic_t hifn_dev_number;

#define ACRYPTO_OP_DECRYPT 0
@@ -286,7 +292,26 @@ static atomic_t hifn_dev_number;
#define HIFN_DMACNFG_DMARESET 0x00000002 /* DMA Reset # */
#define HIFN_DMACNFG_MSTRESET 0x00000001 /* Master Reset # */

-#define HIFN_PLL_7956 0x00001d18 /* 7956 PLL config value */
+/* PLL configuration register */
+#define HIFN_PLL_REF_CLK_HBI 0x00000000 /* HBI reference clock */
+#define HIFN_PLL_REF_CLK_PLL 0x00000001 /* PLL reference clock */
+#define HIFN_PLL_BP 0x00000002 /* Reference clock bypass */
+#define HIFN_PLL_PK_CLK_HBI 0x00000000 /* PK engine HBI clock */
+#define HIFN_PLL_PK_CLK_PLL 0x00000008 /* PK engine PLL clock */
+#define HIFN_PLL_PE_CLK_HBI 0x00000000 /* PE engine HBI clock */
+#define HIFN_PLL_PE_CLK_PLL 0x00000010 /* PE engine PLL clock */
+#define HIFN_PLL_RESERVED_1 0x00000400 /* Reserved bit, must be 1 */
+#define HIFN_PLL_ND_SHIFT 11 /* Clock multiplier shift */
+#define HIFN_PLL_ND_MULT_2 0x00000000 /* PLL clock multiplier 2 */
+#define HIFN_PLL_ND_MULT_4 0x00000800 /* PLL clock multiplier 4 */
+#define HIFN_PLL_ND_MULT_6 0x00001000 /* PLL clock multiplier 6 */
+#define HIFN_PLL_ND_MULT_8 0x00001800 /* PLL clock multiplier 8 */
+#define HIFN_PLL_ND_MULT_10 0x00002000 /* PLL clock multiplier 10 */
+#define HIFN_PLL_ND_MULT_12 0x00002800 /* PLL clock multiplier 12 */
+#define HIFN_PLL_IS_1_8 0x00000000 /* charge pump (mult. 1-8) */
+#define HIFN_PLL_IS_9_12 0x00010000 /* charge pump (mult. 9-12) */
+
+#define HIFN_PLL_FCK_MAX 266 /* Maximum PLL frequency */

/* Public key reset register (HIFN_1_PUB_RESET) */
#define HIFN_PUBRST_RESET 0x00000001 /* reset public/rng unit */
@@ -871,6 +896,64 @@ static void hifn_init_dma(struct hifn_device *dev)
dma->cmdk = dma->srck = dma->dstk = dma->resk = 0;
}

+/*
+ * Initialize the PLL. We need to know the frequency of the reference clock
+ * to calculate the optimal multiplier. For PCI we assume 66MHz, since that
+ * allows us to operate without the risk of overclocking the chip. If it
+ * actually uses 33MHz, the chip will operate at half the speed, this can be
+ * overriden by specifying the frequency as module parameter (pci33).
+ *
+ * Unfortunately the PCI clock is not very suitable since the HIFN needs a
+ * stable clock and the PCI clock frequency may vary, so the default is the
+ * external clock. There is no way to find out its frequency, we default to
+ * 66MHz since according to Mike Ham of HiFn, almost every board in existence
+ * has an external crystal populated at 66MHz.
+ */
+static void hifn_init_pll(struct hifn_device *dev)
+{
+ unsigned int freq, m;
+ u32 pllcfg;
+
+ pllcfg = HIFN_1_PLL | HIFN_PLL_RESERVED_1;
+
+ if (strncmp(hifn_pll_ref, "ext", 3) == 0)
+ pllcfg |= HIFN_PLL_REF_CLK_PLL;
+ else
+ pllcfg |= HIFN_PLL_REF_CLK_HBI;
+
+ if (hifn_pll_ref[3] != '\0')
+ freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
+ else {
+ freq = 66;
+ printk(KERN_INFO "hifn795x: assuming %uMHz clock speed, "
+ "override with hifn_pll_ref=%.3s<frequency>\n",
+ freq, hifn_pll_ref);
+ }
+
+ m = HIFN_PLL_FCK_MAX / freq;
+
+ pllcfg |= (m / 2 - 1) << HIFN_PLL_ND_SHIFT;
+ if (m <= 8)
+ pllcfg |= HIFN_PLL_IS_1_8;
+ else
+ pllcfg |= HIFN_PLL_IS_9_12;
+
+ /* Select clock source and enable clock bypass */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_HBI | HIFN_PLL_PE_CLK_HBI | HIFN_PLL_BP);
+
+ /* Let the chip lock to the input clock */
+ mdelay(10);
+
+ /* Disable clock bypass */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_HBI | HIFN_PLL_PE_CLK_HBI);
+
+ /* Switch the engines to the PLL */
+ hifn_write_1(dev, HIFN_1_PLL, pllcfg |
+ HIFN_PLL_PK_CLK_PLL | HIFN_PLL_PE_CLK_PLL);
+}
+
static void hifn_init_registers(struct hifn_device *dev)
{
u32 dptr = dev->desc_dma;
@@ -938,7 +1021,7 @@ static void hifn_init_registers(struct hifn_device *dev)
#else
hifn_write_0(dev, HIFN_0_PUCNFG, 0x10342);
#endif
- hifn_write_1(dev, HIFN_1_PLL, HIFN_PLL_7956);
+ hifn_init_pll(dev);

hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
hifn_write_1(dev, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
@@ -2621,8 +2704,31 @@ static struct pci_driver hifn_pci_driver = {

static int __devinit hifn_init(void)
{
+ unsigned int freq;
int err;

+ if (strncmp(hifn_pll_ref, "ext", 3) &&
+ strncmp(hifn_pll_ref, "pci", 3)) {
+ printk(KERN_ERR "hifn795x: invalid hifn_pll_ref clock, "
+ "must be pci or ext");
+ return -EINVAL;
+ }
+
+ /*
+ * For the 7955/7956 the reference clock frequency must be in the
+ * range of 20MHz-100MHz. For the 7954 the upper bound is 66.67MHz,
+ * but this chip is currently not supported.
+ */
+ if (hifn_pll_ref[3] != '\0') {
+ freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
+ if (freq < 20 || freq > 100) {
+ printk(KERN_ERR "hifn795x: invalid hifn_pll_ref "
+ "frequency, must be in the range "
+ "of 20-100");
+ return -EINVAL;
+ }
+ }
+
err = pci_register_driver(&hifn_pci_driver);
if (err < 0) {
dprintk("Failed to register PCI driver for %s device.\n",

2007-11-18 21:32:59

by Patrick McHardy

[permalink] [raw]
Subject: [HIFN 03/03]: Add support for using the random number generator

[HIFN]: Add support for using the random number generator

Signed-off-by: Patrick McHardy <[email protected]>

---
commit 352a65d036f53c1e124bef4205d6fcedb78eac2c
tree 190bb0b4a1795e55096552f743af996df2766070
parent 70467fae3a656562f86adefdfe6d54e3ca20feeb
author Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:17 +0100
committer Patrick McHardy <[email protected]> Sun, 18 Nov 2007 22:09:17 +0100

drivers/crypto/hifn_795x.c | 82 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 81 insertions(+), 1 deletions(-)

diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index de594bc..1a19700 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -31,6 +31,8 @@
#include <linux/highmem.h>
#include <linux/interrupt.h>
#include <linux/crypto.h>
+#include <linux/hw_random.h>
+#include <linux/ktime.h>

#include <crypto/algapi.h>
#include <crypto/des.h>
@@ -458,6 +460,14 @@ struct hifn_device

struct crypto_queue queue;
struct list_head alg_list;
+
+ unsigned int pk_clk_freq;
+
+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+ unsigned int rng_wait_time;
+ ktime_t rngtime;
+ struct hwrng rng;
+#endif
};

#define HIFN_D_LENGTH 0x0000ffff
@@ -785,6 +795,56 @@ static struct pci2id {
}
};

+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+static int hifn_rng_data_present(struct hwrng *rng, int wait)
+{
+ struct hifn_device *dev = (struct hifn_device *)rng->priv;
+ s64 nsec;
+
+ nsec = ktime_to_ns(ktime_sub(ktime_get(), dev->rngtime));
+ nsec -= dev->rng_wait_time;
+ if (nsec <= 0)
+ return 1;
+ if (!wait)
+ return 0;
+ ndelay(nsec);
+ return 1;
+}
+
+static int hifn_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ struct hifn_device *dev = (struct hifn_device *)rng->priv;
+
+ *data = hifn_read_1(dev, HIFN_1_RNG_DATA);
+ dev->rngtime = ktime_get();
+ return 4;
+}
+
+static int hifn_register_rng(struct hifn_device *dev)
+{
+ /*
+ * We must wait at least 256 Pk_clk cycles between two reads of the rng.
+ */
+ dev->rng_wait_time = DIV_ROUND_UP(NSEC_PER_SEC, dev->pk_clk_freq) *
+ 256;
+
+ dev->rng.name = dev->name;
+ dev->rng.data_present = hifn_rng_data_present,
+ dev->rng.data_read = hifn_rng_data_read,
+ dev->rng.priv = (unsigned long)dev;
+
+ return hwrng_register(&dev->rng);
+}
+
+static void hifn_unregister_rng(struct hifn_device *dev)
+{
+ hwrng_unregister(&dev->rng);
+}
+#else
+#define hifn_register_rng(dev) 0
+#define hifn_unregister_rng(dev)
+#endif
+
static int hifn_init_pubrng(struct hifn_device *dev)
{
int i;
@@ -820,6 +880,11 @@ static int hifn_init_pubrng(struct hifn_device *dev)
dprintk("Chip %s: RNG engine has been successfully initialised.\n",
dev->name);

+#if defined(CONFIG_HW_RANDOM) || defined(CONFIG_HW_RANDOM_MODULE)
+ /* First value must be discarded */
+ hifn_read_1(dev, HIFN_1_RNG_DATA);
+ dev->rngtime = ktime_get();
+#endif
return 0;
}

@@ -952,6 +1017,14 @@ static void hifn_init_pll(struct hifn_device *dev)
/* Switch the engines to the PLL */
hifn_write_1(dev, HIFN_1_PLL, pllcfg |
HIFN_PLL_PK_CLK_PLL | HIFN_PLL_PE_CLK_PLL);
+
+ /*
+ * The Fpk_clk runs at half the total speed. Its frequency is needed to
+ * calculate the minimum time between two reads of the rng. Since 33MHz
+ * is actually 33.333... we overestimate the frequency here, resulting
+ * in slightly larger intervals.
+ */
+ dev->pk_clk_freq = 1000000 * (freq + 1) * m / 2;
}

static void hifn_init_registers(struct hifn_device *dev)
@@ -2609,10 +2682,14 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
goto err_out_stop_device;

- err = hifn_register_alg(dev);
+ err = hifn_register_rng(dev);
if (err)
goto err_out_stop_device;

+ err = hifn_register_alg(dev);
+ if (err)
+ goto err_out_unregister_rng;
+
INIT_DELAYED_WORK(&dev->work, hifn_work);
schedule_delayed_work(&dev->work, HZ);

@@ -2622,6 +2699,8 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)

return 0;

+err_out_unregister_rng:
+ hifn_unregister_rng(dev);
err_out_stop_device:
hifn_reset_dma(dev, 1);
hifn_stop_device(dev);
@@ -2662,6 +2741,7 @@ static void hifn_remove(struct pci_dev *pdev)
cancel_delayed_work(&dev->work);
flush_scheduled_work();

+ hifn_unregister_rng(dev);
hifn_unregister_alg(dev);
hifn_reset_dma(dev, 1);
hifn_stop_device(dev);

2007-11-19 10:35:16

by Evgeniy Polyakov

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

Hi Patrick.

On Sun, Nov 18, 2007 at 10:32:52PM +0100, Patrick McHardy ([email protected]) wrote:
> These patches add support for using the HIFN rng.

> Patrick McHardy (3):
> [HWRNG]: move status polling loop to data_present callbacks
> [HIFN]: Improve PLL initialization
> [HIFN]: Add support for using the random number generator

Thanks a lot for your patches.
Ack for all three.

--
Evgeniy Polyakov

2007-11-19 18:21:11

by Michael Büsch

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

On Sunday 18 November 2007 22:32:52 Patrick McHardy wrote:
> These patches add support for using the HIFN rng.

Acked-by: Michael Buesch <[email protected]>

Patrick, can you send this patchset to Andrew for inclusion into -mm?


--
Greetings Michael.

2007-11-19 18:25:49

by Patrick McHardy

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

Michael Buesch wrote:
> On Sunday 18 November 2007 22:32:52 Patrick McHardy wrote:
>> These patches add support for using the HIFN rng.
>
> Acked-by: Michael Buesch <[email protected]>
>
> Patrick, can you send this patchset to Andrew for inclusion into -mm?


I think he already pulls from Herbert's cryptodev-2.6 tree
(otherwise the patches useless anyway since the HIFN driver
is only in that tree).

2007-11-19 19:05:36

by Michael Büsch

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

On Monday 19 November 2007 19:25:25 Patrick McHardy wrote:
> Michael Buesch wrote:
> > On Sunday 18 November 2007 22:32:52 Patrick McHardy wrote:
> >> These patches add support for using the HIFN rng.
> >
> > Acked-by: Michael Buesch <[email protected]>
> >
> > Patrick, can you send this patchset to Andrew for inclusion into -mm?
>
>
> I think he already pulls from Herbert's cryptodev-2.6 tree
> (otherwise the patches useless anyway since the HIFN driver
> is only in that tree).
>
>
>

Ok, well. Then apply it to any tree you like and push it
upstream somehow. I'm OK with it.

--
Greetings Michael.

2007-11-21 05:08:32

by Herbert Xu

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

On Sun, Nov 18, 2007 at 10:32:52PM +0100, Patrick McHardy wrote:
> These patches add support for using the HIFN rng.

All applied. Thanks a lot Patrick!
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2007-11-25 06:40:02

by Andrew Morton

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

On Sun, 18 Nov 2007 22:32:52 +0100 (MET) Patrick McHardy <[email protected]> wrote:

> These patches add support for using the HIFN rng.

Dumb question: what is HIFN?

2007-11-25 06:47:08

by Herbert Xu

[permalink] [raw]
Subject: Re: [HIFN 00/03]: RNG support v2

On Sat, Nov 24, 2007 at 10:38:45PM -0800, Andrew Morton wrote:
> On Sun, 18 Nov 2007 22:32:52 +0100 (MET) Patrick McHardy <[email protected]> wrote:
>
> > These patches add support for using the HIFN rng.
>
> Dumb question: what is HIFN?

They make crypto hardware: http://www.hifn.com.

Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt