132 lines
3.6 KiB
Diff
132 lines
3.6 KiB
Diff
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
|