From patchwork Wed Oct 3 07:48:39 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peter A. G. Crosthwaite" X-Patchwork-Id: 188731 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D67452C00E8 for ; Wed, 3 Oct 2012 18:25:31 +1000 (EST) Received: from localhost ([::1]:36220 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TJKGn-0006OD-E1 for incoming@patchwork.ozlabs.org; Wed, 03 Oct 2012 04:25:29 -0400 Received: from eggs.gnu.org ([208.118.235.92]:38764) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TJJik-0002yY-1Q for qemu-devel@nongnu.org; Wed, 03 Oct 2012 03:50:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TJJiZ-0005dS-Q3 for qemu-devel@nongnu.org; Wed, 03 Oct 2012 03:50:16 -0400 Received: from mail-pb0-f45.google.com ([209.85.160.45]:42045) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TJJiZ-0005N5-HK for qemu-devel@nongnu.org; Wed, 03 Oct 2012 03:50:07 -0400 Received: by mail-pb0-f45.google.com with SMTP id rp2so9717064pbb.4 for ; Wed, 03 Oct 2012 00:50:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :in-reply-to:references:x-gm-message-state; bh=n0lVaGlT4GRh71Wb7ES/JeX2vHsNb36TE94UkiOjr3M=; b=HSADyEkA3xy/R1c7ekR8ROmXQrnOf2W/RvqZ64mPY1WLNmfqrlLTZ8hipTkeTeZCOU FacedQdBCXLMjkYuIH1CCqSrQGqYVFVDspMdgIJrdSsF09QV0WxzhXHI3YsqyLalPw7Q cdumRcy/N8sj6M/rvjpkCg6pqsfS3HmyrcW/VFEyXcpJerml0wHmjbrVwtpLx3joYvgX OVhtaREP6U6eljH54EkFhhIYurQSKLT44PD+cVWXzQgaeULYMiw5tGmn1r/3quHnP4Fo wo9VwiiMm2eVgbL2Cwysz/qKMDCaV2me+fbLV8/gBtlKgAt46hJT5xJ1KnL72jTrcBdo 3QiQ== Received: by 10.68.216.2 with SMTP id om2mr10762231pbc.88.1349250606952; Wed, 03 Oct 2012 00:50:06 -0700 (PDT) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id qn1sm2220818pbc.55.2012.10.03.00.50.02 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 03 Oct 2012 00:50:06 -0700 (PDT) From: Peter Crosthwaite To: qemu-devel@nongnu.org, paul@codesourcery.com, edgar.iglesias@gmail.com, peter.maydell@linaro.org, stefanha@gmail.com Date: Wed, 3 Oct 2012 17:48:39 +1000 Message-Id: <14595c77e36dee88d3cf6714145ebf25f1c54d8e.1349249771.git.peter.crosthwaite@xilinx.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQkAEM76naR3h1593G67zzvOz+8trBvxBc+KmDImLNU3HWXHlnRA694zXbx3lNkxqegG4G4m X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.160.45 Cc: blauwirbel@gmail.com, "Peter A. G. Crosthwaite" , i.mitsyanko@samsung.com Subject: [Qemu-devel] [PATCH v8 11/14] xilinx_spips: Xilinx Zynq SPI cntrlr device model 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Peter A. G. Crosthwaite Added device model for the Xilinx Zynq SPI controller (SPIPS). Signed-off-by: Peter A. G. Crosthwaite --- Changed from v7: Fixed &->&& logic on if condition is cs_select logic Fixed MODE_SEL bit field definition Changed from v6: Addressed Blue Swirl review s/interupt/interrupt s/defintion/definition constified TypeInfo hw/arm/Makefile.objs | 1 + hw/xilinx_spips.c | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+), 0 deletions(-) create mode 100644 hw/xilinx_spips.c diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 2b39fb3..6d049e7 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,6 +1,7 @@ obj-y = integratorcp.o versatilepb.o arm_pic.o obj-y += arm_boot.o obj-y += xilinx_zynq.o zynq_slcr.o +obj-y += xilinx_spips.o obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o diff --git a/hw/xilinx_spips.c b/hw/xilinx_spips.c new file mode 100644 index 0000000..a886c5d --- /dev/null +++ b/hw/xilinx_spips.c @@ -0,0 +1,352 @@ +/* + * QEMU model of the Xilinx Zynq SPI controller + * + * Copyright (c) 2012 Peter A. G. Crosthwaite + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "sysemu.h" +#include "ptimer.h" +#include "qemu-log.h" +#include "fifo.h" +#include "ssi.h" + +#ifdef XILINX_SPIPS_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +/* config register */ +#define R_CONFIG (0x00 / 4) +#define MODEFAIL_GEN_EN (1 << 17) +#define MAN_START_COM (1 << 16) +#define MAN_START_EN (1 << 15) +#define MANUAL_CS (1 << 14) +#define CS (0xF << 10) +#define CS_SHIFT (10) +#define PERI_SEL (1 << 9) +#define REF_CLK (1 << 8) +#define FIFO_WIDTH (3 << 6) +#define BAUD_RATE_DIV (7 << 3) +#define CLK_PH (1 << 2) +#define CLK_POL (1 << 1) +#define MODE_SEL (1 << 0) + +/* interrupt mechanism */ +#define R_INTR_STATUS (0x04 / 4) +#define R_INTR_EN (0x08 / 4) +#define R_INTR_DIS (0x0C / 4) +#define R_INTR_MASK (0x10 / 4) +#define IXR_TX_FIFO_UNDERFLOW (1 << 6) +#define IXR_RX_FIFO_FULL (1 << 5) +#define IXR_RX_FIFO_NOT_EMPTY (1 << 4) +#define IXR_TX_FIFO_FULL (1 << 3) +#define IXR_TX_FIFO_NOT_FULL (1 << 2) +#define IXR_TX_FIFO_MODE_FAIL (1 << 1) +#define IXR_RX_FIFO_OVERFLOW (1 << 0) +#define IXR_ALL ((IXR_TX_FIFO_UNDERFLOW<<1)-1) + +#define R_EN (0x14 / 4) +#define R_DELAY (0x18 / 4) +#define R_TX_DATA (0x1C / 4) +#define R_RX_DATA (0x20 / 4) +#define R_SLAVE_IDLE_COUNT (0x24 / 4) +#define R_TX_THRES (0x28 / 4) +#define R_RX_THRES (0x2C / 4) +#define R_MOD_ID (0xFC / 4) + +#define R_MAX (R_MOD_ID+1) + +/* size of TXRX FIFOs */ +#define NUM_CS_LINES 4 +#define RXFF_A 32 +#define TXFF_A 32 + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + int irqline; + + qemu_irq cs_lines[NUM_CS_LINES]; + SSIBus *spi; + + Fifo8 rx_fifo; + Fifo8 tx_fifo; + + uint32_t regs[R_MAX]; +} XilinxSPIPS; + +static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) +{ + int i; + bool found = false; + int field = s->regs[R_CONFIG] >> CS_SHIFT; + + for (i = 0; i < NUM_CS_LINES; i++) { + if (~field & (1 << i) && !found) { + found = true; + DB_PRINT("selecting slave %d\n", i); + qemu_set_irq(s->cs_lines[i], 0); + } else { + qemu_set_irq(s->cs_lines[i], 1); + } + } +} + +static void xilinx_spips_update_ixr(XilinxSPIPS *s) +{ + /* These are set/cleared as they occur */ + s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW | + IXR_TX_FIFO_MODE_FAIL); + /* these are pure functions of fifo state, set them here */ + s->regs[R_INTR_STATUS] |= + (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) | + (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) | + (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) | + (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0); + /* drive external interrupt pin */ + int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] & + IXR_ALL); + if (new_irqline != s->irqline) { + s->irqline = new_irqline; + qemu_set_irq(s->irq, s->irqline); + } +} + +static void xilinx_spips_reset(DeviceState *d) +{ + XilinxSPIPS *s = DO_UPCAST(XilinxSPIPS, busdev.qdev, d); + + int i; + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + fifo8_reset(&s->rx_fifo); + fifo8_reset(&s->rx_fifo); + /* non zero resets */ + s->regs[R_CONFIG] |= MODEFAIL_GEN_EN; + s->regs[R_SLAVE_IDLE_COUNT] = 0xFF; + s->regs[R_TX_THRES] = 1; + s->regs[R_RX_THRES] = 1; + /* FIXME: move magic number definition somewhere sensible */ + s->regs[R_MOD_ID] = 0x01090106; + xilinx_spips_update_ixr(s); + xilinx_spips_update_cs_lines(s); +} + +static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) +{ + for (;;) { + uint32_t r; + uint8_t value; + + if (fifo8_is_empty(&s->tx_fifo)) { + s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW; + break; + } else { + value = fifo8_pop(&s->tx_fifo); + } + + r = ssi_transfer(s->spi, (uint32_t)value); + DB_PRINT("tx = %02x rx = %02x\n", value, r); + if (fifo8_is_full(&s->rx_fifo)) { + s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW; + DB_PRINT("rx FIFO overflow"); + } else { + fifo8_push(&s->rx_fifo, (uint8_t)r); + } + } + xilinx_spips_update_ixr(s); +} + +static uint64_t xilinx_spips_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + XilinxSPIPS *s = opaque; + uint32_t mask = ~0; + uint32_t ret; + + addr >>= 2; + switch (addr) { + case R_CONFIG: + mask = 0x0002FFFF; + break; + case R_INTR_STATUS: + case R_INTR_MASK: + mask = IXR_ALL; + break; + case R_EN: + mask = 0x1; + break; + case R_SLAVE_IDLE_COUNT: + mask = 0xFF; + break; + case R_MOD_ID: + mask = 0x01FFFFFF; + break; + case R_INTR_EN: + case R_INTR_DIS: + case R_TX_DATA: + mask = 0; + break; + case R_RX_DATA: + ret = (uint32_t)fifo8_pop(&s->rx_fifo); + DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); + xilinx_spips_update_ixr(s); + return ret; + } + DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, s->regs[addr] & mask); + return s->regs[addr] & mask; + +} + +static void xilinx_spips_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + int mask = ~0; + int man_start_com = 0; + XilinxSPIPS *s = opaque; + + DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value); + addr >>= 2; + switch (addr) { + case R_CONFIG: + mask = 0x0002FFFF; + if (value & MAN_START_COM) { + man_start_com = 1; + } + break; + case R_INTR_STATUS: + mask = IXR_ALL; + s->regs[R_INTR_STATUS] &= ~(mask & value); + goto no_reg_update; + case R_INTR_DIS: + mask = IXR_ALL; + s->regs[R_INTR_MASK] &= ~(mask & value); + goto no_reg_update; + case R_INTR_EN: + mask = IXR_ALL; + s->regs[R_INTR_MASK] |= mask & value; + goto no_reg_update; + case R_EN: + mask = 0x1; + break; + case R_SLAVE_IDLE_COUNT: + mask = 0xFF; + break; + case R_RX_DATA: + case R_INTR_MASK: + case R_MOD_ID: + mask = 0; + break; + case R_TX_DATA: + fifo8_push(&s->tx_fifo, (uint8_t)value); + goto no_reg_update; + } + s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask); +no_reg_update: + if (man_start_com) { + xilinx_spips_flush_txfifo(s); + } + xilinx_spips_update_ixr(s); + xilinx_spips_update_cs_lines(s); +} + +static const MemoryRegionOps spips_ops = { + .read = xilinx_spips_read, + .write = xilinx_spips_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int xilinx_spips_init(SysBusDevice *dev) +{ + XilinxSPIPS *s = FROM_SYSBUS(typeof(*s), dev); + int i; + + DB_PRINT("inited device model\n"); + + sysbus_init_irq(dev, &s->irq); + for (i = 0; i < NUM_CS_LINES; ++i) { + sysbus_init_irq(dev, &s->cs_lines[i]); + } + + memory_region_init_io(&s->iomem, &spips_ops, s, "spi", R_MAX*4); + sysbus_init_mmio(dev, &s->iomem); + + s->irqline = -1; + s->spi = ssi_create_bus(&dev->qdev, "spi"); + + fifo8_create(&s->rx_fifo, RXFF_A); + fifo8_create(&s->tx_fifo, TXFF_A); + + return 0; +} + +static int xilinx_spips_post_load(void *opaque, int version_id) +{ + xilinx_spips_update_ixr((XilinxSPIPS *)opaque); + xilinx_spips_update_cs_lines((XilinxSPIPS *)opaque); + return 0; +} + +static const VMStateDescription vmstate_xilinx_spips = { + .name = "xilinx_spips", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = xilinx_spips_post_load, + .fields = (VMStateField[]) { + VMSTATE_FIFO8(tx_fifo, XilinxSPIPS), + VMSTATE_FIFO8(rx_fifo, XilinxSPIPS), + VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void xilinx_spips_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = xilinx_spips_init; + dc->reset = xilinx_spips_reset; + dc->vmsd = &vmstate_xilinx_spips; +} + +static const TypeInfo xilinx_spips_info = { + .name = "xilinx,spips", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XilinxSPIPS), + .class_init = xilinx_spips_class_init, +}; + +static void xilinx_spips_register_types(void) +{ + type_register_static(&xilinx_spips_info); +} + +type_init(xilinx_spips_register_types)