Blame view
drivers/nvmem/sunxi_sid.c
5.17 KB
055f5df53
|
1 |
// SPDX-License-Identifier: GPL-2.0+ |
3d0b16a66
|
2 3 4 5 6 |
/* * Allwinner sunXi SoCs Security ID support. * * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com> |
3d0b16a66
|
7 |
*/ |
3d0b16a66
|
8 9 |
#include <linux/device.h> #include <linux/io.h> |
1a9636426
|
10 |
#include <linux/iopoll.h> |
3d0b16a66
|
11 12 13 |
#include <linux/module.h> #include <linux/nvmem-provider.h> #include <linux/of.h> |
4a72cda53
|
14 |
#include <linux/of_device.h> |
3d0b16a66
|
15 |
#include <linux/platform_device.h> |
3d0b16a66
|
16 17 |
#include <linux/slab.h> #include <linux/random.h> |
1a9636426
|
18 19 20 21 22 23 24 25 |
/* Registers and special values for doing register-based SID readout on H3 */ #define SUN8I_SID_PRCTL 0x40 #define SUN8I_SID_RDKEY 0x60 #define SUN8I_SID_OFFSET_MASK 0x1FF #define SUN8I_SID_OFFSET_SHIFT 16 #define SUN8I_SID_OP_LOCK (0xAC << 8) #define SUN8I_SID_READ BIT(1) |
4a72cda53
|
26 |
struct sunxi_sid_cfg { |
1a9636426
|
27 |
u32 value_offset; |
4a72cda53
|
28 |
u32 size; |
1a9636426
|
29 |
bool need_register_readout; |
4a72cda53
|
30 |
}; |
3d0b16a66
|
31 32 |
struct sunxi_sid { void __iomem *base; |
1a9636426
|
33 |
u32 value_offset; |
3d0b16a66
|
34 |
}; |
9c7b16eb3
|
35 36 |
static int sunxi_sid_read(void *context, unsigned int offset, void *val, size_t bytes) |
3d0b16a66
|
37 38 |
{ struct sunxi_sid *sid = context; |
1a9636426
|
39 |
|
273a474ee
|
40 |
memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes); |
3d0b16a66
|
41 42 43 |
return 0; } |
1a9636426
|
44 |
static int sun8i_sid_register_readout(const struct sunxi_sid *sid, |
0ab09d651
|
45 46 |
const unsigned int offset, u32 *out) |
1a9636426
|
47 48 49 50 51 |
{ u32 reg_val; int ret; /* Set word, lock access, and set read command */ |
0ab09d651
|
52 |
reg_val = (offset & SUN8I_SID_OFFSET_MASK) |
1a9636426
|
53 54 55 56 57 58 59 60 |
<< SUN8I_SID_OFFSET_SHIFT; reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ; writel(reg_val, sid->base + SUN8I_SID_PRCTL); ret = readl_poll_timeout(sid->base + SUN8I_SID_PRCTL, reg_val, !(reg_val & SUN8I_SID_READ), 100, 250000); if (ret) return ret; |
0ab09d651
|
61 62 |
if (out) *out = readl(sid->base + SUN8I_SID_RDKEY); |
1a9636426
|
63 |
writel(0, sid->base + SUN8I_SID_PRCTL); |
0ab09d651
|
64 65 66 67 68 69 70 71 72 |
return 0; } /* * On Allwinner H3, the value on the 0x200 offset of the SID controller seems * to be not reliable at all. * Read by the registers instead. */ |
0ab09d651
|
73 74 75 76 |
static int sun8i_sid_read_by_reg(void *context, unsigned int offset, void *val, size_t bytes) { struct sunxi_sid *sid = context; |
de2a3eaea
|
77 |
u32 word; |
0ab09d651
|
78 |
int ret; |
de2a3eaea
|
79 80 81 |
/* .stride = 4 so offset is guaranteed to be aligned */ while (bytes >= 4) { ret = sun8i_sid_register_readout(sid, offset, val); |
0ab09d651
|
82 83 |
if (ret) return ret; |
de2a3eaea
|
84 85 86 87 |
val += 4; offset += 4; bytes -= 4; |
0ab09d651
|
88 |
} |
de2a3eaea
|
89 90 91 92 93 94 95 96 97 |
if (!bytes) return 0; /* Handle any trailing bytes */ ret = sun8i_sid_register_readout(sid, offset, &word); if (ret) return ret; memcpy(val, &word, bytes); |
1a9636426
|
98 99 |
return 0; } |
3d0b16a66
|
100 101 102 103 |
static int sunxi_sid_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; |
7fa5ad23d
|
104 |
struct nvmem_config *nvmem_cfg; |
3d0b16a66
|
105 |
struct nvmem_device *nvmem; |
3d0b16a66
|
106 |
struct sunxi_sid *sid; |
9c4adfb5d
|
107 |
int size; |
3d0b16a66
|
108 |
char *randomness; |
4a72cda53
|
109 |
const struct sunxi_sid_cfg *cfg; |
3d0b16a66
|
110 111 112 113 |
sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); if (!sid) return -ENOMEM; |
4a72cda53
|
114 115 116 |
cfg = of_device_get_match_data(dev); if (!cfg) return -EINVAL; |
1a9636426
|
117 |
sid->value_offset = cfg->value_offset; |
4a72cda53
|
118 |
|
3d0b16a66
|
119 120 121 122 |
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sid->base = devm_ioremap_resource(dev, res); if (IS_ERR(sid->base)) return PTR_ERR(sid->base); |
4a72cda53
|
123 |
size = cfg->size; |
7fa5ad23d
|
124 125 126 127 128 129 130 131 132 133 134 |
nvmem_cfg = devm_kzalloc(dev, sizeof(*nvmem_cfg), GFP_KERNEL); if (!nvmem_cfg) return -ENOMEM; nvmem_cfg->dev = dev; nvmem_cfg->name = "sunxi-sid"; nvmem_cfg->read_only = true; nvmem_cfg->size = cfg->size; nvmem_cfg->word_size = 1; nvmem_cfg->stride = 4; nvmem_cfg->priv = sid; |
0ab09d651
|
135 |
if (cfg->need_register_readout) |
7fa5ad23d
|
136 |
nvmem_cfg->reg_read = sun8i_sid_read_by_reg; |
0ab09d651
|
137 |
else |
7fa5ad23d
|
138 139 140 |
nvmem_cfg->reg_read = sunxi_sid_read; nvmem = devm_nvmem_register(dev, nvmem_cfg); |
3d0b16a66
|
141 142 |
if (IS_ERR(nvmem)) return PTR_ERR(nvmem); |
6396bb221
|
143 |
randomness = kzalloc(size, GFP_KERNEL); |
6eed8dd9a
|
144 145 |
if (!randomness) return -ENOMEM; |
fb727077b
|
146 |
|
7fa5ad23d
|
147 |
nvmem_cfg->reg_read(sid, 0, randomness, size); |
3d0b16a66
|
148 149 150 151 152 153 |
add_device_randomness(randomness, size); kfree(randomness); platform_set_drvdata(pdev, nvmem); return 0; |
3d0b16a66
|
154 |
} |
4a72cda53
|
155 156 157 158 159 160 161 |
static const struct sunxi_sid_cfg sun4i_a10_cfg = { .size = 0x10, }; static const struct sunxi_sid_cfg sun7i_a20_cfg = { .size = 0x200, }; |
1a9636426
|
162 163 164 165 166 |
static const struct sunxi_sid_cfg sun8i_h3_cfg = { .value_offset = 0x200, .size = 0x100, .need_register_readout = true, }; |
b7fe57b80
|
167 168 169 |
static const struct sunxi_sid_cfg sun50i_a64_cfg = { .value_offset = 0x200, .size = 0x100, |
2ac00e34a
|
170 |
.need_register_readout = true, |
b7fe57b80
|
171 |
}; |
fc1eb6ebb
|
172 173 174 175 |
static const struct sunxi_sid_cfg sun50i_h6_cfg = { .value_offset = 0x200, .size = 0x200, }; |
3d0b16a66
|
176 |
static const struct of_device_id sunxi_sid_of_match[] = { |
4a72cda53
|
177 178 |
{ .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, |
da75b8909
|
179 |
{ .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, |
1a9636426
|
180 |
{ .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, |
b7fe57b80
|
181 |
{ .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, |
da75b8909
|
182 |
{ .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, |
fc1eb6ebb
|
183 |
{ .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, |
3d0b16a66
|
184 185 186 187 188 189 |
{/* sentinel */}, }; MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); static struct platform_driver sunxi_sid_driver = { .probe = sunxi_sid_probe, |
3d0b16a66
|
190 191 192 193 194 195 196 197 198 199 |
.driver = { .name = "eeprom-sunxi-sid", .of_match_table = sunxi_sid_of_match, }, }; module_platform_driver(sunxi_sid_driver); MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); MODULE_DESCRIPTION("Allwinner sunxi security id driver"); MODULE_LICENSE("GPL"); |