diff mbox series

[1/3] Add dummy i.MXS STMP register support

Message ID 20200301170443.12904-2-linux@roeck-us.net
State New
Headers show
Series Wire up USB controllers on fsl-imx6 and fsl-imx6ul | expand

Commit Message

Guenter Roeck March 1, 2020, 5:04 p.m. UTC
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
diff mbox series

Patch

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index bdd77d8020..68af3f1e2a 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -123,6 +123,9 @@  config AUX
     bool
     select I2C
 
+config STMP
+    bool
+
 config UNIMP
     bool
 
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index da993f45b7..942653854c 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -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
 
diff --git a/hw/misc/stmp.c b/hw/misc/stmp.c
new file mode 100644
index 0000000000..eb909fccfe
--- /dev/null
+++ b/hw/misc/stmp.c
@@ -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)
diff --git a/include/hw/misc/stmp.h b/include/hw/misc/stmp.h
new file mode 100644
index 0000000000..941ceb25dd
--- /dev/null
+++ b/include/hw/misc/stmp.h
@@ -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