1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2006-2007 PA Semi, Inc |
4 | * |
5 | * Maintained by: Olof Johansson <olof@lixom.net> |
6 | * |
7 | * Driver for the PWRficient onchip rng |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/hw_random.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/io.h> |
17 | |
18 | #define SDCRNG_CTL_REG 0x00 |
19 | #define SDCRNG_CTL_FVLD_M 0x0000f000 |
20 | #define SDCRNG_CTL_FVLD_S 12 |
21 | #define SDCRNG_CTL_KSZ 0x00000800 |
22 | #define SDCRNG_CTL_RSRC_CRG 0x00000010 |
23 | #define SDCRNG_CTL_RSRC_RRG 0x00000000 |
24 | #define SDCRNG_CTL_CE 0x00000004 |
25 | #define SDCRNG_CTL_RE 0x00000002 |
26 | #define SDCRNG_CTL_DR 0x00000001 |
27 | #define SDCRNG_CTL_SELECT_RRG_RNG (SDCRNG_CTL_RE | SDCRNG_CTL_RSRC_RRG) |
28 | #define SDCRNG_CTL_SELECT_CRG_RNG (SDCRNG_CTL_CE | SDCRNG_CTL_RSRC_CRG) |
29 | #define SDCRNG_VAL_REG 0x20 |
30 | |
31 | #define MODULE_NAME "pasemi_rng" |
32 | |
33 | static int pasemi_rng_data_present(struct hwrng *rng, int wait) |
34 | { |
35 | void __iomem *rng_regs = (void __iomem *)rng->priv; |
36 | int data, i; |
37 | |
38 | for (i = 0; i < 20; i++) { |
39 | data = (in_le32(rng_regs + SDCRNG_CTL_REG) |
40 | & SDCRNG_CTL_FVLD_M) ? 1 : 0; |
41 | if (data || !wait) |
42 | break; |
43 | udelay(10); |
44 | } |
45 | return data; |
46 | } |
47 | |
48 | static int pasemi_rng_data_read(struct hwrng *rng, u32 *data) |
49 | { |
50 | void __iomem *rng_regs = (void __iomem *)rng->priv; |
51 | *data = in_le32(rng_regs + SDCRNG_VAL_REG); |
52 | return 4; |
53 | } |
54 | |
55 | static int pasemi_rng_init(struct hwrng *rng) |
56 | { |
57 | void __iomem *rng_regs = (void __iomem *)rng->priv; |
58 | u32 ctl; |
59 | |
60 | ctl = SDCRNG_CTL_DR | SDCRNG_CTL_SELECT_RRG_RNG | SDCRNG_CTL_KSZ; |
61 | out_le32(rng_regs + SDCRNG_CTL_REG, ctl); |
62 | out_le32(rng_regs + SDCRNG_CTL_REG, ctl & ~SDCRNG_CTL_DR); |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | static void pasemi_rng_cleanup(struct hwrng *rng) |
68 | { |
69 | void __iomem *rng_regs = (void __iomem *)rng->priv; |
70 | u32 ctl; |
71 | |
72 | ctl = SDCRNG_CTL_RE | SDCRNG_CTL_CE; |
73 | out_le32(rng_regs + SDCRNG_CTL_REG, |
74 | in_le32(rng_regs + SDCRNG_CTL_REG) & ~ctl); |
75 | } |
76 | |
77 | static struct hwrng pasemi_rng = { |
78 | .name = MODULE_NAME, |
79 | .init = pasemi_rng_init, |
80 | .cleanup = pasemi_rng_cleanup, |
81 | .data_present = pasemi_rng_data_present, |
82 | .data_read = pasemi_rng_data_read, |
83 | }; |
84 | |
85 | static int rng_probe(struct platform_device *pdev) |
86 | { |
87 | void __iomem *rng_regs; |
88 | |
89 | rng_regs = devm_platform_ioremap_resource(pdev, index: 0); |
90 | if (IS_ERR(ptr: rng_regs)) |
91 | return PTR_ERR(ptr: rng_regs); |
92 | |
93 | pasemi_rng.priv = (unsigned long)rng_regs; |
94 | |
95 | pr_info("Registering PA Semi RNG\n" ); |
96 | return devm_hwrng_register(dev: &pdev->dev, rng: &pasemi_rng); |
97 | } |
98 | |
99 | static const struct of_device_id rng_match[] = { |
100 | { .compatible = "1682m-rng" , }, |
101 | { .compatible = "pasemi,pwrficient-rng" , }, |
102 | { }, |
103 | }; |
104 | MODULE_DEVICE_TABLE(of, rng_match); |
105 | |
106 | static struct platform_driver rng_driver = { |
107 | .driver = { |
108 | .name = "pasemi-rng" , |
109 | .of_match_table = rng_match, |
110 | }, |
111 | .probe = rng_probe, |
112 | }; |
113 | |
114 | module_platform_driver(rng_driver); |
115 | |
116 | MODULE_LICENSE("GPL" ); |
117 | MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>" ); |
118 | MODULE_DESCRIPTION("H/W RNG driver for PA Semi processor" ); |
119 | |