onenand.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 643 行 · 第 1/2 页
C
643 行
/* * OneNAND flash memories emulation. * * Copyright (C) 2008 Nokia Corporation * Written by Andrzej Zaborowski <andrew@openedhand.com> * * 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) version 3 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */#include "qemu-common.h"#include "flash.h"#include "irq.h"#include "sysemu.h"#include "block.h"/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */#define PAGE_SHIFT 11/* Fixed */#define BLOCK_SHIFT (PAGE_SHIFT + 6)struct onenand_s { uint32_t id; int shift; target_phys_addr_t base; qemu_irq intr; qemu_irq rdy; BlockDriverState *bdrv; BlockDriverState *bdrv_cur; uint8_t *image; uint8_t *otp; uint8_t *current; ram_addr_t ram; uint8_t *boot[2]; uint8_t *data[2][2]; int iomemtype; int cycle; int otpmode; uint16_t addr[8]; uint16_t unladdr[8]; int bufaddr; int count; uint16_t command; uint16_t config[2]; uint16_t status; uint16_t intstatus; uint16_t wpstatus; struct ecc_state_s ecc; int density_mask; int secs; int secs_cur; int blocks; uint8_t *blockwp;};enum { ONEN_BUF_BLOCK = 0, ONEN_BUF_BLOCK2 = 1, ONEN_BUF_DEST_BLOCK = 2, ONEN_BUF_DEST_PAGE = 3, ONEN_BUF_PAGE = 7,};enum { ONEN_ERR_CMD = 1 << 10, ONEN_ERR_ERASE = 1 << 11, ONEN_ERR_PROG = 1 << 12, ONEN_ERR_LOAD = 1 << 13,};enum { ONEN_INT_RESET = 1 << 4, ONEN_INT_ERASE = 1 << 5, ONEN_INT_PROG = 1 << 6, ONEN_INT_LOAD = 1 << 7, ONEN_INT = 1 << 15,};enum { ONEN_LOCK_LOCKTIGHTEN = 1 << 0, ONEN_LOCK_LOCKED = 1 << 1, ONEN_LOCK_UNLOCKED = 1 << 2,};void onenand_base_update(void *opaque, target_phys_addr_t new){ struct onenand_s *s = (struct onenand_s *) opaque; s->base = new; /* XXX: We should use IO_MEM_ROMD but we broke it earlier... * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to * write boot commands. Also take note of the BWPS bit. */ cpu_register_physical_memory(s->base + (0x0000 << s->shift), 0x0200 << s->shift, s->iomemtype); cpu_register_physical_memory(s->base + (0x0200 << s->shift), 0xbe00 << s->shift, (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM); if (s->iomemtype) cpu_register_physical_memory(s->base + (0xc000 << s->shift), 0x4000 << s->shift, s->iomemtype);}void onenand_base_unmap(void *opaque){ struct onenand_s *s = (struct onenand_s *) opaque; cpu_register_physical_memory(s->base, 0x10000 << s->shift, IO_MEM_UNASSIGNED);}static void onenand_intr_update(struct onenand_s *s){ qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1);}/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */static void onenand_reset(struct onenand_s *s, int cold){ memset(&s->addr, 0, sizeof(s->addr)); s->command = 0; s->count = 1; s->bufaddr = 0; s->config[0] = 0x40c0; s->config[1] = 0x0000; onenand_intr_update(s); qemu_irq_raise(s->rdy); s->status = 0x0000; s->intstatus = cold ? 0x8080 : 0x8010; s->unladdr[0] = 0; s->unladdr[1] = 0; s->wpstatus = 0x0002; s->cycle = 0; s->otpmode = 0; s->bdrv_cur = s->bdrv; s->current = s->image; s->secs_cur = s->secs; if (cold) { /* Lock the whole flash */ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0) cpu_abort(cpu_single_env, "%s: Loading the BootRAM failed.\n", __FUNCTION__); }}static inline int onenand_load_main(struct onenand_s *s, int sec, int secn, void *dest){ if (s->bdrv_cur) return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0; else if (sec + secn > s->secs_cur) return 1; memcpy(dest, s->current + (sec << 9), secn << 9); return 0;}static inline int onenand_prog_main(struct onenand_s *s, int sec, int secn, void *src){ if (s->bdrv_cur) return bdrv_write(s->bdrv_cur, sec, src, secn) < 0; else if (sec + secn > s->secs_cur) return 1; memcpy(s->current + (sec << 9), src, secn << 9); return 0;}static inline int onenand_load_spare(struct onenand_s *s, int sec, int secn, void *dest){ uint8_t buf[512]; if (s->bdrv_cur) { if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) return 1; memcpy(dest, buf + ((sec & 31) << 4), secn << 4); } else if (sec + secn > s->secs_cur) return 1; else memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); return 0;}static inline int onenand_prog_spare(struct onenand_s *s, int sec, int secn, void *src){ uint8_t buf[512]; if (s->bdrv_cur) { if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) return 1; memcpy(buf + ((sec & 31) << 4), src, secn << 4); return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0; } else if (sec + secn > s->secs_cur) return 1; memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4); return 0;}static inline int onenand_erase(struct onenand_s *s, int sec, int num){ /* TODO: optimise */ uint8_t buf[512]; memset(buf, 0xff, sizeof(buf)); for (; num > 0; num --, sec ++) { if (onenand_prog_main(s, sec, 1, buf)) return 1; if (onenand_prog_spare(s, sec, 1, buf)) return 1; } return 0;}static void onenand_command(struct onenand_s *s, int cmd){ int b; int sec; void *buf;#define SETADDR(block, page) \ sec = (s->addr[page] & 3) + \ ((((s->addr[page] >> 2) & 0x3f) + \ (((s->addr[block] & 0xfff) | \ (s->addr[block] >> 15 ? \ s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));#define SETBUF_M() \ buf = (s->bufaddr & 8) ? \ s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ buf += (s->bufaddr & 3) << 9;#define SETBUF_S() \ buf = (s->bufaddr & 8) ? \ s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ buf += (s->bufaddr & 3) << 4; switch (cmd) { case 0x00: /* Load single/multiple sector data unit into buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_M() if (onenand_load_main(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;#if 0 SETBUF_S() if (onenand_load_spare(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;#endif /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) * then we need two split the read/write into two chunks. */ s->intstatus |= ONEN_INT | ONEN_INT_LOAD; break; case 0x13: /* Load single/multiple spare sector into buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_S() if (onenand_load_spare(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) * then we need two split the read/write into two chunks. */ s->intstatus |= ONEN_INT | ONEN_INT_LOAD; break; case 0x80: /* Program single/multiple sector data unit from buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_M() if (onenand_prog_main(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;#if 0 SETBUF_S() if (onenand_prog_spare(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;#endif /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) * then we need two split the read/write into two chunks. */ s->intstatus |= ONEN_INT | ONEN_INT_PROG; break; case 0x1a: /* Program single/multiple spare area sector from buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) SETBUF_S() if (onenand_prog_spare(s, sec, s->count, buf)) s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?