162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RNG driver for Freescale RNGC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2017 Martin Kaiser <martin@kaiser.cx> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/hw_random.h> 1862306a36Sopenharmony_ci#include <linux/completion.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/bitfield.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define RNGC_VER_ID 0x0000 2362306a36Sopenharmony_ci#define RNGC_COMMAND 0x0004 2462306a36Sopenharmony_ci#define RNGC_CONTROL 0x0008 2562306a36Sopenharmony_ci#define RNGC_STATUS 0x000C 2662306a36Sopenharmony_ci#define RNGC_ERROR 0x0010 2762306a36Sopenharmony_ci#define RNGC_FIFO 0x0014 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* the fields in the ver id register */ 3062306a36Sopenharmony_ci#define RNG_TYPE GENMASK(31, 28) 3162306a36Sopenharmony_ci#define RNGC_VER_MAJ_SHIFT 8 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* the rng_type field */ 3462306a36Sopenharmony_ci#define RNGC_TYPE_RNGB 0x1 3562306a36Sopenharmony_ci#define RNGC_TYPE_RNGC 0x2 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define RNGC_CMD_CLR_ERR BIT(5) 3962306a36Sopenharmony_ci#define RNGC_CMD_CLR_INT BIT(4) 4062306a36Sopenharmony_ci#define RNGC_CMD_SEED BIT(1) 4162306a36Sopenharmony_ci#define RNGC_CMD_SELF_TEST BIT(0) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define RNGC_CTRL_MASK_ERROR BIT(6) 4462306a36Sopenharmony_ci#define RNGC_CTRL_MASK_DONE BIT(5) 4562306a36Sopenharmony_ci#define RNGC_CTRL_AUTO_SEED BIT(4) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define RNGC_STATUS_ERROR BIT(16) 4862306a36Sopenharmony_ci#define RNGC_STATUS_FIFO_LEVEL_MASK GENMASK(11, 8) 4962306a36Sopenharmony_ci#define RNGC_STATUS_SEED_DONE BIT(5) 5062306a36Sopenharmony_ci#define RNGC_STATUS_ST_DONE BIT(4) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define RNGC_TIMEOUT 3000 /* 3 sec */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic bool self_test = true; 5862306a36Sopenharmony_cimodule_param(self_test, bool, 0); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct imx_rngc { 6162306a36Sopenharmony_ci struct device *dev; 6262306a36Sopenharmony_ci struct clk *clk; 6362306a36Sopenharmony_ci void __iomem *base; 6462306a36Sopenharmony_ci struct hwrng rng; 6562306a36Sopenharmony_ci struct completion rng_op_done; 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * err_reg is written only by the irq handler and read only 6862306a36Sopenharmony_ci * when interrupts are masked, we need no spinlock 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci u32 err_reg; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic inline void imx_rngc_irq_mask_clear(struct imx_rngc *rngc) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci u32 ctrl, cmd; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* mask interrupts */ 7962306a36Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 8062306a36Sopenharmony_ci ctrl |= RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR; 8162306a36Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * CLR_INT clears the interrupt only if there's no error 8562306a36Sopenharmony_ci * CLR_ERR clear the interrupt and the error register if there 8662306a36Sopenharmony_ci * is an error 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 8962306a36Sopenharmony_ci cmd |= RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR; 9062306a36Sopenharmony_ci writel(cmd, rngc->base + RNGC_COMMAND); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic inline void imx_rngc_irq_unmask(struct imx_rngc *rngc) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u32 ctrl; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 9862306a36Sopenharmony_ci ctrl &= ~(RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR); 9962306a36Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int imx_rngc_self_test(struct imx_rngc *rngc) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u32 cmd; 10562306a36Sopenharmony_ci int ret; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci imx_rngc_irq_unmask(rngc); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* run self test */ 11062306a36Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 11162306a36Sopenharmony_ci writel(cmd | RNGC_CMD_SELF_TEST, rngc->base + RNGC_COMMAND); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); 11462306a36Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 11562306a36Sopenharmony_ci if (!ret) 11662306a36Sopenharmony_ci return -ETIMEDOUT; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return rngc->err_reg ? -EIO : 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 12462306a36Sopenharmony_ci unsigned int status; 12562306a36Sopenharmony_ci int retval = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while (max >= sizeof(u32)) { 12862306a36Sopenharmony_ci status = readl(rngc->base + RNGC_STATUS); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* is there some error while reading this random number? */ 13162306a36Sopenharmony_ci if (status & RNGC_STATUS_ERROR) 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (status & RNGC_STATUS_FIFO_LEVEL_MASK) { 13562306a36Sopenharmony_ci /* retrieve a random number from FIFO */ 13662306a36Sopenharmony_ci *(u32 *)data = readl(rngc->base + RNGC_FIFO); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci retval += sizeof(u32); 13962306a36Sopenharmony_ci data += sizeof(u32); 14062306a36Sopenharmony_ci max -= sizeof(u32); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return retval ? retval : -EIO; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic irqreturn_t imx_rngc_irq(int irq, void *priv) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct imx_rngc *rngc = (struct imx_rngc *)priv; 15062306a36Sopenharmony_ci u32 status; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * clearing the interrupt will also clear the error register 15462306a36Sopenharmony_ci * read error and status before clearing 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci status = readl(rngc->base + RNGC_STATUS); 15762306a36Sopenharmony_ci rngc->err_reg = readl(rngc->base + RNGC_ERROR); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE)) 16262306a36Sopenharmony_ci complete(&rngc->rng_op_done); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return IRQ_HANDLED; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int imx_rngc_init(struct hwrng *rng) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 17062306a36Sopenharmony_ci u32 cmd, ctrl; 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* clear error */ 17462306a36Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 17562306a36Sopenharmony_ci writel(cmd | RNGC_CMD_CLR_ERR, rngc->base + RNGC_COMMAND); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci imx_rngc_irq_unmask(rngc); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* create seed, repeat while there is some statistical error */ 18062306a36Sopenharmony_ci do { 18162306a36Sopenharmony_ci /* seed creation */ 18262306a36Sopenharmony_ci cmd = readl(rngc->base + RNGC_COMMAND); 18362306a36Sopenharmony_ci writel(cmd | RNGC_CMD_SEED, rngc->base + RNGC_COMMAND); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_TIMEOUT)); 18662306a36Sopenharmony_ci if (!ret) { 18762306a36Sopenharmony_ci ret = -ETIMEDOUT; 18862306a36Sopenharmony_ci goto err; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (rngc->err_reg) { 19462306a36Sopenharmony_ci ret = -EIO; 19562306a36Sopenharmony_ci goto err; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * enable automatic seeding, the rngc creates a new seed automatically 20062306a36Sopenharmony_ci * after serving 2^20 random 160-bit words 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci ctrl = readl(rngc->base + RNGC_CONTROL); 20362306a36Sopenharmony_ci ctrl |= RNGC_CTRL_AUTO_SEED; 20462306a36Sopenharmony_ci writel(ctrl, rngc->base + RNGC_CONTROL); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * if initialisation was successful, we keep the interrupt 20862306a36Sopenharmony_ci * unmasked until imx_rngc_cleanup is called 20962306a36Sopenharmony_ci * we mask the interrupt ourselves if we return an error 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cierr: 21462306a36Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void imx_rngc_cleanup(struct hwrng *rng) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int __init imx_rngc_probe(struct platform_device *pdev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct imx_rngc *rngc; 22862306a36Sopenharmony_ci int ret; 22962306a36Sopenharmony_ci int irq; 23062306a36Sopenharmony_ci u32 ver_id; 23162306a36Sopenharmony_ci u8 rng_type; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci rngc = devm_kzalloc(&pdev->dev, sizeof(*rngc), GFP_KERNEL); 23462306a36Sopenharmony_ci if (!rngc) 23562306a36Sopenharmony_ci return -ENOMEM; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rngc->base = devm_platform_ioremap_resource(pdev, 0); 23862306a36Sopenharmony_ci if (IS_ERR(rngc->base)) 23962306a36Sopenharmony_ci return PTR_ERR(rngc->base); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rngc->clk = devm_clk_get_enabled(&pdev->dev, NULL); 24262306a36Sopenharmony_ci if (IS_ERR(rngc->clk)) 24362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(rngc->clk), "Cannot get rng_clk\n"); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 24662306a36Sopenharmony_ci if (irq < 0) 24762306a36Sopenharmony_ci return irq; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ver_id = readl(rngc->base + RNGC_VER_ID); 25062306a36Sopenharmony_ci rng_type = FIELD_GET(RNG_TYPE, ver_id); 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * This driver supports only RNGC and RNGB. (There's a different 25362306a36Sopenharmony_ci * driver for RNGA.) 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) 25662306a36Sopenharmony_ci return -ENODEV; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci init_completion(&rngc->rng_op_done); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci rngc->rng.name = pdev->name; 26162306a36Sopenharmony_ci rngc->rng.init = imx_rngc_init; 26262306a36Sopenharmony_ci rngc->rng.read = imx_rngc_read; 26362306a36Sopenharmony_ci rngc->rng.cleanup = imx_rngc_cleanup; 26462306a36Sopenharmony_ci rngc->rng.quality = 19; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci rngc->dev = &pdev->dev; 26762306a36Sopenharmony_ci platform_set_drvdata(pdev, rngc); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci imx_rngc_irq_mask_clear(rngc); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, 27262306a36Sopenharmony_ci irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n"); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (self_test) { 27762306a36Sopenharmony_ci ret = imx_rngc_self_test(rngc); 27862306a36Sopenharmony_ci if (ret) 27962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "self test failed\n"); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = devm_hwrng_register(&pdev->dev, &rngc->rng); 28362306a36Sopenharmony_ci if (ret) 28462306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, "hwrng registration failed\n"); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci dev_info(&pdev->dev, 28762306a36Sopenharmony_ci "Freescale RNG%c registered (HW revision %d.%02d)\n", 28862306a36Sopenharmony_ci rng_type == RNGC_TYPE_RNGB ? 'B' : 'C', 28962306a36Sopenharmony_ci (ver_id >> RNGC_VER_MAJ_SHIFT) & 0xff, ver_id & 0xff); 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int imx_rngc_suspend(struct device *dev) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct imx_rngc *rngc = dev_get_drvdata(dev); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci clk_disable_unprepare(rngc->clk); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int imx_rngc_resume(struct device *dev) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct imx_rngc *rngc = dev_get_drvdata(dev); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci clk_prepare_enable(rngc->clk); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic const struct of_device_id imx_rngc_dt_ids[] = { 31462306a36Sopenharmony_ci { .compatible = "fsl,imx25-rngb" }, 31562306a36Sopenharmony_ci { /* sentinel */ } 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_rngc_dt_ids); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic struct platform_driver imx_rngc_driver = { 32062306a36Sopenharmony_ci .driver = { 32162306a36Sopenharmony_ci .name = KBUILD_MODNAME, 32262306a36Sopenharmony_ci .pm = pm_sleep_ptr(&imx_rngc_pm_ops), 32362306a36Sopenharmony_ci .of_match_table = imx_rngc_dt_ids, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cimodule_platform_driver_probe(imx_rngc_driver, imx_rngc_probe); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 33062306a36Sopenharmony_ciMODULE_DESCRIPTION("H/W RNGC driver for i.MX"); 33162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 332