qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v6 08/13] m25p80: Initial implementation of SPI


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH v6 08/13] m25p80: Initial implementation of SPI flash device
Date: Thu, 20 Sep 2012 11:37:58 +1000

On Thu, Sep 20, 2012 at 4:25 AM, Blue Swirl <address@hidden> wrote:
> On Tue, Sep 18, 2012 at 2:11 AM, Peter A. G. Crosthwaite
>> +    int64_t dirty_page;
>> +
>> +    uint64_t waddr;
>> +    int write_enable;
>> +
>> +    char *part_name;
>
> With 'const char *' here..
>
>> +    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;
>> +    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;
>> +    default:
>> +        assert(false);
>> +    }
>> +
>> +    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("deselect\n");
>> +    }
>> +
>> +    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";
>
> ... fishy casts to remove 'const' like this could be avoided.
>

Doesn't work see comment below,

>> +    }
>> +
>> +    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),

the ->part_name prop is defined here an string object property which
is of type char* and not const char* so if you covert part_name to
const you get this warning:

hw/m25p80.c:545: error: invalid operands to binary - (have ‘char **’
and ‘const char **’)

Its coming from whatever the macro expansion of DEFINE_PROP_STRING is.

Regards,
Peter

>> +    DEFINE_PROP_END_OF_LIST(),
>> +};



reply via email to

[Prev in Thread] Current Thread [Next in Thread]