[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI fla
From: |
Peter Maydell |
Subject: |
Re: [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI flash device |
Date: |
Fri, 5 Oct 2012 13:22:00 +0100 |
On 5 October 2012 01:08, Peter Crosthwaite
<address@hidden> wrote:
> From: Peter A. G. Crosthwaite <address@hidden>
>
> Added device model for m25p80 style SPI flash family.
>
> Signed-off-by: Peter A. G. Crosthwaite <address@hidden>
> ---
> 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 <address@hidden>
> + * Copyright (C) 2012 Peter A. G. Crosthwaite <address@hidden>
> + * 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
- [Qemu-devel] [PATCH 06/14] stellaris: Removed SSI mux, (continued)
- [Qemu-devel] [PATCH 06/14] stellaris: Removed SSI mux, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 07/14] hw: Added generic FIFO API., Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 10/14] petalogix-ml605: added SPI controller with n25q128, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 13/14] MAINTAINERS: Added maintainerships for SSI, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 11/14] xilinx_spips: Xilinx Zynq SPI cntrlr device model, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 12/14] xilinx_zynq: Added SPI controllers + flashes, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 14/14] ssi: Add slave autoconnect helper, Peter Crosthwaite, 2012/10/04
- [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI flash device, Peter Crosthwaite, 2012/10/04
- Re: [Qemu-devel] [PATCH 08/14] m25p80: Initial implementation of SPI flash device,
Peter Maydell <=
- [Qemu-devel] [PATCH 09/14] xilinx_spi: Initial impl. of Xilinx SPI controller, Peter Crosthwaite, 2012/10/04
- Re: [Qemu-devel] [PULL 0/14] Ehnahced SSI bus support + M25P80 SPI flash + Xilinx SPI controller, Peter Maydell, 2012/10/05