162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RNG driver for Freescale RNGA 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. 662306a36Sopenharmony_ci * Author: Alan Carvalho de Assis <acassis@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This driver is based on other RNG drivers. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/hw_random.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* RNGA Registers */ 2362306a36Sopenharmony_ci#define RNGA_CONTROL 0x00 2462306a36Sopenharmony_ci#define RNGA_STATUS 0x04 2562306a36Sopenharmony_ci#define RNGA_ENTROPY 0x08 2662306a36Sopenharmony_ci#define RNGA_OUTPUT_FIFO 0x0c 2762306a36Sopenharmony_ci#define RNGA_MODE 0x10 2862306a36Sopenharmony_ci#define RNGA_VERIFICATION_CONTROL 0x14 2962306a36Sopenharmony_ci#define RNGA_OSC_CONTROL_COUNTER 0x18 3062306a36Sopenharmony_ci#define RNGA_OSC1_COUNTER 0x1c 3162306a36Sopenharmony_ci#define RNGA_OSC2_COUNTER 0x20 3262306a36Sopenharmony_ci#define RNGA_OSC_COUNTER_STATUS 0x24 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* RNGA Registers Range */ 3562306a36Sopenharmony_ci#define RNG_ADDR_RANGE 0x28 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* RNGA Control Register */ 3862306a36Sopenharmony_ci#define RNGA_CONTROL_SLEEP 0x00000010 3962306a36Sopenharmony_ci#define RNGA_CONTROL_CLEAR_INT 0x00000008 4062306a36Sopenharmony_ci#define RNGA_CONTROL_MASK_INTS 0x00000004 4162306a36Sopenharmony_ci#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002 4262306a36Sopenharmony_ci#define RNGA_CONTROL_GO 0x00000001 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define RNGA_STATUS_LEVEL_MASK 0x0000ff00 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* RNGA Status Register */ 4762306a36Sopenharmony_ci#define RNGA_STATUS_OSC_DEAD 0x80000000 4862306a36Sopenharmony_ci#define RNGA_STATUS_SLEEP 0x00000010 4962306a36Sopenharmony_ci#define RNGA_STATUS_ERROR_INT 0x00000008 5062306a36Sopenharmony_ci#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004 5162306a36Sopenharmony_ci#define RNGA_STATUS_LAST_READ_STATUS 0x00000002 5262306a36Sopenharmony_ci#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct mxc_rng { 5562306a36Sopenharmony_ci struct device *dev; 5662306a36Sopenharmony_ci struct hwrng rng; 5762306a36Sopenharmony_ci void __iomem *mem; 5862306a36Sopenharmony_ci struct clk *clk; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int mxc_rnga_data_present(struct hwrng *rng, int wait) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci for (i = 0; i < 20; i++) { 6762306a36Sopenharmony_ci /* how many random numbers are in FIFO? [0-16] */ 6862306a36Sopenharmony_ci int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) & 6962306a36Sopenharmony_ci RNGA_STATUS_LEVEL_MASK) >> 8; 7062306a36Sopenharmony_ci if (level || !wait) 7162306a36Sopenharmony_ci return !!level; 7262306a36Sopenharmony_ci udelay(10); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int mxc_rnga_data_read(struct hwrng *rng, u32 * data) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int err; 8062306a36Sopenharmony_ci u32 ctrl; 8162306a36Sopenharmony_ci struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* retrieve a random number from FIFO */ 8462306a36Sopenharmony_ci *data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* some error while reading this random number? */ 8762306a36Sopenharmony_ci err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* if error: clear error interrupt, but doesn't return random number */ 9062306a36Sopenharmony_ci if (err) { 9162306a36Sopenharmony_ci dev_dbg(mxc_rng->dev, "Error while reading random number!\n"); 9262306a36Sopenharmony_ci ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); 9362306a36Sopenharmony_ci __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT, 9462306a36Sopenharmony_ci mxc_rng->mem + RNGA_CONTROL); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci } else 9762306a36Sopenharmony_ci return 4; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int mxc_rnga_init(struct hwrng *rng) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 ctrl, osc; 10362306a36Sopenharmony_ci struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* wake up */ 10662306a36Sopenharmony_ci ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); 10762306a36Sopenharmony_ci __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* verify if oscillator is working */ 11062306a36Sopenharmony_ci osc = __raw_readl(mxc_rng->mem + RNGA_STATUS); 11162306a36Sopenharmony_ci if (osc & RNGA_STATUS_OSC_DEAD) { 11262306a36Sopenharmony_ci dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n"); 11362306a36Sopenharmony_ci return -ENODEV; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* go running */ 11762306a36Sopenharmony_ci ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); 11862306a36Sopenharmony_ci __raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void mxc_rnga_cleanup(struct hwrng *rng) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 ctrl; 12662306a36Sopenharmony_ci struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* stop rnga */ 13162306a36Sopenharmony_ci __raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int __init mxc_rnga_probe(struct platform_device *pdev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int err; 13762306a36Sopenharmony_ci struct mxc_rng *mxc_rng; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci mxc_rng = devm_kzalloc(&pdev->dev, sizeof(*mxc_rng), GFP_KERNEL); 14062306a36Sopenharmony_ci if (!mxc_rng) 14162306a36Sopenharmony_ci return -ENOMEM; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mxc_rng->dev = &pdev->dev; 14462306a36Sopenharmony_ci mxc_rng->rng.name = "mxc-rnga"; 14562306a36Sopenharmony_ci mxc_rng->rng.init = mxc_rnga_init; 14662306a36Sopenharmony_ci mxc_rng->rng.cleanup = mxc_rnga_cleanup; 14762306a36Sopenharmony_ci mxc_rng->rng.data_present = mxc_rnga_data_present; 14862306a36Sopenharmony_ci mxc_rng->rng.data_read = mxc_rnga_data_read; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci mxc_rng->clk = devm_clk_get(&pdev->dev, NULL); 15162306a36Sopenharmony_ci if (IS_ERR(mxc_rng->clk)) { 15262306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not get rng_clk!\n"); 15362306a36Sopenharmony_ci return PTR_ERR(mxc_rng->clk); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci err = clk_prepare_enable(mxc_rng->clk); 15762306a36Sopenharmony_ci if (err) 15862306a36Sopenharmony_ci return err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mxc_rng->mem = devm_platform_ioremap_resource(pdev, 0); 16162306a36Sopenharmony_ci if (IS_ERR(mxc_rng->mem)) { 16262306a36Sopenharmony_ci err = PTR_ERR(mxc_rng->mem); 16362306a36Sopenharmony_ci goto err_ioremap; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = hwrng_register(&mxc_rng->rng); 16762306a36Sopenharmony_ci if (err) { 16862306a36Sopenharmony_ci dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err); 16962306a36Sopenharmony_ci goto err_ioremap; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cierr_ioremap: 17562306a36Sopenharmony_ci clk_disable_unprepare(mxc_rng->clk); 17662306a36Sopenharmony_ci return err; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int __exit mxc_rnga_remove(struct platform_device *pdev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct mxc_rng *mxc_rng = platform_get_drvdata(pdev); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci hwrng_unregister(&mxc_rng->rng); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci clk_disable_unprepare(mxc_rng->clk); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct of_device_id mxc_rnga_of_match[] = { 19162306a36Sopenharmony_ci { .compatible = "fsl,imx21-rnga", }, 19262306a36Sopenharmony_ci { .compatible = "fsl,imx31-rnga", }, 19362306a36Sopenharmony_ci { /* sentinel */ }, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxc_rnga_of_match); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct platform_driver mxc_rnga_driver = { 19862306a36Sopenharmony_ci .driver = { 19962306a36Sopenharmony_ci .name = "mxc_rnga", 20062306a36Sopenharmony_ci .of_match_table = mxc_rnga_of_match, 20162306a36Sopenharmony_ci }, 20262306a36Sopenharmony_ci .remove = __exit_p(mxc_rnga_remove), 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cimodule_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 20862306a36Sopenharmony_ciMODULE_DESCRIPTION("H/W RNGA driver for i.MX"); 20962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 210