diff mbox

xilinx_zynq: merged support for ULPI PHY and ULPI viewport from xilinx/qemu

Message ID 1459281084-13346-1-git-send-email-jbenz@mailbox.org
State New
Headers show

Commit Message

jbenz@mailbox.org March 29, 2016, 7:51 p.m. UTC
Signed-off-by: Joscha Benz <jbenz@mailbox.org>
---
 hw/usb/hcd-ehci-sysbus.c | 175 ++++++++++++++++++++++++++++++---
 hw/usb/hcd-ehci.c        |   1 +
 hw/usb/hcd-ehci.h        |  31 ++++++
 include/hw/register.h    | 245 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 441 insertions(+), 11 deletions(-)
 create mode 100644 include/hw/register.h

Comments

Alistair Francis March 29, 2016, 8:23 p.m. UTC | #1
On Tue, Mar 29, 2016 at 12:51 PM,  <jbenz@mailbox.org> wrote:
> Signed-off-by: Joscha Benz <jbenz@mailbox.org>

Hello Joscha ,

Thanks for the patch. In future can you please use git send-email to
send the patches instead of attaching the patch file. You can find
more information on doing this at:
http://wiki.qemu.org/Contribute/SubmitAPatch

What are you trying to do here? Why are you trying to apply the Xilinx
model of the device? This relies on the register API (which you have
included in your patch) which isn't in mainline QEMU yet, so that part
can't be accepted just yet. We are working at upstreaming that at the
moment though.

Thanks,

Alistair

> ---
>  hw/usb/hcd-ehci-sysbus.c | 175 ++++++++++++++++++++++++++++++---
>  hw/usb/hcd-ehci.c        |   1 +
>  hw/usb/hcd-ehci.h        |  31 ++++++
>  include/hw/register.h    | 245 +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 441 insertions(+), 11 deletions(-)
>  create mode 100644 include/hw/register.h
>
jbenz@mailbox.org March 30, 2016, 10:46 a.m. UTC | #2
Hi,

On 29.03.2016 22:23, Alistair Francis wrote:
> On Tue, Mar 29, 2016 at 12:51 PM,  <jbenz@mailbox.org> wrote:
>> Signed-off-by: Joscha Benz <jbenz@mailbox.org>
> 
> Hello Joscha ,
> 
> Thanks for the patch. In future can you please use git send-email to
> send the patches instead of attaching the patch file. You can find
> more information on doing this at:
> http://wiki.qemu.org/Contribute/SubmitAPatch

Of course.

> What are you trying to do here? Why are you trying to apply the Xilinx
> model of the device? This relies on the register API (which you have
> included in your patch) which isn't in mainline QEMU yet, so that part
> can't be accepted just yet. We are working at upstreaming that at the
> moment though.

I was trying to get USB support for that device. That's why i tried to
apply the Xilinx model since it has USB support.

Regards,

Joscha
Alistair Francis April 1, 2016, 12:40 a.m. UTC | #3
On Wed, Mar 30, 2016 at 3:46 AM, Joscha Benz <jbenz@mailbox.org> wrote:
> Hi,
>
> On 29.03.2016 22:23, Alistair Francis wrote:
>> On Tue, Mar 29, 2016 at 12:51 PM,  <jbenz@mailbox.org> wrote:
>>> Signed-off-by: Joscha Benz <jbenz@mailbox.org>
>>
>> Hello Joscha ,
>>
>> Thanks for the patch. In future can you please use git send-email to
>> send the patches instead of attaching the patch file. You can find
>> more information on doing this at:
>> http://wiki.qemu.org/Contribute/SubmitAPatch
>
> Of course.

Great! Thanks

>
>> What are you trying to do here? Why are you trying to apply the Xilinx
>> model of the device? This relies on the register API (which you have
>> included in your patch) which isn't in mainline QEMU yet, so that part
>> can't be accepted just yet. We are working at upstreaming that at the
>> moment though.
>
> I was trying to get USB support for that device. That's why i tried to
> apply the Xilinx model since it has USB support.

