All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Cc: blauwirbel@gmail.com, edgar.iglesias@gmail.com,
	aliguori@us.ibm.com, qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI flash device
Date: Fri, 5 Oct 2012 13:22:00 +0100	[thread overview]
Message-ID: <CAFEAcA_CmXSEs=X2f-jy38Awp6=2YcadxpW3yMHQO2BxoHFOVQ@mail.gmail.com> (raw)
In-Reply-To: <1349395739-26502-9-git-send-email-peter.crosthwaite@xilinx.com>

On 5 October 2012 01:08, Peter Crosthwaite
<peter.crosthwaite@petalogix.com> wrote:
> From: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
>
> Added device model for m25p80 style SPI flash family.
>
> Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
> ---
>  default-configs/arm-softmmu.mak          |    1 +
>  default-configs/microblaze-softmmu.mak   |    2 +
>  default-configs/microblazeel-softmmu.mak |    2 +
>  hw/Makefile.objs                         |    1 +
>  hw/m25p80.c                              |  574 ++++++++++++++++++++++++++++++
>  5 files changed, 580 insertions(+), 0 deletions(-)
>  create mode 100644 hw/m25p80.c
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index f335a72..2f1a5c9 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -22,6 +22,7 @@ CONFIG_ADS7846=y
>  CONFIG_MAX111X=y
>  CONFIG_SSI=y
>  CONFIG_SSI_SD=y
> +CONFIG_SSI_M25P80=y
>  CONFIG_LAN9118=y
>  CONFIG_SMC91C111=y
>  CONFIG_DS1338=y
> diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
> index 64c9485..2f442e5 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,5 @@ CONFIG_PFLASH_CFI01=y
>  CONFIG_SERIAL=y
>  CONFIG_XILINX=y
>  CONFIG_XILINX_AXI=y
> +CONFIG_SSI=y
> +CONFIG_SSI_M25P80=y
> diff --git a/default-configs/microblazeel-softmmu.mak b/default-configs/microblazeel-softmmu.mak
> index a962276..af9a3cd 100644
> --- a/default-configs/microblazeel-softmmu.mak
> +++ b/default-configs/microblazeel-softmmu.mak
> @@ -5,3 +5,5 @@ CONFIG_PFLASH_CFI01=y
>  CONFIG_SERIAL=y
>  CONFIG_XILINX=y
>  CONFIG_XILINX_AXI=y
> +CONFIG_SSI=y
> +CONFIG_SSI_M25P80=y
> diff --git a/hw/Makefile.objs b/hw/Makefile.objs
> index e18ae34..7342cf9 100644
> --- a/hw/Makefile.objs
> +++ b/hw/Makefile.objs
> @@ -174,6 +174,7 @@ common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o
>  common-obj-y += scsi-generic.o scsi-bus.o
>  common-obj-y += hid.o
>  common-obj-$(CONFIG_SSI) += ssi.o
> +common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
>  common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
>  common-obj-$(CONFIG_SD) += sd.o
>  common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
> diff --git a/hw/m25p80.c b/hw/m25p80.c
> new file mode 100644
> index 0000000..7f08e22
> --- /dev/null
> +++ b/hw/m25p80.c
> @@ -0,0 +1,574 @@
> +/*
> + * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
> + * set. Known devices table current as of Jun/2012 and taked from linux.

"taken".

> + * See drivers/mtd/devices/m25p80.c.
> + *
> + * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
> + * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
> + * Copyright (C) 2012 PetaLogix
> + *
> + * 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) a 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 <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "hw.h"
> +#include "blockdev.h"
> +#include "ssi.h"
> +#include "devices.h"
> +
> +#ifdef M25P80_ERR_DEBUG
> +#define DB_PRINT(...) do { \
> +    fprintf(stderr,  ": %s: ", __func__); \
> +    fprintf(stderr, ## __VA_ARGS__); \
> +    } while (0);
> +#else
> +    #define DB_PRINT(...)
> +#endif
> +
> +typedef struct FlashPartInfo {
> +    const char *part_name;
> +    /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
> +    uint32_t jedec;
> +    /* extended jedec code */
> +    uint16_t ext_jedec;
> +    /* there is confusion between manufacturers as to what a sector is. In this
> +     * device model, a "sector" is the size that is erased by the ERASE_SECTOR
> +     * command (opcode 0xd8).
> +     */
> +    uint32_t sector_size;
> +    uint32_t n_sectors;
> +    uint32_t page_size;
> +    uint8_t flags;
> +    /* erase capabilities */
> +#define ER_4K 1
> +#define ER_32K 2
> +    /* set to allow the page program command to write 0s back to 1. Useful for
> +     * modelling EEPROM with SPI flash command set
> +     */
> +#define WR_1 0x100

