cregit-Linux how code gets into the kernel

Release 4.17 drivers/char/hw_random/ks-sa-rng.c

/*
 * Random Number Generator driver for the Keystone SOC
 *
 * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com
 *
 * Authors:     Sandeep Nair
 *              Vitaly Andrianov
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * 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/hw_random.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/delay.h>


#define SA_CMD_STATUS_OFS			0x8

/* TRNG enable control in SA System module*/

#define SA_CMD_STATUS_REG_TRNG_ENABLE		BIT(3)

/* TRNG start control in TRNG module */

#define TRNG_CNTL_REG_TRNG_ENABLE		BIT(10)

/* Data ready indicator in STATUS register */

#define TRNG_STATUS_REG_READY			BIT(0)

/* Data ready clear control in INTACK register */

#define TRNG_INTACK_REG_READY			BIT(0)

/*
 * Number of samples taken to gather entropy during startup.
 * If value is 0, the number of samples is 2^24 else
 * equals value times 2^8.
 */

#define TRNG_DEF_STARTUP_CYCLES			0

#define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT	16

/*
 * Minimum number of samples taken to regenerate entropy
 * If value is 0, the number of samples is 2^24 else
 * equals value times 2^6.
 */

#define TRNG_DEF_MIN_REFILL_CYCLES		1

#define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT	0

/*
 * Maximum number of samples taken to regenerate entropy
 * If value is 0, the number of samples is 2^24 else
 * equals value times 2^8.
 */

#define TRNG_DEF_MAX_REFILL_CYCLES		0

#define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT	16

/* Number of CLK input cycles between samples */

#define TRNG_DEF_CLK_DIV_CYCLES			0

#define TRNG_CFG_REG_SAMPLE_DIV_SHIFT		8

/* Maximum retries to get rng data */

#define SA_MAX_RNG_DATA_RETRIES			5
/* Delay between retries (in usecs) */

#define SA_RNG_DATA_RETRY_DELAY			5


struct trng_regs {
	
u32	output_l;
	
u32	output_h;
	
u32	status;
	
u32	intmask;
	
u32	intack;
	
u32	control;
	
u32	config;
};


struct ks_sa_rng {
	
struct device	*dev;
	
struct hwrng	rng;
	
struct clk	*clk;
	
struct regmap	*regmap_cfg;
	
struct trng_regs *reg_rng;
};


static int ks_sa_rng_init(struct hwrng *rng) { u32 value; struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); /* Enable RNG module */ regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, SA_CMD_STATUS_REG_TRNG_ENABLE, SA_CMD_STATUS_REG_TRNG_ENABLE); /* Configure RNG module */ writel(0, &ks_sa_rng->reg_rng->control); value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT; writel(value, &ks_sa_rng->reg_rng->control); value = (TRNG_DEF_MIN_REFILL_CYCLES << TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) | (TRNG_DEF_MAX_REFILL_CYCLES << TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) | (TRNG_DEF_CLK_DIV_CYCLES << TRNG_CFG_REG_SAMPLE_DIV_SHIFT); writel(value, &ks_sa_rng->reg_rng->config); /* Disable all interrupts from TRNG */ writel(0, &ks_sa_rng->reg_rng->intmask); /* Enable RNG */ value = readl(&ks_sa_rng->reg_rng->control); value |= TRNG_CNTL_REG_TRNG_ENABLE; writel(value, &ks_sa_rng->reg_rng->control); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov160100.00%1100.00%
Total160100.00%1100.00%


static void ks_sa_rng_cleanup(struct hwrng *rng) { struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); /* Disable RNG */ writel(0, &ks_sa_rng->reg_rng->control); regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, SA_CMD_STATUS_REG_TRNG_ENABLE, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov61100.00%1100.00%
Total61100.00%1100.00%


static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) { struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); /* Read random data */ data[0] = readl(&ks_sa_rng->reg_rng->output_l); data[1] = readl(&ks_sa_rng->reg_rng->output_h); writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack); return sizeof(u32) * 2; }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov90100.00%1100.00%
Total90100.00%1100.00%


static int ks_sa_rng_data_present(struct hwrng *rng, int wait) { struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); u32 ready; int j; for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) { ready = readl(&ks_sa_rng->reg_rng->status); ready &= TRNG_STATUS_REG_READY; if (ready || !wait) break; udelay(SA_RNG_DATA_RETRY_DELAY); } return ready; }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov91100.00%1100.00%
Total91100.00%1100.00%


static int ks_sa_rng_probe(struct platform_device *pdev) { struct ks_sa_rng *ks_sa_rng; struct device *dev = &pdev->dev; int ret; struct resource *mem; ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL); if (!ks_sa_rng) return -ENOMEM; ks_sa_rng->dev = dev; ks_sa_rng->rng = (struct hwrng) { .name = "ks_sa_hwrng", .init = ks_sa_rng_init, .data_read = ks_sa_rng_data_read, .data_present = ks_sa_rng_data_present, .cleanup = ks_sa_rng_cleanup, }; ks_sa_rng->rng.priv = (unsigned long)dev; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ks_sa_rng->reg_rng = devm_ioremap_resource(dev, mem); if (IS_ERR(ks_sa_rng->reg_rng)) return PTR_ERR(ks_sa_rng->reg_rng); ks_sa_rng->regmap_cfg = syscon_regmap_lookup_by_phandle(dev->of_node, "ti,syscon-sa-cfg"); if (IS_ERR(ks_sa_rng->regmap_cfg)) { dev_err(dev, "syscon_node_to_regmap failed\n"); return -EINVAL; } pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "Failed to enable SA power-domain\n"); pm_runtime_disable(dev); return ret; } platform_set_drvdata(pdev, ks_sa_rng); return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng); }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov241100.00%1100.00%
Total241100.00%1100.00%


static int ks_sa_rng_remove(struct platform_device *pdev) { pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov30100.00%1100.00%
Total30100.00%1100.00%

static const struct of_device_id ks_sa_rng_dt_match[] = { { .compatible = "ti,keystone-rng", }, { }, }; MODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match); static struct platform_driver ks_sa_rng_driver = { .driver = { .name = "ks-sa-rng", .of_match_table = ks_sa_rng_dt_match, }, .probe = ks_sa_rng_probe, .remove = ks_sa_rng_remove, }; module_platform_driver(ks_sa_rng_driver); MODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver"); MODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
Vitaly Andrianov915100.00%1100.00%
Total915100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.