Do you have any more specifics on what you need? At the moment we
won't be able to take all of the code until the full register API is
accepted.

Thanks,

Alistair

>
> Regards,
>
> Joscha
>
jbenz@mailbox.org April 4, 2016, 3:04 p.m. UTC | #4
Hi,

excuse me for my delayed response.

On 01.04.2016 02:40, Alistair Francis wrote:

> Do you have any more specifics on what you need? At the moment we
> won't be able to take all of the code until the full register API is
> accepted.

I recently ported the USB host controller driver from Linux to Genode OS
and was trying to get USB support to QEMU so that further USB based
development can be done just using QEMU.

Regards,

Joscha
Alistair Francis Nov. 11, 2016, 11:45 p.m. UTC | #5
On Mon, Apr 4, 2016 at 8:04 AM, Joscha Benz <jbenz@mailbox.org> wrote:
> Hi,
>
> excuse me for my delayed response.
>
> On 01.04.2016 02:40, Alistair Francis wrote:
>
>> Do you have any more specifics on what you need? At the moment we
>> won't be able to take all of the code until the full register API is
>> accepted.
>
> I recently ported the USB host controller driver from Linux to Genode OS
> and was trying to get USB support to QEMU so that further USB based
> development can be done just using QEMU.

Hey Joscha,

I meant to email you earlier. The register API has been accepted to
mainline QEMU now.

Do you want to rebase your patch on top of the latest master and re-sent it?

Thanks,

Alistair

>
> Regards,
>
> Joscha
diff mbox

Patch

diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c
index 6c20604..4866450 100644
--- a/hw/usb/hcd-ehci-sysbus.c
+++ b/hw/usb/hcd-ehci-sysbus.c
@@ -17,6 +17,7 @@ 
 
 #include "qemu/osdep.h"
 #include "hw/usb/hcd-ehci.h"
+#include "hw/register.h"
 
 static const VMStateDescription vmstate_ehci_sysbus = {
     .name        = "ehci-sysbus",
@@ -43,15 +44,6 @@  static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp)
     sysbus_init_irq(d, &s->irq);
 }
 
-static void usb_ehci_sysbus_reset(DeviceState *dev)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(dev);
-    EHCISysBusState *i = SYS_BUS_EHCI(d);
-    EHCIState *s = &i->ehci;
-
-    ehci_reset(s);
-}
-
 static void ehci_sysbus_init(Object *obj)
 {
     SysBusDevice *d = SYS_BUS_DEVICE(obj);
@@ -80,7 +72,6 @@  static void ehci_sysbus_class_init(ObjectClass *klass, void *data)
     dc->realize = usb_ehci_sysbus_realize;
     dc->vmsd = &vmstate_ehci_sysbus;
     dc->props = ehci_sysbus_properties;
-    dc->reset = usb_ehci_sysbus_reset;
     set_bit(DEVICE_CATEGORY_USB, dc->categories);
 }
 
@@ -94,20 +85,182 @@  static const TypeInfo ehci_type_info = {
     .class_size    = sizeof(SysBusEHCIClass),
 };
 
