Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family
0003  *
0004  * Copyright (C) 2009 Nokia Corporation
0005  * Author: Juha Yrjola <juha.yrjola@solidboot.com>
0006  *
0007  * Copyright (C) 2013 Pali Rohár <pali@kernel.org>
0008  *
0009  * This file is licensed under  the terms of the GNU General Public
0010  * License version 2. This program is licensed "as is" without any
0011  * warranty of any kind, whether express or implied.
0012  */
0013 
0014 #include <linux/module.h>
0015 #include <linux/init.h>
0016 #include <linux/random.h>
0017 #include <linux/hw_random.h>
0018 #include <linux/workqueue.h>
0019 #include <linux/clk.h>
0020 #include <linux/err.h>
0021 #include <linux/io.h>
0022 #include <linux/of.h>
0023 #include <linux/of_device.h>
0024 #include <linux/platform_device.h>
0025 #include <linux/pm_runtime.h>
0026 
0027 #define RNG_RESET           0x01
0028 #define RNG_GEN_PRNG_HW_INIT        0x02
0029 #define RNG_GEN_HW          0x08
0030 
0031 struct omap_rom_rng {
0032     struct clk *clk;
0033     struct device *dev;
0034     struct hwrng ops;
0035     u32 (*rom_rng_call)(u32 ptr, u32 count, u32 flag);
0036 };
0037 
0038 static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w)
0039 {
0040     struct omap_rom_rng *ddata;
0041     u32 ptr;
0042     int r;
0043 
0044     ddata = (struct omap_rom_rng *)rng->priv;
0045 
0046     r = pm_runtime_get_sync(ddata->dev);
0047     if (r < 0) {
0048         pm_runtime_put_noidle(ddata->dev);
0049 
0050         return r;
0051     }
0052 
0053     ptr = virt_to_phys(data);
0054     r = ddata->rom_rng_call(ptr, 4, RNG_GEN_HW);
0055     if (r != 0)
0056         r = -EINVAL;
0057     else
0058         r = 4;
0059 
0060     pm_runtime_mark_last_busy(ddata->dev);
0061     pm_runtime_put_autosuspend(ddata->dev);
0062 
0063     return r;
0064 }
0065 
0066 static int __maybe_unused omap_rom_rng_runtime_suspend(struct device *dev)
0067 {
0068     struct omap_rom_rng *ddata;
0069     int r;
0070 
0071     ddata = dev_get_drvdata(dev);
0072 
0073     r = ddata->rom_rng_call(0, 0, RNG_RESET);
0074     if (r != 0)
0075         dev_err(dev, "reset failed: %d\n", r);
0076 
0077     clk_disable_unprepare(ddata->clk);
0078 
0079     return 0;
0080 }
0081 
0082 static int __maybe_unused omap_rom_rng_runtime_resume(struct device *dev)
0083 {
0084     struct omap_rom_rng *ddata;
0085     int r;
0086 
0087     ddata = dev_get_drvdata(dev);
0088 
0089     r = clk_prepare_enable(ddata->clk);
0090     if (r < 0)
0091         return r;
0092 
0093     r = ddata->rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT);
0094     if (r != 0) {
0095         clk_disable_unprepare(ddata->clk);
0096         dev_err(dev, "HW init failed: %d\n", r);
0097 
0098         return -EIO;
0099     }
0100 
0101     return 0;
0102 }
0103 
0104 static void omap_rom_rng_finish(void *data)
0105 {
0106     struct omap_rom_rng *ddata = data;
0107 
0108     pm_runtime_dont_use_autosuspend(ddata->dev);
0109     pm_runtime_disable(ddata->dev);
0110 }
0111 
0112 static int omap3_rom_rng_probe(struct platform_device *pdev)
0113 {
0114     struct omap_rom_rng *ddata;
0115     int ret = 0;
0116 
0117     ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0118     if (!ddata)
0119         return -ENOMEM;
0120 
0121     ddata->dev = &pdev->dev;
0122     ddata->ops.priv = (unsigned long)ddata;
0123     ddata->ops.name = "omap3-rom";
0124     ddata->ops.read = of_device_get_match_data(&pdev->dev);
0125     ddata->ops.quality = 900;
0126     if (!ddata->ops.read) {
0127         dev_err(&pdev->dev, "missing rom code handler\n");
0128 
0129         return -ENODEV;
0130     }
0131     dev_set_drvdata(ddata->dev, ddata);
0132 
0133     ddata->rom_rng_call = pdev->dev.platform_data;
0134     if (!ddata->rom_rng_call) {
0135         dev_err(ddata->dev, "rom_rng_call is NULL\n");
0136         return -EINVAL;
0137     }
0138 
0139     ddata->clk = devm_clk_get(ddata->dev, "ick");
0140     if (IS_ERR(ddata->clk)) {
0141         dev_err(ddata->dev, "unable to get RNG clock\n");
0142         return PTR_ERR(ddata->clk);
0143     }
0144 
0145     pm_runtime_enable(&pdev->dev);
0146     pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
0147     pm_runtime_use_autosuspend(&pdev->dev);
0148 
0149     ret = devm_add_action_or_reset(ddata->dev, omap_rom_rng_finish,
0150                        ddata);
0151     if (ret)
0152         return ret;
0153 
0154     return devm_hwrng_register(ddata->dev, &ddata->ops);
0155 }
0156 
0157 static const struct of_device_id omap_rom_rng_match[] = {
0158     { .compatible = "nokia,n900-rom-rng", .data = omap3_rom_rng_read, },
0159     { /* sentinel */ },
0160 };
0161 MODULE_DEVICE_TABLE(of, omap_rom_rng_match);
0162 
0163 static const struct dev_pm_ops omap_rom_rng_pm_ops = {
0164     SET_SYSTEM_SLEEP_PM_OPS(omap_rom_rng_runtime_suspend,
0165                 omap_rom_rng_runtime_resume)
0166 };
0167 
0168 static struct platform_driver omap3_rom_rng_driver = {
0169     .driver = {
0170         .name       = "omap3-rom-rng",
0171         .of_match_table = omap_rom_rng_match,
0172         .pm = &omap_rom_rng_pm_ops,
0173     },
0174     .probe      = omap3_rom_rng_probe,
0175 };
0176 
0177 module_platform_driver(omap3_rom_rng_driver);
0178 
0179 MODULE_ALIAS("platform:omap3-rom-rng");
0180 MODULE_AUTHOR("Juha Yrjola");
0181 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
0182 MODULE_LICENSE("GPL");