@@ -123,6 +123,9 @@ config AUX
bool
select I2C
+config STMP
+ bool
+
config UNIMP
bool
@@ -9,6 +9,7 @@ common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
common-obj-$(CONFIG_EDU) += edu.o
common-obj-$(CONFIG_PCA9552) += pca9552.o
+common-obj-$(CONFIG_STMP) += stmp.o
common-obj-$(CONFIG_UNIMP) += unimp.o
common-obj-$(CONFIG_FW_CFG_DMA) += vmcoreinfo.o
new file mode 100644
@@ -0,0 +1,121 @@
+/*
+ * MXS "STMP" dummy device
+ *
+ * This is a dummy device which follows MXS "STMP" register layout.
+ * It's useful for stubbing out regions of an SoC or board
+ * map which correspond to devices that have not yet been
+ * implemented, yet require "STMP" device specific reset support.
+ * This is often sufficient to placate initial guest device
+ * driver probing such that the system will come up.
+ *
+ * Derived from "unimplemented" device code.
+ * Copyright Linaro Limited, 2017
+ * Written by Peter Maydell
+ *
+ * Written by Guenter Roeck
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/misc/stmp.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+
+#define REG_VAL 0x0
+#define REG_SET 0x4
+#define REG_CLR 0x8
+#define REG_TOG 0xc
+
+#define STMP_MODULE_CLKGATE (1 << 30)
+#define STMP_MODULE_SFTRST (1 << 31)
+
+static uint64_t stmp_read(void *opaque, hwaddr offset, unsigned size)
+{
+ StmpDeviceState *s = STMP_DEVICE(opaque);
+
+ switch (offset) {
+ case REG_VAL:
+ return s->regval;
+ default:
+ return 0;
+ }
+}
+
+static void stmp_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ StmpDeviceState *s = STMP_DEVICE(opaque);
+
+ switch (offset) {
+ case REG_VAL:
+ s->regval = value;
+ break;
+ case REG_SET:
+ s->regval |= value;
+ if (s->have_reset && (value & STMP_MODULE_SFTRST)) {
+ s->regval |= STMP_MODULE_CLKGATE;
+ }
+ break;
+ case REG_CLR:
+ s->regval &= ~value;
+ break;
+ case REG_TOG:
+ s->regval ^= value;
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps stmp_ops = {
+ .read = stmp_read,
+ .write = stmp_write,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void stmp_realize(DeviceState *dev, Error **errp)
+{
+ StmpDeviceState *s = STMP_DEVICE(dev);
+
+ if (s->name == NULL) {
+ error_setg(errp, "property 'name' not specified");
+ return;
+ }
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &stmp_ops, s,
+ s->name, 0x10);
+ sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+}
+
+static Property stmp_properties[] = {
+ DEFINE_PROP_STRING("name", StmpDeviceState, name),
+ DEFINE_PROP_BOOL("have-reset", StmpDeviceState, have_reset, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void stmp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = stmp_realize;
+ device_class_set_props(dc, stmp_properties);
+}
+
+static const TypeInfo stmp_info = {
+ .name = TYPE_STMP_DEVICE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(StmpDeviceState),
+ .class_init = stmp_class_init,
+};
+
+static void stmp_register_types(void)
+{
+ type_register_static(&stmp_info);
+}
+
+type_init(stmp_register_types)
new file mode 100644
@@ -0,0 +1,47 @@
+/*
+ * "Stmp" device
+ *
+ * Written by Guenter Roeck
+ */
+
+#ifndef HW_MISC_STMP_H
+#define HW_MISC_STMP_H
+
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+
+#define TYPE_STMP_DEVICE "stmp-device"
+
+#define STMP_DEVICE(obj) \
+ OBJECT_CHECK(StmpDeviceState, (obj), TYPE_STMP_DEVICE)
+
+typedef struct {
+ SysBusDevice parent_obj;
+ MemoryRegion iomem;
+ char *name;
+ bool have_reset;
+ uint32_t regval;
+} StmpDeviceState;
+
+/**
+ * create_stmp_device: create and map a dummy device with STMP register layout
+ * @name: name of the device for debug logging
+ * @have_reset: True if the register has reset functionality
+ * @base: base address of the device's MMIO region
+ *
+ * This utility function creates and maps an instance of stmp-device,
+ * which is a dummy device which follows STMP register layout.
+ */
+static inline void create_stmp_device(const char *name, bool have_reset,
+ hwaddr base)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_STMP_DEVICE);
+
+ qdev_prop_set_string(dev, "name", name);
+ qdev_prop_set_bit(dev, "have-reset", have_reset);
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, base, 0);
+}
+
+#endif
STMP registers are registers with a specific register layout. When using this layout, a register is implemented as set of four: - The register itself - A register to set individual register bits - A register to reset individual register bits - A register to toggle individual register bits This register layout is used in various i.MXS SoCs. In some cases, bit 31 of a STMP register has special reset functionality. Setting the reset bit resets the chip or block and then sets bit 30. This functionality is common enough that the Linux kernel implements a library function to support it. This patch implements an STMP register as a special device called STMP device. An STMP device can be instantiated on top of an unimplemented device. Each instance implements a single register of this unimplemented device. In some cases, this is necessary and sufficient to be able to load a driver. The term "STMP" originates from the Linux kernel. Its origin and meaning is unknown to the author, but it seemed to make sense to use the same terminology here. Signed-off-by: Guenter Roeck <linux@roeck-us.net> --- hw/misc/Kconfig | 3 + hw/misc/Makefile.objs | 1 + hw/misc/stmp.c | 121 +++++++++++++++++++++++++++++++++++++++++ include/hw/misc/stmp.h | 47 ++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 hw/misc/stmp.c create mode 100644 include/hw/misc/stmp.h