+enum PS7USBRegs {
+    XLNX_ID = 0x0,
+    XLNX_HWGENERAL = 0x4,
+    XLNX_HWHOST = 0x8,
+    XLNX_HWTXBUF = 0x10,
+    XLNX_HWRXBUF = 0x14,
+    XLNX_DCIVERSION = 0x120,
+    XLNX_DCCPARAMS  = 0x124,
+};
+
+/* FIXME: Add the functionality of remaining phy registers */
+enum ULPIRegs {
+    VENDOR_ID_L = 0x0,
+    VENDOR_ID_H = 0x1,
+    PRODUCT_ID_L = 0x2,
+    PRODUCT_ID_H = 0x3,
+    SCRATCH_REG_0 = 0x16,
+};
+
+REG32(ULPI_VIEWPORT, PS7USB_ULPIVP_OFFSET)
+    FIELD(ULPI_VIEWPORT, ULPIDATWR, 8, 0)
+    FIELD(ULPI_VIEWPORT, ULPIDATRD, 8, 8)
+    FIELD(ULPI_VIEWPORT, ULPIADDR, 8, 16)
+    FIELD(ULPI_VIEWPORT, ULPIPORT, 3, 24)
+    FIELD(ULPI_VIEWPORT, ULPISS, 1, 27)
+    FIELD(ULPI_VIEWPORT, ULPIRW, 1, 29)
+    FIELD(ULPI_VIEWPORT, ULPIRUN, 1, 30)
+    FIELD(ULPI_VIEWPORT, ULPIWU, 1, 31)
+
+static void ehci_xlnx_reset(DeviceState *dev)
+{
+    PS7USBState *s = XLNX_PS7_USB(dev);
+
+    /* Show phy in normal functioning state after init */
+    s->ulpi_viewport = 0x8000000;
+    /* Vendor and product ID are as per micron ulpi phy specifications */
+    s->ulpireg[VENDOR_ID_L] = 0x24;
+    s->ulpireg[VENDOR_ID_H] = 0x04;
+    s->ulpireg[PRODUCT_ID_L] = 0x4;
+    s->ulpireg[PRODUCT_ID_H] = 0x0;
+
+}
+
 static void ehci_xlnx_class_init(ObjectClass *oc, void *data)
 {
     SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc);
     DeviceClass *dc = DEVICE_CLASS(oc);
 
+    dc->reset = ehci_xlnx_reset;
     set_bit(DEVICE_CATEGORY_USB, dc->categories);
     sec->capsbase = 0x100;
     sec->opregbase = 0x140;
 }
 
