232 lines · c
1/*2 * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>3 *4 * This file is licensed under the terms of the GNU General Public5 * License version 2. This program is licensed "as is" without any6 * warranty of any kind, whether express or implied.7 */8 9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/mod_devicetable.h>12#include <linux/slab.h>13#include <linux/err.h>14#include <linux/clk.h>15#include <linux/io.h>16#include <linux/iopoll.h>17#include <linux/hw_random.h>18#include <linux/of.h>19#include <linux/platform_device.h>20#include <linux/pm_runtime.h>21 22#define TRNG_CR 0x0023#define TRNG_MR 0x0424#define TRNG_ISR 0x1c25#define TRNG_ISR_DATRDY BIT(0)26#define TRNG_ODATA 0x5027 28#define TRNG_KEY 0x524e4700 /* RNG */29 30#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */31 32struct atmel_trng_data {33 bool has_half_rate;34};35 36struct atmel_trng {37 struct clk *clk;38 void __iomem *base;39 struct hwrng rng;40 bool has_half_rate;41};42 43static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)44{45 int ready;46 47 ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;48 if (!ready && wait)49 readl_poll_timeout(trng->base + TRNG_ISR, ready,50 ready & TRNG_ISR_DATRDY, 1000, 20000);51 52 return !!ready;53}54 55static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,56 bool wait)57{58 struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);59 u32 *data = buf;60 int ret;61 62 ret = pm_runtime_get_sync((struct device *)trng->rng.priv);63 if (ret < 0) {64 pm_runtime_put_sync((struct device *)trng->rng.priv);65 return ret;66 }67 68 ret = atmel_trng_wait_ready(trng, wait);69 if (!ret)70 goto out;71 72 *data = readl(trng->base + TRNG_ODATA);73 /*74 * ensure data ready is only set again AFTER the next data word is ready75 * in case it got set between checking ISR and reading ODATA, so we76 * don't risk re-reading the same word77 */78 readl(trng->base + TRNG_ISR);79 ret = 4;80 81out:82 pm_runtime_mark_last_busy((struct device *)trng->rng.priv);83 pm_runtime_put_sync_autosuspend((struct device *)trng->rng.priv);84 return ret;85}86 87static int atmel_trng_init(struct atmel_trng *trng)88{89 unsigned long rate;90 int ret;91 92 ret = clk_prepare_enable(trng->clk);93 if (ret)94 return ret;95 96 if (trng->has_half_rate) {97 rate = clk_get_rate(trng->clk);98 99 /* if peripheral clk is above 100MHz, set HALFR */100 if (rate > 100000000)101 writel(TRNG_HALFR, trng->base + TRNG_MR);102 }103 104 writel(TRNG_KEY | 1, trng->base + TRNG_CR);105 106 return 0;107}108 109static void atmel_trng_cleanup(struct atmel_trng *trng)110{111 writel(TRNG_KEY, trng->base + TRNG_CR);112 clk_disable_unprepare(trng->clk);113}114 115static int atmel_trng_probe(struct platform_device *pdev)116{117 struct atmel_trng *trng;118 const struct atmel_trng_data *data;119 int ret;120 121 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);122 if (!trng)123 return -ENOMEM;124 125 trng->base = devm_platform_ioremap_resource(pdev, 0);126 if (IS_ERR(trng->base))127 return PTR_ERR(trng->base);128 129 trng->clk = devm_clk_get(&pdev->dev, NULL);130 if (IS_ERR(trng->clk))131 return PTR_ERR(trng->clk);132 data = of_device_get_match_data(&pdev->dev);133 if (!data)134 return -ENODEV;135 136 trng->has_half_rate = data->has_half_rate;137 trng->rng.name = pdev->name;138 trng->rng.read = atmel_trng_read;139 trng->rng.priv = (unsigned long)&pdev->dev;140 platform_set_drvdata(pdev, trng);141 142#ifndef CONFIG_PM143 ret = atmel_trng_init(trng);144 if (ret)145 return ret;146#endif147 148 pm_runtime_set_autosuspend_delay(&pdev->dev, 100);149 pm_runtime_use_autosuspend(&pdev->dev);150 pm_runtime_enable(&pdev->dev);151 152 ret = devm_hwrng_register(&pdev->dev, &trng->rng);153 if (ret) {154 pm_runtime_disable(&pdev->dev);155 pm_runtime_set_suspended(&pdev->dev);156#ifndef CONFIG_PM157 atmel_trng_cleanup(trng);158#endif159 }160 161 return ret;162}163 164static void atmel_trng_remove(struct platform_device *pdev)165{166 struct atmel_trng *trng = platform_get_drvdata(pdev);167 168 atmel_trng_cleanup(trng);169 pm_runtime_disable(&pdev->dev);170 pm_runtime_set_suspended(&pdev->dev);171}172 173static int __maybe_unused atmel_trng_runtime_suspend(struct device *dev)174{175 struct atmel_trng *trng = dev_get_drvdata(dev);176 177 atmel_trng_cleanup(trng);178 179 return 0;180}181 182static int __maybe_unused atmel_trng_runtime_resume(struct device *dev)183{184 struct atmel_trng *trng = dev_get_drvdata(dev);185 186 return atmel_trng_init(trng);187}188 189static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {190 SET_RUNTIME_PM_OPS(atmel_trng_runtime_suspend,191 atmel_trng_runtime_resume, NULL)192 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,193 pm_runtime_force_resume)194};195 196static const struct atmel_trng_data at91sam9g45_config = {197 .has_half_rate = false,198};199 200static const struct atmel_trng_data sam9x60_config = {201 .has_half_rate = true,202};203 204static const struct of_device_id atmel_trng_dt_ids[] = {205 {206 .compatible = "atmel,at91sam9g45-trng",207 .data = &at91sam9g45_config,208 }, {209 .compatible = "microchip,sam9x60-trng",210 .data = &sam9x60_config,211 }, {212 /* sentinel */213 }214};215MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);216 217static struct platform_driver atmel_trng_driver = {218 .probe = atmel_trng_probe,219 .remove_new = atmel_trng_remove,220 .driver = {221 .name = "atmel-trng",222 .pm = pm_ptr(&atmel_trng_pm_ops),223 .of_match_table = atmel_trng_dt_ids,224 },225};226 227module_platform_driver(atmel_trng_driver);228 229MODULE_LICENSE("GPL");230MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");231MODULE_DESCRIPTION("Atmel true random number generator driver");232