[Top][All Lists]
[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: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [PATCH v6 08/13] m25p80: Initial implementation of SPI flash device |
Date: |
Sat, 22 Sep 2012 12:09:21 +0000 |
On Thu, Sep 20, 2012 at 1:37 AM, Peter Crosthwaite
<address@hidden> wrote:
> 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.
OK. Probably DEFINE_PROP_CONST_STRING could be added later for
completeness and preserving const purity.
>
> Regards,
> Peter
>
>>> + DEFINE_PROP_END_OF_LIST(),
>>> +};
- [Qemu-devel] [PATCH v6 02/13] ssi: Implemented CS behaviour, (continued)
- [Qemu-devel] [PATCH v6 02/13] ssi: Implemented CS behaviour, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 03/13] ssi: Added create_slave_no_init(), Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 04/13] qdev: allow multiple qdev_init_gpio_in() calls, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 05/13] hw/stellaris: Removed gpio_out init array., Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 06/13] stellaris: Removed SSI mux, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 07/13] hw: Added generic FIFO API., Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 08/13] m25p80: Initial implementation of SPI flash device, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 09/13] xilinx_spi: Initial impl. of Xilinx SPI controller, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 10/13] petalogix-ml605: added SPI controller with n25q128, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 11/13] xilinx_spips: Xilinx Zynq SPI cntrlr device model, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 12/13] xilinx_zynq: Added SPI controllers + flashes, Peter A. G. Crosthwaite, 2012/09/17
- [Qemu-devel] [PATCH v6 13/13] MAINTAINERS: Added maintainerships for SSI, Peter A. G. Crosthwaite, 2012/09/17