Blame view
drivers/nvmem/sunxi_sid.c
5.17 KB
055f5df53 nvmem: sunxi-sid:... |
1 |
// SPDX-License-Identifier: GPL-2.0+ |
3d0b16a66 nvmem: sunxi: Mov... |
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 nvmem: sunxi: Mov... |
7 |
*/ |
3d0b16a66 nvmem: sunxi: Mov... |
8 9 |
#include <linux/device.h> #include <linux/io.h> |
1a9636426 nvmem: sunxi-sid:... |
10 |
#include <linux/iopoll.h> |
3d0b16a66 nvmem: sunxi: Mov... |
11 12 13 |
#include <linux/module.h> #include <linux/nvmem-provider.h> #include <linux/of.h> |
4a72cda53 nvmem: sunxi-sid:... |
14 |
#include <linux/of_device.h> |
3d0b16a66 nvmem: sunxi: Mov... |
15 |
#include <linux/platform_device.h> |
3d0b16a66 nvmem: sunxi: Mov... |
16 17 |
#include <linux/slab.h> #include <linux/random.h> |
1a9636426 nvmem: sunxi-sid:... |
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 nvmem: sunxi-sid:... |
26 |
struct sunxi_sid_cfg { |
1a9636426 nvmem: sunxi-sid:... |
27 |
u32 value_offset; |
4a72cda53 nvmem: sunxi-sid:... |
28 |
u32 size; |
1a9636426 nvmem: sunxi-sid:... |
29 |
bool need_register_readout; |
4a72cda53 nvmem: sunxi-sid:... |
30 |
}; |
3d0b16a66 nvmem: sunxi: Mov... |
31 32 |
struct sunxi_sid { void __iomem *base; |
1a9636426 nvmem: sunxi-sid:... |
33 |
u32 value_offset; |
3d0b16a66 nvmem: sunxi: Mov... |
34 |
}; |
9c7b16eb3 nvmem: sunxi-sid:... |
35 36 |
static int sunxi_sid_read(void *context, unsigned int offset, void *val, size_t bytes) |
3d0b16a66 nvmem: sunxi: Mov... |
37 38 |
{ struct sunxi_sid *sid = context; |
1a9636426 nvmem: sunxi-sid:... |
39 |
|
273a474ee nvmem: sunxi_sid:... |
40 |
memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes); |
3d0b16a66 nvmem: sunxi: Mov... |
41 42 43 |
return 0; } |
1a9636426 nvmem: sunxi-sid:... |
44 |
static int sun8i_sid_register_readout(const struct sunxi_sid *sid, |
0ab09d651 nvmem: sunxi-sid:... |
45 46 |
const unsigned int offset, u32 *out) |
1a9636426 nvmem: sunxi-sid:... |
47 48 49 50 51 |
{ u32 reg_val; int ret; /* Set word, lock access, and set read command */ |
0ab09d651 nvmem: sunxi-sid:... |
52 |
reg_val = (offset & SUN8I_SID_OFFSET_MASK) |
1a9636426 nvmem: sunxi-sid:... |
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 nvmem: sunxi-sid:... |
61 62 |
if (out) *out = readl(sid->base + SUN8I_SID_RDKEY); |
1a9636426 nvmem: sunxi-sid:... |
63 |
writel(0, sid->base + SUN8I_SID_PRCTL); |
0ab09d651 nvmem: sunxi-sid:... |
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 nvmem: sunxi-sid:... |
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 nvmem: sunxi_sid:... |
77 |
u32 word; |
0ab09d651 nvmem: sunxi-sid:... |
78 |
int ret; |
de2a3eaea nvmem: sunxi_sid:... |
79 80 81 |
/* .stride = 4 so offset is guaranteed to be aligned */ while (bytes >= 4) { ret = sun8i_sid_register_readout(sid, offset, val); |
0ab09d651 nvmem: sunxi-sid:... |
82 83 |
if (ret) return ret; |
de2a3eaea nvmem: sunxi_sid:... |
84 85 86 87 |
val += 4; offset += 4; bytes -= 4; |
0ab09d651 nvmem: sunxi-sid:... |
88 |
} |
de2a3eaea nvmem: sunxi_sid:... |
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 nvmem: sunxi-sid:... |
98 99 |
return 0; } |
3d0b16a66 nvmem: sunxi: Mov... |
100 101 102 103 |
static int sunxi_sid_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; |
7fa5ad23d nvmem: sunxi_sid:... |
104 |
struct nvmem_config *nvmem_cfg; |
3d0b16a66 nvmem: sunxi: Mov... |
105 |
struct nvmem_device *nvmem; |
3d0b16a66 nvmem: sunxi: Mov... |
106 |
struct sunxi_sid *sid; |
9c4adfb5d nvmem: sunxi_sid:... |
107 |
int size; |
3d0b16a66 nvmem: sunxi: Mov... |
108 |
char *randomness; |
4a72cda53 nvmem: sunxi-sid:... |
109 |
const struct sunxi_sid_cfg *cfg; |
3d0b16a66 nvmem: sunxi: Mov... |
110 111 112 113 |
sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); if (!sid) return -ENOMEM; |
4a72cda53 nvmem: sunxi-sid:... |
114 115 116 |
cfg = of_device_get_match_data(dev); if (!cfg) return -EINVAL; |
1a9636426 nvmem: sunxi-sid:... |
117 |
sid->value_offset = cfg->value_offset; |
4a72cda53 nvmem: sunxi-sid:... |
118 |
|
3d0b16a66 nvmem: sunxi: Mov... |
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 nvmem: sunxi-sid:... |
123 |
size = cfg->size; |
7fa5ad23d nvmem: sunxi_sid:... |
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 nvmem: sunxi-sid:... |
135 |
if (cfg->need_register_readout) |
7fa5ad23d nvmem: sunxi_sid:... |
136 |
nvmem_cfg->reg_read = sun8i_sid_read_by_reg; |
0ab09d651 nvmem: sunxi-sid:... |
137 |
else |
7fa5ad23d nvmem: sunxi_sid:... |
138 139 140 |
nvmem_cfg->reg_read = sunxi_sid_read; nvmem = devm_nvmem_register(dev, nvmem_cfg); |
3d0b16a66 nvmem: sunxi: Mov... |
141 142 |
if (IS_ERR(nvmem)) return PTR_ERR(nvmem); |
6396bb221 treewide: kzalloc... |
143 |
randomness = kzalloc(size, GFP_KERNEL); |
6eed8dd9a nvmem: sunxi_sid:... |
144 145 |
if (!randomness) return -ENOMEM; |
fb727077b nvmem: sunxi: Che... |
146 |
|
7fa5ad23d nvmem: sunxi_sid:... |
147 |
nvmem_cfg->reg_read(sid, 0, randomness, size); |
3d0b16a66 nvmem: sunxi: Mov... |
148 149 150 151 152 153 |
add_device_randomness(randomness, size); kfree(randomness); platform_set_drvdata(pdev, nvmem); return 0; |
3d0b16a66 nvmem: sunxi: Mov... |
154 |
} |
4a72cda53 nvmem: sunxi-sid:... |
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 nvmem: sunxi-sid:... |
162 163 164 165 166 |
static const struct sunxi_sid_cfg sun8i_h3_cfg = { .value_offset = 0x200, .size = 0x100, .need_register_readout = true, }; |
b7fe57b80 nvmem: sunxi-sid:... |
167 168 169 |
static const struct sunxi_sid_cfg sun50i_a64_cfg = { .value_offset = 0x200, .size = 0x100, |
2ac00e34a nvmem: sunxi_sid:... |
170 |
.need_register_readout = true, |
b7fe57b80 nvmem: sunxi-sid:... |
171 |
}; |
fc1eb6ebb nvmem: sunxi_sid:... |
172 173 174 175 |
static const struct sunxi_sid_cfg sun50i_h6_cfg = { .value_offset = 0x200, .size = 0x200, }; |
3d0b16a66 nvmem: sunxi: Mov... |
176 |
static const struct of_device_id sunxi_sid_of_match[] = { |
4a72cda53 nvmem: sunxi-sid:... |
177 178 |
{ .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, |
da75b8909 nvmem: sunxi_sid:... |
179 |
{ .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, |
1a9636426 nvmem: sunxi-sid:... |
180 |
{ .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, |
b7fe57b80 nvmem: sunxi-sid:... |
181 |
{ .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, |
da75b8909 nvmem: sunxi_sid:... |
182 |
{ .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, |
fc1eb6ebb nvmem: sunxi_sid:... |
183 |
{ .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, |
3d0b16a66 nvmem: sunxi: Mov... |
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 nvmem: sunxi: Mov... |
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"); |