nvmem: sunxi-sid: fix H3 SID controller support
authorIcenowy Zheng <icenowy@aosc.io>
Fri, 9 Mar 2018 14:47:17 +0000 (14:47 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Mar 2018 18:28:15 +0000 (19:28 +0100)
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

index 99bd54d..26bb637 100644 (file)
@@ -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);