+static uint64_t xlnx_devreg_read(void *opaque, hwaddr addr, unsigned size)
+{
+    EHCIState *s = opaque;
+        /* DCIVERSION and DCCPARAMS are mapped at 0x20 words distance from
+         * end of capacity registers
+         */
+    hwaddr offset = s->capsbase + 0x20 + addr;
+
+    switch (offset) {
+    case XLNX_DCIVERSION:
+        return 0x00000001;
+    case XLNX_DCCPARAMS:
+        /* Host mode enabled
+         * Number of endpoints fixed to 12 as per zynq-7000
+         */
+        return 0x0000010C;
+    }
+    return 0;
+}
+
+static uint64_t xlnx_hwreg_read(void *opaque, hwaddr addr, unsigned size)
+{
+    /* All the following registers will just read out default values as per
+     * dwc_usb2_hs_device_controller spec
+     */
+    switch (addr) {
+    case XLNX_ID:
+        return XLNX_ID_DEFVAL;
+    case XLNX_HWGENERAL:
+        return XLNX_HWGENERAL_DEFVAL;
+    case XLNX_HWHOST:
+        return XLNX_HWHOST_DEFVAL;
+    case XLNX_HWTXBUF:
+        return XLNX_HWTXBUF_DEFVAL;
+    case XLNX_HWRXBUF:
+        return XLNX_HWRXBUF_DEFVAL;
+    }
+    return 0;
+}
+
+static uint64_t xlnx_ulpi_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PS7USBState *s = opaque;
+
+    return s->ulpi_viewport;
+}
+
+static void xlnx_ulpi_write(void *opaque, hwaddr addr, uint64_t data,
+                            unsigned size)
+{
+    PS7USBState *s = opaque;
+    uint8_t ulpiaddr;
+    /* Clear RW feilds before writes */
+    s->ulpi_viewport &= ~ULPIREG_RWBITS_MASK;
+    s->ulpi_viewport |= data & ULPIREG_RWBITS_MASK;
+
+    /* ULPI Wake Up call : Clear the bit when set */
+    if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIWU)) {
+        s->ulpi_viewport = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIWU, 0);
+    }
+
+    if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRUN)) {
+        ulpiaddr = F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIADDR);
+
+        if (F_EX32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRW)) {
+            s->ulpireg[ulpiaddr] = F_EX32(s->ulpi_viewport, ULPI_VIEWPORT,
+                                          ULPIDATWR);
+        } else {
+            s->ulpi_viewport = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT,
+                                      ULPIDATRD, s->ulpireg[ulpiaddr]);
+        }
+
+        s->ulpi_viewport = F_DP32(s->ulpi_viewport, ULPI_VIEWPORT, ULPIRUN, 0);
+    }
+}
+
+static const MemoryRegionOps ps7usb_devreg_ops = {
+    .read = xlnx_devreg_read,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ps7usb_hwreg_ops = {
+    .read = xlnx_hwreg_read,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ps7usb_ulpi_ops = {
+    .read = xlnx_ulpi_read,
+    .write = xlnx_ulpi_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ehci_xlnx_init(Object *Obj)
+{
+    EHCISysBusState *p = SYS_BUS_EHCI(Obj);
+    PS7USBState *s = XLNX_PS7_USB(Obj);
+    EHCIState *pp = &p->ehci;
+    memory_region_init_io(&s->mem_hwreg, Obj, &ps7usb_hwreg_ops, pp,
+                          "ps7usb_hwreg", PS7USB_HWREG_SIZE);
+    memory_region_add_subregion(&pp->mem, PS7USB_HWREG_OFFSET, &s->mem_hwreg);
+
+    memory_region_init_io(&s->mem_devreg, Obj, &ps7usb_devreg_ops, pp,
+                          "ps7usb_devicemode", PS7USB_DEVREG_SIZE);
+    memory_region_add_subregion(&pp->mem, PS7USB_DEVREG_OFFSET, &s->mem_devreg);
+
+    memory_region_init_io(&s->mem_ulpi, Obj, &ps7usb_ulpi_ops, s,
+                          "ps7usb_ulpi_viewport", PS7USB_ULPIVP_SIZE);
+    memory_region_add_subregion(&pp->mem, PS7USB_ULPIVP_OFFSET, &s->mem_ulpi);
+}
+
 static const TypeInfo ehci_xlnx_type_info = {
-    .name          = "xlnx,ps7-usb",
+    .name          = TYPE_XLNX_PS7_USB,
     .parent        = TYPE_SYS_BUS_EHCI,
     .class_init    = ehci_xlnx_class_init,
+    .instance_size = sizeof(PS7USBState),
+    .instance_init = ehci_xlnx_init,
 };
 
 static void ehci_exynos4210_class_init(ObjectClass *oc, void *data)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 159f58d..bbda633 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2478,6 +2478,7 @@  void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp)
     s->async_bh = qemu_bh_new(ehci_frame_timer, s);
     s->device = dev;
 
+    qemu_register_reset(ehci_reset, s);
     s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s);
 }
 
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 3021842..4cf0a29 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -342,6 +342,7 @@  typedef struct EHCIPCIState {
 #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb"
 #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb"
 #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb"
+#define TYPE_XLNX_PS7_USB "xlnx,ps7-usb"
 
 #define SYS_BUS_EHCI(obj) \
     OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI)
@@ -380,4 +381,34 @@  typedef struct FUSBH200EHCIState {
     MemoryRegion mem_vendor;
 } FUSBH200EHCIState;
 
+#define XLNX_PS7_USB(obj) \
+    OBJECT_CHECK(PS7USBState, (obj), TYPE_XLNX_PS7_USB)
+
+#define PS7USB_DEVREG_OFFSET 0x120
+#define PS7USB_DEVREG_SIZE   0x8
+#define PS7USB_HWREG_OFFSET  0x0
+#define PS7USB_HWREG_SIZE    0x18
+#define PS7USB_ULPIVP_OFFSET 0x170
+#define PS7USB_ULPIVP_SIZE   0x4
+
+#define XLNX_ID_DEFVAL        0xE441FA05
+#define XLNX_HWGENERAL_DEFVAL 0x83
+#define XLNX_HWHOST_DEFVAL    0x10020001
+#define XLNX_HWTXBUF_DEFVAL   0x80060A10
+#define XLNX_HWRXBUF_DEFVAL   0xA10
+
+#define ULPIREG_RWBITS_MASK   0xE0FF00FF
+
+typedef struct PS7USBState {
+    EHCISysBusState parent_obj;
+
+    uint32_t ulpi_viewport;
+    uint8_t ulpireg[0x19];
+
+    MemoryRegion mem_devreg;
+    MemoryRegion mem_hwreg;
+    MemoryRegion mem_ulpi;
+} PS7USBState;
+
+
 #endif
