qemu/hw/omap_gpmc.c
<<
>>
Prefs
   1/*
   2 * TI OMAP general purpose memory controller emulation.
   3 *
   4 * Copyright (C) 2007-2009 Nokia Corporation
   5 * Original code written by Andrzej Zaborowski <andrew@openedhand.com>
   6 * Enhancements for OMAP3 and NAND support written by Juha Riihimäki
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 or
  11 * (at your option) any later version of the License.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along
  19 * with this program; if not, see <http://www.gnu.org/licenses/>.
  20 */
  21#include "hw.h"
  22#include "flash.h"
  23#include "omap.h"
  24
  25/* General-Purpose Memory Controller */
  26struct omap_gpmc_s {
  27    qemu_irq irq;
  28
  29    uint8_t sysconfig;
  30    uint16_t irqst;
  31    uint16_t irqen;
  32    uint16_t timeout;
  33    uint16_t config;
  34    uint32_t prefconfig[2];
  35    int prefcontrol;
  36    int preffifo;
  37    int prefcount;
  38    struct omap_gpmc_cs_file_s {
  39        uint32_t config[7];
  40        target_phys_addr_t base;
  41        size_t size;
  42        int iomemtype;
  43        void (*base_update)(void *opaque, target_phys_addr_t new);
  44        void (*unmap)(void *opaque);
  45        void *opaque;
  46    } cs_file[8];
  47    int ecc_cs;
  48    int ecc_ptr;
  49    uint32_t ecc_cfg;
  50    ECCState ecc[9];
  51};
  52
  53static void omap_gpmc_int_update(struct omap_gpmc_s *s)
  54{
  55    qemu_set_irq(s->irq, s->irqen & s->irqst);
  56}
  57
  58static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask)
  59{
  60    /* TODO: check for overlapping regions and report access errors */
  61    if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
  62                    (base < 0 || base >= 0x40) ||
  63                    (base & 0x0f & ~mask)) {
  64        fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
  65                        __FUNCTION__);
  66        return;
  67    }
  68
  69    if (!f->opaque)
  70        return;
  71
  72    f->base = base << 24;
  73    f->size = (0x0fffffff & ~(mask << 24)) + 1;
  74    /* TODO: rather than setting the size of the mapping (which should be
  75     * constant), the mask should cause wrapping of the address space, so
  76     * that the same memory becomes accessible at every <i>size</i> bytes
  77     * starting from <i>base</i>.  */
  78    if (f->iomemtype)
  79        cpu_register_physical_memory(f->base, f->size, f->iomemtype);
  80
  81    if (f->base_update)
  82        f->base_update(f->opaque, f->base);
  83}
  84
  85static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f)
  86{
  87    if (f->size) {
  88        if (f->unmap)
  89            f->unmap(f->opaque);
  90        if (f->iomemtype)
  91            cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED);
  92        f->base = 0;
  93        f->size = 0;
  94    }
  95}
  96
  97void omap_gpmc_reset(struct omap_gpmc_s *s)
  98{
  99    int i;
 100
 101    s->sysconfig = 0;
 102    s->irqst = 0;
 103    s->irqen = 0;
 104    omap_gpmc_int_update(s);
 105    s->timeout = 0;
 106    s->config = 0xa00;
 107    s->prefconfig[0] = 0x00004000;
 108    s->prefconfig[1] = 0x00000000;
 109    s->prefcontrol = 0;
 110    s->preffifo = 0;
 111    s->prefcount = 0;
 112    for (i = 0; i < 8; i ++) {
 113        if (s->cs_file[i].config[6] & (1 << 6))                 /* CSVALID */
 114            omap_gpmc_cs_unmap(s->cs_file + i);
 115        s->cs_file[i].config[0] = i ? 1 << 12 : 0;
 116        s->cs_file[i].config[1] = 0x101001;
 117        s->cs_file[i].config[2] = 0x020201;
 118        s->cs_file[i].config[3] = 0x10031003;
 119        s->cs_file[i].config[4] = 0x10f1111;
 120        s->cs_file[i].config[5] = 0;
 121        s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
 122        if (s->cs_file[i].config[6] & (1 << 6))                 /* CSVALID */
 123            omap_gpmc_cs_map(&s->cs_file[i],
 124                            s->cs_file[i].config[6] & 0x1f,     /* MASKADDR */
 125                        (s->cs_file[i].config[6] >> 8 & 0xf));  /* BASEADDR */
 126    }
 127    omap_gpmc_cs_map(s->cs_file, 0, 0xf);
 128    s->ecc_cs = 0;
 129    s->ecc_ptr = 0;
 130    s->ecc_cfg = 0x3fcff000;
 131    for (i = 0; i < 9; i ++)
 132        ecc_reset(&s->ecc[i]);
 133}
 134
 135static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
 136{
 137    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
 138    int cs;
 139    struct omap_gpmc_cs_file_s *f;
 140
 141    switch (addr) {
 142    case 0x000: /* GPMC_REVISION */
 143        return 0x20;
 144
 145    case 0x010: /* GPMC_SYSCONFIG */
 146        return s->sysconfig;
 147
 148    case 0x014: /* GPMC_SYSSTATUS */
 149        return 1;                                               /* RESETDONE */
 150
 151    case 0x018: /* GPMC_IRQSTATUS */
 152        return s->irqst;
 153
 154    case 0x01c: /* GPMC_IRQENABLE */
 155        return s->irqen;
 156
 157    case 0x040: /* GPMC_TIMEOUT_CONTROL */
 158        return s->timeout;
 159
 160    case 0x044: /* GPMC_ERR_ADDRESS */
 161    case 0x048: /* GPMC_ERR_TYPE */
 162        return 0;
 163
 164    case 0x050: /* GPMC_CONFIG */
 165        return s->config;
 166
 167    case 0x054: /* GPMC_STATUS */
 168        return 0x001;
 169
 170    case 0x060 ... 0x1d4:
 171        cs = (addr - 0x060) / 0x30;
 172        addr -= cs * 0x30;
 173        f = s->cs_file + cs;
 174        switch (addr) {
 175            case 0x60:  /* GPMC_CONFIG1 */
 176                return f->config[0];
 177            case 0x64:  /* GPMC_CONFIG2 */
 178                return f->config[1];
 179            case 0x68:  /* GPMC_CONFIG3 */
 180                return f->config[2];
 181            case 0x6c:  /* GPMC_CONFIG4 */
 182                return f->config[3];
 183            case 0x70:  /* GPMC_CONFIG5 */
 184                return f->config[4];
 185            case 0x74:  /* GPMC_CONFIG6 */
 186                return f->config[5];
 187            case 0x78:  /* GPMC_CONFIG7 */
 188                return f->config[6];
 189            case 0x84:  /* GPMC_NAND_DATA */
 190                return 0;
 191        }
 192        break;
 193
 194    case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */
 195        return s->prefconfig[0];
 196    case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */
 197        return s->prefconfig[1];
 198    case 0x1ec: /* GPMC_PREFETCH_CONTROL */
 199        return s->prefcontrol;
 200    case 0x1f0: /* GPMC_PREFETCH_STATUS */
 201        return (s->preffifo << 24) |
 202                ((s->preffifo >
 203                  ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
 204                s->prefcount;
 205
 206    case 0x1f4: /* GPMC_ECC_CONFIG */
 207        return s->ecc_cs;
 208    case 0x1f8: /* GPMC_ECC_CONTROL */
 209        return s->ecc_ptr;
 210    case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */
 211        return s->ecc_cfg;
 212    case 0x200 ... 0x220:       /* GPMC_ECC_RESULT */
 213        cs = (addr & 0x1f) >> 2;
 214        /* TODO: check correctness */
 215        return
 216                ((s->ecc[cs].cp    &  0x07) <<  0) |
 217                ((s->ecc[cs].cp    &  0x38) << 13) |
 218                ((s->ecc[cs].lp[0] & 0x1ff) <<  3) |
 219                ((s->ecc[cs].lp[1] & 0x1ff) << 19);
 220
 221    case 0x230: /* GPMC_TESTMODE_CTRL */
 222        return 0;
 223    case 0x234: /* GPMC_PSA_LSB */
 224    case 0x238: /* GPMC_PSA_MSB */
 225        return 0x00000000;
 226    }
 227
 228    OMAP_BAD_REG(addr);
 229    return 0;
 230}
 231
 232static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
 233                uint32_t value)
 234{
 235    struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
 236    int cs;
 237    struct omap_gpmc_cs_file_s *f;
 238
 239    switch (addr) {
 240    case 0x000: /* GPMC_REVISION */
 241    case 0x014: /* GPMC_SYSSTATUS */
 242    case 0x054: /* GPMC_STATUS */
 243    case 0x1f0: /* GPMC_PREFETCH_STATUS */
 244    case 0x200 ... 0x220:       /* GPMC_ECC_RESULT */
 245    case 0x234: /* GPMC_PSA_LSB */
 246    case 0x238: /* GPMC_PSA_MSB */
 247        OMAP_RO_REG(addr);
 248        break;
 249
 250    case 0x010: /* GPMC_SYSCONFIG */
 251        if ((value >> 3) == 0x3)
 252            fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
 253                            __FUNCTION__, value >> 3);
 254        if (value & 2)
 255            omap_gpmc_reset(s);
 256        s->sysconfig = value & 0x19;
 257        break;
 258
 259    case 0x018: /* GPMC_IRQSTATUS */
 260        s->irqen = ~value;
 261        omap_gpmc_int_update(s);
 262        break;
 263
 264    case 0x01c: /* GPMC_IRQENABLE */
 265        s->irqen = value & 0xf03;
 266        omap_gpmc_int_update(s);
 267        break;
 268
 269    case 0x040: /* GPMC_TIMEOUT_CONTROL */
 270        s->timeout = value & 0x1ff1;
 271        break;
 272
 273    case 0x044: /* GPMC_ERR_ADDRESS */
 274    case 0x048: /* GPMC_ERR_TYPE */
 275        break;
 276
 277    case 0x050: /* GPMC_CONFIG */
 278        s->config = value & 0xf13;
 279        break;
 280
 281    case 0x060 ... 0x1d4:
 282        cs = (addr - 0x060) / 0x30;
 283        addr -= cs * 0x30;
 284        f = s->cs_file + cs;
 285        switch (addr) {
 286            case 0x60:  /* GPMC_CONFIG1 */
 287                f->config[0] = value & 0xffef3e13;
 288                break;
 289            case 0x64:  /* GPMC_CONFIG2 */
 290                f->config[1] = value & 0x001f1f8f;
 291                break;
 292            case 0x68:  /* GPMC_CONFIG3 */
 293                f->config[2] = value & 0x001f1f8f;
 294                break;
 295            case 0x6c:  /* GPMC_CONFIG4 */
 296                f->config[3] = value & 0x1f8f1f8f;
 297                break;
 298            case 0x70:  /* GPMC_CONFIG5 */
 299                f->config[4] = value & 0x0f1f1f1f;
 300                break;
 301            case 0x74:  /* GPMC_CONFIG6 */
 302                f->config[5] = value & 0x00000fcf;
 303                break;
 304            case 0x78:  /* GPMC_CONFIG7 */
 305                if ((f->config[6] ^ value) & 0xf7f) {
 306                    if (f->config[6] & (1 << 6))                /* CSVALID */
 307                        omap_gpmc_cs_unmap(f);
 308                    if (value & (1 << 6))                       /* CSVALID */
 309                        omap_gpmc_cs_map(f, value & 0x1f,       /* MASKADDR */
 310                                        (value >> 8 & 0xf));    /* BASEADDR */
 311                }
 312                f->config[6] = value & 0x00000f7f;
 313                break;
 314            case 0x7c:  /* GPMC_NAND_COMMAND */
 315            case 0x80:  /* GPMC_NAND_ADDRESS */
 316            case 0x84:  /* GPMC_NAND_DATA */
 317                break;
 318
 319            default:
 320                goto bad_reg;
 321        }
 322        break;
 323
 324    case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */
 325        s->prefconfig[0] = value & 0x7f8f7fbf;
 326        /* TODO: update interrupts, fifos, dmas */
 327        break;
 328
 329    case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */
 330        s->prefconfig[1] = value & 0x3fff;
 331        break;
 332
 333    case 0x1ec: /* GPMC_PREFETCH_CONTROL */
 334        s->prefcontrol = value & 1;
 335        if (s->prefcontrol) {
 336            if (s->prefconfig[0] & 1)
 337                s->preffifo = 0x40;
 338            else
 339                s->preffifo = 0x00;
 340        }
 341        /* TODO: start */
 342        break;
 343
 344    case 0x1f4: /* GPMC_ECC_CONFIG */
 345        s->ecc_cs = 0x8f;
 346        break;
 347    case 0x1f8: /* GPMC_ECC_CONTROL */
 348        if (value & (1 << 8))
 349            for (cs = 0; cs < 9; cs ++)
 350                ecc_reset(&s->ecc[cs]);
 351        s->ecc_ptr = value & 0xf;
 352        if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
 353            s->ecc_ptr = 0;
 354            s->ecc_cs &= ~1;
 355        }
 356        break;
 357    case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */
 358        s->ecc_cfg = value & 0x3fcff1ff;
 359        break;
 360    case 0x230: /* GPMC_TESTMODE_CTRL */
 361        if (value & 7)
 362            fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
 363        break;
 364
 365    default:
 366    bad_reg:
 367        OMAP_BAD_REG(addr);
 368        return;
 369    }
 370}
 371
 372static CPUReadMemoryFunc * const omap_gpmc_readfn[] = {
 373    omap_badwidth_read32,       /* TODO */
 374    omap_badwidth_read32,       /* TODO */
 375    omap_gpmc_read,
 376};
 377
 378static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = {
 379    omap_badwidth_write32,      /* TODO */
 380    omap_badwidth_write32,      /* TODO */
 381    omap_gpmc_write,
 382};
 383
 384struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq)
 385{
 386    int iomemtype;
 387    struct omap_gpmc_s *s = (struct omap_gpmc_s *)
 388            qemu_mallocz(sizeof(struct omap_gpmc_s));
 389
 390    omap_gpmc_reset(s);
 391
 392    iomemtype = cpu_register_io_memory(omap_gpmc_readfn,
 393                    omap_gpmc_writefn, s, DEVICE_NATIVE_ENDIAN);
 394    cpu_register_physical_memory(base, 0x1000, iomemtype);
 395
 396    return s;
 397}
 398
 399void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
 400                void (*base_upd)(void *opaque, target_phys_addr_t new),
 401                void (*unmap)(void *opaque), void *opaque)
 402{
 403    struct omap_gpmc_cs_file_s *f;
 404
 405    if (cs < 0 || cs >= 8) {
 406        fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
 407        exit(-1);
 408    }
 409    f = &s->cs_file[cs];
 410
 411    f->iomemtype = iomemtype;
 412    f->base_update = base_upd;
 413    f->unmap = unmap;
 414    f->opaque = opaque;
 415
 416    if (f->config[6] & (1 << 6))                                /* CSVALID */
 417        omap_gpmc_cs_map(f, f->config[6] & 0x1f,                /* MASKADDR */
 418                        (f->config[6] >> 8 & 0xf));             /* BASEADDR */
 419}
 420