From patchwork Tue Mar 29 19:51:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: jbenz@mailbox.org X-Patchwork-Id: 8690241 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4810FC0553 for ; Tue, 29 Mar 2016 19:52:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E179F2020F for ; Tue, 29 Mar 2016 19:52:47 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9C0F62013D for ; Tue, 29 Mar 2016 19:52:45 +0000 (UTC) Received: from localhost ([::1]:49616 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1akzgy-0000ES-Up for patchwork-qemu-devel@patchwork.kernel.org; Tue, 29 Mar 2016 15:52:44 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53778) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1akzgn-0000Bq-3Z for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1akzgi-0000kD-H3 for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:33 -0400 Received: from mx1.mailbox.org ([80.241.60.212]:38935) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1akzgi-0000j0-4Y for qemu-devel@nongnu.org; Tue, 29 Mar 2016 15:52:28 -0400 Received: from smtp1.mailbox.org (smtp1.mailbox.org [80.241.60.240]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.mailbox.org (Postfix) with ESMTPS id 9085542D4F; Tue, 29 Mar 2016 21:52:26 +0200 (CEST) Authentication-Results: gerste.heinlein-support.de (amavisd-new); dkim=pass (2048-bit key) reason="pass (just generated, assumed good)" header.d=mailbox.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mailbox.org; h= content-type:content-type:mime-version:message-id:date:date :subject:subject:from:from:received; s=mail20150812; t= 1459281144; bh=eIQW39PMGUJSBZC5JC8kNC4lVZXOBM4x9SbdkiMPKnQ=; b=K twLJ74EodNETtBEQ8C4HUj8Eiz7bXzXfs33P9wCsP/j45PeJshViomWLvhJdUBrW 0GKtmuNDfRX7Eu/+pJmQCUFsqJSqeFbvTftU59O4yNo4LriiTrl2KqL7xIVDBDKi wm4GPBtKsW2RzZpCSVocOYGVfKzSeZNySqG0J7WCGR5Bv+DMnki2Z9dDDvjoZFNJ pnKWr5m29pWxRbqbdv7qGK4ST2vsLnppBkXfnFNXzVCbO7YWwfnHuwffknbq0taI Qxi0+VKi3xUdYkWB7+Ho7Es8EpleyAd5Zy3bqIruaLOxqydQfWhrjhpI35UxNOJs rIxsyx1Yfzxn6r1OEP2ew== X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp1.mailbox.org ([80.241.60.240]) by gerste.heinlein-support.de (gerste.heinlein-support.de [91.198.250.173]) (amavisd-new, port 10030) with ESMTP id EYVWyZKW2tYN; Tue, 29 Mar 2016 21:52:24 +0200 (CEST) From: jbenz@mailbox.org To: qemu-devel@nongnu.org Date: Tue, 29 Mar 2016 21:51:24 +0200 Message-Id: <1459281084-13346-1-git-send-email-jbenz@mailbox.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 80.241.60.212 Cc: Joscha Benz , Peter Crosthwaite , Alistair Francis Subject: [Qemu-devel] [PATCH] xilinx_zynq: merged support for ULPI PHY and ULPI viewport from xilinx/qemu X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Joscha Benz --- 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 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 + * + * 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