diff --git a/include/hw/register.h b/include/hw/register.h
new file mode 100644
index 0000000..1213135
--- /dev/null
+++ b/include/hw/register.h
@@ -0,0 +1,245 @@ 
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2013 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef REGISTER_H
+#define REGISTER_H
+
+#include "hw/qdev-core.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+
+typedef struct RegisterInfo RegisterInfo;
+typedef struct RegisterAccessInfo RegisterAccessInfo;
+typedef struct RegisterDecodeInfo RegisterDecodeInfo;
+
+/**
+ * A register access error message
+ * @mask: Bits in the register the error applies to
+ * @reason: Reason why this access is an error
+ */
+
+typedef struct RegisterAccessError {
+    uint64_t mask;
+    const char *reason;
+} RegisterAccessError;
+
+#define REG_GPIO_POL_HIGH 0
+#define REG_GPIO_POL_LOW  1
+typedef struct RegisterGPIOMapping {
+    const char *name;
+    uint8_t bit_pos;
+    bool input;
+    bool polarity;
+    uint8_t num;
+    uint8_t width;
+} RegisterGPIOMapping;
+
+/**
+ * Access description for a register that is part of guest accessible device
+ * state.
+ *
+ * @name: String name of the register
+ * @ro: whether or not the bit is read-only
+ * @w1c: bits with the common write 1 to clear semantic.
+ * @reset: reset value.
+ * @cor: Bits that are clear on read
+ * @rsvd: Bits that are reserved and should not be changed
+ *
+ * @ge1: Bits that when written 1 indicate a guest error
+ * @ge0: Bits that when written 0 indicate a guest error
+ * @ui1: Bits that when written 1 indicate use of an unimplemented feature
+ * @ui0: Bits that when written 0 indicate use of an unimplemented feature
+ *
+ * @pre_write: Pre write callback. Passed the value that's to be written,
+ * immediately before the actual write. The returned value is what is written,
+ * giving the handler a chance to modify the written value.
+ * @post_write: Post write callback. Passed the written value. Most write side
+ * effects should be implemented here.
+ *
+ * @post_read: Post read callback. Passes the value that is about to be returned
+ * for a read. The return value from this function is what is ultimately read,
+ * allowing this function to modify the value before return to the client.
+ */
+
+#define REG_DECODE_READ (1 << 0)
+#define REG_DECODE_WRITE (1 << 1)
+#define REG_DECODE_EXECUTE (1 << 2)
+#define REG_DECODE_RW (REG_DECODE_READ | REG_DECODE_WRITE)
+
+struct RegisterAccessInfo {
+    const char *name;
+    uint64_t ro;
+    uint64_t w1c;
+    uint64_t reset;
+    uint64_t cor;
+    uint64_t rsvd;
+    /* HACK - get rid of me */
+    uint64_t inhibit_reset;
+
+    const RegisterAccessError *ge0;
+    const RegisterAccessError *ge1;
+    const RegisterAccessError *ui0;
+    const RegisterAccessError *ui1;
+
+    uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val);
+    void (*post_write)(RegisterInfo *reg, uint64_t val);
+
+    uint64_t (*post_read)(RegisterInfo *reg, uint64_t val);
+
+    const RegisterGPIOMapping *gpios;
+
+    size_t storage;
+    int data_size;
+
+    struct {
+        hwaddr addr;
+        uint8_t flags;
+    } decode;
+
+    void *opaque;
+};
+
+/**
+ * A register that is part of guest accessible state
+ * @data: pointer to the register data. Will be cast
+ * to the relevant uint type depending on data_size.
+ * @data_size: Size of the register in bytes. Must be
+ * 1, 2, 4 or 8
+ * @data_big_endian: Define endianess of data register
+ *
+ * @access: Access desciption of this register
+ *
+ * @debug: Whether or not verbose debug is enabled
+ * @prefix: String prefix for log and debug messages
+ *
+ * @opaque: Opaque data for the register
+ *
+ * @mem: optional Memory region for the register
+ */
+
+struct RegisterInfo {
+    DeviceState parent_obj;
+
+    void *data;
+    int data_size;
+
+    const RegisterAccessInfo *access;
+
+    bool debug;
+    const char *prefix;
+
+    void *opaque;
+    /* private */
+    bool read_lite;
+    bool write_lite;
+
+    MemoryRegion mem;
+};
+
+#define TYPE_REGISTER "qemu,register"
+#define REGISTER(obj) OBJECT_CHECK(RegisterInfo, (obj), TYPE_REGISTER)
+
+struct RegisterDecodeInfo {
+    RegisterInfo *reg;
+    hwaddr addr;
+    unsigned len;
+};
+
+/**
+ * write a value to a register, subject to its restrictions
+ * @reg: register to write to
+ * @val: value to write
+ * @we: write enable mask
+ */
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we);
+
+/**
+ * read a value from a register, subject to its restrictions
+ * @reg: register to read from
+ * returns: value read
+ */
+
+uint64_t register_read(RegisterInfo *reg);
+
+/**
+ * reset a register
+ * @reg: register to reset
+ */
+
+void register_reset(RegisterInfo *reg);
+
+/**
+ * initialize a register. Gpio's are setup as IOs to the specified device.
+ * @reg: Register to initialize
+ */
+
+void register_init(RegisterInfo *reg);
+
+/**
+ * Refresh GPIO outputs based on diff between old value register current value.
+ * GPIOs are refreshed for fields where the old value differs to the current
+ * value.
+ *
+ * @reg: Register to refresh GPIO outs
+ * @old_value: previous value of register
+ */
+
+void register_refresh_gpios(RegisterInfo *reg, uint64_t old_value);
+
+void register_write_memory_be(void *opaque, hwaddr addr, uint64_t value,
+                              unsigned size);
+void register_write_memory_le(void *opaque, hwaddr addr, uint64_t value,
+                              unsigned size);
+
+uint64_t register_read_memory_be(void *opaque, hwaddr addr, unsigned size);
+uint64_t register_read_memory_le(void *opaque, hwaddr addr, unsigned size);
+
+/* Define constants for a 32 bit register */
+
+#define REG32(reg, addr) \
+enum { A_ ## reg = (addr) }; \
+enum { R_ ## reg = (addr) / 4 };
+
+/* Define SHIFT, LEGTH and MASK constants for a field within a register */
+
+#define FIELD(reg, field, length, shift) \
+enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \
+enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \
+enum { R_ ## reg ## _ ## field ## _MASK = (((1ULL << (length)) - 1) \
+                                          << (shift)) }; \
+
+/* Extract a field from a register */
+
+#define F_EX32(storage, reg, field) \
+    extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \
+              R_ ## reg ## _ ## field ## _LENGTH)
+
+/* Extract a field from an array of registers */
+
+#define AF_EX32(regs, reg, field) \
+    F_EX32((regs)[R_ ## reg], reg, field)
+
+/* Deposit a register field.  */
+
+#define F_DP32(storage, reg, field, val) ({                               \
+    struct {                                                              \
+        unsigned int v:R_ ## reg ## _ ## field ## _LENGTH;                \
+    } v = { .v = val };                                                   \
+    uint32_t d;                                                           \
+    d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT,           \
+                  R_ ## reg ## _ ## field ## _LENGTH, v.v);               \
+    d; })
+
+/* Deposit a field to array of registers.  */
+
+#define AF_DP32(regs, reg, field, val) \
+    (regs)[R_ ## reg] = F_DP32((regs)[R_ ## reg], reg, field, val);
+#endif