qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

sifive_u_otp.c (8903B)


      1 /*
      2  * QEMU SiFive U OTP (One-Time Programmable) Memory interface
      3  *
      4  * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
      5  *
      6  * Simple model of the OTP to emulate register reads made by the SDK BSP
      7  *
      8  * This program is free software; you can redistribute it and/or modify it
      9  * under the terms and conditions of the GNU General Public License,
     10  * version 2 or later, as published by the Free Software Foundation.
     11  *
     12  * This program is distributed in the hope it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along with
     18  * this program.  If not, see <http://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include "qemu/osdep.h"
     22 #include "qapi/error.h"
     23 #include "hw/qdev-properties.h"
     24 #include "hw/qdev-properties-system.h"
     25 #include "hw/sysbus.h"
     26 #include "qemu/error-report.h"
     27 #include "qemu/log.h"
     28 #include "qemu/module.h"
     29 #include "hw/misc/sifive_u_otp.h"
     30 #include "sysemu/blockdev.h"
     31 #include "sysemu/block-backend.h"
     32 
     33 #define WRITTEN_BIT_ON 0x1
     34 
     35 #define SET_FUSEARRAY_BIT(map, i, off, bit)    \
     36     map[i] = bit ? (map[i] | bit << off) : (map[i] & ~(0x1 << off))
     37 
     38 #define GET_FUSEARRAY_BIT(map, i, off)    \
     39     ((map[i] >> off) & 0x1)
     40 
     41 static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
     42 {
     43     SiFiveUOTPState *s = opaque;
     44 
     45     switch (addr) {
     46     case SIFIVE_U_OTP_PA:
     47         return s->pa;
     48     case SIFIVE_U_OTP_PAIO:
     49         return s->paio;
     50     case SIFIVE_U_OTP_PAS:
     51         return s->pas;
     52     case SIFIVE_U_OTP_PCE:
     53         return s->pce;
     54     case SIFIVE_U_OTP_PCLK:
     55         return s->pclk;
     56     case SIFIVE_U_OTP_PDIN:
     57         return s->pdin;
     58     case SIFIVE_U_OTP_PDOUT:
     59         if ((s->pce & SIFIVE_U_OTP_PCE_EN) &&
     60             (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) &&
     61             (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) {
     62 
     63             /* read from backend */
     64             if (s->blk) {
     65                 int32_t buf;
     66 
     67                 if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
     68                               SIFIVE_U_OTP_FUSE_WORD, &buf, 0) < 0) {
     69                     error_report("read error index<%d>", s->pa);
     70                     return 0xff;
     71                 }
     72 
     73                 return buf;
     74             }
     75 
     76             return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK];
     77         } else {
     78             return 0xff;
     79         }
     80     case SIFIVE_U_OTP_PDSTB:
     81         return s->pdstb;
     82     case SIFIVE_U_OTP_PPROG:
     83         return s->pprog;
     84     case SIFIVE_U_OTP_PTC:
     85         return s->ptc;
     86     case SIFIVE_U_OTP_PTM:
     87         return s->ptm;
     88     case SIFIVE_U_OTP_PTM_REP:
     89         return s->ptm_rep;
     90     case SIFIVE_U_OTP_PTR:
     91         return s->ptr;
     92     case SIFIVE_U_OTP_PTRIM:
     93         return s->ptrim;
     94     case SIFIVE_U_OTP_PWE:
     95         return s->pwe;
     96     }
     97 
     98     qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n",
     99                   __func__, addr);
    100     return 0;
    101 }
    102 
    103 static void sifive_u_otp_write(void *opaque, hwaddr addr,
    104                                uint64_t val64, unsigned int size)
    105 {
    106     SiFiveUOTPState *s = opaque;
    107     uint32_t val32 = (uint32_t)val64;
    108 
    109     switch (addr) {
    110     case SIFIVE_U_OTP_PA:
    111         s->pa = val32 & SIFIVE_U_OTP_PA_MASK;
    112         break;
    113     case SIFIVE_U_OTP_PAIO:
    114         s->paio = val32;
    115         break;
    116     case SIFIVE_U_OTP_PAS:
    117         s->pas = val32;
    118         break;
    119     case SIFIVE_U_OTP_PCE:
    120         s->pce = val32;
    121         break;
    122     case SIFIVE_U_OTP_PCLK:
    123         s->pclk = val32;
    124         break;
    125     case SIFIVE_U_OTP_PDIN:
    126         s->pdin = val32;
    127         break;
    128     case SIFIVE_U_OTP_PDOUT:
    129         /* read-only */
    130         break;
    131     case SIFIVE_U_OTP_PDSTB:
    132         s->pdstb = val32;
    133         break;
    134     case SIFIVE_U_OTP_PPROG:
    135         s->pprog = val32;
    136         break;
    137     case SIFIVE_U_OTP_PTC:
    138         s->ptc = val32;
    139         break;
    140     case SIFIVE_U_OTP_PTM:
    141         s->ptm = val32;
    142         break;
    143     case SIFIVE_U_OTP_PTM_REP:
    144         s->ptm_rep = val32;
    145         break;
    146     case SIFIVE_U_OTP_PTR:
    147         s->ptr = val32;
    148         break;
    149     case SIFIVE_U_OTP_PTRIM:
    150         s->ptrim = val32;
    151         break;
    152     case SIFIVE_U_OTP_PWE:
    153         s->pwe = val32 & SIFIVE_U_OTP_PWE_EN;
    154 
    155         /* PWE is enabled. Ignore PAS=1 (no redundancy cell) */
    156         if (s->pwe && !s->pas) {
    157             if (GET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio)) {
    158                 qemu_log_mask(LOG_GUEST_ERROR,
    159                               "write once error: idx<%u>, bit<%u>\n",
    160                               s->pa, s->paio);
    161                 break;
    162             }
    163 
    164             /* write bit data */
    165             SET_FUSEARRAY_BIT(s->fuse, s->pa, s->paio, s->pdin);
    166 
    167             /* write to backend */
    168             if (s->blk) {
    169                 if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
    170                                SIFIVE_U_OTP_FUSE_WORD, &s->fuse[s->pa], 0)
    171                     < 0) {
    172                     error_report("write error index<%d>", s->pa);
    173                 }
    174             }
    175 
    176             /* update written bit */
    177             SET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio, WRITTEN_BIT_ON);
    178         }
    179 
    180         break;
    181     default:
    182         qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx
    183                       " v=0x%x\n", __func__, addr, val32);
    184     }
    185 }
    186 
    187 static const MemoryRegionOps sifive_u_otp_ops = {
    188     .read = sifive_u_otp_read,
    189     .write = sifive_u_otp_write,
    190     .endianness = DEVICE_NATIVE_ENDIAN,
    191     .valid = {
    192         .min_access_size = 4,
    193         .max_access_size = 4
    194     }
    195 };
    196 
    197 static Property sifive_u_otp_properties[] = {
    198     DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0),
    199     DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk),
    200     DEFINE_PROP_END_OF_LIST(),
    201 };
    202 
    203 static void sifive_u_otp_realize(DeviceState *dev, Error **errp)
    204 {
    205     SiFiveUOTPState *s = SIFIVE_U_OTP(dev);
    206     DriveInfo *dinfo;
    207 
    208     memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s,
    209                           TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE);
    210     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
    211 
    212     dinfo = drive_get(IF_PFLASH, 0, 0);
    213     if (!dinfo) {
    214         dinfo = drive_get(IF_NONE, 0, 0);
    215         if (dinfo) {
    216             warn_report("using \"-drive if=none\" for the OTP is deprecated, "
    217                         "use \"-drive if=pflash\" instead.");
    218         }
    219     }
    220     if (dinfo) {
    221         int ret;
    222         uint64_t perm;
    223         int filesize;
    224         BlockBackend *blk;
    225 
    226         blk = blk_by_legacy_dinfo(dinfo);
    227         filesize = SIFIVE_U_OTP_NUM_FUSES * SIFIVE_U_OTP_FUSE_WORD;
    228         if (blk_getlength(blk) < filesize) {
    229             error_setg(errp, "OTP drive size < 16K");
    230             return;
    231         }
    232 
    233         qdev_prop_set_drive_err(dev, "drive", blk, errp);
    234 
    235         if (s->blk) {
    236             perm = BLK_PERM_CONSISTENT_READ |
    237                    (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
    238             ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
    239             if (ret < 0) {
    240                 return;
    241             }
    242 
    243             if (blk_pread(s->blk, 0, filesize, s->fuse, 0) < 0) {
    244                 error_setg(errp, "failed to read the initial flash content");
    245                 return;
    246             }
    247         }
    248     }
    249 
    250     /* Initialize all fuses' initial value to 0xFFs */
    251     memset(s->fuse, 0xff, sizeof(s->fuse));
    252 
    253     /* Make a valid content of serial number */
    254     s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial;
    255     s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial);
    256 
    257     if (s->blk) {
    258         /* Put serial number to backend as well*/
    259         uint32_t serial_data;
    260         int index = SIFIVE_U_OTP_SERIAL_ADDR;
    261 
    262         serial_data = s->serial;
    263         if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD,
    264                        SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) {
    265             error_setg(errp, "failed to write index<%d>", index);
    266             return;
    267         }
    268 
    269         serial_data = ~(s->serial);
    270         if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD,
    271                        SIFIVE_U_OTP_FUSE_WORD, &serial_data, 0) < 0) {
    272             error_setg(errp, "failed to write index<%d>", index + 1);
    273             return;
    274         }
    275     }
    276 
    277     /* Initialize write-once map */
    278     memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo));
    279 }
    280 
    281 static void sifive_u_otp_class_init(ObjectClass *klass, void *data)
    282 {
    283     DeviceClass *dc = DEVICE_CLASS(klass);
    284 
    285     device_class_set_props(dc, sifive_u_otp_properties);
    286     dc->realize = sifive_u_otp_realize;
    287 }
    288 
    289 static const TypeInfo sifive_u_otp_info = {
    290     .name          = TYPE_SIFIVE_U_OTP,
    291     .parent        = TYPE_SYS_BUS_DEVICE,
    292     .instance_size = sizeof(SiFiveUOTPState),
    293     .class_init    = sifive_u_otp_class_init,
    294 };
    295 
    296 static void sifive_u_otp_register_types(void)
    297 {
    298     type_register_static(&sifive_u_otp_info);
    299 }
    300 
    301 type_init(sifive_u_otp_register_types)