From patchwork Fri Oct 19 06:40:29 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Crosthwaite X-Patchwork-Id: 192574 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 A50AF2C0098 for ; Fri, 19 Oct 2012 17:42:07 +1100 (EST) Received: from localhost ([::1]:32768 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TP6HU-0005kk-GV for incoming@patchwork.ozlabs.org; Fri, 19 Oct 2012 02:42:04 -0400 Received: from eggs.gnu.org ([208.118.235.92]:51833) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TP6Gj-0003w8-Bm for qemu-devel@nongnu.org; Fri, 19 Oct 2012 02:41:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TP6Ge-0003yN-Sp for qemu-devel@nongnu.org; Fri, 19 Oct 2012 02:41:17 -0400 Received: from mail-pb0-f45.google.com ([209.85.160.45]:34089) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TP6Ge-0003iM-Ne for qemu-devel@nongnu.org; Fri, 19 Oct 2012 02:41:12 -0400 Received: by mail-pb0-f45.google.com with SMTP id rp2so204353pbb.4 for ; Thu, 18 Oct 2012 23:41:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:in-reply-to:references:x-gm-message-state; bh=GuMX48WnZrlFDVoGMVeMKa4FkSIcGnizScy7Dv0RvM8=; b=X6zaZYbMcd5fUI3C4L3yWayBtD7z1XQNg/1H1eYAIn5vuKHS7mJfytX/JLV0oy34vC OHVaUMG4oO+IVqx054zvLDp1zlUVjHjiSG3HyttN81QwNELEvxv1PD/aJsi6yAk8nJjf B0fcfnL/xijwnOdzqmTMeht2A3mGjYko5+Dm4p2wpAp/x+vDknHUgs4egPgcz7KJaZFY Cvn7xsnyPkFoK8ShmQR0lQVRQ1JpgJZQ4LO3eJWuMOIKo+5mGga3EfX0R9Pn18zOrXXA YZR0Pyx4QWDgoTJn3R75UNlMpUC3pmsKQJRAzScPBtEf1SxAV30bDj5mfyYvXgt4M5JZ H3pA== Received: by 10.68.233.196 with SMTP id ty4mr2719486pbc.23.1350628872287; Thu, 18 Oct 2012 23:41:12 -0700 (PDT) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id vc2sm757106pbc.64.2012.10.18.23.41.09 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 18 Oct 2012 23:41:11 -0700 (PDT) From: Peter Crosthwaite To: qemu-devel@nongnu.org, edgar.iglesias@gmail.com, peter.maydell@linaro.org Date: Fri, 19 Oct 2012 16:40:29 +1000 Message-Id: X-Mailer: git-send-email 1.7.0.4 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQkg64odIPsJkP4d6EYMiQnbtOFZLzGoLqVV3c+oX9/Y3LyzdTxKVRXDaGuYUjrT7a0sA8qh X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.160.45 Cc: Peter Crosthwaite , john.williams@xilinx.com Subject: [Qemu-devel] [PATCH v1 5/7] hw: Model of Primecell pl35x mem controller 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 Initial device model for the pl35x series of memory controllers. The SRAM interface is just implemented as a passthrough using memory regions. NAND interfaces are modelled. Signed-off-by: Peter Crosthwaite --- default-configs/arm-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/pl35x.c | 299 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+), 0 deletions(-) create mode 100644 hw/pl35x.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2f1a5c9..b24bf68 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -41,6 +41,7 @@ CONFIG_PL110=y CONFIG_PL181=y CONFIG_PL190=y CONFIG_PL310=y +CONFIG_PL35X=y CONFIG_CADENCE=y CONFIG_XGMAC=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 854faa9..502f139 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -88,6 +88,7 @@ common-obj-$(CONFIG_PL110) += pl110.o common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_PL190) += pl190.o common-obj-$(CONFIG_PL310) += arm_l2x0.o +common-obj-$(CONFIG_PL35X) += pl35x.o common-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o diff --git a/hw/pl35x.c b/hw/pl35x.c new file mode 100644 index 0000000..ec3d194 --- /dev/null +++ b/hw/pl35x.c @@ -0,0 +1,299 @@ +/* + * QEMU model of Primcell PL353 + * + * Copyright (c) 2012 Xilinx Inc. + * Copyright (c) 2012 Peter Crosthwaite . + * Copyright (c) 2011 Edgar E. Iglesias. + * + * 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 "hw.h" +#include "qemu-timer.h" +#include "sysbus.h" +#include "sysemu.h" +#include "flash.h" + +#ifdef PL35X_ERR_DEBUG +#define DB_PRINT(...) do { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } while (0); +#else + #define DB_PRINT(...) +#endif + +typedef struct PL35xItf { + MemoryRegion mm; + DeviceState *dev; + uint8_t nand_pending_addr_cycles; +} PL35xItf; + +typedef struct PL35xState { + SysBusDevice busdev; + MemoryRegion mmio; + + /* FIXME: add support for multiple chip selects/interface */ + + PL35xItf itf[2]; + + /* FIXME: add Interrupt support */ + + /* FIXME: add ECC support */ + + uint8_t x; /* the "x" in pl35x */ +} PL35xState; + +static uint64_t pl35x_read(void *opaque, target_phys_addr_t addr, + unsigned int size) +{ + PL35xState *s = opaque; + uint32_t r = 0; + int rdy; + + addr >>= 2; + switch (addr) { + case 0x0: + if (s->itf[0].dev && object_dynamic_cast(OBJECT(s->itf[0].dev), + "nand")) { + nand_getpins(s->itf[0].dev, &rdy); + r |= (!!rdy) << 5; + } + if (s->itf[1].dev && object_dynamic_cast(OBJECT(s->itf[1].dev), + "nand")) { + nand_getpins(s->itf[1].dev, &rdy); + r |= (!!rdy) << 6; + } + break; + default: + DB_PRINT("Unimplemented SMC read access reg=" TARGET_FMT_plx "\n", + addr * 4); + break; + } + return r; +} + +static void pl35x_write(void *opaque, target_phys_addr_t addr, uint64_t value64, + unsigned int size) +{ + DB_PRINT("addr=%x v=%x\n", addr, (unsigned)value64); + addr >>= 2; + /* FIXME: implement */ + DB_PRINT("Unimplemented SMC write access reg=" TARGET_FMT_plx "\n", + addr * 4); +} + +static const MemoryRegionOps pl35x_ops = { + .read = pl35x_read, + .write = pl35x_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static uint64_t nand_read(void *opaque, target_phys_addr_t addr, + unsigned int size) +{ + PL35xItf *s = opaque; + unsigned int len = size; + int shift = 0; + uint32_t r = 0; + + while (len--) { + uint8_t r8; + + r8 = nand_getio(s->dev) & 0xff; + r |= r8 << shift; + shift += 8; + } + DB_PRINT("addr=%x r=%x size=%d\n", (unsigned)addr, r, size); + return r; +} + +static void nand_write(void *opaque, target_phys_addr_t addr, uint64_t value64, + unsigned int size) +{ + struct PL35xItf *s = opaque; + bool data_phase, ecmd_valid; + unsigned int addr_cycles = 0; + uint16_t start_cmd, end_cmd; + uint32_t value = value64; + uint32_t nandaddr = value; + + DB_PRINT("addr=%x v=%x size=%d\n", addr, value, size); + + /* Decode the various signals. */ + data_phase = (addr >> 19) & 1; + ecmd_valid = (addr >> 20) & 1; + start_cmd = (addr >> 3) & 0xff; + end_cmd = (addr >> 11) & 0xff; + if (!data_phase) { + addr_cycles = (addr >> 21) & 7; + } + + if (!data_phase) { + DB_PRINT("start_cmd=%x end_cmd=%x (valid=%d) acycl=%d\n", + start_cmd, end_cmd, ecmd_valid, addr_cycles); + } + + /* Writing data to the NAND. */ + if (data_phase) { + nand_setpins(s->dev, 0, 0, 0, 1, 0); + while (size--) { + nand_setio(s->dev, value & 0xff); + value >>= 8; + } + } + + /* Writing Start cmd. */ + if (!data_phase && !s->nand_pending_addr_cycles) { + nand_setpins(s->dev, 1, 0, 0, 1, 0); + nand_setio(s->dev, start_cmd); + } + + if (!addr_cycles) { + s->nand_pending_addr_cycles = 0; + } + if (s->nand_pending_addr_cycles) { + addr_cycles = s->nand_pending_addr_cycles; + s->nand_pending_addr_cycles = 0; + } + if (addr_cycles > 4) { + s->nand_pending_addr_cycles = addr_cycles - 4; + addr_cycles = 4; + } + while (addr_cycles) { + nand_setpins(s->dev, 0, 1, 0, 1, 0); + DB_PRINT("nand cycl=%d addr=%x\n", addr_cycles, nandaddr & 0xff); + nand_setio(s->dev, nandaddr & 0xff); + nandaddr >>= 8; + addr_cycles--; + } + + /* Writing commands. One or two (Start and End). */ + if (ecmd_valid && !s->nand_pending_addr_cycles) { + nand_setpins(s->dev, 1, 0, 0, 1, 0); + nand_setio(s->dev, end_cmd); + } +} + +static const MemoryRegionOps nand_ops = { + .read = nand_read, + .write = nand_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4 + } +}; + +static void pl35x_init_sram(SysBusDevice *dev, PL35xItf *itf) +{ + /* d Just needs to be a valid sysbus device with at least one memory + * region + */ + SysBusDevice *sbd = SYS_BUS_DEVICE(itf->dev); + + memory_region_init(&itf->mm, "pl35x.sram", 1 << 24); + sysbus_mmio_map_to_region(sbd, 0, 0, &itf->mm); + sysbus_init_mmio(dev, &itf->mm); +} + +static void pl35x_init_nand(SysBusDevice *dev, PL35xItf *itf) +{ + /* d Must be a NAND flash */ + object_dynamic_cast_assert(OBJECT(itf->dev), "nand"); + + memory_region_init_io(&itf->mm, &nand_ops, itf, "pl35x.nand", 1 << 24); + sysbus_init_mmio(dev, &itf->mm); +} + +static int pl35x_init(SysBusDevice *dev) +{ + PL35xState *s = FROM_SYSBUS(typeof(*s), dev); + int itfn = 0; + + memory_region_init_io(&s->mmio, &pl35x_ops, s, "pl35x_io", 0x1000); + sysbus_init_mmio(dev, &s->mmio); + if (s->x != 1) { /* everything cept PL351 has at least one SRAM */ + pl35x_init_sram(dev, &s->itf[itfn]); + itfn++; + } + if (s->x & 0x1) { /* PL351 and PL353 have NAND */ + pl35x_init_nand(dev, &s->itf[itfn]); + } else if (s->x == 4) { /* PL354 has a second SRAM */ + pl35x_init_sram(dev, &s->itf[itfn]); + } + return 0; +} +static void pl35x_initfn(Object *obj) +{ + PL35xState *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + Error *errp = NULL; + + object_property_add_link(obj, "dev0", TYPE_DEVICE, + (Object **)&s->itf[0].dev, &errp); + assert_no_error(errp); + object_property_add_link(obj, "dev1", TYPE_DEVICE, + (Object **)&s->itf[1].dev, &errp); + assert_no_error(errp); +} + +static Property pl35x_properties[] = { + DEFINE_PROP_UINT8("x", PL35xState, x, 3), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_pl35x = { + .name = "pl35x", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(itf[0].nand_pending_addr_cycles, PL35xState), + VMSTATE_UINT8(itf[1].nand_pending_addr_cycles, PL35xState), + VMSTATE_END_OF_LIST() + } +}; + +static void pl35x_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = pl35x_init; + dc->props = pl35x_properties; + dc->vmsd = &vmstate_pl35x; +} + +static TypeInfo pl35x_info = { + .name = "arm.pl35x", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PL35xState), + .class_init = pl35x_class_init, + .instance_init = pl35x_initfn, +}; + +static void pl35x_register_types(void) +{ + type_register_static(&pl35x_info); +} + +type_init(pl35x_register_types)