Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fcd06755 authored by Patrick McHardy's avatar Patrick McHardy Committed by Herbert Xu
Browse files

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

parent 37a8023c
Loading
Loading
Loading
Loading
+81 −1
Original line number Diff line number Diff line
@@ -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);