What are these #defines doing inside the struct definition?

> +} FlashPartInfo;
> +
> +/* adapted from linux */
> +
> +#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
> +    .part_name = (_part_name),\
> +    .jedec = (_jedec),\
> +    .ext_jedec = (_ext_jedec),\
> +    .sector_size = (_sector_size),\
> +    .n_sectors = (_n_sectors),\
> +    .page_size = 256,\
> +    .flags = (_flags),\
> +
> +static const FlashPartInfo known_devices[] = {
> +    /* Atmel -- some are (confusingly) marketed as "DataFlash" */
> +    { INFO("at25fs010",   0x1f6601,      0,  32 << 10,   4, ER_4K) },
> +    { INFO("at25fs040",   0x1f6604,      0,  64 << 10,   8, ER_4K) },
> +
> +    { INFO("at25df041a",  0x1f4401,      0,  64 << 10,   8, ER_4K) },
> +    { INFO("at25df321a",  0x1f4701,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("at25df641",   0x1f4800,      0,  64 << 10, 128, ER_4K) },
> +
> +    { INFO("at26f004",    0x1f0400,      0,  64 << 10,   8, ER_4K) },
> +    { INFO("at26df081a",  0x1f4501,      0,  64 << 10,  16, ER_4K) },
> +    { INFO("at26df161a",  0x1f4601,      0,  64 << 10,  32, ER_4K) },
> +    { INFO("at26df321",   0x1f4700,      0,  64 << 10,  64, ER_4K) },
> +
> +    /* EON -- en25xxx */
> +    { INFO("en25f32",     0x1c3116,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("en25p32",     0x1c2016,      0,  64 << 10,  64, 0) },
> +    { INFO("en25q32b",    0x1c3016,      0,  64 << 10,  64, 0) },
> +    { INFO("en25p64",     0x1c2017,      0,  64 << 10, 128, 0) },
> +
> +    /* Intel/Numonyx -- xxxs33b */
> +    { INFO("160s33b",     0x898911,      0,  64 << 10,  32, 0) },
> +    { INFO("320s33b",     0x898912,      0,  64 << 10,  64, 0) },
> +    { INFO("640s33b",     0x898913,      0,  64 << 10, 128, 0) },
> +
> +    /* Macronix */
> +    { INFO("mx25l4005a",  0xc22013,      0,  64 << 10,   8, ER_4K) },
> +    { INFO("mx25l8005",   0xc22014,      0,  64 << 10,  16, 0) },
> +    { INFO("mx25l1606e",  0xc22015,      0,  64 << 10,  32, ER_4K) },
> +    { INFO("mx25l3205d",  0xc22016,      0,  64 << 10,  64, 0) },
> +    { INFO("mx25l6405d",  0xc22017,      0,  64 << 10, 128, 0) },
> +    { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
> +    { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
> +    { INFO("mx25l25635e", 0xc22019,      0,  64 << 10, 512, 0) },
> +    { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
> +
> +    /* Spansion -- single (large) sector size only, at least
> +     * for the chips listed here (without boot sectors).
> +     */
> +    { INFO("s25sl004a",   0x010212,      0,  64 << 10,   8, 0) },
> +    { INFO("s25sl008a",   0x010213,      0,  64 << 10,  16, 0) },
> +    { INFO("s25sl016a",   0x010214,      0,  64 << 10,  32, 0) },
> +    { INFO("s25sl032a",   0x010215,      0,  64 << 10,  64, 0) },
> +    { INFO("s25sl032p",   0x010215, 0x4d00,  64 << 10,  64, ER_4K) },
> +    { INFO("s25sl064a",   0x010216,      0,  64 << 10, 128, 0) },
> +    { INFO("s25fl256s0",  0x010219, 0x4d00, 256 << 10, 128, 0) },
> +    { INFO("s25fl256s1",  0x010219, 0x4d01,  64 << 10, 512, 0) },
> +    { INFO("s25fl512s",   0x010220, 0x4d00, 256 << 10, 256, 0) },
> +    { INFO("s70fl01gs",   0x010221, 0x4d00, 256 << 10, 256, 0) },
> +    { INFO("s25sl12800",  0x012018, 0x0300, 256 << 10,  64, 0) },
> +    { INFO("s25sl12801",  0x012018, 0x0301,  64 << 10, 256, 0) },
> +    { INFO("s25fl129p0",  0x012018, 0x4d00, 256 << 10,  64, 0) },
> +    { INFO("s25fl129p1",  0x012018, 0x4d01,  64 << 10, 256, 0) },
> +    { INFO("s25fl016k",   0xef4015,      0,  64 << 10,  32, ER_4K | ER_32K) },
> +    { INFO("s25fl064k",   0xef4017,      0,  64 << 10, 128, ER_4K | ER_32K) },
> +
> +    /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
> +    { INFO("sst25vf040b", 0xbf258d,      0,  64 << 10,   8, ER_4K) },
> +    { INFO("sst25vf080b", 0xbf258e,      0,  64 << 10,  16, ER_4K) },
> +    { INFO("sst25vf016b", 0xbf2541,      0,  64 << 10,  32, ER_4K) },
> +    { INFO("sst25vf032b", 0xbf254a,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("sst25wf512",  0xbf2501,      0,  64 << 10,   1, ER_4K) },
> +    { INFO("sst25wf010",  0xbf2502,      0,  64 << 10,   2, ER_4K) },
> +    { INFO("sst25wf020",  0xbf2503,      0,  64 << 10,   4, ER_4K) },
> +    { INFO("sst25wf040",  0xbf2504,      0,  64 << 10,   8, ER_4K) },
> +
> +    /* ST Microelectronics -- newer production may have feature updates */
> +    { INFO("m25p05",      0x202010,      0,  32 << 10,   2, 0) },
> +    { INFO("m25p10",      0x202011,      0,  32 << 10,   4, 0) },
> +    { INFO("m25p20",      0x202012,      0,  64 << 10,   4, 0) },
> +    { INFO("m25p40",      0x202013,      0,  64 << 10,   8, 0) },
> +    { INFO("m25p80",      0x202014,      0,  64 << 10,  16, 0) },
> +    { INFO("m25p16",      0x202015,      0,  64 << 10,  32, 0) },
> +    { INFO("m25p32",      0x202016,      0,  64 << 10,  64, 0) },
> +    { INFO("m25p64",      0x202017,      0,  64 << 10, 128, 0) },
> +    { INFO("m25p128",     0x202018,      0, 256 << 10,  64, 0) },
> +
> +    { INFO("m45pe10",     0x204011,      0,  64 << 10,   2, 0) },
> +    { INFO("m45pe80",     0x204014,      0,  64 << 10,  16, 0) },
> +    { INFO("m45pe16",     0x204015,      0,  64 << 10,  32, 0) },
> +
> +    { INFO("m25pe80",     0x208014,      0,  64 << 10,  16, 0) },
> +    { INFO("m25pe16",     0x208015,      0,  64 << 10,  32, ER_4K) },
> +
> +    { INFO("m25px32",     0x207116,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("m25px32-s0",  0x207316,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("m25px32-s1",  0x206316,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("m25px64",     0x207117,      0,  64 << 10, 128, 0) },
> +
> +    /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
> +    { INFO("w25x10",      0xef3011,      0,  64 << 10,   2, ER_4K) },
> +    { INFO("w25x20",      0xef3012,      0,  64 << 10,   4, ER_4K) },
> +    { INFO("w25x40",      0xef3013,      0,  64 << 10,   8, ER_4K) },
> +    { INFO("w25x80",      0xef3014,      0,  64 << 10,  16, ER_4K) },
> +    { INFO("w25x16",      0xef3015,      0,  64 << 10,  32, ER_4K) },
> +    { INFO("w25x32",      0xef3016,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("w25q32",      0xef4016,      0,  64 << 10,  64, ER_4K) },
> +    { INFO("w25x64",      0xef3017,      0,  64 << 10, 128, ER_4K) },
> +    { INFO("w25q64",      0xef4017,      0,  64 << 10, 128, ER_4K) },
> +
> +    /* Numonyx -- n25q128 */
> +    { INFO("n25q128",      0x20ba18,      0,  64 << 10, 256, 0) },
> +
> +    { },
> +};
> +
> +typedef enum {
> +    NOP = 0,
> +    PP = 0x2,
> +    READ = 0x3,
> +    WRDI = 0x4,
> +    RDSR = 0x5,
> +    WREN = 0x6,
> +    FAST_READ = 0xb,
> +    ERASE_4K = 0x20,
> +    ERASE_32K = 0x52,
> +    ERASE_SECTOR = 0xd8,
> +    JEDEC_READ = 0x9f,
> +    BULK_ERASE = 0xc7,
> +} FlashCMD;
> +
> +typedef enum {
> +    STATE_IDLE,
> +    STATE_PAGE_PROGRAM,
> +    STATE_READ,
> +    STATE_COLLECTING_DATA,
> +    STATE_READING_DATA,
> +} CMDState;
> +
> +typedef struct Flash {
> +    SSISlave ssidev;
> +    uint32_t r;
> +
> +    BlockDriverState *bdrv;
> +    CMDState state;
> +
> +    uint8_t *storage;
> +    uint32_t size;
> +    int page_size;
> +
> +    uint8_t data[16];
> +    uint32_t len;
> +    uint32_t pos;
> +    uint8_t needed_bytes;
> +    FlashCMD cmd_in_progress;
> +
> +    int64_t dirty_page;
> +
> +    uint64_t waddr;
> +    int write_enable;
> +
> +    char *part_name;
> +    const FlashPartInfo *pi;
> +
> +} Flash;
> +
> +static void bdrv_sync_complete(void *opaque, int ret)
> +{
> +    /* do nothing. Masters do not directly interact with the backing store,
> +     * only the working copy so no mutexing required.
> +     */
> +}
> +
> +static void flash_sync_page(Flash *s, int page)
> +{
> +    if (s->bdrv) {
> +        int bdrv_sector, nb_sectors;
> +        QEMUIOVector iov;
> +
> +        bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
> +        nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
> +        qemu_iovec_init(&iov, 1);
> +        qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
> +                                                nb_sectors * BDRV_SECTOR_SIZE);
> +        bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors,
> +                                                bdrv_sync_complete, NULL);
> +    }
> +}
> +
> +static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
> +{
> +    int64_t start, end;
> +    int nb_sectors;
> +    QEMUIOVector iov;
> +
> +    if (!s->bdrv) {
> +        return;
> +    }
> +
> +    assert(!(len % BDRV_SECTOR_SIZE));
> +    start = off / BDRV_SECTOR_SIZE;
> +    end = (off + len) / BDRV_SECTOR_SIZE;
> +    nb_sectors = end - start;

end and start are 64 bits but nb_sectors might be only 32 bits...

> +    qemu_iovec_init(&iov, 1);
> +    qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
> +                                        nb_sectors * BDRV_SECTOR_SIZE);
> +    bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
> +}
> +
> +static void flash_erase(Flash *s, int offset, FlashCMD cmd)
> +{
> +    uint32_t len;
> +    uint8_t capa_to_assert = 0;
> +
> +    switch (cmd) {
> +    case ERASE_4K:
> +        len = 4 << 10;
> +        capa_to_assert = ER_4K;
> +        break;
> +    case ERASE_32K:
> +        len = 32 << 10;
> +        capa_to_assert = ER_32K;
> +        break;
> +    case ERASE_SECTOR:
> +        len = s->pi->sector_size;
> +        break;
> +    case BULK_ERASE:
> +        len = s->size;
> +        break;
> +    default:
> +        abort();
> +    }
> +
> +    DB_PRINT("offset = %#x, len = %d\n", offset, len);
> +    if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
> +        hw_error("m25p80: %dk erase size not supported by device\n", len);
> +    }
> +
> +    if (!s->write_enable) {
> +        DB_PRINT("erase with write protect!\n");
> +        return;
> +    }
> +    memset(s->storage + offset, 0xff, len);
> +    flash_sync_area(s, offset, len);
> +}
> +
> +static inline void flash_sync_dirty(Flash *s, int64_t newpage)
> +{
> +    if (s->dirty_page >= 0 && s->dirty_page != newpage) {
> +        flash_sync_page(s, s->dirty_page);
> +        s->dirty_page = newpage;
> +    }
> +}
> +
> +static inline
> +void flash_write8(Flash *s, uint64_t addr, uint8_t data)
> +{
> +    int64_t page = addr / s->pi->page_size;
> +    uint8_t prev = s->storage[s->waddr];
> +
> +    if (!s->write_enable) {
> +        DB_PRINT("write with write protect!\n");
> +    }
> +
> +    if ((prev ^ data) & data) {
> +        DB_PRINT("programming zero to one! addr=%lx  %x -> %x\n",
> +                  addr, prev, data);
> +    }
> +
> +    if (s->pi->flags & WR_1) {
> +        s->storage[s->waddr] = data;
> +    } else {
> +        s->storage[s->waddr] &= data;
> +    }
> +
> +    flash_sync_dirty(s, page);
> +    s->dirty_page = page;
> +}
> +
> +static void complete_collecting_data(Flash *s)
> +{
> +    s->waddr = s->data[0] << 16;
> +    s->waddr |= s->data[1] << 8;
> +    s->waddr |= s->data[2];
> +
> +    switch (s->cmd_in_progress) {
> +    case PP:
> +        s->state = STATE_PAGE_PROGRAM;
> +        break;
> +    case READ:
> +    case FAST_READ:
> +        s->state = STATE_READ;
> +        break;
> +    case ERASE_4K:
> +    case ERASE_32K:
> +    case ERASE_SECTOR:
> +        flash_erase(s, s->waddr, s->cmd_in_progress);
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static void decode_new_cmd(Flash *s, uint32_t value)
> +{
> +    s->cmd_in_progress = value;
> +    DB_PRINT("decoded new command:%x\n", value);
> +
> +    switch (value) {
> +
> +    case ERASE_4K:
> +    case ERASE_32K:
> +    case ERASE_SECTOR:
> +    case READ:
> +    case PP:
> +        s->needed_bytes = 3;
> +        s->pos = 0;
> +        s->len = 0;
> +        s->state = STATE_COLLECTING_DATA;
> +        break;
> +
> +    case FAST_READ:
> +        s->needed_bytes = 4;
> +        s->pos = 0;
> +        s->len = 0;
> +        s->state = STATE_COLLECTING_DATA;
> +        break;
> +
> +    case WRDI:
> +        s->write_enable = 0;
> +        break;
> +    case WREN:
> +        s->write_enable = 1;
> +        break;
> +
> +    case RDSR:
> +        s->data[0] = (!!s->write_enable) << 1;
> +        s->pos = 0;
> +        s->len = 1;;
> +        s->state = STATE_READING_DATA;
> +        break;
> +
> +    case JEDEC_READ:
> +        DB_PRINT("populated jedec code\n");
> +        s->data[0] = (s->pi->jedec >> 16) & 0xff;
> +        s->data[1] = (s->pi->jedec >> 8) & 0xff;
> +        s->data[2] = s->pi->jedec & 0xff;
> +        if (s->pi->ext_jedec) {
> +            s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
> +            s->data[4] = s->pi->ext_jedec & 0xff;
> +            s->len = 5;
> +        } else {
> +            s->len = 3;
> +        }
> +        s->pos = 0;
> +        s->state = STATE_READING_DATA;
> +        break;
> +
> +    case BULK_ERASE:
> +        if (s->write_enable) {
> +            DB_PRINT("chip erase\n");
> +            flash_erase(s, 0, BULK_ERASE);
> +        } else {
> +            DB_PRINT("chip erase with write protect!\n");
> +        }
> +        break;
> +    case NOP:
> +        break;
> +    default:
> +        DB_PRINT("Unknown cmd %x\n", value);
> +        break;
> +    }
> +}
> +
> +static int m25p80_cs(SSISlave *ss, bool select)
> +{
> +    Flash *s = FROM_SSI_SLAVE(Flash, ss);
> +
> +    if (select) {
> +        s->len = 0;
> +        s->pos = 0;
> +        s->state = STATE_IDLE;
> +        flash_sync_dirty(s, -1);
> +    }
> +
> +    DB_PRINT("%sselect\n", select ? "de" : "");
> +
> +    return 0;
> +}
> +
> +static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
> +{
> +    Flash *s = FROM_SSI_SLAVE(Flash, ss);
> +    uint32_t r = 0;
> +
> +    switch (s->state) {
> +
> +    case STATE_PAGE_PROGRAM:
> +        DB_PRINT("page program waddr=%lx data=%x\n", s->waddr, (uint8_t)tx);
> +        flash_write8(s, s->waddr, (uint8_t)tx);
> +        s->waddr++;
> +        break;
> +
> +    case STATE_READ:
> +        r = s->storage[s->waddr];
> +        DB_PRINT("READ 0x%lx=%x\n", s->waddr, r);
> +        s->waddr = (s->waddr + 1) % s->size;
> +        break;
> +
> +    case STATE_COLLECTING_DATA:
> +        s->data[s->len] = (uint8_t)tx;
> +        s->len++;
> +
> +        if (s->len == s->needed_bytes) {
> +            complete_collecting_data(s);
> +        }
> +        break;
> +
> +    case STATE_READING_DATA:
> +        r = s->data[s->pos];
> +        s->pos++;
> +        if (s->pos == s->len) {
> +            s->pos = 0;
> +            s->state = STATE_IDLE;
> +        }
> +        break;
> +
> +    default:
> +    case STATE_IDLE:
> +        decode_new_cmd(s, (uint8_t)tx);
> +        break;
> +    }
> +
> +    return r;
> +}
> +
> +static int m25p80_init(SSISlave *ss)
> +{
> +    DriveInfo *dinfo;
> +    Flash *s = FROM_SSI_SLAVE(Flash, ss);
> +    const FlashPartInfo *i;
> +
> +    if (!s->part_name) { /* default to actual m25p80 if no partname given */
> +        s->part_name = (char *)"m25p80";
> +    }
> +
> +    i = known_devices;
> +    for (i = known_devices;; i++) {
> +        assert(i);
> +        if (!i->part_name) {
> +            fprintf(stderr, "Unknown SPI flash part: \"%s\"\n", s->part_name);
> +            return 1;
> +        } else if (!strcmp(i->part_name, s->part_name)) {
> +            s->pi = i;
> +            break;
> +        }
> +    }
> +
> +    s->size = s->pi->sector_size * s->pi->n_sectors;
> +    s->dirty_page = -1;
> +    s->storage = qemu_blockalign(s->bdrv, s->size);
> +
> +    dinfo = drive_get_next(IF_MTD);
> +
> +    if (dinfo && dinfo->bdrv) {
> +        int rsize;
> +
> +        DB_PRINT("Binding to IF_MTD drive\n");
> +        s->bdrv = dinfo->bdrv;
> +        rsize = MIN(bdrv_getlength(s->bdrv), s->size);
> +        /* FIXME: Move to late init */
> +        if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
> +                                                    BDRV_SECTOR_SIZE))) {
> +            fprintf(stderr, "Failed to initialize SPI flash!\n");
> +            return 1;
> +        }
> +    } else {
> +        memset(s->storage, 0xFF, s->size);
> +    }
> +
> +    return 0;
> +}
> +
> +static Property m25p80_properties[] = {
> +    DEFINE_PROP_STRING("partname", Flash, part_name),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void m25p80_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
> +
> +    k->init = m25p80_init;
> +    k->transfer = m25p80_transfer8;
> +    k->set_cs = m25p80_cs;
> +    k->cs_polarity = SSI_CS_LOW;
> +    dc->props = m25p80_properties;
> +}

Missing save/restore support.

-- PMM

  reply	other threads:[~2012-10-05 12:22 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-10-05  0:08 [Qemu-devel] [PULL 0/14] Ehnahced SSI bus support + M25P80 SPI flash + Xilinx SPI controller Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 01/14] ssi: Support for multiple attached devices Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 02/14] ssi: Implemented CS behaviour Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 03/14] ssi: Added create_slave_no_init() Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 04/14] qdev: allow multiple qdev_init_gpio_in() calls Peter Crosthwaite
2012-10-05  8:50   ` Peter Maydell
2012-10-05  0:08 ` [Qemu-devel] [PATCH 05/14] hw/stellaris: Removed gpio_out init array Peter Crosthwaite
2012-10-05 12:31   ` Peter Maydell
2012-10-05 14:17     ` Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 06/14] stellaris: Removed SSI mux Peter Crosthwaite
2012-10-05 12:34   ` Peter Maydell
2012-10-05 14:14     ` Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 07/14] hw: Added generic FIFO API Peter Crosthwaite
2012-10-05 12:45   ` Peter Maydell
2012-10-05  0:08 ` [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI flash device Peter Crosthwaite
2012-10-05 12:22   ` Peter Maydell [this message]
2012-10-05  0:08 ` [Qemu-devel] [PATCH 09/14] xilinx_spi: Initial impl. of Xilinx SPI controller Peter Crosthwaite
2012-10-05 12:42   ` Peter Maydell
2012-10-05 12:49     ` Peter Crosthwaite
2012-10-05 12:52       ` Peter Maydell
2012-10-05  0:08 ` [Qemu-devel] [PATCH 10/14] petalogix-ml605: added SPI controller with n25q128 Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 11/14] xilinx_spips: Xilinx Zynq SPI cntrlr device model Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 12/14] xilinx_zynq: Added SPI controllers + flashes Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 13/14] MAINTAINERS: Added maintainerships for SSI Peter Crosthwaite
2012-10-05  0:08 ` [Qemu-devel] [PATCH 14/14] ssi: Add slave autoconnect helper Peter Crosthwaite
2012-10-05 12:50 ` [Qemu-devel] [PULL 0/14] Ehnahced SSI bus support + M25P80 SPI flash + Xilinx SPI controller Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAFEAcA_CmXSEs=X2f-jy38Awp6=2YcadxpW3yMHQO2BxoHFOVQ@mail.gmail.com' \
    --to=peter.maydell@linaro.org \
    --cc=aliguori@us.ibm.com \
    --cc=blauwirbel@gmail.com \
    --cc=edgar.iglesias@gmail.com \
    --cc=peter.crosthwaite@petalogix.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.