From patchwork Mon May 31 15:54:17 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: cmchao X-Patchwork-Id: 54109 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E13B0B7D6E for ; Tue, 1 Jun 2010 02:21:55 +1000 (EST) Received: from localhost ([127.0.0.1]:48965 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OJ7LZ-0006aa-Pf for incoming@patchwork.ozlabs.org; Mon, 31 May 2010 11:56:13 -0400 Received: from [140.186.70.92] (port=43695 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OJ7KR-0006Ud-GM for qemu-devel@nongnu.org; Mon, 31 May 2010 11:55:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OJ7KN-0001db-PQ for qemu-devel@nongnu.org; Mon, 31 May 2010 11:55:02 -0400 Received: from mail-pz0-f196.google.com ([209.85.222.196]:36965) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OJ7KN-0001aS-GU for qemu-devel@nongnu.org; Mon, 31 May 2010 11:54:59 -0400 Received: by mail-pz0-f196.google.com with SMTP id 34so2316803pzk.29 for ; Mon, 31 May 2010 08:54:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:subject:date :message-id:x-mailer:in-reply-to:references; bh=BXo/WxneTs6UeSXWcZEC+AS8J25ruWIUxMbW0VM1qEU=; b=lZJSruM5nWSDRGoxUGhaPlltg8G7e9fLbAYsPbfct77Q0w16D9pY3LYtAT5ItwZ1Hz Qs9G9pwx0qzdSy+kvqvHV9tmPoQO8TcauDujb7nM3aTlmwa+L1lQnsL0PK4Xb/oHI3lj mG1YV5JWBpqHGnFKJDoqmRF/3LK2rduTMy9VU= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:subject:date:message-id:x-mailer:in-reply-to:references; b=IgBIj7iD9VyM0UDEvbfRaSzEbWqrJDQ1q0dKQ8Q+J5TWObUPV5jpAuDak1vcVsN7xL w2echxlXaAH6w5YgMx8GcN+/Hdamse8t5mWoBQEZM9Q1KbzMEi+9IONrmrWV1m4eqTZo VnW5Fz3GTYSQBvZLwWC0FULE0qECbTxm9BvMw= Received: by 10.114.236.2 with SMTP id j2mr3720428wah.110.1275321298804; Mon, 31 May 2010 08:54:58 -0700 (PDT) Received: from localhost.localdomain (114-44-102-240.dynamic.hinet.net [114.44.102.240]) by mx.google.com with ESMTPS id f11sm50661974wai.23.2010.05.31.08.54.54 (version=TLSv1/SSLv3 cipher=RC4-MD5); Mon, 31 May 2010 08:54:57 -0700 (PDT) From: cmchao To: qemu-devel@nongnu.org Date: Mon, 31 May 2010 23:54:17 +0800 Message-Id: <1275321264-1131-6-git-send-email-cmchao@gmail.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1275321264-1131-1-git-send-email-cmchao@gmail.com> References: <1275321264-1131-1-git-send-email-cmchao@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) Subject: [Qemu-devel] [PATCH v2 05/12] hw/omap2.c : separate gpmc(general purpose memory controller) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: cmchao --- Makefile.target | 2 +- hw/omap.h | 2 + hw/omap2.c | 396 ---------------------------------------------------- hw/omap_gpmc.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 422 insertions(+), 397 deletions(-) create mode 100644 hw/omap_gpmc.c diff --git a/Makefile.target b/Makefile.target index f6234bc..b932f79 100644 --- a/Makefile.target +++ b/Makefile.target @@ -265,7 +265,7 @@ obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-arm-y += gumstix.o obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o omap_gpio.o -obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o +obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o omap_gpmc.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o diff --git a/hw/omap.h b/hw/omap.h index b898230..c596953 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -93,8 +93,10 @@ struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, struct omap_sdrc_s; struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base); +/* OMAP2 general purpose memory controller */ struct omap_gpmc_s; struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq); +void omap_gpmc_reset(struct omap_gpmc_s *s); void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, void (*base_upd)(void *opaque, target_phys_addr_t new), void (*unmap)(void *opaque), void *opaque); diff --git a/hw/omap2.c b/hw/omap2.c index e310eef..b1e5aae 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -2988,402 +2988,6 @@ struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base) return s; } -/* General-Purpose Memory Controller */ -struct omap_gpmc_s { - qemu_irq irq; - - uint8_t sysconfig; - uint16_t irqst; - uint16_t irqen; - uint16_t timeout; - uint16_t config; - uint32_t prefconfig[2]; - int prefcontrol; - int preffifo; - int prefcount; - struct omap_gpmc_cs_file_s { - uint32_t config[7]; - target_phys_addr_t base; - size_t size; - int iomemtype; - void (*base_update)(void *opaque, target_phys_addr_t new); - void (*unmap)(void *opaque); - void *opaque; - } cs_file[8]; - int ecc_cs; - int ecc_ptr; - uint32_t ecc_cfg; - ECCState ecc[9]; -}; - -static void omap_gpmc_int_update(struct omap_gpmc_s *s) -{ - qemu_set_irq(s->irq, s->irqen & s->irqst); -} - -static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask) -{ - /* TODO: check for overlapping regions and report access errors */ - if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) || - (base < 0 || base >= 0x40) || - (base & 0x0f & ~mask)) { - fprintf(stderr, "%s: wrong cs address mapping/decoding!\n", - __FUNCTION__); - return; - } - - if (!f->opaque) - return; - - f->base = base << 24; - f->size = (0x0fffffff & ~(mask << 24)) + 1; - /* TODO: rather than setting the size of the mapping (which should be - * constant), the mask should cause wrapping of the address space, so - * that the same memory becomes accessible at every size bytes - * starting from base. */ - if (f->iomemtype) - cpu_register_physical_memory(f->base, f->size, f->iomemtype); - - if (f->base_update) - f->base_update(f->opaque, f->base); -} - -static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f) -{ - if (f->size) { - if (f->unmap) - f->unmap(f->opaque); - if (f->iomemtype) - cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED); - f->base = 0; - f->size = 0; - } -} - -static void omap_gpmc_reset(struct omap_gpmc_s *s) -{ - int i; - - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - omap_gpmc_int_update(s); - s->timeout = 0; - s->config = 0xa00; - s->prefconfig[0] = 0x00004000; - s->prefconfig[1] = 0x00000000; - s->prefcontrol = 0; - s->preffifo = 0; - s->prefcount = 0; - for (i = 0; i < 8; i ++) { - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(s->cs_file + i); - s->cs_file[i].config[0] = i ? 1 << 12 : 0; - s->cs_file[i].config[1] = 0x101001; - s->cs_file[i].config[2] = 0x020201; - s->cs_file[i].config[3] = 0x10031003; - s->cs_file[i].config[4] = 0x10f1111; - s->cs_file[i].config[5] = 0; - s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6); - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(&s->cs_file[i], - s->cs_file[i].config[6] & 0x1f, /* MASKADDR */ - (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */ - } - omap_gpmc_cs_map(s->cs_file, 0, 0xf); - s->ecc_cs = 0; - s->ecc_ptr = 0; - s->ecc_cfg = 0x3fcff000; - for (i = 0; i < 9; i ++) - ecc_reset(&s->ecc[i]); -} - -static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - return 0x20; - - case 0x010: /* GPMC_SYSCONFIG */ - return s->sysconfig; - - case 0x014: /* GPMC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* GPMC_IRQSTATUS */ - return s->irqst; - - case 0x01c: /* GPMC_IRQENABLE */ - return s->irqen; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - return s->timeout; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - return 0; - - case 0x050: /* GPMC_CONFIG */ - return s->config; - - case 0x054: /* GPMC_STATUS */ - return 0x001; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - return f->config[0]; - case 0x64: /* GPMC_CONFIG2 */ - return f->config[1]; - case 0x68: /* GPMC_CONFIG3 */ - return f->config[2]; - case 0x6c: /* GPMC_CONFIG4 */ - return f->config[3]; - case 0x70: /* GPMC_CONFIG5 */ - return f->config[4]; - case 0x74: /* GPMC_CONFIG6 */ - return f->config[5]; - case 0x78: /* GPMC_CONFIG7 */ - return f->config[6]; - case 0x84: /* GPMC_NAND_DATA */ - return 0; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - return s->prefconfig[0]; - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - return s->prefconfig[1]; - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - return s->prefcontrol; - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - return (s->preffifo << 24) | - ((s->preffifo > - ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) | - s->prefcount; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - return s->ecc_cs; - case 0x1f8: /* GPMC_ECC_CONTROL */ - return s->ecc_ptr; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - return s->ecc_cfg; - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - cs = (addr & 0x1f) >> 2; - /* TODO: check correctness */ - return - ((s->ecc[cs].cp & 0x07) << 0) | - ((s->ecc[cs].cp & 0x38) << 13) | - ((s->ecc[cs].lp[0] & 0x1ff) << 3) | - ((s->ecc[cs].lp[1] & 0x1ff) << 19); - - case 0x230: /* GPMC_TESTMODE_CTRL */ - return 0; - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - return 0x00000000; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - case 0x014: /* GPMC_SYSSTATUS */ - case 0x054: /* GPMC_STATUS */ - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - OMAP_RO_REG(addr); - break; - - case 0x010: /* GPMC_SYSCONFIG */ - if ((value >> 3) == 0x3) - fprintf(stderr, "%s: bad SDRAM idle mode %i\n", - __FUNCTION__, value >> 3); - if (value & 2) - omap_gpmc_reset(s); - s->sysconfig = value & 0x19; - break; - - case 0x018: /* GPMC_IRQSTATUS */ - s->irqen = ~value; - omap_gpmc_int_update(s); - break; - - case 0x01c: /* GPMC_IRQENABLE */ - s->irqen = value & 0xf03; - omap_gpmc_int_update(s); - break; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - s->timeout = value & 0x1ff1; - break; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - break; - - case 0x050: /* GPMC_CONFIG */ - s->config = value & 0xf13; - break; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - f->config[0] = value & 0xffef3e13; - break; - case 0x64: /* GPMC_CONFIG2 */ - f->config[1] = value & 0x001f1f8f; - break; - case 0x68: /* GPMC_CONFIG3 */ - f->config[2] = value & 0x001f1f8f; - break; - case 0x6c: /* GPMC_CONFIG4 */ - f->config[3] = value & 0x1f8f1f8f; - break; - case 0x70: /* GPMC_CONFIG5 */ - f->config[4] = value & 0x0f1f1f1f; - break; - case 0x74: /* GPMC_CONFIG6 */ - f->config[5] = value & 0x00000fcf; - break; - case 0x78: /* GPMC_CONFIG7 */ - if ((f->config[6] ^ value) & 0xf7f) { - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(f); - if (value & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */ - (value >> 8 & 0xf)); /* BASEADDR */ - } - f->config[6] = value & 0x00000f7f; - break; - case 0x7c: /* GPMC_NAND_COMMAND */ - case 0x80: /* GPMC_NAND_ADDRESS */ - case 0x84: /* GPMC_NAND_DATA */ - break; - - default: - goto bad_reg; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - s->prefconfig[0] = value & 0x7f8f7fbf; - /* TODO: update interrupts, fifos, dmas */ - break; - - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - s->prefconfig[1] = value & 0x3fff; - break; - - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - s->prefcontrol = value & 1; - if (s->prefcontrol) { - if (s->prefconfig[0] & 1) - s->preffifo = 0x40; - else - s->preffifo = 0x00; - } - /* TODO: start */ - break; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - s->ecc_cs = 0x8f; - break; - case 0x1f8: /* GPMC_ECC_CONTROL */ - if (value & (1 << 8)) - for (cs = 0; cs < 9; cs ++) - ecc_reset(&s->ecc[cs]); - s->ecc_ptr = value & 0xf; - if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { - s->ecc_ptr = 0; - s->ecc_cs &= ~1; - } - break; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - s->ecc_cfg = value & 0x3fcff1ff; - break; - case 0x230: /* GPMC_TESTMODE_CTRL */ - if (value & 7) - fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__); - break; - - default: - bad_reg: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_gpmc_readfn[] = { - omap_badwidth_read32, /* TODO */ - omap_badwidth_read32, /* TODO */ - omap_gpmc_read, -}; - -static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = { - omap_badwidth_write32, /* TODO */ - omap_badwidth_write32, /* TODO */ - omap_gpmc_write, -}; - -struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq) -{ - int iomemtype; - struct omap_gpmc_s *s = (struct omap_gpmc_s *) - qemu_mallocz(sizeof(struct omap_gpmc_s)); - - omap_gpmc_reset(s); - - iomemtype = cpu_register_io_memory(omap_gpmc_readfn, - omap_gpmc_writefn, s); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, - void (*base_upd)(void *opaque, target_phys_addr_t new), - void (*unmap)(void *opaque), void *opaque) -{ - struct omap_gpmc_cs_file_s *f; - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - f->iomemtype = iomemtype; - f->base_update = base_upd; - f->unmap = unmap; - f->opaque = opaque; - - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */ - (f->config[6] >> 8 & 0xf)); /* BASEADDR */ -} - /* General chip reset */ static void omap2_mpu_reset(void *opaque) { diff --git a/hw/omap_gpmc.c b/hw/omap_gpmc.c new file mode 100644 index 0000000..72cfecc --- /dev/null +++ b/hw/omap_gpmc.c @@ -0,0 +1,419 @@ +/* + * TI OMAP general purpose memory controller emulation. + * + * Copyright (C) 2007-2009 Nokia Corporation + * Original code written by Andrzej Zaborowski + * Enhancements for OMAP3 and NAND support written by Juha Riihimäki + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#include "hw.h" +#include "flash.h" +#include "omap.h" + +/* General-Purpose Memory Controller */ +struct omap_gpmc_s { + qemu_irq irq; + + uint8_t sysconfig; + uint16_t irqst; + uint16_t irqen; + uint16_t timeout; + uint16_t config; + uint32_t prefconfig[2]; + int prefcontrol; + int preffifo; + int prefcount; + struct omap_gpmc_cs_file_s { + uint32_t config[7]; + target_phys_addr_t base; + size_t size; + int iomemtype; + void (*base_update)(void *opaque, target_phys_addr_t new); + void (*unmap)(void *opaque); + void *opaque; + } cs_file[8]; + int ecc_cs; + int ecc_ptr; + uint32_t ecc_cfg; + ECCState ecc[9]; +}; + +static void omap_gpmc_int_update(struct omap_gpmc_s *s) +{ + qemu_set_irq(s->irq, s->irqen & s->irqst); +} + +static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask) +{ + /* TODO: check for overlapping regions and report access errors */ + if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) || + (base < 0 || base >= 0x40) || + (base & 0x0f & ~mask)) { + fprintf(stderr, "%s: wrong cs address mapping/decoding!\n", + __FUNCTION__); + return; + } + + if (!f->opaque) + return; + + f->base = base << 24; + f->size = (0x0fffffff & ~(mask << 24)) + 1; + /* TODO: rather than setting the size of the mapping (which should be + * constant), the mask should cause wrapping of the address space, so + * that the same memory becomes accessible at every size bytes + * starting from base. */ + if (f->iomemtype) + cpu_register_physical_memory(f->base, f->size, f->iomemtype); + + if (f->base_update) + f->base_update(f->opaque, f->base); +} + +static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f) +{ + if (f->size) { + if (f->unmap) + f->unmap(f->opaque); + if (f->iomemtype) + cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED); + f->base = 0; + f->size = 0; + } +} + +void omap_gpmc_reset(struct omap_gpmc_s *s) +{ + int i; + + s->sysconfig = 0; + s->irqst = 0; + s->irqen = 0; + omap_gpmc_int_update(s); + s->timeout = 0; + s->config = 0xa00; + s->prefconfig[0] = 0x00004000; + s->prefconfig[1] = 0x00000000; + s->prefcontrol = 0; + s->preffifo = 0; + s->prefcount = 0; + for (i = 0; i < 8; i ++) { + if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_unmap(s->cs_file + i); + s->cs_file[i].config[0] = i ? 1 << 12 : 0; + s->cs_file[i].config[1] = 0x101001; + s->cs_file[i].config[2] = 0x020201; + s->cs_file[i].config[3] = 0x10031003; + s->cs_file[i].config[4] = 0x10f1111; + s->cs_file[i].config[5] = 0; + s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6); + if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(&s->cs_file[i], + s->cs_file[i].config[6] & 0x1f, /* MASKADDR */ + (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */ + } + omap_gpmc_cs_map(s->cs_file, 0, 0xf); + s->ecc_cs = 0; + s->ecc_ptr = 0; + s->ecc_cfg = 0x3fcff000; + for (i = 0; i < 9; i ++) + ecc_reset(&s->ecc[i]); +} + +static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + int cs; + struct omap_gpmc_cs_file_s *f; + + switch (addr) { + case 0x000: /* GPMC_REVISION */ + return 0x20; + + case 0x010: /* GPMC_SYSCONFIG */ + return s->sysconfig; + + case 0x014: /* GPMC_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x018: /* GPMC_IRQSTATUS */ + return s->irqst; + + case 0x01c: /* GPMC_IRQENABLE */ + return s->irqen; + + case 0x040: /* GPMC_TIMEOUT_CONTROL */ + return s->timeout; + + case 0x044: /* GPMC_ERR_ADDRESS */ + case 0x048: /* GPMC_ERR_TYPE */ + return 0; + + case 0x050: /* GPMC_CONFIG */ + return s->config; + + case 0x054: /* GPMC_STATUS */ + return 0x001; + + case 0x060 ... 0x1d4: + cs = (addr - 0x060) / 0x30; + addr -= cs * 0x30; + f = s->cs_file + cs; + switch (addr) { + case 0x60: /* GPMC_CONFIG1 */ + return f->config[0]; + case 0x64: /* GPMC_CONFIG2 */ + return f->config[1]; + case 0x68: /* GPMC_CONFIG3 */ + return f->config[2]; + case 0x6c: /* GPMC_CONFIG4 */ + return f->config[3]; + case 0x70: /* GPMC_CONFIG5 */ + return f->config[4]; + case 0x74: /* GPMC_CONFIG6 */ + return f->config[5]; + case 0x78: /* GPMC_CONFIG7 */ + return f->config[6]; + case 0x84: /* GPMC_NAND_DATA */ + return 0; + } + break; + + case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ + return s->prefconfig[0]; + case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ + return s->prefconfig[1]; + case 0x1ec: /* GPMC_PREFETCH_CONTROL */ + return s->prefcontrol; + case 0x1f0: /* GPMC_PREFETCH_STATUS */ + return (s->preffifo << 24) | + ((s->preffifo > + ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) | + s->prefcount; + + case 0x1f4: /* GPMC_ECC_CONFIG */ + return s->ecc_cs; + case 0x1f8: /* GPMC_ECC_CONTROL */ + return s->ecc_ptr; + case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ + return s->ecc_cfg; + case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ + cs = (addr & 0x1f) >> 2; + /* TODO: check correctness */ + return + ((s->ecc[cs].cp & 0x07) << 0) | + ((s->ecc[cs].cp & 0x38) << 13) | + ((s->ecc[cs].lp[0] & 0x1ff) << 3) | + ((s->ecc[cs].lp[1] & 0x1ff) << 19); + + case 0x230: /* GPMC_TESTMODE_CTRL */ + return 0; + case 0x234: /* GPMC_PSA_LSB */ + case 0x238: /* GPMC_PSA_MSB */ + return 0x00000000; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + int cs; + struct omap_gpmc_cs_file_s *f; + + switch (addr) { + case 0x000: /* GPMC_REVISION */ + case 0x014: /* GPMC_SYSSTATUS */ + case 0x054: /* GPMC_STATUS */ + case 0x1f0: /* GPMC_PREFETCH_STATUS */ + case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ + case 0x234: /* GPMC_PSA_LSB */ + case 0x238: /* GPMC_PSA_MSB */ + OMAP_RO_REG(addr); + break; + + case 0x010: /* GPMC_SYSCONFIG */ + if ((value >> 3) == 0x3) + fprintf(stderr, "%s: bad SDRAM idle mode %i\n", + __FUNCTION__, value >> 3); + if (value & 2) + omap_gpmc_reset(s); + s->sysconfig = value & 0x19; + break; + + case 0x018: /* GPMC_IRQSTATUS */ + s->irqen = ~value; + omap_gpmc_int_update(s); + break; + + case 0x01c: /* GPMC_IRQENABLE */ + s->irqen = value & 0xf03; + omap_gpmc_int_update(s); + break; + + case 0x040: /* GPMC_TIMEOUT_CONTROL */ + s->timeout = value & 0x1ff1; + break; + + case 0x044: /* GPMC_ERR_ADDRESS */ + case 0x048: /* GPMC_ERR_TYPE */ + break; + + case 0x050: /* GPMC_CONFIG */ + s->config = value & 0xf13; + break; + + case 0x060 ... 0x1d4: + cs = (addr - 0x060) / 0x30; + addr -= cs * 0x30; + f = s->cs_file + cs; + switch (addr) { + case 0x60: /* GPMC_CONFIG1 */ + f->config[0] = value & 0xffef3e13; + break; + case 0x64: /* GPMC_CONFIG2 */ + f->config[1] = value & 0x001f1f8f; + break; + case 0x68: /* GPMC_CONFIG3 */ + f->config[2] = value & 0x001f1f8f; + break; + case 0x6c: /* GPMC_CONFIG4 */ + f->config[3] = value & 0x1f8f1f8f; + break; + case 0x70: /* GPMC_CONFIG5 */ + f->config[4] = value & 0x0f1f1f1f; + break; + case 0x74: /* GPMC_CONFIG6 */ + f->config[5] = value & 0x00000fcf; + break; + case 0x78: /* GPMC_CONFIG7 */ + if ((f->config[6] ^ value) & 0xf7f) { + if (f->config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_unmap(f); + if (value & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */ + (value >> 8 & 0xf)); /* BASEADDR */ + } + f->config[6] = value & 0x00000f7f; + break; + case 0x7c: /* GPMC_NAND_COMMAND */ + case 0x80: /* GPMC_NAND_ADDRESS */ + case 0x84: /* GPMC_NAND_DATA */ + break; + + default: + goto bad_reg; + } + break; + + case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ + s->prefconfig[0] = value & 0x7f8f7fbf; + /* TODO: update interrupts, fifos, dmas */ + break; + + case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ + s->prefconfig[1] = value & 0x3fff; + break; + + case 0x1ec: /* GPMC_PREFETCH_CONTROL */ + s->prefcontrol = value & 1; + if (s->prefcontrol) { + if (s->prefconfig[0] & 1) + s->preffifo = 0x40; + else + s->preffifo = 0x00; + } + /* TODO: start */ + break; + + case 0x1f4: /* GPMC_ECC_CONFIG */ + s->ecc_cs = 0x8f; + break; + case 0x1f8: /* GPMC_ECC_CONTROL */ + if (value & (1 << 8)) + for (cs = 0; cs < 9; cs ++) + ecc_reset(&s->ecc[cs]); + s->ecc_ptr = value & 0xf; + if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { + s->ecc_ptr = 0; + s->ecc_cs &= ~1; + } + break; + case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ + s->ecc_cfg = value & 0x3fcff1ff; + break; + case 0x230: /* GPMC_TESTMODE_CTRL */ + if (value & 7) + fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__); + break; + + default: + bad_reg: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_gpmc_readfn[] = { + omap_badwidth_read32, /* TODO */ + omap_badwidth_read32, /* TODO */ + omap_gpmc_read, +}; + +static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = { + omap_badwidth_write32, /* TODO */ + omap_badwidth_write32, /* TODO */ + omap_gpmc_write, +}; + +struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq) +{ + int iomemtype; + struct omap_gpmc_s *s = (struct omap_gpmc_s *) + qemu_mallocz(sizeof(struct omap_gpmc_s)); + + omap_gpmc_reset(s); + + iomemtype = cpu_register_io_memory(omap_gpmc_readfn, + omap_gpmc_writefn, s); + cpu_register_physical_memory(base, 0x1000, iomemtype); + + return s; +} + +void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, + void (*base_upd)(void *opaque, target_phys_addr_t new), + void (*unmap)(void *opaque), void *opaque) +{ + struct omap_gpmc_cs_file_s *f; + + if (cs < 0 || cs >= 8) { + fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); + exit(-1); + } + f = &s->cs_file[cs]; + + f->iomemtype = iomemtype; + f->base_update = base_upd; + f->unmap = unmap; + f->opaque = opaque; + + if (f->config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */ + (f->config[6] >> 8 & 0xf)); /* BASEADDR */ +}