xref: /openbmc/qemu/hw/nvram/xlnx-bbram.c (revision ad80e367)
1 /*
2  * QEMU model of the Xilinx BBRAM Battery Backed RAM
3  *
4  * Copyright (c) 2014-2021 Xilinx Inc.
5  * Copyright (c) 2023 Advanced Micro Devices, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "hw/nvram/xlnx-bbram.h"
28 
29 #include "qemu/error-report.h"
30 #include "qemu/log.h"
31 #include "qapi/error.h"
32 #include "sysemu/blockdev.h"
33 #include "migration/vmstate.h"
34 #include "hw/qdev-properties.h"
35 #include "hw/qdev-properties-system.h"
36 #include "hw/nvram/xlnx-efuse.h"
37 
38 #ifndef XLNX_BBRAM_ERR_DEBUG
39 #define XLNX_BBRAM_ERR_DEBUG 0
40 #endif
41 
42 REG32(BBRAM_STATUS, 0x0)
43     FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1)
44     FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1)
45     FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1)
46     FIELD(BBRAM_STATUS, PGM_MODE, 0, 1)
47 REG32(BBRAM_CTRL, 0x4)
48     FIELD(BBRAM_CTRL, ZEROIZE, 0, 1)
49 REG32(PGM_MODE, 0x8)
50 REG32(BBRAM_AES_CRC, 0xc)
51 REG32(BBRAM_0, 0x10)
52 REG32(BBRAM_1, 0x14)
53 REG32(BBRAM_2, 0x18)
54 REG32(BBRAM_3, 0x1c)
55 REG32(BBRAM_4, 0x20)
56 REG32(BBRAM_5, 0x24)
57 REG32(BBRAM_6, 0x28)
58 REG32(BBRAM_7, 0x2c)
59 REG32(BBRAM_8, 0x30)
60 REG32(BBRAM_SLVERR, 0x34)
61     FIELD(BBRAM_SLVERR, ENABLE, 0, 1)
62 REG32(BBRAM_ISR, 0x38)
63     FIELD(BBRAM_ISR, APB_SLVERR, 0, 1)
64 REG32(BBRAM_IMR, 0x3c)
65     FIELD(BBRAM_IMR, APB_SLVERR, 0, 1)
66 REG32(BBRAM_IER, 0x40)
67     FIELD(BBRAM_IER, APB_SLVERR, 0, 1)
68 REG32(BBRAM_IDR, 0x44)
69     FIELD(BBRAM_IDR, APB_SLVERR, 0, 1)
70 REG32(BBRAM_MSW_LOCK, 0x4c)
71     FIELD(BBRAM_MSW_LOCK, VAL, 0, 1)
72 
73 #define R_MAX (R_BBRAM_MSW_LOCK + 1)
74 
75 #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0)
76 
77 #define BBRAM_PGM_MAGIC 0x757bdf0d
78 
79 QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs));
80 
bbram_msw_locked(XlnxBBRam * s)81 static bool bbram_msw_locked(XlnxBBRam *s)
82 {
83     return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0;
84 }
85 
bbram_pgm_enabled(XlnxBBRam * s)86 static bool bbram_pgm_enabled(XlnxBBRam *s)
87 {
88     return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0;
89 }
90 
bbram_bdrv_error(XlnxBBRam * s,int rc,gchar * detail)91 static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail)
92 {
93     Error *errp = NULL;
94 
95     error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.",
96                      blk_name(s->blk), detail);
97     error_report("%s", error_get_pretty(errp));
98     error_free(errp);
99 
100     g_free(detail);
101 }
102 
bbram_bdrv_read(XlnxBBRam * s,Error ** errp)103 static void bbram_bdrv_read(XlnxBBRam *s, Error **errp)
104 {
105     uint32_t *ram = &s->regs[R_BBRAM_0];
106     int nr = RAM_MAX;
107 
108     if (!s->blk) {
109         return;
110     }
111 
112     s->blk_ro = !blk_supports_write_perm(s->blk);
113     if (!s->blk_ro) {
114         int rc;
115 
116         rc = blk_set_perm(s->blk,
117                           (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE),
118                           BLK_PERM_ALL, NULL);
119         if (rc) {
120             s->blk_ro = true;
121         }
122     }
123     if (s->blk_ro) {
124         warn_report("%s: Skip saving updates to read-only BBRAM backstore.",
125                     blk_name(s->blk));
126     }
127 
128     if (blk_pread(s->blk, 0, nr, ram, 0) < 0) {
129         error_setg(errp,
130                    "%s: Failed to read %u bytes from BBRAM backstore.",
131                    blk_name(s->blk), nr);
132         return;
133     }
134 
135     /* Convert from little-endian backstore for each 32-bit word */
136     nr /= 4;
137     while (nr--) {
138         ram[nr] = le32_to_cpu(ram[nr]);
139     }
140 }
141 
bbram_bdrv_sync(XlnxBBRam * s,uint64_t hwaddr)142 static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr)
143 {
144     uint32_t le32;
145     unsigned offset;
146     int rc;
147 
148     assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8);
149 
150     /* Backstore is always in little-endian */
151     le32 = cpu_to_le32(s->regs[hwaddr / 4]);
152 
153     /* Update zeroized flag */
154     if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) {
155         ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0);
156     }
157 
158     if (!s->blk || s->blk_ro) {
159         return;
160     }
161 
162     offset = hwaddr - A_BBRAM_0;
163     rc = blk_pwrite(s->blk, offset, 4, &le32, 0);
164     if (rc < 0) {
165         bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset));
166     }
167 }
168 
bbram_bdrv_zero(XlnxBBRam * s)169 static void bbram_bdrv_zero(XlnxBBRam *s)
170 {
171     int rc;
172 
173     ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1);
174 
175     if (!s->blk || s->blk_ro) {
176         return;
177     }
178 
179     rc = blk_make_zero(s->blk, 0);
180     if (rc < 0) {
181         bbram_bdrv_error(s, rc, g_strdup("zeroizing"));
182     }
183 
184     /* Restore bbram8 if it is non-zero */
185     if (s->regs[R_BBRAM_8]) {
186         bbram_bdrv_sync(s, A_BBRAM_8);
187     }
188 }
189 
bbram_zeroize(XlnxBBRam * s)190 static void bbram_zeroize(XlnxBBRam *s)
191 {
192     int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */
193 
194     memset(&s->regs[R_BBRAM_0], 0, nr);
195     bbram_bdrv_zero(s);
196 }
197 
bbram_update_irq(XlnxBBRam * s)198 static void bbram_update_irq(XlnxBBRam *s)
199 {
200     bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR];
201 
202     qemu_set_irq(s->irq_bbram, pending);
203 }
204 
bbram_ctrl_postw(RegisterInfo * reg,uint64_t val64)205 static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64)
206 {
207     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
208     uint32_t val = val64;
209 
210     if (val & R_BBRAM_CTRL_ZEROIZE_MASK) {
211         bbram_zeroize(s);
212         /* The bit is self clearing */
213         s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK;
214     }
215 }
216 
bbram_pgm_mode_postw(RegisterInfo * reg,uint64_t val64)217 static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64)
218 {
219     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
220     uint32_t val = val64;
221 
222     if (val == BBRAM_PGM_MAGIC) {
223         bbram_zeroize(s);
224 
225         /* The status bit is cleared only by POR */
226         ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1);
227     }
228 }
229 
bbram_aes_crc_postw(RegisterInfo * reg,uint64_t val64)230 static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64)
231 {
232     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
233     uint32_t calc_crc;
234 
235     if (!bbram_pgm_enabled(s)) {
236         /* We are not in programming mode, don't do anything */
237         return;
238     }
239 
240     /* Perform the AES integrity check */
241     s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK;
242 
243     /*
244      * Set check status.
245      *
246      * ZynqMP BBRAM check has a zero-u32 prepended; see:
247      *  https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311
248      */
249     calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0],
250                                    (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads);
251 
252     ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS,
253                      (s->regs[R_BBRAM_AES_CRC] == calc_crc));
254 }
255 
bbram_key_prew(RegisterInfo * reg,uint64_t val64)256 static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64)
257 {
258     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
259     uint32_t original_data = *(uint32_t *) reg->data;
260 
261     if (bbram_pgm_enabled(s)) {
262         return val64;
263     } else {
264         /* We are not in programming mode, don't do anything */
265         qemu_log_mask(LOG_GUEST_ERROR,
266                       "Not in programming mode, dropping the write\n");
267         return original_data;
268     }
269 }
270 
bbram_key_postw(RegisterInfo * reg,uint64_t val64)271 static void bbram_key_postw(RegisterInfo *reg, uint64_t val64)
272 {
273     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
274 
275     bbram_bdrv_sync(s, reg->access->addr);
276 }
277 
bbram_wo_postr(RegisterInfo * reg,uint64_t val)278 static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val)
279 {
280     return 0;
281 }
282 
bbram_r8_postr(RegisterInfo * reg,uint64_t val)283 static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val)
284 {
285     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
286 
287     return s->bbram8_wo ? bbram_wo_postr(reg, val) : val;
288 }
289 
bbram_r8_readonly(XlnxBBRam * s)290 static bool bbram_r8_readonly(XlnxBBRam *s)
291 {
292     return !bbram_pgm_enabled(s) || bbram_msw_locked(s);
293 }
294 
bbram_r8_prew(RegisterInfo * reg,uint64_t val64)295 static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64)
296 {
297     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
298 
299     if (bbram_r8_readonly(s)) {
300         val64 = *(uint32_t *)reg->data;
301     }
302 
303     return val64;
304 }
305 
bbram_r8_postw(RegisterInfo * reg,uint64_t val64)306 static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64)
307 {
308     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
309 
310     if (!bbram_r8_readonly(s)) {
311         bbram_bdrv_sync(s, A_BBRAM_8);
312     }
313 }
314 
bbram_msw_lock_prew(RegisterInfo * reg,uint64_t val64)315 static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64)
316 {
317     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
318 
319     /* Never lock if bbram8 is wo; and, only POR can clear the lock */
320     if (s->bbram8_wo) {
321         val64 = 0;
322     } else {
323         val64 |= s->regs[R_BBRAM_MSW_LOCK];
324     }
325 
326     return val64;
327 }
328 
bbram_isr_postw(RegisterInfo * reg,uint64_t val64)329 static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64)
330 {
331     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
332 
333     bbram_update_irq(s);
334 }
335 
bbram_ier_prew(RegisterInfo * reg,uint64_t val64)336 static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64)
337 {
338     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
339     uint32_t val = val64;
340 
341     s->regs[R_BBRAM_IMR] &= ~val;
342     bbram_update_irq(s);
343     return 0;
344 }
345 
bbram_idr_prew(RegisterInfo * reg,uint64_t val64)346 static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64)
347 {
348     XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
349     uint32_t val = val64;
350 
351     s->regs[R_BBRAM_IMR] |= val;
352     bbram_update_irq(s);
353     return 0;
354 }
355 
356 static RegisterAccessInfo bbram_ctrl_regs_info[] = {
357     {   .name = "BBRAM_STATUS",  .addr = A_BBRAM_STATUS,
358         .rsvd = 0xee,
359         .ro = 0x3ff,
360     },{ .name = "BBRAM_CTRL",  .addr = A_BBRAM_CTRL,
361         .post_write = bbram_ctrl_postw,
362     },{ .name = "PGM_MODE",  .addr = A_PGM_MODE,
363         .post_write = bbram_pgm_mode_postw,
364     },{ .name = "BBRAM_AES_CRC",  .addr = A_BBRAM_AES_CRC,
365         .post_write = bbram_aes_crc_postw,
366         .post_read = bbram_wo_postr,
367     },{ .name = "BBRAM_0",  .addr = A_BBRAM_0,
368         .pre_write = bbram_key_prew,
369         .post_write = bbram_key_postw,
370         .post_read = bbram_wo_postr,
371     },{ .name = "BBRAM_1",  .addr = A_BBRAM_1,
372         .pre_write = bbram_key_prew,
373         .post_write = bbram_key_postw,
374         .post_read = bbram_wo_postr,
375     },{ .name = "BBRAM_2",  .addr = A_BBRAM_2,
376         .pre_write = bbram_key_prew,
377         .post_write = bbram_key_postw,
378         .post_read = bbram_wo_postr,
379     },{ .name = "BBRAM_3",  .addr = A_BBRAM_3,
380         .pre_write = bbram_key_prew,
381         .post_write = bbram_key_postw,
382         .post_read = bbram_wo_postr,
383     },{ .name = "BBRAM_4",  .addr = A_BBRAM_4,
384         .pre_write = bbram_key_prew,
385         .post_write = bbram_key_postw,
386         .post_read = bbram_wo_postr,
387     },{ .name = "BBRAM_5",  .addr = A_BBRAM_5,
388         .pre_write = bbram_key_prew,
389         .post_write = bbram_key_postw,
390         .post_read = bbram_wo_postr,
391     },{ .name = "BBRAM_6",  .addr = A_BBRAM_6,
392         .pre_write = bbram_key_prew,
393         .post_write = bbram_key_postw,
394         .post_read = bbram_wo_postr,
395     },{ .name = "BBRAM_7",  .addr = A_BBRAM_7,
396         .pre_write = bbram_key_prew,
397         .post_write = bbram_key_postw,
398         .post_read = bbram_wo_postr,
399     },{ .name = "BBRAM_8",  .addr = A_BBRAM_8,
400         .pre_write = bbram_r8_prew,
401         .post_write = bbram_r8_postw,
402         .post_read = bbram_r8_postr,
403     },{ .name = "BBRAM_SLVERR",  .addr = A_BBRAM_SLVERR,
404         .rsvd = ~1,
405     },{ .name = "BBRAM_ISR",  .addr = A_BBRAM_ISR,
406         .w1c = 0x1,
407         .post_write = bbram_isr_postw,
408     },{ .name = "BBRAM_IMR",  .addr = A_BBRAM_IMR,
409         .ro = 0x1,
410     },{ .name = "BBRAM_IER",  .addr = A_BBRAM_IER,
411         .pre_write = bbram_ier_prew,
412     },{ .name = "BBRAM_IDR",  .addr = A_BBRAM_IDR,
413         .pre_write = bbram_idr_prew,
414     },{ .name = "BBRAM_MSW_LOCK",  .addr = A_BBRAM_MSW_LOCK,
415         .pre_write = bbram_msw_lock_prew,
416         .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK,
417     }
418 };
419 
bbram_ctrl_reset_hold(Object * obj,ResetType type)420 static void bbram_ctrl_reset_hold(Object *obj, ResetType type)
421 {
422     XlnxBBRam *s = XLNX_BBRAM(obj);
423     unsigned int i;
424 
425     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
426         if (i < R_BBRAM_0 || i > R_BBRAM_8) {
427             register_reset(&s->regs_info[i]);
428         }
429     }
430 
431     bbram_update_irq(s);
432 }
433 
434 static const MemoryRegionOps bbram_ctrl_ops = {
435     .read = register_read_memory,
436     .write = register_write_memory,
437     .endianness = DEVICE_LITTLE_ENDIAN,
438     .valid = {
439         .min_access_size = 4,
440         .max_access_size = 4,
441     },
442 };
443 
bbram_ctrl_realize(DeviceState * dev,Error ** errp)444 static void bbram_ctrl_realize(DeviceState *dev, Error **errp)
445 {
446     XlnxBBRam *s = XLNX_BBRAM(dev);
447 
448     if (s->crc_zpads) {
449         s->bbram8_wo = true;
450     }
451 
452     bbram_bdrv_read(s, errp);
453 }
454 
bbram_ctrl_init(Object * obj)455 static void bbram_ctrl_init(Object *obj)
456 {
457     XlnxBBRam *s = XLNX_BBRAM(obj);
458     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
459     RegisterInfoArray *reg_array;
460 
461     reg_array =
462         register_init_block32(DEVICE(obj), bbram_ctrl_regs_info,
463                               ARRAY_SIZE(bbram_ctrl_regs_info),
464                               s->regs_info, s->regs,
465                               &bbram_ctrl_ops,
466                               XLNX_BBRAM_ERR_DEBUG,
467                               R_MAX * 4);
468 
469     sysbus_init_mmio(sbd, &reg_array->mem);
470     sysbus_init_irq(sbd, &s->irq_bbram);
471 }
472 
bbram_prop_set_drive(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)473 static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name,
474                                  void *opaque, Error **errp)
475 {
476     DeviceState *dev = DEVICE(obj);
477 
478     qdev_prop_drive.set(obj, v, name, opaque, errp);
479 
480     /* Fill initial data if backend is attached after realized */
481     if (dev->realized) {
482         bbram_bdrv_read(XLNX_BBRAM(obj), errp);
483     }
484 }
485 
bbram_prop_get_drive(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)486 static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name,
487                                  void *opaque, Error **errp)
488 {
489     qdev_prop_drive.get(obj, v, name, opaque, errp);
490 }
491 
bbram_prop_release_drive(Object * obj,const char * name,void * opaque)492 static void bbram_prop_release_drive(Object *obj, const char *name,
493                                      void *opaque)
494 {
495     qdev_prop_drive.release(obj, name, opaque);
496 }
497 
498 static const PropertyInfo bbram_prop_drive = {
499     .name  = "str",
500     .description = "Node name or ID of a block device to use as BBRAM backend",
501     .realized_set_allowed = true,
502     .get = bbram_prop_get_drive,
503     .set = bbram_prop_set_drive,
504     .release = bbram_prop_release_drive,
505 };
506 
507 static const VMStateDescription vmstate_bbram_ctrl = {
508     .name = TYPE_XLNX_BBRAM,
509     .version_id = 1,
510     .minimum_version_id = 1,
511     .fields = (const VMStateField[]) {
512         VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX),
513         VMSTATE_END_OF_LIST(),
514     }
515 };
516 
517 static Property bbram_ctrl_props[] = {
518     DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *),
519     DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1),
520     DEFINE_PROP_END_OF_LIST(),
521 };
522 
bbram_ctrl_class_init(ObjectClass * klass,void * data)523 static void bbram_ctrl_class_init(ObjectClass *klass, void *data)
524 {
525     DeviceClass *dc = DEVICE_CLASS(klass);
526     ResettableClass *rc = RESETTABLE_CLASS(klass);
527 
528     rc->phases.hold = bbram_ctrl_reset_hold;
529     dc->realize = bbram_ctrl_realize;
530     dc->vmsd = &vmstate_bbram_ctrl;
531     device_class_set_props(dc, bbram_ctrl_props);
532 }
533 
534 static const TypeInfo bbram_ctrl_info = {
535     .name          = TYPE_XLNX_BBRAM,
536     .parent        = TYPE_SYS_BUS_DEVICE,
537     .instance_size = sizeof(XlnxBBRam),
538     .class_init    = bbram_ctrl_class_init,
539     .instance_init = bbram_ctrl_init,
540 };
541 
bbram_ctrl_register_types(void)542 static void bbram_ctrl_register_types(void)
543 {
544     type_register_static(&bbram_ctrl_info);
545 }
546 
547 type_init(bbram_ctrl_register_types)
548