[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v1 5/7] hw: Model of Primecell pl35x mem controller
From: |
Peter Crosthwaite |
Subject: |
[Qemu-devel] [PATCH v1 5/7] hw: Model of Primecell pl35x mem controller |
Date: |
Fri, 19 Oct 2012 16:40:29 +1000 |
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 <address@hidden>
---
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 <address@hidden>.
+ * 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)
--
1.7.0.4
- Re: [Qemu-devel] [PATCH v1 1/7] pflash_cfi0x: remove unused base field, (continued)
- [Qemu-devel] [PATCH v1 2/7] pflash_cfi01: remove unused total_len field, Peter Crosthwaite, 2012/10/19
- [Qemu-devel] [PATCH v1 3/7] pflash_cfi0x: QOMified, Peter Crosthwaite, 2012/10/19
- [Qemu-devel] [PATCH v1 4/7] sysbus/sysbus_mmio_map: parameterise mapped region, Peter Crosthwaite, 2012/10/19
- [Qemu-devel] [PATCH v1 5/7] hw: Model of Primecell pl35x mem controller,
Peter Crosthwaite <=
- [Qemu-devel] [PATCH v1 6/7] xilinx_zynq: add pl353, Peter Crosthwaite, 2012/10/19
- [Qemu-devel] [PATCH v1 7/7] nand: Reset addressing after READSTATUS., Peter Crosthwaite, 2012/10/19