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 <li...@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 --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 -- 2.17.1