0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/module.h>
0011 #include <linux/kernel.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/hw_random.h>
0014 #include <linux/delay.h>
0015 #include <linux/of_address.h>
0016 #include <linux/of_platform.h>
0017 #include <linux/io.h>
0018
0019 #define SDCRNG_CTL_REG 0x00
0020 #define SDCRNG_CTL_FVLD_M 0x0000f000
0021 #define SDCRNG_CTL_FVLD_S 12
0022 #define SDCRNG_CTL_KSZ 0x00000800
0023 #define SDCRNG_CTL_RSRC_CRG 0x00000010
0024 #define SDCRNG_CTL_RSRC_RRG 0x00000000
0025 #define SDCRNG_CTL_CE 0x00000004
0026 #define SDCRNG_CTL_RE 0x00000002
0027 #define SDCRNG_CTL_DR 0x00000001
0028 #define SDCRNG_CTL_SELECT_RRG_RNG (SDCRNG_CTL_RE | SDCRNG_CTL_RSRC_RRG)
0029 #define SDCRNG_CTL_SELECT_CRG_RNG (SDCRNG_CTL_CE | SDCRNG_CTL_RSRC_CRG)
0030 #define SDCRNG_VAL_REG 0x20
0031
0032 #define MODULE_NAME "pasemi_rng"
0033
0034 static int pasemi_rng_data_present(struct hwrng *rng, int wait)
0035 {
0036 void __iomem *rng_regs = (void __iomem *)rng->priv;
0037 int data, i;
0038
0039 for (i = 0; i < 20; i++) {
0040 data = (in_le32(rng_regs + SDCRNG_CTL_REG)
0041 & SDCRNG_CTL_FVLD_M) ? 1 : 0;
0042 if (data || !wait)
0043 break;
0044 udelay(10);
0045 }
0046 return data;
0047 }
0048
0049 static int pasemi_rng_data_read(struct hwrng *rng, u32 *data)
0050 {
0051 void __iomem *rng_regs = (void __iomem *)rng->priv;
0052 *data = in_le32(rng_regs + SDCRNG_VAL_REG);
0053 return 4;
0054 }
0055
0056 static int pasemi_rng_init(struct hwrng *rng)
0057 {
0058 void __iomem *rng_regs = (void __iomem *)rng->priv;
0059 u32 ctl;
0060
0061 ctl = SDCRNG_CTL_DR | SDCRNG_CTL_SELECT_RRG_RNG | SDCRNG_CTL_KSZ;
0062 out_le32(rng_regs + SDCRNG_CTL_REG, ctl);
0063 out_le32(rng_regs + SDCRNG_CTL_REG, ctl & ~SDCRNG_CTL_DR);
0064
0065 return 0;
0066 }
0067
0068 static void pasemi_rng_cleanup(struct hwrng *rng)
0069 {
0070 void __iomem *rng_regs = (void __iomem *)rng->priv;
0071 u32 ctl;
0072
0073 ctl = SDCRNG_CTL_RE | SDCRNG_CTL_CE;
0074 out_le32(rng_regs + SDCRNG_CTL_REG,
0075 in_le32(rng_regs + SDCRNG_CTL_REG) & ~ctl);
0076 }
0077
0078 static struct hwrng pasemi_rng = {
0079 .name = MODULE_NAME,
0080 .init = pasemi_rng_init,
0081 .cleanup = pasemi_rng_cleanup,
0082 .data_present = pasemi_rng_data_present,
0083 .data_read = pasemi_rng_data_read,
0084 };
0085
0086 static int rng_probe(struct platform_device *pdev)
0087 {
0088 void __iomem *rng_regs;
0089
0090 rng_regs = devm_platform_ioremap_resource(pdev, 0);
0091 if (IS_ERR(rng_regs))
0092 return PTR_ERR(rng_regs);
0093
0094 pasemi_rng.priv = (unsigned long)rng_regs;
0095
0096 pr_info("Registering PA Semi RNG\n");
0097 return devm_hwrng_register(&pdev->dev, &pasemi_rng);
0098 }
0099
0100 static const struct of_device_id rng_match[] = {
0101 { .compatible = "1682m-rng", },
0102 { .compatible = "pasemi,pwrficient-rng", },
0103 { },
0104 };
0105 MODULE_DEVICE_TABLE(of, rng_match);
0106
0107 static struct platform_driver rng_driver = {
0108 .driver = {
0109 .name = "pasemi-rng",
0110 .of_match_table = rng_match,
0111 },
0112 .probe = rng_probe,
0113 };
0114
0115 module_platform_driver(rng_driver);
0116
0117 MODULE_LICENSE("GPL");
0118 MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
0119 MODULE_DESCRIPTION("H/W RNG driver for PA Semi processor");