Commit 0ab09d65 authored by Icenowy Zheng's avatar Icenowy Zheng Committed by Greg Kroah-Hartman
Browse files

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: default avatarIcenowy Zheng <icenowy@aosc.io>
Signed-off-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 80b820ca
Loading
Loading
Loading
Loading
+50 −21
Original line number Diff line number Diff line
@@ -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,25 +174,11 @@ 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;
	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);
@@ -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);