1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
From 0ab09d651b5858f9bc7d5f74e725334a661828e0 Mon Sep 17 00:00:00 2001
From: Icenowy Zheng <icenowy@aosc.io>
Date: Fri, 9 Mar 2018 14:47:17 +0000
Subject: nvmem: sunxi-sid: fix H3 SID controller support
It seems that doing some operation will make the value pre-read on H3
SID controller wrong again, so all operation should be performed by
register.
Change the SID reading to use register only.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/nvmem/sunxi_sid.c | 71 +++++++++++++++++++++++++++++++++--------------
1 file changed, 50 insertions(+), 21 deletions(-)
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index 99bd54d..26bb637 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -85,13 +85,14 @@ static int sunxi_sid_read(void *context, unsigned int offset,
}
static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
- const unsigned int word)
+ const unsigned int offset,
+ u32 *out)
{
u32 reg_val;
int ret;
/* Set word, lock access, and set read command */
- reg_val = (word & SUN8I_SID_OFFSET_MASK)
+ reg_val = (offset & SUN8I_SID_OFFSET_MASK)
<< SUN8I_SID_OFFSET_SHIFT;
reg_val |= SUN8I_SID_OP_LOCK | SUN8I_SID_READ;
writel(reg_val, sid->base + SUN8I_SID_PRCTL);
@@ -101,7 +102,49 @@ static int sun8i_sid_register_readout(const struct sunxi_sid *sid,
if (ret)
return ret;
+ if (out)
+ *out = readl(sid->base + SUN8I_SID_RDKEY);
+
writel(0, sid->base + SUN8I_SID_PRCTL);
+
+ 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.
+ */
+static int sun8i_sid_read_byte_by_reg(const struct sunxi_sid *sid,
+ const unsigned int offset,
+ u8 *out)
+{
+ u32 word;
+ int ret;
+
+ ret = sun8i_sid_register_readout(sid, offset & ~0x03, &word);
+
+ if (ret)
+ return ret;
+
+ *out = (word >> ((offset & 0x3) * 8)) & 0xff;
+
+ return 0;
+}
+
+static int sun8i_sid_read_by_reg(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct sunxi_sid *sid = context;
+ u8 *buf = val;
+ int ret;
+
+ while (bytes--) {
+ ret = sun8i_sid_read_byte_by_reg(sid, offset++, buf++);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -131,26 +174,12 @@ static int sunxi_sid_probe(struct platform_device *pdev)
size = cfg->size;
- if (cfg->need_register_readout) {
- /*
- * H3's SID controller have a bug that the value at 0x200
- * offset is not the correct value when the hardware is reseted.
- * However, after doing a register-based read operation, the
- * value become right.
- * Do a full read operation here, but ignore its value
- * (as it's more fast to read by direct MMIO value than
- * with registers)
- */
- for (i = 0; i < (size >> 2); i++) {
- ret = sun8i_sid_register_readout(sid, i);
- if (ret)
- return ret;
- }
- }
-
econfig.size = size;
econfig.dev = dev;
- econfig.reg_read = sunxi_sid_read;
+ if (cfg->need_register_readout)
+ econfig.reg_read = sun8i_sid_read_by_reg;
+ else
+ econfig.reg_read = sunxi_sid_read;
econfig.priv = sid;
nvmem = nvmem_register(&econfig);
if (IS_ERR(nvmem))
@@ -163,7 +192,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
}
for (i = 0; i < size; i++)
- randomness[i] = sunxi_sid_read_byte(sid, i);
+ econfig.reg_read(sid, i, &randomness[i], 1);
add_device_randomness(randomness, size);
kfree(randomness);
--
cgit v1.1
|