xref: /openbmc/qemu/hw/misc/sifive_u_otp.c (revision e803a7f9)
10fa9e329SBin Meng /*
20fa9e329SBin Meng  * QEMU SiFive U OTP (One-Time Programmable) Memory interface
30fa9e329SBin Meng  *
40fa9e329SBin Meng  * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
50fa9e329SBin Meng  *
60fa9e329SBin Meng  * Simple model of the OTP to emulate register reads made by the SDK BSP
70fa9e329SBin Meng  *
80fa9e329SBin Meng  * This program is free software; you can redistribute it and/or modify it
90fa9e329SBin Meng  * under the terms and conditions of the GNU General Public License,
100fa9e329SBin Meng  * version 2 or later, as published by the Free Software Foundation.
110fa9e329SBin Meng  *
120fa9e329SBin Meng  * This program is distributed in the hope it will be useful, but WITHOUT
130fa9e329SBin Meng  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
140fa9e329SBin Meng  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
150fa9e329SBin Meng  * more details.
160fa9e329SBin Meng  *
170fa9e329SBin Meng  * You should have received a copy of the GNU General Public License along with
180fa9e329SBin Meng  * this program.  If not, see <http://www.gnu.org/licenses/>.
190fa9e329SBin Meng  */
200fa9e329SBin Meng 
210fa9e329SBin Meng #include "qemu/osdep.h"
2251b6c1bbSGreen Wan #include "qapi/error.h"
230fa9e329SBin Meng #include "hw/qdev-properties.h"
24ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
250fa9e329SBin Meng #include "hw/sysbus.h"
26a033d800SBin Meng #include "qemu/error-report.h"
270fa9e329SBin Meng #include "qemu/log.h"
280fa9e329SBin Meng #include "qemu/module.h"
290fa9e329SBin Meng #include "hw/misc/sifive_u_otp.h"
3051b6c1bbSGreen Wan #include "sysemu/blockdev.h"
3151b6c1bbSGreen Wan #include "sysemu/block-backend.h"
320fa9e329SBin Meng 
33a54d2591SGreen Wan #define WRITTEN_BIT_ON 0x1
34a54d2591SGreen Wan 
35a54d2591SGreen Wan #define SET_FUSEARRAY_BIT(map, i, off, bit)    \
36a54d2591SGreen Wan     map[i] = bit ? (map[i] | bit << off) : (map[i] & ~(0x1 << off))
37a54d2591SGreen Wan 
38a54d2591SGreen Wan #define GET_FUSEARRAY_BIT(map, i, off)    \
39a54d2591SGreen Wan     ((map[i] >> off) & 0x1)
40a54d2591SGreen Wan 
sifive_u_otp_read(void * opaque,hwaddr addr,unsigned int size)410fa9e329SBin Meng static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
420fa9e329SBin Meng {
430fa9e329SBin Meng     SiFiveUOTPState *s = opaque;
440fa9e329SBin Meng 
450fa9e329SBin Meng     switch (addr) {
460fa9e329SBin Meng     case SIFIVE_U_OTP_PA:
470fa9e329SBin Meng         return s->pa;
480fa9e329SBin Meng     case SIFIVE_U_OTP_PAIO:
490fa9e329SBin Meng         return s->paio;
500fa9e329SBin Meng     case SIFIVE_U_OTP_PAS:
510fa9e329SBin Meng         return s->pas;
520fa9e329SBin Meng     case SIFIVE_U_OTP_PCE:
530fa9e329SBin Meng         return s->pce;
540fa9e329SBin Meng     case SIFIVE_U_OTP_PCLK:
550fa9e329SBin Meng         return s->pclk;
560fa9e329SBin Meng     case SIFIVE_U_OTP_PDIN:
570fa9e329SBin Meng         return s->pdin;
580fa9e329SBin Meng     case SIFIVE_U_OTP_PDOUT:
590fa9e329SBin Meng         if ((s->pce & SIFIVE_U_OTP_PCE_EN) &&
600fa9e329SBin Meng             (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) &&
610fa9e329SBin Meng             (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) {
6251b6c1bbSGreen Wan 
6351b6c1bbSGreen Wan             /* read from backend */
6451b6c1bbSGreen Wan             if (s->blk) {
6551b6c1bbSGreen Wan                 int32_t buf;
6651b6c1bbSGreen Wan 
67*a9262f55SAlberto Faria                 if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
68*a9262f55SAlberto Faria                               SIFIVE_U_OTP_FUSE_WORD, &buf, 0) < 0) {
69a033d800SBin Meng                     error_report("read error index<%d>", s->pa);
7029b5fe0dSGreen Wan                     return 0xff;
7129b5fe0dSGreen Wan                 }
7229b5fe0dSGreen Wan 
7351b6c1bbSGreen Wan                 return buf;
7451b6c1bbSGreen Wan             }
7551b6c1bbSGreen Wan 
760fa9e329SBin Meng             return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK];
770fa9e329SBin Meng         } else {
780fa9e329SBin Meng             return 0xff;
790fa9e329SBin Meng         }
800fa9e329SBin Meng     case SIFIVE_U_OTP_PDSTB:
810fa9e329SBin Meng         return s->pdstb;
820fa9e329SBin Meng     case SIFIVE_U_OTP_PPROG:
830fa9e329SBin Meng         return s->pprog;
840fa9e329SBin Meng     case SIFIVE_U_OTP_PTC:
850fa9e329SBin Meng         return s->ptc;
860fa9e329SBin Meng     case SIFIVE_U_OTP_PTM:
870fa9e329SBin Meng         return s->ptm;
880fa9e329SBin Meng     case SIFIVE_U_OTP_PTM_REP:
890fa9e329SBin Meng         return s->ptm_rep;
900fa9e329SBin Meng     case SIFIVE_U_OTP_PTR:
910fa9e329SBin Meng         return s->ptr;
920fa9e329SBin Meng     case SIFIVE_U_OTP_PTRIM:
930fa9e329SBin Meng         return s->ptrim;
940fa9e329SBin Meng     case SIFIVE_U_OTP_PWE:
950fa9e329SBin Meng         return s->pwe;
960fa9e329SBin Meng     }
970fa9e329SBin Meng 
980fa9e329SBin Meng     qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
990fa9e329SBin Meng                   __func__, addr);
1000fa9e329SBin Meng     return 0;
1010fa9e329SBin Meng }
1020fa9e329SBin Meng 
sifive_u_otp_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)1030fa9e329SBin Meng static void sifive_u_otp_write(void *opaque, hwaddr addr,
1040fa9e329SBin Meng                                uint64_t val64, unsigned int size)
1050fa9e329SBin Meng {
1060fa9e329SBin Meng     SiFiveUOTPState *s = opaque;
1070fa9e329SBin Meng     uint32_t val32 = (uint32_t)val64;
1080fa9e329SBin Meng 
1090fa9e329SBin Meng     switch (addr) {
1100fa9e329SBin Meng     case SIFIVE_U_OTP_PA:
1110fa9e329SBin Meng         s->pa = val32 & SIFIVE_U_OTP_PA_MASK;
1120fa9e329SBin Meng         break;
1130fa9e329SBin Meng     case SIFIVE_U_OTP_PAIO:
1140fa9e329SBin Meng         s->paio = val32;
1150fa9e329SBin Meng         break;
1160fa9e329SBin Meng     case SIFIVE_U_OTP_PAS:
1170fa9e329SBin Meng         s->pas = val32;
1180fa9e329SBin Meng         break;
1190fa9e329SBin Meng     case SIFIVE_U_OTP_PCE:
1200fa9e329SBin Meng         s->pce = val32;
1210fa9e329SBin Meng         break;
1220fa9e329SBin Meng     case SIFIVE_U_OTP_PCLK:
1230fa9e329SBin Meng         s->pclk = val32;
1240fa9e329SBin Meng         break;
1250fa9e329SBin Meng     case SIFIVE_U_OTP_PDIN:
1260fa9e329SBin Meng         s->pdin = val32;
1270fa9e329SBin Meng         break;
1280fa9e329SBin Meng     case SIFIVE_U_OTP_PDOUT:
1290fa9e329SBin Meng         /* read-only */
1300fa9e329SBin Meng         break;
1310fa9e329SBin Meng     case SIFIVE_U_OTP_PDSTB:
1320fa9e329SBin Meng         s->pdstb = val32;
1330fa9e329SBin Meng         break;
1340fa9e329SBin Meng     case SIFIVE_U_OTP_PPROG:
1350fa9e329SBin Meng         s->pprog = val32;
1360fa9e329SBin Meng         break;
1370fa9e329SBin Meng     case SIFIVE_U_OTP_PTC:
1380fa9e329SBin Meng         s->ptc = val32;
1390fa9e329SBin Meng         break;
1400fa9e329SBin Meng     case SIFIVE_U_OTP_PTM:
1410fa9e329SBin Meng         s->ptm = val32;
1420fa9e329SBin Meng         break;
1430fa9e329SBin Meng     case SIFIVE_U_OTP_PTM_REP:
1440fa9e329SBin Meng         s->ptm_rep = val32;
1450fa9e329SBin Meng         break;
1460fa9e329SBin Meng     case SIFIVE_U_OTP_PTR:
1470fa9e329SBin Meng         s->ptr = val32;
1480fa9e329SBin Meng         break;
1490fa9e329SBin Meng     case SIFIVE_U_OTP_PTRIM:
1500fa9e329SBin Meng         s->ptrim = val32;
1510fa9e329SBin Meng         break;
1520fa9e329SBin Meng     case SIFIVE_U_OTP_PWE:
153a54d2591SGreen Wan         s->pwe = val32 & SIFIVE_U_OTP_PWE_EN;
154a54d2591SGreen Wan 
155a54d2591SGreen Wan         /* PWE is enabled. Ignore PAS=1 (no redundancy cell) */
156a54d2591SGreen Wan         if (s->pwe && !s->pas) {
157a54d2591SGreen Wan             if (GET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio)) {
158a54d2591SGreen Wan                 qemu_log_mask(LOG_GUEST_ERROR,
159a54d2591SGreen Wan                               "write once error: idx<%u>, bit<%u>\n",
160a54d2591SGreen Wan                               s->pa, s->paio);
161a54d2591SGreen Wan                 break;
162a54d2591SGreen Wan             }
163a54d2591SGreen Wan 
164a54d2591SGreen Wan             /* write bit data */
165a54d2591SGreen Wan             SET_FUSEARRAY_BIT(s->fuse, s->pa, s->paio, s->pdin);
166a54d2591SGreen Wan 
16751b6c1bbSGreen Wan             /* write to backend */
16851b6c1bbSGreen Wan             if (s->blk) {
16929b5fe0dSGreen Wan                 if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
170*a9262f55SAlberto Faria                                SIFIVE_U_OTP_FUSE_WORD, &s->fuse[s->pa], 0)
171*a9262f55SAlberto Faria                     < 0) {
172a033d800SBin Meng                     error_report("write error index<%d>", s->pa);
17329b5fe0dSGreen Wan                 }
17451b6c1bbSGreen Wan             }
17551b6c1bbSGreen Wan 
176a54d2591SGreen Wan             /* update written bit */
177a54d2591SGreen Wan             SET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio, WRITTEN_BIT_ON);
178a54d2591SGreen Wan         }
179a54d2591SGreen Wan 
1800fa9e329SBin Meng         break;
1810fa9e329SBin Meng     default:
1820fa9e329SBin Meng         qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
1830fa9e329SBin Meng                       " v=0x%x\n", __func__, addr, val32);
1840fa9e329SBin Meng     }
1850fa9e329SBin Meng }
1860fa9e329SBin Meng 
1870fa9e329SBin Meng static const MemoryRegionOps sifive_u_otp_ops = {
1880fa9e329SBin Meng     .read = sifive_u_otp_read,
1890fa9e329SBin Meng     .write = sifive_u_otp_write,
1900fa9e329SBin Meng     .endianness = DEVICE_NATIVE_ENDIAN,
1910fa9e329SBin Meng     .valid = {
1920fa9e329SBin Meng         .min_access_size = 4,
1930fa9e329SBin Meng         .max_access_size = 4
1940fa9e329SBin Meng     }
1950fa9e329SBin Meng };
1960fa9e329SBin Meng 
1970fa9e329SBin Meng static Property sifive_u_otp_properties[] = {
1980fa9e329SBin Meng     DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0),
19951b6c1bbSGreen Wan     DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk),
2000fa9e329SBin Meng     DEFINE_PROP_END_OF_LIST(),
2010fa9e329SBin Meng };
2020fa9e329SBin Meng 
sifive_u_otp_realize(DeviceState * dev,Error ** errp)2030fa9e329SBin Meng static void sifive_u_otp_realize(DeviceState *dev, Error **errp)
2040fa9e329SBin Meng {
2050fa9e329SBin Meng     SiFiveUOTPState *s = SIFIVE_U_OTP(dev);
20651b6c1bbSGreen Wan     DriveInfo *dinfo;
2070fa9e329SBin Meng 
2080fa9e329SBin Meng     memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s,
2090fa9e329SBin Meng                           TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE);
2100fa9e329SBin Meng     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
21151b6c1bbSGreen Wan 
21264eaa820SMarkus Armbruster     dinfo = drive_get(IF_PFLASH, 0, 0);
2136b717a8dSThomas Huth     if (dinfo) {
21451b6c1bbSGreen Wan         int ret;
21551b6c1bbSGreen Wan         uint64_t perm;
21651b6c1bbSGreen Wan         int filesize;
21751b6c1bbSGreen Wan         BlockBackend *blk;
21851b6c1bbSGreen Wan 
21951b6c1bbSGreen Wan         blk = blk_by_legacy_dinfo(dinfo);
22051b6c1bbSGreen Wan         filesize = SIFIVE_U_OTP_NUM_FUSES * SIFIVE_U_OTP_FUSE_WORD;
22151b6c1bbSGreen Wan         if (blk_getlength(blk) < filesize) {
22251b6c1bbSGreen Wan             error_setg(errp, "OTP drive size < 16K");
22351b6c1bbSGreen Wan             return;
22451b6c1bbSGreen Wan         }
22551b6c1bbSGreen Wan 
22651b6c1bbSGreen Wan         qdev_prop_set_drive_err(dev, "drive", blk, errp);
22751b6c1bbSGreen Wan 
22851b6c1bbSGreen Wan         if (s->blk) {
22951b6c1bbSGreen Wan             perm = BLK_PERM_CONSISTENT_READ |
23086b1cf32SKevin Wolf                    (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
23151b6c1bbSGreen Wan             ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
23251b6c1bbSGreen Wan             if (ret < 0) {
23351b6c1bbSGreen Wan                 return;
23451b6c1bbSGreen Wan             }
23551b6c1bbSGreen Wan 
236*a9262f55SAlberto Faria             if (blk_pread(s->blk, 0, filesize, s->fuse, 0) < 0) {
23751b6c1bbSGreen Wan                 error_setg(errp, "failed to read the initial flash content");
238526e7443SPhilippe Mathieu-Daudé                 return;
23951b6c1bbSGreen Wan             }
24051b6c1bbSGreen Wan         }
24151b6c1bbSGreen Wan     }
2420fa9e329SBin Meng 
2430fa9e329SBin Meng     /* Initialize all fuses' initial value to 0xFFs */
2440fa9e329SBin Meng     memset(s->fuse, 0xff, sizeof(s->fuse));
2450fa9e329SBin Meng 
2460fa9e329SBin Meng     /* Make a valid content of serial number */
2470fa9e329SBin Meng     s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial;
2480fa9e329SBin Meng     s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial);
249a54d2591SGreen Wan 
25051b6c1bbSGreen Wan     if (s->blk) {
25151b6c1bbSGreen Wan         /* Put serial number to backend as well*/
25251b6c1bbSGreen Wan         uint32_t serial_data;
25351b6c1bbSGreen Wan         int index = SIFIVE_U_OTP_SERIAL_ADDR;
25451b6c1bbSGreen Wan 
25551b6c1bbSGreen Wan         serial_data = s->serial;
25629b5fe0dSGreen Wan         if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD,
257*a9262f55SAlberto Faria                        SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) {
258526e7443SPhilippe Mathieu-Daudé             error_setg(errp, "failed to write index<%d>", index);
259526e7443SPhilippe Mathieu-Daudé             return;
26029b5fe0dSGreen Wan         }
26151b6c1bbSGreen Wan 
26251b6c1bbSGreen Wan         serial_data = ~(s->serial);
26329b5fe0dSGreen Wan         if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD,
264*a9262f55SAlberto Faria                        SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) {
265526e7443SPhilippe Mathieu-Daudé             error_setg(errp, "failed to write index<%d>", index + 1);
266526e7443SPhilippe Mathieu-Daudé             return;
26729b5fe0dSGreen Wan         }
26851b6c1bbSGreen Wan     }
26951b6c1bbSGreen Wan 
270a54d2591SGreen Wan     /* Initialize write-once map */
271a54d2591SGreen Wan     memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo));
2720fa9e329SBin Meng }
2730fa9e329SBin Meng 
sifive_u_otp_class_init(ObjectClass * klass,void * data)2740fa9e329SBin Meng static void sifive_u_otp_class_init(ObjectClass *klass, void *data)
2750fa9e329SBin Meng {
2760fa9e329SBin Meng     DeviceClass *dc = DEVICE_CLASS(klass);
2770fa9e329SBin Meng 
2780fa9e329SBin Meng     device_class_set_props(dc, sifive_u_otp_properties);
2790fa9e329SBin Meng     dc->realize = sifive_u_otp_realize;
2800fa9e329SBin Meng }
2810fa9e329SBin Meng 
2820fa9e329SBin Meng static const TypeInfo sifive_u_otp_info = {
2830fa9e329SBin Meng     .name          = TYPE_SIFIVE_U_OTP,
2840fa9e329SBin Meng     .parent        = TYPE_SYS_BUS_DEVICE,
2850fa9e329SBin Meng     .instance_size = sizeof(SiFiveUOTPState),
2860fa9e329SBin Meng     .class_init    = sifive_u_otp_class_init,
2870fa9e329SBin Meng };
2880fa9e329SBin Meng 
sifive_u_otp_register_types(void)2890fa9e329SBin Meng static void sifive_u_otp_register_types(void)
2900fa9e329SBin Meng {
2910fa9e329SBin Meng     type_register_static(&sifive_u_otp_info);
2920fa9e329SBin Meng }
2930fa9e329SBin Meng 
2940fa9e329SBin Meng type_init(sifive_u_otp_register_types)
295