xref: /openbmc/qemu/hw/ppc/pnv_pnor.c (revision 77c05b0b)
1 /*
2  * QEMU PowerNV PNOR simple model
3  *
4  * Copyright (c) 2015-2019, IBM Corporation.
5  *
6  * This code is licensed under the GPL version 2 or later. See the
7  * COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "qapi/error.h"
12 #include "qemu/error-report.h"
13 #include "qemu/log.h"
14 #include "qemu/units.h"
15 #include "sysemu/block-backend.h"
16 #include "sysemu/blockdev.h"
17 #include "hw/loader.h"
18 #include "hw/ppc/pnv_pnor.h"
19 #include "hw/qdev-properties.h"
20 #include "hw/qdev-properties-system.h"
21 
22 static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size)
23 {
24     PnvPnor *s = PNV_PNOR(opaque);
25     uint64_t ret = 0;
26     int i;
27 
28     for (i = 0; i < size; i++) {
29         ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1));
30     }
31 
32     return ret;
33 }
34 
35 static void pnv_pnor_update(PnvPnor *s, int offset, int size)
36 {
37     int offset_end;
38     int ret;
39 
40     if (s->blk) {
41         return;
42     }
43 
44     offset_end = offset + size;
45     offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
46     offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
47 
48     ret = blk_pwrite(s->blk, offset, s->storage + offset,
49                      offset_end - offset, 0);
50     if (ret < 0) {
51         error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset,
52                      strerror(-ret));
53     }
54 }
55 
56 static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data,
57                            unsigned size)
58 {
59     PnvPnor *s = PNV_PNOR(opaque);
60     int i;
61 
62     for (i = 0; i < size; i++) {
63         s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF;
64     }
65     pnv_pnor_update(s, addr, size);
66 }
67 
68 /*
69  * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash
70  * is BIG.
71  */
72 static const MemoryRegionOps pnv_pnor_ops = {
73     .read = pnv_pnor_read,
74     .write = pnv_pnor_write,
75     .endianness = DEVICE_BIG_ENDIAN,
76     .valid = {
77         .min_access_size = 1,
78         .max_access_size = 4,
79     },
80 };
81 
82 static void pnv_pnor_realize(DeviceState *dev, Error **errp)
83 {
84     PnvPnor *s = PNV_PNOR(dev);
85     int ret;
86 
87     if (s->blk) {
88         uint64_t perm = BLK_PERM_CONSISTENT_READ |
89                         (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE);
90         ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp);
91         if (ret < 0) {
92             return;
93         }
94 
95         s->size = blk_getlength(s->blk);
96         if (s->size <= 0) {
97             error_setg(errp, "failed to get flash size");
98             return;
99         }
100 
101         s->storage = blk_blockalign(s->blk, s->size);
102 
103         if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) {
104             error_setg(errp, "failed to read the initial flash content");
105             return;
106         }
107     } else {
108         s->storage = blk_blockalign(NULL, s->size);
109         memset(s->storage, 0xFF, s->size);
110     }
111 
112     memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s,
113                           TYPE_PNV_PNOR, s->size);
114 }
115 
116 static Property pnv_pnor_properties[] = {
117     DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB),
118     DEFINE_PROP_DRIVE("drive", PnvPnor, blk),
119     DEFINE_PROP_END_OF_LIST(),
120 };
121 
122 static void pnv_pnor_class_init(ObjectClass *klass, void *data)
123 {
124     DeviceClass *dc = DEVICE_CLASS(klass);
125 
126     dc->realize = pnv_pnor_realize;
127     device_class_set_props(dc, pnv_pnor_properties);
128 }
129 
130 static const TypeInfo pnv_pnor_info = {
131     .name          = TYPE_PNV_PNOR,
132     .parent        = TYPE_SYS_BUS_DEVICE,
133     .instance_size = sizeof(PnvPnor),
134     .class_init    = pnv_pnor_class_init,
135 };
136 
137 static void pnv_pnor_register_types(void)
138 {
139     type_register_static(&pnv_pnor_info);
140 }
141 
142 type_init(pnv_pnor_register_types)
143