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>
This commit is contained in:
Icenowy Zheng 2018-03-09 14:47:17 +00:00 committed by Greg Kroah-Hartman
parent 80b820cae4
commit 0ab09d651b
1 changed files with 50 additions and 21 deletions

View 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);