xref: /openbmc/linux/drivers/nvmem/sunxi_sid.c (revision 3d0b16a66c8a9d10294572c6f79df4f15a27825d)
1*3d0b16a6SMaxime Ripard /*
2*3d0b16a6SMaxime Ripard  * Allwinner sunXi SoCs Security ID support.
3*3d0b16a6SMaxime Ripard  *
4*3d0b16a6SMaxime Ripard  * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
5*3d0b16a6SMaxime Ripard  * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
6*3d0b16a6SMaxime Ripard  *
7*3d0b16a6SMaxime Ripard  * This program is free software; you can redistribute it and/or modify
8*3d0b16a6SMaxime Ripard  * it under the terms of the GNU General Public License as published by
9*3d0b16a6SMaxime Ripard  * the Free Software Foundation; either version 2 of the License, or
10*3d0b16a6SMaxime Ripard  * (at your option) any later version.
11*3d0b16a6SMaxime Ripard  *
12*3d0b16a6SMaxime Ripard  * This program is distributed in the hope that it will be useful,
13*3d0b16a6SMaxime Ripard  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*3d0b16a6SMaxime Ripard  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*3d0b16a6SMaxime Ripard  * GNU General Public License for more details.
16*3d0b16a6SMaxime Ripard  *
17*3d0b16a6SMaxime Ripard  */
18*3d0b16a6SMaxime Ripard 
19*3d0b16a6SMaxime Ripard 
20*3d0b16a6SMaxime Ripard #include <linux/device.h>
21*3d0b16a6SMaxime Ripard #include <linux/io.h>
22*3d0b16a6SMaxime Ripard #include <linux/module.h>
23*3d0b16a6SMaxime Ripard #include <linux/nvmem-provider.h>
24*3d0b16a6SMaxime Ripard #include <linux/of.h>
25*3d0b16a6SMaxime Ripard #include <linux/platform_device.h>
26*3d0b16a6SMaxime Ripard #include <linux/regmap.h>
27*3d0b16a6SMaxime Ripard #include <linux/slab.h>
28*3d0b16a6SMaxime Ripard #include <linux/random.h>
29*3d0b16a6SMaxime Ripard 
30*3d0b16a6SMaxime Ripard 
31*3d0b16a6SMaxime Ripard static struct nvmem_config econfig = {
32*3d0b16a6SMaxime Ripard 	.name = "sunxi-sid",
33*3d0b16a6SMaxime Ripard 	.read_only = true,
34*3d0b16a6SMaxime Ripard 	.owner = THIS_MODULE,
35*3d0b16a6SMaxime Ripard };
36*3d0b16a6SMaxime Ripard 
37*3d0b16a6SMaxime Ripard struct sunxi_sid {
38*3d0b16a6SMaxime Ripard 	void __iomem		*base;
39*3d0b16a6SMaxime Ripard };
40*3d0b16a6SMaxime Ripard 
41*3d0b16a6SMaxime Ripard /* We read the entire key, due to a 32 bit read alignment requirement. Since we
42*3d0b16a6SMaxime Ripard  * want to return the requested byte, this results in somewhat slower code and
43*3d0b16a6SMaxime Ripard  * uses 4 times more reads as needed but keeps code simpler. Since the SID is
44*3d0b16a6SMaxime Ripard  * only very rarely probed, this is not really an issue.
45*3d0b16a6SMaxime Ripard  */
46*3d0b16a6SMaxime Ripard static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid,
47*3d0b16a6SMaxime Ripard 			      const unsigned int offset)
48*3d0b16a6SMaxime Ripard {
49*3d0b16a6SMaxime Ripard 	u32 sid_key;
50*3d0b16a6SMaxime Ripard 
51*3d0b16a6SMaxime Ripard 	sid_key = ioread32be(sid->base + round_down(offset, 4));
52*3d0b16a6SMaxime Ripard 	sid_key >>= (offset % 4) * 8;
53*3d0b16a6SMaxime Ripard 
54*3d0b16a6SMaxime Ripard 	return sid_key; /* Only return the last byte */
55*3d0b16a6SMaxime Ripard }
56*3d0b16a6SMaxime Ripard 
57*3d0b16a6SMaxime Ripard static int sunxi_sid_read(void *context,
58*3d0b16a6SMaxime Ripard 			    const void *reg, size_t reg_size,
59*3d0b16a6SMaxime Ripard 			    void *val, size_t val_size)
60*3d0b16a6SMaxime Ripard {
61*3d0b16a6SMaxime Ripard 	struct sunxi_sid *sid = context;
62*3d0b16a6SMaxime Ripard 	unsigned int offset = *(u32 *)reg;
63*3d0b16a6SMaxime Ripard 	u8 *buf = val;
64*3d0b16a6SMaxime Ripard 
65*3d0b16a6SMaxime Ripard 	while (val_size) {
66*3d0b16a6SMaxime Ripard 		*buf++ = sunxi_sid_read_byte(sid, offset);
67*3d0b16a6SMaxime Ripard 		val_size--;
68*3d0b16a6SMaxime Ripard 		offset++;
69*3d0b16a6SMaxime Ripard 	}
70*3d0b16a6SMaxime Ripard 
71*3d0b16a6SMaxime Ripard 	return 0;
72*3d0b16a6SMaxime Ripard }
73*3d0b16a6SMaxime Ripard 
74*3d0b16a6SMaxime Ripard static int sunxi_sid_write(void *context, const void *data, size_t count)
75*3d0b16a6SMaxime Ripard {
76*3d0b16a6SMaxime Ripard 	/* Unimplemented, dummy to keep regmap core happy */
77*3d0b16a6SMaxime Ripard 	return 0;
78*3d0b16a6SMaxime Ripard }
79*3d0b16a6SMaxime Ripard 
80*3d0b16a6SMaxime Ripard static struct regmap_bus sunxi_sid_bus = {
81*3d0b16a6SMaxime Ripard 	.read = sunxi_sid_read,
82*3d0b16a6SMaxime Ripard 	.write = sunxi_sid_write,
83*3d0b16a6SMaxime Ripard 	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
84*3d0b16a6SMaxime Ripard 	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
85*3d0b16a6SMaxime Ripard };
86*3d0b16a6SMaxime Ripard 
87*3d0b16a6SMaxime Ripard static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg)
88*3d0b16a6SMaxime Ripard {
89*3d0b16a6SMaxime Ripard 	return false;
90*3d0b16a6SMaxime Ripard }
91*3d0b16a6SMaxime Ripard 
92*3d0b16a6SMaxime Ripard static struct regmap_config sunxi_sid_regmap_config = {
93*3d0b16a6SMaxime Ripard 	.reg_bits = 32,
94*3d0b16a6SMaxime Ripard 	.val_bits = 8,
95*3d0b16a6SMaxime Ripard 	.reg_stride = 1,
96*3d0b16a6SMaxime Ripard 	.writeable_reg = sunxi_sid_writeable_reg,
97*3d0b16a6SMaxime Ripard };
98*3d0b16a6SMaxime Ripard 
99*3d0b16a6SMaxime Ripard static int sunxi_sid_probe(struct platform_device *pdev)
100*3d0b16a6SMaxime Ripard {
101*3d0b16a6SMaxime Ripard 	struct device *dev = &pdev->dev;
102*3d0b16a6SMaxime Ripard 	struct resource *res;
103*3d0b16a6SMaxime Ripard 	struct nvmem_device *nvmem;
104*3d0b16a6SMaxime Ripard 	struct regmap *regmap;
105*3d0b16a6SMaxime Ripard 	struct sunxi_sid *sid;
106*3d0b16a6SMaxime Ripard 	int i, size;
107*3d0b16a6SMaxime Ripard 	char *randomness;
108*3d0b16a6SMaxime Ripard 
109*3d0b16a6SMaxime Ripard 	sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL);
110*3d0b16a6SMaxime Ripard 	if (!sid)
111*3d0b16a6SMaxime Ripard 		return -ENOMEM;
112*3d0b16a6SMaxime Ripard 
113*3d0b16a6SMaxime Ripard 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
114*3d0b16a6SMaxime Ripard 	sid->base = devm_ioremap_resource(dev, res);
115*3d0b16a6SMaxime Ripard 	if (IS_ERR(sid->base))
116*3d0b16a6SMaxime Ripard 		return PTR_ERR(sid->base);
117*3d0b16a6SMaxime Ripard 
118*3d0b16a6SMaxime Ripard 	size = resource_size(res) - 1;
119*3d0b16a6SMaxime Ripard 	sunxi_sid_regmap_config.max_register = size;
120*3d0b16a6SMaxime Ripard 
121*3d0b16a6SMaxime Ripard 	regmap = devm_regmap_init(dev, &sunxi_sid_bus, sid,
122*3d0b16a6SMaxime Ripard 				  &sunxi_sid_regmap_config);
123*3d0b16a6SMaxime Ripard 	if (IS_ERR(regmap)) {
124*3d0b16a6SMaxime Ripard 		dev_err(dev, "regmap init failed\n");
125*3d0b16a6SMaxime Ripard 		return PTR_ERR(regmap);
126*3d0b16a6SMaxime Ripard 	}
127*3d0b16a6SMaxime Ripard 
128*3d0b16a6SMaxime Ripard 	econfig.dev = dev;
129*3d0b16a6SMaxime Ripard 	nvmem = nvmem_register(&econfig);
130*3d0b16a6SMaxime Ripard 	if (IS_ERR(nvmem))
131*3d0b16a6SMaxime Ripard 		return PTR_ERR(nvmem);
132*3d0b16a6SMaxime Ripard 
133*3d0b16a6SMaxime Ripard 	randomness = kzalloc(sizeof(u8) * size, GFP_KERNEL);
134*3d0b16a6SMaxime Ripard 	for (i = 0; i < size; i++)
135*3d0b16a6SMaxime Ripard 		randomness[i] = sunxi_sid_read_byte(sid, i);
136*3d0b16a6SMaxime Ripard 
137*3d0b16a6SMaxime Ripard 	add_device_randomness(randomness, size);
138*3d0b16a6SMaxime Ripard 	kfree(randomness);
139*3d0b16a6SMaxime Ripard 
140*3d0b16a6SMaxime Ripard 	platform_set_drvdata(pdev, nvmem);
141*3d0b16a6SMaxime Ripard 
142*3d0b16a6SMaxime Ripard 	return 0;
143*3d0b16a6SMaxime Ripard }
144*3d0b16a6SMaxime Ripard 
145*3d0b16a6SMaxime Ripard static int sunxi_sid_remove(struct platform_device *pdev)
146*3d0b16a6SMaxime Ripard {
147*3d0b16a6SMaxime Ripard 	struct nvmem_device *nvmem = platform_get_drvdata(pdev);
148*3d0b16a6SMaxime Ripard 
149*3d0b16a6SMaxime Ripard 	return nvmem_unregister(nvmem);
150*3d0b16a6SMaxime Ripard }
151*3d0b16a6SMaxime Ripard 
152*3d0b16a6SMaxime Ripard static const struct of_device_id sunxi_sid_of_match[] = {
153*3d0b16a6SMaxime Ripard 	{ .compatible = "allwinner,sun4i-a10-sid" },
154*3d0b16a6SMaxime Ripard 	{ .compatible = "allwinner,sun7i-a20-sid" },
155*3d0b16a6SMaxime Ripard 	{/* sentinel */},
156*3d0b16a6SMaxime Ripard };
157*3d0b16a6SMaxime Ripard MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
158*3d0b16a6SMaxime Ripard 
159*3d0b16a6SMaxime Ripard static struct platform_driver sunxi_sid_driver = {
160*3d0b16a6SMaxime Ripard 	.probe = sunxi_sid_probe,
161*3d0b16a6SMaxime Ripard 	.remove = sunxi_sid_remove,
162*3d0b16a6SMaxime Ripard 	.driver = {
163*3d0b16a6SMaxime Ripard 		.name = "eeprom-sunxi-sid",
164*3d0b16a6SMaxime Ripard 		.of_match_table = sunxi_sid_of_match,
165*3d0b16a6SMaxime Ripard 	},
166*3d0b16a6SMaxime Ripard };
167*3d0b16a6SMaxime Ripard module_platform_driver(sunxi_sid_driver);
168*3d0b16a6SMaxime Ripard 
169*3d0b16a6SMaxime Ripard MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
170*3d0b16a6SMaxime Ripard MODULE_DESCRIPTION("Allwinner sunxi security id driver");
171*3d0b16a6SMaxime Ripard MODULE_LICENSE("GPL");
172