1/*
2 * Copyright (C) 2006-2007 PA Semi, Inc
3 *
4 * Maintained by: Olof Johansson <olof@lixom.net>
5 *
6 * Driver for the PWRficient onchip rng
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20 */
21
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/platform_device.h>
25#include <linux/hw_random.h>
26#include <asm/of_platform.h>
27#include <asm/io.h>
28
29#define SDCRNG_CTL_REG			0x00
30#define   SDCRNG_CTL_FVLD_M		0x0000f000
31#define   SDCRNG_CTL_FVLD_S		12
32#define   SDCRNG_CTL_KSZ		0x00000800
33#define   SDCRNG_CTL_RSRC_CRG		0x00000010
34#define   SDCRNG_CTL_RSRC_RRG		0x00000000
35#define   SDCRNG_CTL_CE			0x00000004
36#define   SDCRNG_CTL_RE			0x00000002
37#define   SDCRNG_CTL_DR			0x00000001
38#define   SDCRNG_CTL_SELECT_RRG_RNG	(SDCRNG_CTL_RE | SDCRNG_CTL_RSRC_RRG)
39#define   SDCRNG_CTL_SELECT_CRG_RNG	(SDCRNG_CTL_CE | SDCRNG_CTL_RSRC_CRG)
40#define SDCRNG_VAL_REG			0x20
41
42#define MODULE_NAME "pasemi_rng"
43
44static int pasemi_rng_data_present(struct hwrng *rng)
45{
46	void __iomem *rng_regs = (void __iomem *)rng->priv;
47
48	return (in_le32(rng_regs + SDCRNG_CTL_REG)
49		& SDCRNG_CTL_FVLD_M) ? 1 : 0;
50}
51
52static int pasemi_rng_data_read(struct hwrng *rng, u32 *data)
53{
54	void __iomem *rng_regs = (void __iomem *)rng->priv;
55	*data = in_le32(rng_regs + SDCRNG_VAL_REG);
56	return 4;
57}
58
59static int pasemi_rng_init(struct hwrng *rng)
60{
61	void __iomem *rng_regs = (void __iomem *)rng->priv;
62	u32 ctl;
63
64	ctl = SDCRNG_CTL_DR | SDCRNG_CTL_SELECT_RRG_RNG | SDCRNG_CTL_KSZ;
65	out_le32(rng_regs + SDCRNG_CTL_REG, ctl);
66	out_le32(rng_regs + SDCRNG_CTL_REG, ctl & ~SDCRNG_CTL_DR);
67
68	return 0;
69}
70
71static void pasemi_rng_cleanup(struct hwrng *rng)
72{
73	void __iomem *rng_regs = (void __iomem *)rng->priv;
74	u32 ctl;
75
76	ctl = SDCRNG_CTL_RE | SDCRNG_CTL_CE;
77	out_le32(rng_regs + SDCRNG_CTL_REG,
78		 in_le32(rng_regs + SDCRNG_CTL_REG) & ~ctl);
79}
80
81static struct hwrng pasemi_rng = {
82	.name		= MODULE_NAME,
83	.init		= pasemi_rng_init,
84	.cleanup	= pasemi_rng_cleanup,
85	.data_present	= pasemi_rng_data_present,
86	.data_read	= pasemi_rng_data_read,
87};
88
89static int __devinit rng_probe(struct of_device *ofdev,
90			       const struct of_device_id *match)
91{
92	void __iomem *rng_regs;
93	struct device_node *rng_np = ofdev->node;
94	struct resource res;
95	int err = 0;
96
97	err = of_address_to_resource(rng_np, 0, &res);
98	if (err)
99		return -ENODEV;
100
101	rng_regs = ioremap(res.start, 0x100);
102
103	if (!rng_regs)
104		return -ENOMEM;
105
106	pasemi_rng.priv = (unsigned long)rng_regs;
107
108	printk(KERN_INFO "Registering PA Semi RNG\n");
109
110	err = hwrng_register(&pasemi_rng);
111
112	if (err)
113		iounmap(rng_regs);
114
115	return err;
116}
117
118static int __devexit rng_remove(struct of_device *dev)
119{
120	void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv;
121
122	hwrng_unregister(&pasemi_rng);
123	iounmap(rng_regs);
124
125	return 0;
126}
127
128static struct of_device_id rng_match[] = {
129	{
130		.compatible      = "1682m-rng",
131	},
132	{},
133};
134
135static struct of_platform_driver rng_driver = {
136	.name		= "pasemi-rng",
137	.match_table	= rng_match,
138	.probe		= rng_probe,
139	.remove		= rng_remove,
140};
141
142static int __init rng_init(void)
143{
144	return of_register_platform_driver(&rng_driver);
145}
146module_init(rng_init);
147
148static void __exit rng_exit(void)
149{
150	of_unregister_platform_driver(&rng_driver);
151}
152module_exit(rng_exit);
153
154MODULE_LICENSE("GPL");
155MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
156MODULE_DESCRIPTION("H/W RNG driver for PA Semi processor");
157