qemu/hw/block/pl35x.c
<<
>>
Prefs
   1/*
   2 * QEMU model of Primcell PL35X family of memory controllers
   3 *
   4 * Copyright (c) 2012 Xilinx Inc.
   5 * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@xilinx.com>.
   6 * Copyright (c) 2011 Edgar E. Iglesias.
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/hw.h"
  29#include "hw/block/flash.h"
  30#include "qapi/error.h"
  31#include "qemu/timer.h"
  32#include "sysemu/block-backend.h"
  33#include "exec/address-spaces.h"
  34#include "qemu/host-utils.h"
  35#include "hw/sysbus.h"
  36
  37#ifdef PL35X_ERR_DEBUG
  38#define DB_PRINT(...) do { \
  39    fprintf(stderr,  ": %s: ", __func__); \
  40    fprintf(stderr, ## __VA_ARGS__); \
  41    } while (0);
  42#else
  43    #define DB_PRINT(...)
  44#endif
  45
  46#define TYPE_PL35X "arm.pl35x"
  47
  48#define PL35X(obj) \
  49     OBJECT_CHECK(PL35xState, (obj), TYPE_PL35X)
  50
  51typedef struct PL35xItf {
  52    MemoryRegion mm;
  53    DeviceState *dev;
  54    uint8_t nand_pending_addr_cycles;
  55} PL35xItf;
  56
  57typedef struct PL35xState {
  58    SysBusDevice parent_obj;
  59
  60    MemoryRegion mmio;
  61
  62    /* FIXME: add support for multiple chip selects/interface */
  63
  64    PL35xItf itf[2];
  65
  66    /* FIXME: add Interrupt support */
  67
  68    /* FIXME: add ECC support */
  69
  70    uint8_t x; /* the "x" in pl35x */
  71} PL35xState;
  72
  73static uint64_t pl35x_read(void *opaque, hwaddr addr,
  74                         unsigned int size)
  75{
  76    PL35xState *s = opaque;
  77    uint32_t r = 0;
  78    int rdy;
  79
  80    addr >>= 2;
  81    switch (addr) {
  82    case 0x0:
  83        if (s->itf[0].dev && object_dynamic_cast(OBJECT(s->itf[0].dev),
  84                                                      "nand")) {
  85            nand_getpins(s->itf[0].dev, &rdy);
  86            r |= (!!rdy) << 5;
  87        }
  88        if (s->itf[1].dev && object_dynamic_cast(OBJECT(s->itf[1].dev),
  89                                                      "nand")) {
  90            nand_getpins(s->itf[1].dev, &rdy);
  91            r |= (!!rdy) << 6;
  92        }
  93        break;
  94    default:
  95        DB_PRINT("Unimplemented SMC read access reg=" TARGET_FMT_plx "\n",
  96                 addr * 4);
  97        break;
  98    }
  99    return r;
 100}
 101
 102static void pl35x_write(void *opaque, hwaddr addr, uint64_t value64,
 103                      unsigned int size)
 104{
 105    DB_PRINT("addr=%x v=%x\n", (unsigned)addr, (unsigned)value64);
 106    addr >>= 2;
 107    /* FIXME: implement */
 108    DB_PRINT("Unimplemented SMC write access reg=" TARGET_FMT_plx "\n",
 109                 addr * 4);
 110}
 111
 112static const MemoryRegionOps pl35x_ops = {
 113    .read = pl35x_read,
 114    .write = pl35x_write,
 115    .endianness = DEVICE_NATIVE_ENDIAN,
 116    .valid = {
 117        .min_access_size = 4,
 118        .max_access_size = 4
 119    }
 120};
 121
 122static uint64_t nand_read(void *opaque, hwaddr addr,
 123                           unsigned int size)
 124{
 125    PL35xItf *s = opaque;
 126    unsigned int len = size;
 127    int shift = 0;
 128    uint32_t r = 0;
 129
 130    while (len--) {
 131        uint8_t r8;
 132
 133        r8 = nand_getio(s->dev) & 0xff;
 134        r |= r8 << shift;
 135        shift += 8;
 136    }
 137    DB_PRINT("addr=%x r=%x size=%d\n", (unsigned)addr, r, size);
 138    return r;
 139}
 140
 141static void nand_write(void *opaque, hwaddr addr, uint64_t value64,
 142                       unsigned int size)
 143{
 144    struct PL35xItf *s = opaque;
 145    bool data_phase, ecmd_valid;
 146    unsigned int addr_cycles = 0;
 147    uint16_t start_cmd, end_cmd;
 148    uint32_t value = value64;
 149    uint32_t nandaddr = value;
 150
 151    DB_PRINT("addr=%x v=%x size=%d\n", (unsigned)addr, (unsigned)value, size);
 152
 153    /* Decode the various signals.  */
 154    data_phase = (addr >> 19) & 1;
 155    ecmd_valid = (addr >> 20) & 1;
 156    start_cmd = (addr >> 3) & 0xff;
 157    end_cmd = (addr >> 11) & 0xff;
 158    if (!data_phase) {
 159        addr_cycles = (addr >> 21) & 7;
 160    }
 161
 162    if (!data_phase) {
 163        DB_PRINT("start_cmd=%x end_cmd=%x (valid=%d) acycl=%d\n",
 164                start_cmd, end_cmd, ecmd_valid, addr_cycles);
 165    }
 166
 167    /* Writing data to the NAND.  */
 168    if (data_phase) {
 169        nand_setpins(s->dev, 0, 0, 0, 1, 0);
 170        while (size--) {
 171            nand_setio(s->dev, value & 0xff);
 172            value >>= 8;
 173        }
 174    }
 175
 176    /* Writing Start cmd.  */
 177    if (!data_phase && !s->nand_pending_addr_cycles) {
 178        nand_setpins(s->dev, 1, 0, 0, 1, 0);
 179        nand_setio(s->dev, start_cmd);
 180    }
 181
 182    if (!addr_cycles) {
 183        s->nand_pending_addr_cycles = 0;
 184    }
 185    if (s->nand_pending_addr_cycles) {
 186        addr_cycles = s->nand_pending_addr_cycles;
 187        s->nand_pending_addr_cycles = 0;
 188    }
 189    if (addr_cycles > 4) {
 190        s->nand_pending_addr_cycles = addr_cycles - 4;
 191        addr_cycles = 4;
 192    }
 193    while (addr_cycles) {
 194        nand_setpins(s->dev, 0, 1, 0, 1, 0);
 195        DB_PRINT("nand cycl=%d addr=%x\n", addr_cycles, nandaddr & 0xff);
 196        nand_setio(s->dev, nandaddr & 0xff);
 197        nandaddr >>= 8;
 198        addr_cycles--;
 199    }
 200
 201    /* Writing commands. One or two (Start and End).  */
 202    if (ecmd_valid && !s->nand_pending_addr_cycles) {
 203        nand_setpins(s->dev, 1, 0, 0, 1, 0);
 204        nand_setio(s->dev, end_cmd);
 205    }
 206}
 207
 208static const MemoryRegionOps nand_ops = {
 209    .read = nand_read,
 210    .write = nand_write,
 211    .endianness = DEVICE_NATIVE_ENDIAN,
 212    .valid = {
 213        .min_access_size = 1,
 214        .max_access_size = 4
 215    }
 216};
 217
 218static void pl35x_init_sram(SysBusDevice *dev, PL35xItf *itf)
 219{
 220    /* d Just needs to be a valid sysbus device with at least one memory
 221     * region
 222     */
 223    SysBusDevice *sbd = SYS_BUS_DEVICE(itf->dev);
 224
 225    memory_region_init(&itf->mm, OBJECT(dev), "pl35x.sram", 1 << 24);
 226    if (sbd) {
 227        memory_region_add_subregion(&itf->mm, 0,
 228                                    sysbus_mmio_get_region(sbd, 0));
 229    }
 230    sysbus_init_mmio(dev, &itf->mm);
 231}
 232
 233static void pl35x_init_nand(SysBusDevice *dev, PL35xItf *itf)
 234{
 235    /* d Must be a NAND flash */
 236    assert(object_dynamic_cast(OBJECT(itf->dev), "nand"));
 237
 238    memory_region_init_io(&itf->mm, OBJECT(dev), &nand_ops, itf, "pl35x.nand",
 239                          1 << 24);
 240    sysbus_init_mmio(dev, &itf->mm);
 241}
 242
 243static int pl35x_init(SysBusDevice *dev)
 244{
 245    PL35xState *s = PL35X(dev);
 246    int itfn = 0;
 247
 248    memory_region_init_io(&s->mmio, OBJECT(dev), &pl35x_ops, s, "pl35x_io",
 249                          0x1000);
 250    sysbus_init_mmio(dev, &s->mmio);
 251    if (s->x != 1) { /* everything cept PL351 has at least one SRAM */
 252        pl35x_init_sram(dev, &s->itf[itfn]);
 253        itfn++;
 254    }
 255    if (s->x & 0x1) { /* PL351 and PL353 have NAND */
 256        pl35x_init_nand(dev, &s->itf[itfn]);
 257    } else if (s->x == 4) { /* PL354 has a second SRAM */
 258        pl35x_init_sram(dev, &s->itf[itfn]);
 259    }
 260    return 0;
 261}
 262static void pl35x_initfn(Object *obj)
 263{
 264    PL35xState *s = PL35X(obj);
 265
 266    object_property_add_link(obj, "dev0", TYPE_DEVICE,
 267                             (Object **)&s->itf[0].dev,
 268                             object_property_allow_set_link,
 269                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 270                             &error_abort);
 271    object_property_add_link(obj, "dev1", TYPE_DEVICE,
 272                             (Object **)&s->itf[1].dev,
 273                             object_property_allow_set_link,
 274                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 275                             &error_abort);
 276}
 277
 278static Property pl35x_properties[] = {
 279    DEFINE_PROP_UINT8("x", PL35xState, x, 3),
 280    DEFINE_PROP_END_OF_LIST(),
 281};
 282
 283static const VMStateDescription vmstate_pl35x = {
 284    .name = "pl35x",
 285    .version_id = 1,
 286    .minimum_version_id = 1,
 287    .fields = (VMStateField[]) {
 288        VMSTATE_UINT8(itf[0].nand_pending_addr_cycles, PL35xState),
 289        VMSTATE_UINT8(itf[1].nand_pending_addr_cycles, PL35xState),
 290        VMSTATE_END_OF_LIST()
 291    }
 292};
 293
 294static void pl35x_class_init(ObjectClass *klass, void *data)
 295{
 296    DeviceClass *dc = DEVICE_CLASS(klass);
 297    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 298
 299    k->init = pl35x_init;
 300    dc->props = pl35x_properties;
 301    dc->vmsd = &vmstate_pl35x;
 302}
 303
 304static TypeInfo pl35x_info = {
 305    .name           = TYPE_PL35X,
 306    .parent         = TYPE_SYS_BUS_DEVICE,
 307    .instance_size  = sizeof(PL35xState),
 308    .class_init     = pl35x_class_init,
 309    .instance_init  = pl35x_initfn,
 310};
 311
 312static void pl35x_register_types(void)
 313{
 314    type_register_static(&pl35x_info);
 315}
 316
 317type_init(pl35x_register_types)
 318