]>
Commit | Line | Data |
---|---|---|
3d0b16a6 MR |
1 | /* |
2 | * Allwinner sunXi SoCs Security ID support. | |
3 | * | |
4 | * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> | |
5 | * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
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. | |
3d0b16a6 MR |
16 | */ |
17 | ||
3d0b16a6 MR |
18 | #include <linux/device.h> |
19 | #include <linux/io.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/nvmem-provider.h> | |
22 | #include <linux/of.h> | |
23 | #include <linux/platform_device.h> | |
3d0b16a6 MR |
24 | #include <linux/slab.h> |
25 | #include <linux/random.h> | |
26 | ||
3d0b16a6 MR |
27 | static struct nvmem_config econfig = { |
28 | .name = "sunxi-sid", | |
29 | .read_only = true, | |
9c7b16eb SK |
30 | .stride = 4, |
31 | .word_size = 1, | |
3d0b16a6 MR |
32 | .owner = THIS_MODULE, |
33 | }; | |
34 | ||
35 | struct sunxi_sid { | |
36 | void __iomem *base; | |
37 | }; | |
38 | ||
39 | /* We read the entire key, due to a 32 bit read alignment requirement. Since we | |
40 | * want to return the requested byte, this results in somewhat slower code and | |
41 | * uses 4 times more reads as needed but keeps code simpler. Since the SID is | |
42 | * only very rarely probed, this is not really an issue. | |
43 | */ | |
44 | static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, | |
45 | const unsigned int offset) | |
46 | { | |
47 | u32 sid_key; | |
48 | ||
49 | sid_key = ioread32be(sid->base + round_down(offset, 4)); | |
50 | sid_key >>= (offset % 4) * 8; | |
51 | ||
52 | return sid_key; /* Only return the last byte */ | |
53 | } | |
54 | ||
9c7b16eb SK |
55 | static int sunxi_sid_read(void *context, unsigned int offset, |
56 | void *val, size_t bytes) | |
3d0b16a6 MR |
57 | { |
58 | struct sunxi_sid *sid = context; | |
3d0b16a6 MR |
59 | u8 *buf = val; |
60 | ||
9c7b16eb SK |
61 | while (bytes--) |
62 | *buf++ = sunxi_sid_read_byte(sid, offset++); | |
3d0b16a6 MR |
63 | |
64 | return 0; | |
65 | } | |
66 | ||
3d0b16a6 MR |
67 | static int sunxi_sid_probe(struct platform_device *pdev) |
68 | { | |
69 | struct device *dev = &pdev->dev; | |
70 | struct resource *res; | |
71 | struct nvmem_device *nvmem; | |
3d0b16a6 | 72 | struct sunxi_sid *sid; |
fb727077 | 73 | int ret, i, size; |
3d0b16a6 MR |
74 | char *randomness; |
75 | ||
76 | sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); | |
77 | if (!sid) | |
78 | return -ENOMEM; | |
79 | ||
80 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
81 | sid->base = devm_ioremap_resource(dev, res); | |
82 | if (IS_ERR(sid->base)) | |
83 | return PTR_ERR(sid->base); | |
84 | ||
85 | size = resource_size(res) - 1; | |
9c7b16eb | 86 | econfig.size = resource_size(res); |
3d0b16a6 | 87 | econfig.dev = dev; |
9c7b16eb SK |
88 | econfig.reg_read = sunxi_sid_read; |
89 | econfig.priv = sid; | |
3d0b16a6 MR |
90 | nvmem = nvmem_register(&econfig); |
91 | if (IS_ERR(nvmem)) | |
92 | return PTR_ERR(nvmem); | |
93 | ||
d16abd30 | 94 | randomness = kzalloc(sizeof(u8) * (size), GFP_KERNEL); |
fb727077 MR |
95 | if (!randomness) { |
96 | ret = -EINVAL; | |
97 | goto err_unreg_nvmem; | |
98 | } | |
99 | ||
3d0b16a6 MR |
100 | for (i = 0; i < size; i++) |
101 | randomness[i] = sunxi_sid_read_byte(sid, i); | |
102 | ||
103 | add_device_randomness(randomness, size); | |
104 | kfree(randomness); | |
105 | ||
106 | platform_set_drvdata(pdev, nvmem); | |
107 | ||
108 | return 0; | |
fb727077 MR |
109 | |
110 | err_unreg_nvmem: | |
111 | nvmem_unregister(nvmem); | |
112 | return ret; | |
3d0b16a6 MR |
113 | } |
114 | ||
115 | static int sunxi_sid_remove(struct platform_device *pdev) | |
116 | { | |
117 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | |
118 | ||
119 | return nvmem_unregister(nvmem); | |
120 | } | |
121 | ||
122 | static const struct of_device_id sunxi_sid_of_match[] = { | |
123 | { .compatible = "allwinner,sun4i-a10-sid" }, | |
124 | { .compatible = "allwinner,sun7i-a20-sid" }, | |
125 | {/* sentinel */}, | |
126 | }; | |
127 | MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); | |
128 | ||
129 | static struct platform_driver sunxi_sid_driver = { | |
130 | .probe = sunxi_sid_probe, | |
131 | .remove = sunxi_sid_remove, | |
132 | .driver = { | |
133 | .name = "eeprom-sunxi-sid", | |
134 | .of_match_table = sunxi_sid_of_match, | |
135 | }, | |
136 | }; | |
137 | module_platform_driver(sunxi_sid_driver); | |
138 | ||
139 | MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); | |
140 | MODULE_DESCRIPTION("Allwinner sunxi security id driver"); | |
141 | MODULE_LICENSE("GPL"); |