aboutsummaryrefslogtreecommitdiff
path: root/hw/misc
diff options
context:
space:
mode:
authorGreen Wan <green.wan@sifive.com>2020-10-20 11:37:31 +0800
committerAlistair Francis <alistair.francis@wdc.com>2020-10-22 12:00:46 -0700
commita54d259157e2575b69e2cf7cf03592c74559cb7e (patch)
tree4360abfcffc7237a2fc593abec5d58a6c983adff /hw/misc
parent33a9a57d2c31ec9ed68858911dc490b5de15f342 (diff)
downloadqemu-a54d259157e2575b69e2cf7cf03592c74559cb7e.zip
qemu-a54d259157e2575b69e2cf7cf03592c74559cb7e.tar.gz
qemu-a54d259157e2575b69e2cf7cf03592c74559cb7e.tar.bz2
hw/misc/sifive_u_otp: Add write function and write-once protection
- Add write operation to update fuse data bit when PWE bit is on. - Add array, fuse_wo, to store the 'written' status for all bits of OTP to block the write operation. Signed-off-by: Green Wan <green.wan@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Bin Meng <bin.meng@windriver.com> Tested-by: Bin Meng <bin.meng@windriver.com> Message-id: 20201020033732.12921-2-green.wan@sifive.com Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Diffstat (limited to 'hw/misc')
-rw-r--r--hw/misc/sifive_u_otp.c30
1 files changed, 29 insertions, 1 deletions
diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c
index c2f3c8e..b9238d6 100644
--- a/hw/misc/sifive_u_otp.c
+++ b/hw/misc/sifive_u_otp.c
@@ -25,6 +25,14 @@
#include "qemu/module.h"
#include "hw/misc/sifive_u_otp.h"
+#define WRITTEN_BIT_ON 0x1
+
+#define SET_FUSEARRAY_BIT(map, i, off, bit) \
+ map[i] = bit ? (map[i] | bit << off) : (map[i] & ~(0x1 << off))
+
+#define GET_FUSEARRAY_BIT(map, i, off) \
+ ((map[i] >> off) & 0x1)
+
static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
{
SiFiveUOTPState *s = opaque;
@@ -123,7 +131,24 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr,
s->ptrim = val32;
break;
case SIFIVE_U_OTP_PWE:
- s->pwe = val32;
+ s->pwe = val32 & SIFIVE_U_OTP_PWE_EN;
+
+ /* PWE is enabled. Ignore PAS=1 (no redundancy cell) */
+ if (s->pwe && !s->pas) {
+ if (GET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "write once error: idx<%u>, bit<%u>\n",
+ s->pa, s->paio);
+ break;
+ }
+
+ /* write bit data */
+ SET_FUSEARRAY_BIT(s->fuse, s->pa, s->paio, s->pdin);
+
+ /* update written bit */
+ SET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio, WRITTEN_BIT_ON);
+ }
+
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
@@ -165,6 +190,9 @@ static void sifive_u_otp_reset(DeviceState *dev)
/* Make a valid content of serial number */
s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial;
s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial);
+
+ /* Initialize write-once map */
+ memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo));
}
static void sifive_u_otp_class_init(ObjectClass *klass, void *data)