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 + -
显示快捷键?