Blame view

drivers/nvmem/sunxi_sid.c 5.17 KB
055f5df53   Yangtao Li   nvmem: sunxi-sid:...
1
  // SPDX-License-Identifier: GPL-2.0+
3d0b16a66   Maxime Ripard   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   Maxime Ripard   nvmem: sunxi: Mov...
7
   */
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
8
9
  #include <linux/device.h>
  #include <linux/io.h>
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
10
  #include <linux/iopoll.h>
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
11
12
13
  #include <linux/module.h>
  #include <linux/nvmem-provider.h>
  #include <linux/of.h>
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
14
  #include <linux/of_device.h>
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
15
  #include <linux/platform_device.h>
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
16
17
  #include <linux/slab.h>
  #include <linux/random.h>
1a9636426   Icenowy Zheng   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   Icenowy Zheng   nvmem: sunxi-sid:...
26
  struct sunxi_sid_cfg {
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
27
  	u32	value_offset;
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
28
  	u32	size;
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
29
  	bool	need_register_readout;
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
30
  };
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
31
32
  struct sunxi_sid {
  	void __iomem		*base;
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
33
  	u32			value_offset;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
34
  };
9c7b16eb3   Srinivas Kandagatla   nvmem: sunxi-sid:...
35
36
  static int sunxi_sid_read(void *context, unsigned int offset,
  			  void *val, size_t bytes)
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
37
38
  {
  	struct sunxi_sid *sid = context;
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
39

273a474ee   Chen-Yu Tsai   nvmem: sunxi_sid:...
40
  	memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes);
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
41
42
43
  
  	return 0;
  }
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
44
  static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
0ab09d651   Icenowy Zheng   nvmem: sunxi-sid:...
45
46
  				      const unsigned int offset,
  				      u32 *out)
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
47
48
49
50
51
  {
  	u32 reg_val;
  	int ret;
  
  	/* Set word, lock access, and set read command */
0ab09d651   Icenowy Zheng   nvmem: sunxi-sid:...
52
  	reg_val = (offset & SUN8I_SID_OFFSET_MASK)
1a9636426   Icenowy Zheng   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   Icenowy Zheng   nvmem: sunxi-sid:...
61
62
  	if (out)
  		*out = readl(sid->base + SUN8I_SID_RDKEY);
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
63
  	writel(0, sid->base + SUN8I_SID_PRCTL);
0ab09d651   Icenowy Zheng   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   Icenowy Zheng   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   Chen-Yu Tsai   nvmem: sunxi_sid:...
77
  	u32 word;
0ab09d651   Icenowy Zheng   nvmem: sunxi-sid:...
78
  	int ret;
de2a3eaea   Chen-Yu Tsai   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   Icenowy Zheng   nvmem: sunxi-sid:...
82
83
  		if (ret)
  			return ret;
de2a3eaea   Chen-Yu Tsai   nvmem: sunxi_sid:...
84
85
86
87
  
  		val += 4;
  		offset += 4;
  		bytes -= 4;
0ab09d651   Icenowy Zheng   nvmem: sunxi-sid:...
88
  	}
de2a3eaea   Chen-Yu Tsai   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   Icenowy Zheng   nvmem: sunxi-sid:...
98
99
  	return 0;
  }
3d0b16a66   Maxime Ripard   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   Chen-Yu Tsai   nvmem: sunxi_sid:...
104
  	struct nvmem_config *nvmem_cfg;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
105
  	struct nvmem_device *nvmem;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
106
  	struct sunxi_sid *sid;
9c4adfb5d   Chen-Yu Tsai   nvmem: sunxi_sid:...
107
  	int size;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
108
  	char *randomness;
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
109
  	const struct sunxi_sid_cfg *cfg;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
110
111
112
113
  
  	sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL);
  	if (!sid)
  		return -ENOMEM;
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
114
115
116
  	cfg = of_device_get_match_data(dev);
  	if (!cfg)
  		return -EINVAL;
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
117
  	sid->value_offset = cfg->value_offset;
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
118

3d0b16a66   Maxime Ripard   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   Icenowy Zheng   nvmem: sunxi-sid:...
123
  	size = cfg->size;
7fa5ad23d   Chen-Yu Tsai   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   Icenowy Zheng   nvmem: sunxi-sid:...
135
  	if (cfg->need_register_readout)
7fa5ad23d   Chen-Yu Tsai   nvmem: sunxi_sid:...
136
  		nvmem_cfg->reg_read = sun8i_sid_read_by_reg;
0ab09d651   Icenowy Zheng   nvmem: sunxi-sid:...
137
  	else
7fa5ad23d   Chen-Yu Tsai   nvmem: sunxi_sid:...
138
139
140
  		nvmem_cfg->reg_read = sunxi_sid_read;
  
  	nvmem = devm_nvmem_register(dev, nvmem_cfg);
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
141
142
  	if (IS_ERR(nvmem))
  		return PTR_ERR(nvmem);
6396bb221   Kees Cook   treewide: kzalloc...
143
  	randomness = kzalloc(size, GFP_KERNEL);
6eed8dd9a   Bartosz Golaszewski   nvmem: sunxi_sid:...
144
145
  	if (!randomness)
  		return -ENOMEM;
fb727077b   Maxime Ripard   nvmem: sunxi: Che...
146

7fa5ad23d   Chen-Yu Tsai   nvmem: sunxi_sid:...
147
  	nvmem_cfg->reg_read(sid, 0, randomness, size);
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
148
149
150
151
152
153
  	add_device_randomness(randomness, size);
  	kfree(randomness);
  
  	platform_set_drvdata(pdev, nvmem);
  
  	return 0;
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
154
  }
4a72cda53   Icenowy Zheng   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   Icenowy Zheng   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   Icenowy Zheng   nvmem: sunxi-sid:...
167
168
169
  static const struct sunxi_sid_cfg sun50i_a64_cfg = {
  	.value_offset = 0x200,
  	.size = 0x100,
2ac00e34a   Stefan Mavrodiev   nvmem: sunxi_sid:...
170
  	.need_register_readout = true,
b7fe57b80   Icenowy Zheng   nvmem: sunxi-sid:...
171
  };
fc1eb6ebb   Yangtao Li   nvmem: sunxi_sid:...
172
173
174
175
  static const struct sunxi_sid_cfg sun50i_h6_cfg = {
  	.value_offset = 0x200,
  	.size = 0x200,
  };
3d0b16a66   Maxime Ripard   nvmem: sunxi: Mov...
176
  static const struct of_device_id sunxi_sid_of_match[] = {
4a72cda53   Icenowy Zheng   nvmem: sunxi-sid:...
177
178
  	{ .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg },
  	{ .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg },
da75b8909   Chen-Yu Tsai   nvmem: sunxi_sid:...
179
  	{ .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg },
1a9636426   Icenowy Zheng   nvmem: sunxi-sid:...
180
  	{ .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg },
b7fe57b80   Icenowy Zheng   nvmem: sunxi-sid:...
181
  	{ .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg },
da75b8909   Chen-Yu Tsai   nvmem: sunxi_sid:...
182
  	{ .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg },
fc1eb6ebb   Yangtao Li   nvmem: sunxi_sid:...
183
  	{ .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg },
3d0b16a66   Maxime Ripard   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   Maxime Ripard   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");