linux/drivers/char/hw_random/imx-rngc.c
<<
>>
Prefs
   1/*
   2 * RNG driver for Freescale RNGC
   3 *
   4 * Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
   5 * Copyright (C) 2017 Martin Kaiser <martin@kaiser.cx>
   6 *
   7 * The code contained herein is licensed under the GNU General Public
   8 * License. You may obtain a copy of the GNU General Public License
   9 * Version 2 or later at the following locations:
  10 *
  11 * http://www.opensource.org/licenses/gpl-license.html
  12 * http://www.gnu.org/copyleft/gpl.html
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/clk.h>
  19#include <linux/err.h>
  20#include <linux/platform_device.h>
  21#include <linux/interrupt.h>
  22#include <linux/hw_random.h>
  23#include <linux/completion.h>
  24#include <linux/io.h>
  25
  26#define RNGC_COMMAND                    0x0004
  27#define RNGC_CONTROL                    0x0008
  28#define RNGC_STATUS                     0x000C
  29#define RNGC_ERROR                      0x0010
  30#define RNGC_FIFO                       0x0014
  31
  32#define RNGC_CMD_CLR_ERR                0x00000020
  33#define RNGC_CMD_CLR_INT                0x00000010
  34#define RNGC_CMD_SEED                   0x00000002
  35#define RNGC_CMD_SELF_TEST              0x00000001
  36
  37#define RNGC_CTRL_MASK_ERROR            0x00000040
  38#define RNGC_CTRL_MASK_DONE             0x00000020
  39
  40#define RNGC_STATUS_ERROR               0x00010000
  41#define RNGC_STATUS_FIFO_LEVEL_MASK     0x00000f00
  42#define RNGC_STATUS_FIFO_LEVEL_SHIFT    8
  43#define RNGC_STATUS_SEED_DONE           0x00000020
  44#define RNGC_STATUS_ST_DONE             0x00000010
  45
  46#define RNGC_ERROR_STATUS_STAT_ERR      0x00000008
  47
  48#define RNGC_TIMEOUT  3000 /* 3 sec */
  49
  50
  51static bool self_test = true;
  52module_param(self_test, bool, 0);
  53
  54struct imx_rngc {
  55        struct device           *dev;
  56        struct clk              *clk;
  57        void __iomem            *base;
  58        struct hwrng            rng;
  59        struct completion       rng_op_done;
  60        /*
  61         * err_reg is written only by the irq handler and read only
  62         * when interrupts are masked, we need no spinlock
  63         */
  64        u32                     err_reg;
  65};
  66
  67
  68static inline void imx_rngc_irq_mask_clear(struct imx_rngc *rngc)
  69{
  70        u32 ctrl, cmd;
  71
  72        /* mask interrupts */
  73        ctrl = readl(rngc->base + RNGC_CONTROL);
  74        ctrl |= RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR;
  75        writel(ctrl, rngc->base + RNGC_CONTROL);
  76
  77        /*
  78         * CLR_INT clears the interrupt only if there's no error
  79         * CLR_ERR clear the interrupt and the error register if there
  80         * is an error
  81         */
  82        cmd = readl(rngc->base + RNGC_COMMAND);
  83        cmd |= RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR;
  84        writel(cmd, rngc->base + RNGC_COMMAND);
  85}
  86
  87static inline void imx_rngc_irq_unmask(struct imx_rngc *rngc)
  88{
  89        u32 ctrl;
  90
  91        ctrl = readl(rngc->base + RNGC_CONTROL);
  92        ctrl &= ~(RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR);
  93        writel(ctrl, rngc->base + RNGC_CONTROL);
  94}
  95
  96static int imx_rngc_self_test(struct imx_rngc *rngc)
  97{
  98        u32 cmd;
  99        int ret;
 100
 101        imx_rngc_irq_unmask(rngc);
 102
 103        /* run self test */
 104        cmd = readl(rngc->base + RNGC_COMMAND);
 105        writel(cmd | RNGC_CMD_SELF_TEST, rngc->base + RNGC_COMMAND);
 106
 107        ret = wait_for_completion_timeout(&rngc->rng_op_done, RNGC_TIMEOUT);
 108        if (!ret) {
 109                imx_rngc_irq_mask_clear(rngc);
 110                return -ETIMEDOUT;
 111        }
 112
 113        if (rngc->err_reg != 0)
 114                return -EIO;
 115
 116        return 0;
 117}
 118
 119static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait)
 120{
 121        struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng);
 122        unsigned int status;
 123        unsigned int level;
 124        int retval = 0;
 125
 126        while (max >= sizeof(u32)) {
 127                status = readl(rngc->base + RNGC_STATUS);
 128
 129                /* is there some error while reading this random number? */
 130                if (status & RNGC_STATUS_ERROR)
 131                        break;
 132
 133                /* how many random numbers are in FIFO? [0-16] */
 134                level = (status & RNGC_STATUS_FIFO_LEVEL_MASK) >>
 135                        RNGC_STATUS_FIFO_LEVEL_SHIFT;
 136
 137                if (level) {
 138                        /* retrieve a random number from FIFO */
 139                        *(u32 *)data = readl(rngc->base + RNGC_FIFO);
 140
 141                        retval += sizeof(u32);
 142                        data += sizeof(u32);
 143                        max -= sizeof(u32);
 144                }
 145        }
 146
 147        return retval ? retval : -EIO;
 148}
 149
 150static irqreturn_t imx_rngc_irq(int irq, void *priv)
 151{
 152        struct imx_rngc *rngc = (struct imx_rngc *)priv;
 153        u32 status;
 154
 155        /*
 156         * clearing the interrupt will also clear the error register
 157         * read error and status before clearing
 158         */
 159        status = readl(rngc->base + RNGC_STATUS);
 160        rngc->err_reg = readl(rngc->base + RNGC_ERROR);
 161
 162        imx_rngc_irq_mask_clear(rngc);
 163
 164        if (status & (RNGC_STATUS_SEED_DONE | RNGC_STATUS_ST_DONE))
 165                complete(&rngc->rng_op_done);
 166
 167        return IRQ_HANDLED;
 168}
 169
 170static int imx_rngc_init(struct hwrng *rng)
 171{
 172        struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng);
 173        u32 cmd;
 174        int ret;
 175
 176        /* clear error */
 177        cmd = readl(rngc->base + RNGC_COMMAND);
 178        writel(cmd | RNGC_CMD_CLR_ERR, rngc->base + RNGC_COMMAND);
 179
 180        /* create seed, repeat while there is some statistical error */
 181        do {
 182                imx_rngc_irq_unmask(rngc);
 183
 184                /* seed creation */
 185                cmd = readl(rngc->base + RNGC_COMMAND);
 186                writel(cmd | RNGC_CMD_SEED, rngc->base + RNGC_COMMAND);
 187
 188                ret = wait_for_completion_timeout(&rngc->rng_op_done,
 189                                RNGC_TIMEOUT);
 190
 191                if (!ret) {
 192                        imx_rngc_irq_mask_clear(rngc);
 193                        return -ETIMEDOUT;
 194                }
 195
 196        } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR);
 197
 198        return rngc->err_reg ? -EIO : 0;
 199}
 200
 201static int imx_rngc_probe(struct platform_device *pdev)
 202{
 203        struct imx_rngc *rngc;
 204        struct resource *res;
 205        int ret;
 206        int irq;
 207
 208        rngc = devm_kzalloc(&pdev->dev, sizeof(*rngc), GFP_KERNEL);
 209        if (!rngc)
 210                return -ENOMEM;
 211
 212        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 213        rngc->base = devm_ioremap_resource(&pdev->dev, res);
 214        if (IS_ERR(rngc->base))
 215                return PTR_ERR(rngc->base);
 216
 217        rngc->clk = devm_clk_get(&pdev->dev, NULL);
 218        if (IS_ERR(rngc->clk)) {
 219                dev_err(&pdev->dev, "Can not get rng_clk\n");
 220                return PTR_ERR(rngc->clk);
 221        }
 222
 223        irq = platform_get_irq(pdev, 0);
 224        if (irq <= 0) {
 225                dev_err(&pdev->dev, "Couldn't get irq %d\n", irq);
 226                return irq;
 227        }
 228
 229        ret = clk_prepare_enable(rngc->clk);
 230        if (ret)
 231                return ret;
 232
 233        ret = devm_request_irq(&pdev->dev,
 234                        irq, imx_rngc_irq, 0, pdev->name, (void *)rngc);
 235        if (ret) {
 236                dev_err(rngc->dev, "Can't get interrupt working.\n");
 237                goto err;
 238        }
 239
 240        init_completion(&rngc->rng_op_done);
 241
 242        rngc->rng.name = pdev->name;
 243        rngc->rng.init = imx_rngc_init;
 244        rngc->rng.read = imx_rngc_read;
 245
 246        rngc->dev = &pdev->dev;
 247        platform_set_drvdata(pdev, rngc);
 248
 249        imx_rngc_irq_mask_clear(rngc);
 250
 251        if (self_test) {
 252                ret = imx_rngc_self_test(rngc);
 253                if (ret) {
 254                        dev_err(rngc->dev, "FSL RNGC self test failed.\n");
 255                        goto err;
 256                }
 257        }
 258
 259        ret = hwrng_register(&rngc->rng);
 260        if (ret) {
 261                dev_err(&pdev->dev, "FSL RNGC registering failed (%d)\n", ret);
 262                goto err;
 263        }
 264
 265        dev_info(&pdev->dev, "Freescale RNGC registered.\n");
 266        return 0;
 267
 268err:
 269        clk_disable_unprepare(rngc->clk);
 270
 271        return ret;
 272}
 273
 274static int __exit imx_rngc_remove(struct platform_device *pdev)
 275{
 276        struct imx_rngc *rngc = platform_get_drvdata(pdev);
 277
 278        hwrng_unregister(&rngc->rng);
 279
 280        clk_disable_unprepare(rngc->clk);
 281
 282        return 0;
 283}
 284
 285static int __maybe_unused imx_rngc_suspend(struct device *dev)
 286{
 287        struct imx_rngc *rngc = dev_get_drvdata(dev);
 288
 289        clk_disable_unprepare(rngc->clk);
 290
 291        return 0;
 292}
 293
 294static int __maybe_unused imx_rngc_resume(struct device *dev)
 295{
 296        struct imx_rngc *rngc = dev_get_drvdata(dev);
 297
 298        clk_prepare_enable(rngc->clk);
 299
 300        return 0;
 301}
 302
 303static SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume);
 304
 305static const struct of_device_id imx_rngc_dt_ids[] = {
 306        { .compatible = "fsl,imx25-rngb", .data = NULL, },
 307        { /* sentinel */ }
 308};
 309MODULE_DEVICE_TABLE(of, imx_rngc_dt_ids);
 310
 311static struct platform_driver imx_rngc_driver = {
 312        .driver = {
 313                .name = "imx_rngc",
 314                .pm = &imx_rngc_pm_ops,
 315                .of_match_table = imx_rngc_dt_ids,
 316        },
 317        .remove = __exit_p(imx_rngc_remove),
 318};
 319
 320module_platform_driver_probe(imx_rngc_driver, imx_rngc_probe);
 321
 322MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 323MODULE_DESCRIPTION("H/W RNGC driver for i.MX");
 324MODULE_LICENSE("GPL");
 325