diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/flash.h | 7 | ||||
-rw-r--r-- | hw/ide/ahci.c | 9 | ||||
-rw-r--r-- | hw/ide/ahci.h | 1 | ||||
-rw-r--r-- | hw/ide/atapi.c | 29 | ||||
-rw-r--r-- | hw/ide/core.c | 27 | ||||
-rw-r--r-- | hw/ide/internal.h | 1 | ||||
-rw-r--r-- | hw/ide/macio.c | 40 | ||||
-rw-r--r-- | hw/nseries.c | 52 | ||||
-rw-r--r-- | hw/omap.h | 14 | ||||
-rw-r--r-- | hw/omap2.c | 3 | ||||
-rw-r--r-- | hw/omap_gpmc.c | 713 | ||||
-rw-r--r-- | hw/onenand.c | 164 | ||||
-rw-r--r-- | hw/pci.c | 38 | ||||
-rw-r--r-- | hw/pcie.c | 12 | ||||
-rw-r--r-- | hw/pcie_aer.c | 9 | ||||
-rw-r--r-- | hw/scsi-disk.c | 17 | ||||
-rw-r--r-- | hw/sh_pci.c | 2 | ||||
-rw-r--r-- | hw/sysbus.c | 5 | ||||
-rw-r--r-- | hw/sysbus.h | 1 | ||||
-rw-r--r-- | hw/tusb6010.c | 115 | ||||
-rw-r--r-- | hw/tusb6010.h | 28 | ||||
-rw-r--r-- | hw/vhost.c | 74 | ||||
-rw-r--r-- | hw/vhost.h | 2 | ||||
-rw-r--r-- | hw/vhost_net.c | 16 | ||||
-rw-r--r-- | hw/virtio-blk.c | 20 | ||||
-rw-r--r-- | hw/xen_disk.c | 5 |
26 files changed, 1075 insertions, 329 deletions
diff --git a/hw/flash.h b/hw/flash.h index 140ae39801..270be5e127 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -36,12 +36,7 @@ uint32_t nand_getbuswidth(DeviceState *dev); #define NAND_MFR_MICRON 0x2c /* onenand.c */ -void onenand_base_update(void *opaque, target_phys_addr_t new); -void onenand_base_unmap(void *opaque); -void *onenand_init(BlockDriverState *bdrv, - uint16_t man_id, uint16_t dev_id, uint16_t ver_id, - int regshift, qemu_irq irq); -void *onenand_raw_otp(void *opaque); +void *onenand_raw_otp(DeviceState *onenand_device); /* ecc.c */ typedef struct { diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 29521babf7..f4fa1545bd 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -710,6 +710,7 @@ static void ncq_cb(void *opaque, int ret) DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n", ncq_tfs->tag); + bdrv_acct_done(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct); qemu_sglist_destroy(&ncq_tfs->sglist); ncq_tfs->used = 0; } @@ -756,6 +757,10 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->is_read = 1; DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba); + + bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, + (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, + BDRV_ACCT_READ); ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->sglist, ncq_tfs->lba, ncq_cb, ncq_tfs); @@ -766,6 +771,10 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->is_read = 0; DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba); + + bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, + (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, + BDRV_ACCT_WRITE); ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->sglist, ncq_tfs->lba, ncq_cb, ncq_tfs); diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index e456193b2b..832539c23c 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -258,6 +258,7 @@ typedef struct NCQTransferState { AHCIDevice *drive; BlockDriverAIOCB *aiocb; QEMUSGList sglist; + BlockAcctCookie acct; int is_read; uint16_t sector_count; uint64_t lba; diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index fe2fb0b806..c552320122 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -104,17 +104,20 @@ static void cd_data_to_raw(uint8_t *buf, int lba) memset(buf, 0, 288); } -static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf, - int sector_size) +static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size) { int ret; switch(sector_size) { case 2048: - ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4); + bdrv_acct_start(s->bs, &s->acct, 4 * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + ret = bdrv_read(s->bs, (int64_t)lba << 2, buf, 4); + bdrv_acct_done(s->bs, &s->acct); break; case 2352: - ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4); + bdrv_acct_start(s->bs, &s->acct, 4 * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + ret = bdrv_read(s->bs, (int64_t)lba << 2, buf + 16, 4); + bdrv_acct_done(s->bs, &s->acct); if (ret < 0) return ret; cd_data_to_raw(buf, lba); @@ -181,7 +184,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) } else { /* see if a new sector must be read */ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size); + ret = cd_read_sector(s, s->lba, s->io_buffer, s->cd_sector_size); if (ret < 0) { ide_transfer_stop(s); ide_atapi_io_error(s, ret); @@ -250,6 +253,7 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) s->io_buffer_index = 0; if (s->atapi_dma) { + bdrv_acct_start(s->bs, &s->acct, size, BDRV_ACCT_READ); s->status = READY_STAT | SEEK_STAT | DRQ_STAT; s->bus->dma->ops->start_dma(s->bus->dma, s, ide_atapi_cmd_read_dma_cb); @@ -322,10 +326,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) s->status = READY_STAT | SEEK_STAT; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; ide_set_irq(s->bus); - eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); - ide_set_inactive(s); - return; + goto eot; } s->io_buffer_index = 0; @@ -343,9 +344,11 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) #ifdef DEBUG_AIO printf("aio_read_cd: lba=%u n=%d\n", s->lba, n); #endif + s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); s->bus->dma->iov.iov_len = n * 4 * 512; qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); + s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2, &s->bus->dma->qiov, n * 4, ide_atapi_cmd_read_dma_cb, s); @@ -355,6 +358,12 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) ASC_MEDIUM_NOT_PRESENT); goto eot; } + + return; +eot: + bdrv_acct_done(s->bs, &s->acct); + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); + ide_set_inactive(s); } /* start a CD-CDROM read command with DMA */ @@ -368,6 +377,8 @@ static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, s->io_buffer_size = 0; s->cd_sector_size = sector_size; + bdrv_acct_start(s->bs, &s->acct, s->packet_transfer_size, BDRV_ACCT_READ); + /* XXX: check if BUSY_STAT should be set */ s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; s->bus->dma->ops->start_dma(s->bus->dma, s, diff --git a/hw/ide/core.c b/hw/ide/core.c index d145b19b0c..40abc1edd2 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -473,7 +473,10 @@ void ide_sector_read(IDEState *s) #endif if (n > s->req_nb_sectors) n = s->req_nb_sectors; + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); + bdrv_acct_done(s->bs, &s->acct); if (ret != 0) { if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ)) @@ -610,7 +613,10 @@ handle_rw_error: return; eot: - ide_set_inactive(s); + if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { + bdrv_acct_done(s->bs, &s->acct); + } + ide_set_inactive(s); } static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) @@ -619,6 +625,20 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) s->io_buffer_index = 0; s->io_buffer_size = 0; s->dma_cmd = dma_cmd; + + switch (dma_cmd) { + case IDE_DMA_READ: + bdrv_acct_start(s->bs, &s->acct, s->nsector * BDRV_SECTOR_SIZE, + BDRV_ACCT_READ); + break; + case IDE_DMA_WRITE: + bdrv_acct_start(s->bs, &s->acct, s->nsector * BDRV_SECTOR_SIZE, + BDRV_ACCT_WRITE); + break; + default: + break; + } + s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb); } @@ -641,7 +661,10 @@ void ide_sector_write(IDEState *s) n = s->nsector; if (n > s->req_nb_sectors) n = s->req_nb_sectors; + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); + bdrv_acct_done(s->bs, &s->acct); if (ret != 0) { if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY)) @@ -685,6 +708,7 @@ static void ide_flush_cb(void *opaque, int ret) } } + bdrv_acct_done(s->bs, &s->acct); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); } @@ -698,6 +722,7 @@ void ide_flush_cache(IDEState *s) return; } + bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH); acb = bdrv_aio_flush(s->bs, ide_flush_cb, s); if (acb == NULL) { ide_flush_cb(s, -EIO); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 02e805f070..7f5ef8de1d 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -440,6 +440,7 @@ struct IDEState { int lba; int cd_sector_size; int atapi_dma; /* true if dma is requested for the packet cmd */ + BlockAcctCookie acct; /* ATA DMA state */ int io_buffer_size; QEMUSGList sg; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 44fb3fef60..fdf5d75082 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -52,8 +52,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) m->aiocb = NULL; qemu_sglist_destroy(&s->sg); ide_atapi_io_error(s, ret); - io->dma_end(opaque); - return; + goto done; } if (s->io_buffer_size > 0) { @@ -71,8 +70,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) ide_atapi_cmd_ok(s); if (io->len == 0) { - io->dma_end(opaque); - return; + goto done; } /* launch next transfer */ @@ -92,9 +90,14 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) /* Note: media not present is the most likely case */ ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); - io->dma_end(opaque); - return; + goto done; } + return; + +done: + bdrv_acct_done(s->bs, &s->acct); + io->dma_end(opaque); + return; } static void pmac_ide_transfer_cb(void *opaque, int ret) @@ -109,8 +112,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) m->aiocb = NULL; qemu_sglist_destroy(&s->sg); ide_dma_error(s); - io->dma_end(io); - return; + goto done; } sector_num = ide_get_sector(s); @@ -130,10 +132,8 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) } /* end of DMA ? */ - if (io->len == 0) { - io->dma_end(io); - return; + goto done; } /* launch next transfer */ @@ -163,6 +163,12 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (!m->aiocb) pmac_ide_transfer_cb(io, -1); + return; +done: + if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { + bdrv_acct_done(s->bs, &s->acct); + } + io->dma_end(io); } static void pmac_ide_transfer(DBDMA_io *io) @@ -172,10 +178,22 @@ static void pmac_ide_transfer(DBDMA_io *io) s->io_buffer_size = 0; if (s->drive_kind == IDE_CD) { + bdrv_acct_start(s->bs, &s->acct, io->len, BDRV_ACCT_READ); pmac_ide_atapi_transfer_cb(io, 0); return; } + switch (s->dma_cmd) { + case IDE_DMA_READ: + bdrv_acct_start(s->bs, &s->acct, io->len, BDRV_ACCT_READ); + break; + case IDE_DMA_WRITE: + bdrv_acct_start(s->bs, &s->acct, io->len, BDRV_ACCT_WRITE); + break; + default: + break; + } + pmac_ide_transfer_cb(io, 0); } diff --git a/hw/nseries.c b/hw/nseries.c index f7aae7a59e..af287dd6dc 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -32,7 +32,7 @@ #include "bt.h" #include "loader.h" #include "blockdev.h" -#include "tusb6010.h" +#include "sysbus.h" /* Nokia N8x0 support */ struct n800_s { @@ -49,10 +49,10 @@ struct n800_s { int keymap[0x80]; DeviceState *kbd; - TUSBState *usb; + DeviceState *usb; void *retu; void *tahvo; - void *nand; + DeviceState *nand; }; /* GPIO pins */ @@ -167,13 +167,21 @@ static void n8x0_nand_setup(struct n800_s *s) char *otp_region; DriveInfo *dinfo; - dinfo = drive_get(IF_MTD, 0, 0); + s->nand = qdev_create(NULL, "onenand"); + qdev_prop_set_uint16(s->nand, "manufacturer_id", NAND_MFR_SAMSUNG); /* Either 0x40 or 0x48 are OK for the device ID */ - s->nand = onenand_init(dinfo ? dinfo->bdrv : 0, - NAND_MFR_SAMSUNG, 0x48, 0, 1, - qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO)); - omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, - onenand_base_unmap, s->nand); + qdev_prop_set_uint16(s->nand, "device_id", 0x48); + qdev_prop_set_uint16(s->nand, "version_id", 0); + qdev_prop_set_int32(s->nand, "shift", 1); + dinfo = drive_get(IF_MTD, 0, 0); + if (dinfo && dinfo->bdrv) { + qdev_prop_set_drive_nofail(s->nand, "drive", dinfo->bdrv); + } + qdev_init_nofail(s->nand); + sysbus_connect_irq(sysbus_from_qdev(s->nand), 0, + qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO)); + omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, + sysbus_mmio_get_region(sysbus_from_qdev(s->nand), 0)); otp_region = onenand_raw_otp(s->nand); memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); @@ -756,27 +764,21 @@ static void n8x0_uart_setup(struct n800_s *s) omap_uart_attach(s->cpu->uart[BT_UART], radio); } -static void n8x0_usb_power_cb(void *opaque, int line, int level) -{ - struct n800_s *s = opaque; - - tusb6010_power(s->usb, level); -} - static void n8x0_usb_setup(struct n800_s *s) { - qemu_irq tusb_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TUSB_INT_GPIO); - qemu_irq tusb_pwr = qemu_allocate_irqs(n8x0_usb_power_cb, s, 1)[0]; - TUSBState *tusb = tusb6010_init(tusb_irq); - + SysBusDevice *dev; + s->usb = qdev_create(NULL, "tusb6010"); + dev = sysbus_from_qdev(s->usb); + qdev_init_nofail(s->usb); + sysbus_connect_irq(dev, 0, + qdev_get_gpio_in(s->cpu->gpio, N8X0_TUSB_INT_GPIO)); /* Using the NOR interface */ omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_ASYNC_CS, - tusb6010_async_io(tusb), NULL, NULL, tusb); + sysbus_mmio_get_region(dev, 0)); omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_SYNC_CS, - tusb6010_sync_io(tusb), NULL, NULL, tusb); - - s->usb = tusb; - qdev_connect_gpio_out(s->cpu->gpio, N8X0_TUSB_ENABLE_GPIO, tusb_pwr); + sysbus_mmio_get_region(dev, 1)); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_TUSB_ENABLE_GPIO, + qdev_get_gpio_in(s->usb, 0)); /* tusb_pwr */ } /* Setup done before the main bootloader starts by some early setup code @@ -118,11 +118,12 @@ void omap_sdrc_reset(struct omap_sdrc_s *s); /* OMAP2 general purpose memory controller */ struct omap_gpmc_s; -struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq); +struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, + target_phys_addr_t base, + qemu_irq irq, qemu_irq drq); void omap_gpmc_reset(struct omap_gpmc_s *s); -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem, - void (*base_upd)(void *opaque, target_phys_addr_t new), - void (*unmap)(void *opaque), void *opaque); +void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem); +void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand); /* * Common IRQ numbers for level 1 interrupt handler @@ -788,6 +789,7 @@ i2c_bus *omap_i2c_bus(struct omap_i2c_s *s); # define cpu_is_omap2420(cpu) (cpu->mpu_model == omap2420) # define cpu_is_omap2430(cpu) (cpu->mpu_model == omap2430) # define cpu_is_omap3430(cpu) (cpu->mpu_model == omap3430) +# define cpu_is_omap3630(cpu) (cpu->mpu_model == omap3630) # define cpu_is_omap15xx(cpu) \ (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu)) @@ -799,7 +801,8 @@ i2c_bus *omap_i2c_bus(struct omap_i2c_s *s); # define cpu_class_omap1(cpu) \ (cpu_is_omap15xx(cpu) || cpu_is_omap16xx(cpu)) # define cpu_class_omap2(cpu) cpu_is_omap24xx(cpu) -# define cpu_class_omap3(cpu) cpu_is_omap3430(cpu) +# define cpu_class_omap3(cpu) \ + (cpu_is_omap3430(cpu) || cpu_is_omap3630(cpu)) struct omap_mpu_state_s { enum omap_mpu_model { @@ -813,6 +816,7 @@ struct omap_mpu_state_s { omap2423, omap2430, omap3430, + omap3630, } mpu_model; CPUState *env; diff --git a/hw/omap2.c b/hw/omap2.c index 7e5820a97b..ca088d9f53 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -2402,7 +2402,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5)); s->sdrc = omap_sdrc_init(0x68009000); - s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]); + s->gpmc = omap_gpmc_init(s, 0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ], + s->drq[OMAP24XX_DMA_GPMC]); dinfo = drive_get(IF_SD, 0, 0); if (!dinfo) { diff --git a/hw/omap_gpmc.c b/hw/omap_gpmc.c index 673dddd237..02f0c52107 100644 --- a/hw/omap_gpmc.c +++ b/hw/omap_gpmc.c @@ -27,82 +27,410 @@ /* General-Purpose Memory Controller */ struct omap_gpmc_s { qemu_irq irq; + qemu_irq drq; MemoryRegion iomem; + int accept_256; + uint8_t revision; uint8_t sysconfig; uint16_t irqst; uint16_t irqen; + uint16_t lastirq; uint16_t timeout; uint16_t config; - uint32_t prefconfig[2]; - int prefcontrol; - int preffifo; - int prefcount; struct omap_gpmc_cs_file_s { uint32_t config[7]; - target_phys_addr_t base; - size_t size; MemoryRegion *iomem; MemoryRegion container; - void (*base_update)(void *opaque, target_phys_addr_t new); - void (*unmap)(void *opaque); - void *opaque; + MemoryRegion nandiomem; + DeviceState *dev; } cs_file[8]; int ecc_cs; int ecc_ptr; uint32_t ecc_cfg; ECCState ecc[9]; + struct prefetch { + uint32_t config1; /* GPMC_PREFETCH_CONFIG1 */ + uint32_t transfercount; /* GPMC_PREFETCH_CONFIG2:TRANSFERCOUNT */ + int startengine; /* GPMC_PREFETCH_CONTROL:STARTENGINE */ + int fifopointer; /* GPMC_PREFETCH_STATUS:FIFOPOINTER */ + int count; /* GPMC_PREFETCH_STATUS:COUNTVALUE */ + MemoryRegion iomem; + uint8_t fifo[64]; + } prefetch; }; +#define OMAP_GPMC_8BIT 0 +#define OMAP_GPMC_16BIT 1 +#define OMAP_GPMC_NOR 0 +#define OMAP_GPMC_NAND 2 + +static int omap_gpmc_devtype(struct omap_gpmc_cs_file_s *f) +{ + return (f->config[0] >> 10) & 3; +} + +static int omap_gpmc_devsize(struct omap_gpmc_cs_file_s *f) +{ + /* devsize field is really 2 bits but we ignore the high + * bit to ensure consistent behaviour if the guest sets + * it (values 2 and 3 are reserved in the TRM) + */ + return (f->config[0] >> 12) & 1; +} + +/* Extract the chip-select value from the prefetch config1 register */ +static int prefetch_cs(uint32_t config1) +{ + return (config1 >> 24) & 7; +} + +static int prefetch_threshold(uint32_t config1) +{ + return (config1 >> 8) & 0x7f; +} + static void omap_gpmc_int_update(struct omap_gpmc_s *s) { - qemu_set_irq(s->irq, s->irqen & s->irqst); + /* The TRM is a bit unclear, but it seems to say that + * the TERMINALCOUNTSTATUS bit is set only on the + * transition when the prefetch engine goes from + * active to inactive, whereas the FIFOEVENTSTATUS + * bit is held high as long as the fifo has at + * least THRESHOLD bytes available. + * So we do the latter here, but TERMINALCOUNTSTATUS + * is set elsewhere. + */ + if (s->prefetch.fifopointer >= prefetch_threshold(s->prefetch.config1)) { + s->irqst |= 1; + } + if ((s->irqen & s->irqst) != s->lastirq) { + s->lastirq = s->irqen & s->irqst; + qemu_set_irq(s->irq, s->lastirq); + } } -static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask) +static void omap_gpmc_dma_update(struct omap_gpmc_s *s, int value) { - /* TODO: check for overlapping regions and report access errors */ - if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) || - (base < 0 || base >= 0x40) || - (base & 0x0f & ~mask)) { - fprintf(stderr, "%s: wrong cs address mapping/decoding!\n", - __FUNCTION__); + if (s->prefetch.config1 & 4) { + qemu_set_irq(s->drq, value); + } +} + +/* Access functions for when a NAND-like device is mapped into memory: + * all addresses in the region behave like accesses to the relevant + * GPMC_NAND_DATA_i register (which is actually implemented to call these) + */ +static uint64_t omap_nand_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; + uint64_t v; + nand_setpins(f->dev, 0, 0, 0, 1, 0); + switch (omap_gpmc_devsize(f)) { + case OMAP_GPMC_8BIT: + v = nand_getio(f->dev); + if (size == 1) { + return v; + } + v |= (nand_getio(f->dev) << 8); + if (size == 2) { + return v; + } + v |= (nand_getio(f->dev) << 16); + v |= (nand_getio(f->dev) << 24); + return v; + case OMAP_GPMC_16BIT: + v = nand_getio(f->dev); + if (size == 1) { + /* 8 bit read from 16 bit device : probably a guest bug */ + return v & 0xff; + } + if (size == 2) { + return v; + } + v |= (nand_getio(f->dev) << 16); + return v; + default: + abort(); + } +} + +static void omap_nand_setio(DeviceState *dev, uint64_t value, + int nandsize, int size) +{ + /* Write the specified value to the NAND device, respecting + * both size of the NAND device and size of the write access. + */ + switch (nandsize) { + case OMAP_GPMC_8BIT: + switch (size) { + case 1: + nand_setio(dev, value & 0xff); + break; + case 2: + nand_setio(dev, value & 0xff); + nand_setio(dev, (value >> 8) & 0xff); + break; + case 4: + default: + nand_setio(dev, value & 0xff); + nand_setio(dev, (value >> 8) & 0xff); + nand_setio(dev, (value >> 16) & 0xff); + nand_setio(dev, (value >> 24) & 0xff); + break; + } + case OMAP_GPMC_16BIT: + switch (size) { + case 1: + /* writing to a 16bit device with 8bit access is probably a guest + * bug; pass the value through anyway. + */ + case 2: + nand_setio(dev, value & 0xffff); + break; + case 4: + default: + nand_setio(dev, value & 0xffff); + nand_setio(dev, (value >> 16) & 0xffff); + break; + } + } +} + +static void omap_nand_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; + nand_setpins(f->dev, 0, 0, 0, 1, 0); + omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); +} + +static const MemoryRegionOps omap_nand_ops = { + .read = omap_nand_read, + .write = omap_nand_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void fill_prefetch_fifo(struct omap_gpmc_s *s) +{ + /* Fill the prefetch FIFO by reading data from NAND. + * We do this synchronously, unlike the hardware which + * will do this asynchronously. We refill when the + * FIFO has THRESHOLD bytes free, and we always refill + * as much data as possible starting at the top end + * of the FIFO. + * (We have to refill at THRESHOLD rather than waiting + * for the FIFO to empty to allow for the case where + * the FIFO size isn't an exact multiple of THRESHOLD + * and we're doing DMA transfers.) + * This means we never need to handle wrap-around in + * the fifo-reading code, and the next byte of data + * to read is always fifo[63 - fifopointer]. + */ + int fptr; + int cs = prefetch_cs(s->prefetch.config1); + int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); + int bytes; + /* Don't believe the bit of the OMAP TRM that says that COUNTVALUE + * and TRANSFERCOUNT are in units of 16 bit words for 16 bit NAND. + * Instead believe the bit that says it is always a byte count. + */ + bytes = 64 - s->prefetch.fifopointer; + if (bytes > s->prefetch.count) { + bytes = s->prefetch.count; + } + s->prefetch.count -= bytes; + s->prefetch.fifopointer += bytes; + fptr = 64 - s->prefetch.fifopointer; + /* Move the existing data in the FIFO so it sits just + * before what we're about to read in + */ + while (fptr < (64 - bytes)) { + s->prefetch.fifo[fptr] = s->prefetch.fifo[fptr + bytes]; + fptr++; + } + while (fptr < 64) { + if (is16bit) { + uint32_t v = omap_nand_read(&s->cs_file[cs], 0, 2); + s->prefetch.fifo[fptr++] = v & 0xff; + s->prefetch.fifo[fptr++] = (v >> 8) & 0xff; + } else { + s->prefetch.fifo[fptr++] = omap_nand_read(&s->cs_file[cs], 0, 1); + } + } + if (s->prefetch.startengine && (s->prefetch.count == 0)) { + /* This was the final transfer: raise TERMINALCOUNTSTATUS */ + s->irqst |= 2; + s->prefetch.startengine = 0; + } + /* If there are any bytes in the FIFO at this point then + * we must raise a DMA request (either this is a final part + * transfer, or we filled the FIFO in which case we certainly + * have THRESHOLD bytes available) + */ + if (s->prefetch.fifopointer != 0) { + omap_gpmc_dma_update(s, 1); + } + omap_gpmc_int_update(s); +} + +/* Access functions for a NAND-like device when the prefetch/postwrite + * engine is enabled -- all addresses in the region behave alike: + * data is read or written to the FIFO. + */ +static uint64_t omap_gpmc_prefetch_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + uint32_t data; + if (s->prefetch.config1 & 1) { + /* The TRM doesn't define the behaviour if you read from the + * FIFO when the prefetch engine is in write mode. We choose + * to always return zero. + */ + return 0; + } + /* Note that trying to read an empty fifo repeats the last byte */ + if (s->prefetch.fifopointer) { + s->prefetch.fifopointer--; + } + data = s->prefetch.fifo[63 - s->prefetch.fifopointer]; + if (s->prefetch.fifopointer == + (64 - prefetch_threshold(s->prefetch.config1))) { + /* We've drained THRESHOLD bytes now. So deassert the + * DMA request, then refill the FIFO (which will probably + * assert it again.) + */ + omap_gpmc_dma_update(s, 0); + fill_prefetch_fifo(s); + } + omap_gpmc_int_update(s); + return data; +} + +static void omap_gpmc_prefetch_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + int cs = prefetch_cs(s->prefetch.config1); + if ((s->prefetch.config1 & 1) == 0) { + /* The TRM doesn't define the behaviour of writing to the + * FIFO when the prefetch engine is in read mode. We + * choose to ignore the write. + */ + return; + } + if (s->prefetch.count == 0) { + /* The TRM doesn't define the behaviour of writing to the + * FIFO if the transfer is complete. We choose to ignore. + */ return; } + /* The only reason we do any data buffering in postwrite + * mode is if we are talking to a 16 bit NAND device, in + * which case we need to buffer the first byte of the + * 16 bit word until the other byte arrives. + */ + int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); + if (is16bit) { + /* fifopointer alternates between 64 (waiting for first + * byte of word) and 63 (waiting for second byte) + */ + if (s->prefetch.fifopointer == 64) { + s->prefetch.fifo[0] = value; + s->prefetch.fifopointer--; + } else { + value = (value << 8) | s->prefetch.fifo[0]; + omap_nand_write(&s->cs_file[cs], 0, value, 2); + s->prefetch.count--; + s->prefetch.fifopointer = 64; + } + } else { + /* Just write the byte : fifopointer remains 64 at all times */ + omap_nand_write(&s->cs_file[cs], 0, value, 1); + s->prefetch.count--; + } + if (s->prefetch.count == 0) { + /* Final transfer: raise TERMINALCOUNTSTATUS */ + s->irqst |= 2; + s->prefetch.startengine = 0; + } + omap_gpmc_int_update(s); +} + +static const MemoryRegionOps omap_prefetch_ops = { + .read = omap_gpmc_prefetch_read, + .write = omap_gpmc_prefetch_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 1, + .impl.max_access_size = 1, +}; - if (!f->opaque) +static MemoryRegion *omap_gpmc_cs_memregion(struct omap_gpmc_s *s, int cs) +{ + /* Return the MemoryRegion* to map/unmap for this chipselect */ + struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; + if (omap_gpmc_devtype(f) == OMAP_GPMC_NOR) { + return f->iomem; + } + if ((s->prefetch.config1 & 0x80) && + (prefetch_cs(s->prefetch.config1) == cs)) { + /* The prefetch engine is enabled for this CS: map the FIFO */ + return &s->prefetch.iomem; + } + return &f->nandiomem; +} + +static void omap_gpmc_cs_map(struct omap_gpmc_s *s, int cs) +{ + struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; + uint32_t mask = (f->config[6] >> 8) & 0xf; + uint32_t base = f->config[6] & 0x3f; + uint32_t size; + + if (!f->iomem && !f->dev) { + return; + } + + if (!(f->config[6] & (1 << 6))) { + /* Do nothing unless CSVALID */ return; + } - f->base = base << 24; - f->size = (0x0fffffff & ~(mask << 24)) + 1; + /* TODO: check for overlapping regions and report access errors */ + if (mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf + && !(s->accept_256 && !mask)) { + fprintf(stderr, "%s: invalid chip-select mask address (0x%x)\n", + __func__, mask); + } + + base <<= 24; + size = (0x0fffffff & ~(mask << 24)) + 1; /* TODO: rather than setting the size of the mapping (which should be * constant), the mask should cause wrapping of the address space, so * that the same memory becomes accessible at every <i>size</i> bytes * starting from <i>base</i>. */ - if (f->iomem) { - memory_region_init(&f->container, "omap-gpmc-file", f->size); - memory_region_add_subregion(&f->container, 0, f->iomem); - memory_region_add_subregion(get_system_memory(), f->base, - &f->container); - } - - if (f->base_update) - f->base_update(f->opaque, f->base); + memory_region_init(&f->container, "omap-gpmc-file", size); + memory_region_add_subregion(&f->container, 0, + omap_gpmc_cs_memregion(s, cs)); + memory_region_add_subregion(get_system_memory(), base, + &f->container); } -static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f) +static void omap_gpmc_cs_unmap(struct omap_gpmc_s *s, int cs) { - if (f->size) { - if (f->unmap) - f->unmap(f->opaque); - if (f->iomem) { - memory_region_del_subregion(get_system_memory(), &f->container); - memory_region_del_subregion(&f->container, f->iomem); - memory_region_destroy(&f->container); - } - f->base = 0; - f->size = 0; + struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; + if (!(f->config[6] & (1 << 6))) { + /* Do nothing unless CSVALID */ + return; + } + if (!f->iomem && !f->dev) { + return; } + memory_region_del_subregion(get_system_memory(), &f->container); + memory_region_del_subregion(&f->container, omap_gpmc_cs_memregion(s, cs)); + memory_region_destroy(&f->container); } void omap_gpmc_reset(struct omap_gpmc_s *s) @@ -115,25 +443,32 @@ void omap_gpmc_reset(struct omap_gpmc_s *s) omap_gpmc_int_update(s); s->timeout = 0; s->config = 0xa00; - s->prefconfig[0] = 0x00004000; - s->prefconfig[1] = 0x00000000; - s->prefcontrol = 0; - s->preffifo = 0; - s->prefcount = 0; + s->prefetch.config1 = 0x00004000; + s->prefetch.transfercount = 0x00000000; + s->prefetch.startengine = 0; + s->prefetch.fifopointer = 0; + s->prefetch.count = 0; for (i = 0; i < 8; i ++) { - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(s->cs_file + i); - s->cs_file[i].config[0] = i ? 1 << 12 : 0; + omap_gpmc_cs_unmap(s, i); s->cs_file[i].config[1] = 0x101001; s->cs_file[i].config[2] = 0x020201; s->cs_file[i].config[3] = 0x10031003; s->cs_file[i].config[4] = 0x10f1111; s->cs_file[i].config[5] = 0; s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6); - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(&s->cs_file[i], - s->cs_file[i].config[6] & 0x1f, /* MASKADDR */ - (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */ + + s->cs_file[i].config[6] = 0xf00; + /* In theory we could probe attached devices for some CFG1 + * bits here, but we just retain them across resets as they + * were set initially by omap_gpmc_attach(). + */ + if (i == 0) { + s->cs_file[i].config[0] &= 0x00433e00; + s->cs_file[i].config[6] |= 1 << 6; /* CSVALID */ + omap_gpmc_cs_map(s, i); + } else { + s->cs_file[i].config[0] &= 0x00403c00; + } } s->ecc_cs = 0; s->ecc_ptr = 0; @@ -142,6 +477,24 @@ void omap_gpmc_reset(struct omap_gpmc_s *s) ecc_reset(&s->ecc[i]); } +static int gpmc_wordaccess_only(target_phys_addr_t addr) +{ + /* Return true if the register offset is to a register that + * only permits word width accesses. + * Non-word accesses are only OK for GPMC_NAND_DATA/ADDRESS/COMMAND + * for any chipselect. + */ + if (addr >= 0x60 && addr <= 0x1d4) { + int cs = (addr - 0x60) / 0x30; + addr -= cs * 0x30; + if (addr >= 0x7c && addr < 0x88) { + /* GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA */ + return 0; + } + } + return 1; +} + static uint64_t omap_gpmc_read(void *opaque, target_phys_addr_t addr, unsigned size) { @@ -149,13 +502,13 @@ static uint64_t omap_gpmc_read(void *opaque, target_phys_addr_t addr, int cs; struct omap_gpmc_cs_file_s *f; - if (size != 4) { + if (size != 4 && gpmc_wordaccess_only(addr)) { return omap_badwidth_read32(opaque, addr); } switch (addr) { case 0x000: /* GPMC_REVISION */ - return 0x20; + return s->revision; case 0x010: /* GPMC_SYSCONFIG */ return s->sysconfig; @@ -187,36 +540,39 @@ static uint64_t omap_gpmc_read(void *opaque, target_phys_addr_t addr, addr -= cs * 0x30; f = s->cs_file + cs; switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - return f->config[0]; - case 0x64: /* GPMC_CONFIG2 */ - return f->config[1]; - case 0x68: /* GPMC_CONFIG3 */ - return f->config[2]; - case 0x6c: /* GPMC_CONFIG4 */ - return f->config[3]; - case 0x70: /* GPMC_CONFIG5 */ - return f->config[4]; - case 0x74: /* GPMC_CONFIG6 */ - return f->config[5]; - case 0x78: /* GPMC_CONFIG7 */ - return f->config[6]; - case 0x84: /* GPMC_NAND_DATA */ - return 0; + case 0x60: /* GPMC_CONFIG1 */ + return f->config[0]; + case 0x64: /* GPMC_CONFIG2 */ + return f->config[1]; + case 0x68: /* GPMC_CONFIG3 */ + return f->config[2]; + case 0x6c: /* GPMC_CONFIG4 */ + return f->config[3]; + case 0x70: /* GPMC_CONFIG5 */ + return f->config[4]; + case 0x74: /* GPMC_CONFIG6 */ + return f->config[5]; + case 0x78: /* GPMC_CONFIG7 */ + return f->config[6]; + case 0x84 ... 0x87: /* GPMC_NAND_DATA */ + if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { + return omap_nand_read(f, 0, size); + } + return 0; } break; case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - return s->prefconfig[0]; + return s->prefetch.config1; case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - return s->prefconfig[1]; + return s->prefetch.transfercount; case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - return s->prefcontrol; + return s->prefetch.startengine; case 0x1f0: /* GPMC_PREFETCH_STATUS */ - return (s->preffifo << 24) | - ((s->preffifo > - ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) | - s->prefcount; + return (s->prefetch.fifopointer << 24) | + ((s->prefetch.fifopointer >= + ((s->prefetch.config1 >> 8) & 0x7f) ? 1 : 0) << 16) | + s->prefetch.count; case 0x1f4: /* GPMC_ECC_CONFIG */ return s->ecc_cs; @@ -251,7 +607,7 @@ static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, int cs; struct omap_gpmc_cs_file_s *f; - if (size != 4) { + if (size != 4 && gpmc_wordaccess_only(addr)) { return omap_badwidth_write32(opaque, addr, value); } @@ -276,7 +632,7 @@ static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, break; case 0x018: /* GPMC_IRQSTATUS */ - s->irqen = ~value; + s->irqen &= ~value; omap_gpmc_int_update(s); break; @@ -302,62 +658,109 @@ static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, addr -= cs * 0x30; f = s->cs_file + cs; switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - f->config[0] = value & 0xffef3e13; - break; - case 0x64: /* GPMC_CONFIG2 */ - f->config[1] = value & 0x001f1f8f; - break; - case 0x68: /* GPMC_CONFIG3 */ - f->config[2] = value & 0x001f1f8f; - break; - case 0x6c: /* GPMC_CONFIG4 */ - f->config[3] = value & 0x1f8f1f8f; - break; - case 0x70: /* GPMC_CONFIG5 */ - f->config[4] = value & 0x0f1f1f1f; - break; - case 0x74: /* GPMC_CONFIG6 */ - f->config[5] = value & 0x00000fcf; - break; - case 0x78: /* GPMC_CONFIG7 */ - if ((f->config[6] ^ value) & 0xf7f) { - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(f); - if (value & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */ - (value >> 8 & 0xf)); /* BASEADDR */ - } + case 0x60: /* GPMC_CONFIG1 */ + f->config[0] = value & 0xffef3e13; + break; + case 0x64: /* GPMC_CONFIG2 */ + f->config[1] = value & 0x001f1f8f; + break; + case 0x68: /* GPMC_CONFIG3 */ + f->config[2] = value & 0x001f1f8f; + break; + case 0x6c: /* GPMC_CONFIG4 */ + f->config[3] = value & 0x1f8f1f8f; + break; + case 0x70: /* GPMC_CONFIG5 */ + f->config[4] = value & 0x0f1f1f1f; + break; + case 0x74: /* GPMC_CONFIG6 */ + f->config[5] = value & 0x00000fcf; + break; + case 0x78: /* GPMC_CONFIG7 */ + if ((f->config[6] ^ value) & 0xf7f) { + omap_gpmc_cs_unmap(s, cs); f->config[6] = value & 0x00000f7f; - break; - case 0x7c: /* GPMC_NAND_COMMAND */ - case 0x80: /* GPMC_NAND_ADDRESS */ - case 0x84: /* GPMC_NAND_DATA */ - break; - - default: - goto bad_reg; + omap_gpmc_cs_map(s, cs); + } + break; + case 0x7c ... 0x7f: /* GPMC_NAND_COMMAND */ + if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { + nand_setpins(f->dev, 1, 0, 0, 1, 0); /* CLE */ + omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); + } + break; + case 0x80 ... 0x83: /* GPMC_NAND_ADDRESS */ + if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { + nand_setpins(f->dev, 0, 1, 0, 1, 0); /* ALE */ + omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); + } + break; + case 0x84 ... 0x87: /* GPMC_NAND_DATA */ + if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { + omap_nand_write(f, 0, value, size); + } + break; + default: + goto bad_reg; } break; case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - s->prefconfig[0] = value & 0x7f8f7fbf; - /* TODO: update interrupts, fifos, dmas */ + if (!s->prefetch.startengine) { + uint32_t oldconfig1 = s->prefetch.config1; + uint32_t changed; + s->prefetch.config1 = value & 0x7f8f7fbf; + changed = oldconfig1 ^ s->prefetch.config1; + if (changed & (0x80 | 0x7000000)) { + /* Turning the engine on or off, or mapping it somewhere else. + * cs_map() and cs_unmap() check the prefetch config and + * overall CSVALID bits, so it is sufficient to unmap-and-map + * both the old cs and the new one. + */ + int oldcs = prefetch_cs(oldconfig1); + int newcs = prefetch_cs(s->prefetch.config1); + omap_gpmc_cs_unmap(s, oldcs); + omap_gpmc_cs_map(s, oldcs); + if (newcs != oldcs) { + omap_gpmc_cs_unmap(s, newcs); + omap_gpmc_cs_map(s, newcs); + } + } + } break; case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - s->prefconfig[1] = value & 0x3fff; + if (!s->prefetch.startengine) { + s->prefetch.transfercount = value & 0x3fff; + } break; case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - s->prefcontrol = value & 1; - if (s->prefcontrol) { - if (s->prefconfig[0] & 1) - s->preffifo = 0x40; - else - s->preffifo = 0x00; + if (s->prefetch.startengine != (value & 1)) { + s->prefetch.startengine = value & 1; + if (s->prefetch.startengine) { + /* Prefetch engine start */ + s->prefetch.count = s->prefetch.transfercount; + if (s->prefetch.config1 & 1) { + /* Write */ + s->prefetch.fifopointer = 64; + } else { + /* Read */ + s->prefetch.fifopointer = 0; + fill_prefetch_fifo(s); + } + } else { + /* Prefetch engine forcibly stopped. The TRM + * doesn't define the behaviour if you do this. + * We clear the prefetch count, which means that + * we permit no more writes, and don't read any + * more data from NAND. The CPU can still drain + * the FIFO of unread data. + */ + s->prefetch.count = 0; + } + omap_gpmc_int_update(s); } - /* TODO: start */ break; case 0x1f4: /* GPMC_ECC_CONFIG */ @@ -394,24 +797,47 @@ static const MemoryRegionOps omap_gpmc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq) +struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, + target_phys_addr_t base, + qemu_irq irq, qemu_irq drq) { + int cs; struct omap_gpmc_s *s = (struct omap_gpmc_s *) g_malloc0(sizeof(struct omap_gpmc_s)); - omap_gpmc_reset(s); - memory_region_init_io(&s->iomem, &omap_gpmc_ops, s, "omap-gpmc", 0x1000); memory_region_add_subregion(get_system_memory(), base, &s->iomem); + s->irq = irq; + s->drq = drq; + s->accept_256 = cpu_is_omap3630(mpu); + s->revision = cpu_class_omap3(mpu) ? 0x50 : 0x20; + s->lastirq = 0; + omap_gpmc_reset(s); + + /* We have to register a different IO memory handler for each + * chip select region in case a NAND device is mapped there. We + * make the region the worst-case size of 256MB and rely on the + * container memory region in cs_map to chop it down to the actual + * guest-requested size. + */ + for (cs = 0; cs < 8; cs++) { + memory_region_init_io(&s->cs_file[cs].nandiomem, + &omap_nand_ops, + &s->cs_file[cs], + "omap-nand", + 256 * 1024 * 1024); + } + + memory_region_init_io(&s->prefetch.iomem, &omap_prefetch_ops, s, + "omap-gpmc-prefetch", 256 * 1024 * 1024); return s; } -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem, - void (*base_upd)(void *opaque, target_phys_addr_t new), - void (*unmap)(void *opaque), void *opaque) +void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem) { struct omap_gpmc_cs_file_s *f; + assert(iomem); if (cs < 0 || cs >= 8) { fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); @@ -419,12 +845,29 @@ void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem, } f = &s->cs_file[cs]; + omap_gpmc_cs_unmap(s, cs); + f->config[0] &= ~(0xf << 10); f->iomem = iomem; - f->base_update = base_upd; - f->unmap = unmap; - f->opaque = opaque; + omap_gpmc_cs_map(s, cs); +} - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */ - (f->config[6] >> 8 & 0xf)); /* BASEADDR */ +void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand) +{ + struct omap_gpmc_cs_file_s *f; + assert(nand); + + if (cs < 0 || cs >= 8) { + fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); + exit(-1); + } + f = &s->cs_file[cs]; + + omap_gpmc_cs_unmap(s, cs); + f->config[0] &= ~(0xf << 10); + f->config[0] |= (OMAP_GPMC_NAND << 10); + f->dev = nand; + if (nand_getbuswidth(f->dev) == 16) { + f->config[0] |= OMAP_GPMC_16BIT << 12; + } + omap_gpmc_cs_map(s, cs); } diff --git a/hw/onenand.c b/hw/onenand.c index 00276a03cb..6f68f70698 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -25,6 +25,7 @@ #include "blockdev.h" #include "memory.h" #include "exec-memory.h" +#include "sysbus.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ #define PAGE_SHIFT 11 @@ -33,6 +34,7 @@ #define BLOCK_SHIFT (PAGE_SHIFT + 6) typedef struct { + SysBusDevice busdev; struct { uint16_t man; uint16_t dev; @@ -49,6 +51,7 @@ typedef struct { uint8_t *current; MemoryRegion ram; MemoryRegion mapped_ram; + uint8_t current_direction; uint8_t *boot[2]; uint8_t *data[2][2]; MemoryRegion iomem; @@ -120,27 +123,72 @@ static void onenand_mem_setup(OneNANDState *s) 1); } -void onenand_base_update(void *opaque, target_phys_addr_t new) +static void onenand_intr_update(OneNANDState *s) { - OneNANDState *s = (OneNANDState *) opaque; - - s->base = new; - - memory_region_add_subregion(get_system_memory(), s->base, &s->container); + qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); } -void onenand_base_unmap(void *opaque) +static void onenand_pre_save(void *opaque) { - OneNANDState *s = (OneNANDState *) opaque; - - memory_region_del_subregion(get_system_memory(), &s->container); + OneNANDState *s = opaque; + if (s->current == s->otp) { + s->current_direction = 1; + } else if (s->current == s->image) { + s->current_direction = 2; + } else { + s->current_direction = 0; + } } -static void onenand_intr_update(OneNANDState *s) +static int onenand_post_load(void *opaque, int version_id) { - qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); + OneNANDState *s = opaque; + switch (s->current_direction) { + case 0: + break; + case 1: + s->current = s->otp; + break; + case 2: + s->current = s->image; + break; + default: + return -1; + } + onenand_intr_update(s); + return 0; } +static const VMStateDescription vmstate_onenand = { + .name = "onenand", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = onenand_pre_save, + .post_load = onenand_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(current_direction, OneNANDState), + VMSTATE_INT32(cycle, OneNANDState), + VMSTATE_INT32(otpmode, OneNANDState), + VMSTATE_UINT16_ARRAY(addr, OneNANDState, 8), + VMSTATE_UINT16_ARRAY(unladdr, OneNANDState, 8), + VMSTATE_INT32(bufaddr, OneNANDState), + VMSTATE_INT32(count, OneNANDState), + VMSTATE_UINT16(command, OneNANDState), + VMSTATE_UINT16_ARRAY(config, OneNANDState, 2), + VMSTATE_UINT16(status, OneNANDState), + VMSTATE_UINT16(intstatus, OneNANDState), + VMSTATE_UINT16(wpstatus, OneNANDState), + VMSTATE_INT32(secs_cur, OneNANDState), + VMSTATE_PARTIAL_VBUFFER(blockwp, OneNANDState, blocks), + VMSTATE_UINT8(ecc.cp, OneNANDState), + VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2), + VMSTATE_UINT16(ecc.count, OneNANDState), + VMSTATE_BUFFER_UNSAFE(otp, OneNANDState, 0, ((64 + 2) << PAGE_SHIFT)), + VMSTATE_END_OF_LIST() + } +}; + /* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ static void onenand_reset(OneNANDState *s, int cold) { @@ -167,11 +215,17 @@ static void onenand_reset(OneNANDState *s, int 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) - hw_error("%s: Loading the BootRAM failed.\n", __FUNCTION__); + if (s->bdrv_cur && bdrv_read(s->bdrv_cur, 0, s->boot[0], 8) < 0) { + hw_error("%s: Loading the BootRAM failed.\n", __func__); + } } } +static void onenand_system_reset(DeviceState *dev) +{ + onenand_reset(FROM_SYSBUS(OneNANDState, sysbus_from_qdev(dev)), 1); +} + static inline int onenand_load_main(OneNANDState *s, int sec, int secn, void *dest) { @@ -191,8 +245,8 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, int result = 0; if (secn > 0) { - uint32_t size = (uint32_t) secn * 512; - const uint8_t *sp = (const uint8_t *) src; + uint32_t size = (uint32_t)secn * 512; + const uint8_t *sp = (const uint8_t *)src; uint8_t *dp = 0; if (s->bdrv_cur) { dp = g_malloc(size); @@ -203,7 +257,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, if (sec + secn > s->secs_cur) { result = 1; } else { - dp = (uint8_t *) s->current + (sec << 9); + dp = (uint8_t *)s->current + (sec << 9); } } if (!result) { @@ -245,13 +299,13 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, { int result = 0; if (secn > 0) { - const uint8_t *sp = (const uint8_t *) src; + const uint8_t *sp = (const uint8_t *)src; uint8_t *dp = 0, *dpp = 0; if (s->bdrv_cur) { dp = g_malloc(512); if (!dp || bdrv_read(s->bdrv_cur, - s->secs_cur + (sec >> 5), - dp, 1) < 0) { + s->secs_cur + (sec >> 5), + dp, 1) < 0) { result = 1; } else { dpp = dp + ((sec & 31) << 4); @@ -270,7 +324,7 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, } if (s->bdrv_cur) { result = bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), - dp, 1) < 0; + dp, 1) < 0; } } if (dp) { @@ -326,7 +380,7 @@ fail: return 1; } -static void onenand_command(OneNANDState *s, int cmd) +static void onenand_command(OneNANDState *s) { int b; int sec; @@ -346,7 +400,7 @@ static void onenand_command(OneNANDState *s, int cmd) s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ buf += (s->bufaddr & 3) << 4; - switch (cmd) { + switch (s->command) { case 0x00: /* Load single/multiple sector data unit into buffer */ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) @@ -527,7 +581,7 @@ static void onenand_command(OneNANDState *s, int cmd) s->status |= ONEN_ERR_CMD; s->intstatus |= ONEN_INT; fprintf(stderr, "%s: unknown OneNAND command %x\n", - __FUNCTION__, cmd); + __func__, s->command); } onenand_intr_update(s); @@ -659,7 +713,7 @@ static void onenand_write(void *opaque, target_phys_addr_t addr, if (s->intstatus & (1 << 15)) break; s->command = value; - onenand_command(s, s->command); + onenand_command(s); break; case 0xf221: /* System Configuration 1 */ s->config[0] = value; @@ -700,30 +754,25 @@ static const MemoryRegionOps onenand_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -void *onenand_init(BlockDriverState *bdrv, - uint16_t man_id, uint16_t dev_id, uint16_t ver_id, - int regshift, qemu_irq irq) +static int onenand_initfn(SysBusDevice *dev) { - OneNANDState *s = (OneNANDState *) g_malloc0(sizeof(*s)); - uint32_t size = 1 << (24 + ((dev_id >> 4) & 7)); + OneNANDState *s = (OneNANDState *)dev; + uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7)); void *ram; - - s->shift = regshift; - s->intr = irq; + s->base = (target_phys_addr_t)-1; s->rdy = NULL; - s->id.man = man_id; - s->id.dev = dev_id; - s->id.ver = ver_id; s->blocks = size >> BLOCK_SHIFT; s->secs = size >> 9; s->blockwp = g_malloc(s->blocks); - s->density_mask = (dev_id & 0x08) ? (1 << (6 + ((dev_id >> 4) & 7))) : 0; + s->density_mask = (s->id.dev & 0x08) + ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0; memory_region_init_io(&s->iomem, &onenand_ops, s, "onenand", 0x10000 << s->shift); - s->bdrv = bdrv; if (!s->bdrv) { s->image = memset(g_malloc(size + (size >> 5)), - 0xff, size + (size >> 5)); + 0xff, size + (size >> 5)); + } else { + s->bdrv_cur = s->bdrv; } s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); @@ -736,15 +785,40 @@ void *onenand_init(BlockDriverState *bdrv, s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); onenand_mem_setup(s); + sysbus_init_irq(dev, &s->intr); + sysbus_init_mmio_region(dev, &s->container); + vmstate_register(&dev->qdev, + ((s->shift & 0x7f) << 24) + | ((s->id.man & 0xff) << 16) + | ((s->id.dev & 0xff) << 8) + | (s->id.ver & 0xff), + &vmstate_onenand, s); + return 0; +} - onenand_reset(s, 1); +static SysBusDeviceInfo onenand_info = { + .init = onenand_initfn, + .qdev.name = "onenand", + .qdev.size = sizeof(OneNANDState), + .qdev.reset = onenand_system_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT16("manufacturer_id", OneNANDState, id.man, 0), + DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0), + DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0), + DEFINE_PROP_INT32("shift", OneNANDState, shift, 0), + DEFINE_PROP_DRIVE("drive", OneNANDState, bdrv), + DEFINE_PROP_END_OF_LIST() + } +}; - return s; +static void onenand_register_device(void) +{ + sysbus_register_withprop(&onenand_info); } -void *onenand_raw_otp(void *opaque) +void *onenand_raw_otp(DeviceState *onenand_device) { - OneNANDState *s = (OneNANDState *) opaque; - - return s->otp; + return FROM_SYSBUS(OneNANDState, sysbus_from_qdev(onenand_device))->otp; } + +device_init(onenand_register_device) @@ -1811,6 +1811,25 @@ static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, return next; } +static uint8_t pci_find_capability_at_offset(PCIDevice *pdev, uint8_t offset) +{ + uint8_t next, prev, found = 0; + + if (!(pdev->used[offset])) { + return 0; + } + + assert(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST); + + for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); + prev = next + PCI_CAP_LIST_NEXT) { + if (next <= offset && next > found) { + found = next; + } + } + return found; +} + /* Patch the PCI vendor and device ids in a PCI rom image if necessary. This is needed for an option rom which is used for more than one device. */ static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) @@ -1952,11 +1971,30 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t offset, uint8_t size) { uint8_t *config; + int i, overlapping_cap; + if (!offset) { offset = pci_find_space(pdev, size); if (!offset) { return -ENOSPC; } + } else { + /* Verify that capabilities don't overlap. Note: device assignment + * depends on this check to verify that the device is not broken. + * Should never trigger for emulated devices, but it's helpful + * for debugging these. */ + for (i = offset; i < offset + size; i++) { + overlapping_cap = pci_find_capability_at_offset(pdev, i); + if (overlapping_cap) { + fprintf(stderr, "ERROR: %04x:%02x:%02x.%x " + "Attempt to add PCI capability %x at offset " + "%x overlaps existing capability %x at offset %x\n", + pci_find_domain(pdev->bus), pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + cap_id, offset, overlapping_cap, i); + return -EINVAL; + } + } } config = pdev->config + offset; @@ -175,6 +175,14 @@ static void hotplug_event_notify(PCIDevice *dev) } } +static void hotplug_event_clear(PCIDevice *dev) +{ + hotplug_event_update_event_status(dev); + if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { + qemu_set_irq(dev->irq[dev->exp.hpev_intx], 0); + } +} + /* * A PCI Express Hot-Plug Event has occurred, so update slot status register * and notify OS of the event if necessary. @@ -320,6 +328,10 @@ void pcie_cap_slot_write_config(PCIDevice *dev, uint8_t *exp_cap = dev->config + pos; uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) { + hotplug_event_clear(dev); + } + if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { return; } diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c index 2ae65ec807..62c06eafd6 100644 --- a/hw/pcie_aer.c +++ b/hw/pcie_aer.c @@ -415,7 +415,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) int i; assert(err->status); - assert(err->status & (err->status - 1)); + assert(!(err->status & (err->status - 1))); errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); errcap |= PCI_ERR_CAP_FEP(first_bit); @@ -495,7 +495,7 @@ static int pcie_aer_record_error(PCIDevice *dev, int fep = PCI_ERR_CAP_FEP(errcap); assert(err->status); - assert(err->status & (err->status - 1)); + assert(!(err->status & (err->status - 1))); if (errcap & PCI_ERR_CAP_MHRE && (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { @@ -979,20 +979,21 @@ int do_pcie_aer_inejct_error(Monitor *mon, if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { char *e = NULL; error_status = strtoul(error_name, &e, 0); - correctable = !!qdict_get_int(qdict, "correctable"); + correctable = qdict_get_try_bool(qdict, "correctable", 0); if (!e || *e != '\0') { monitor_printf(mon, "invalid error status value. \"%s\"", error_name); return -EINVAL; } } + err.status = error_status; err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; err.flags = 0; if (correctable) { err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; } - if (qdict_get_int(qdict, "advisory_non_fatal")) { + if (qdict_get_try_bool(qdict, "advisory_non_fatal", 0)) { err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; } if (qdict_haskey(qdict, "header0")) { diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index d94b1eb53c..3cc830ff95 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -57,6 +57,7 @@ typedef struct SCSIDiskReq { struct iovec iov; QEMUIOVector qiov; uint32_t status; + BlockAcctCookie acct; } SCSIDiskReq; struct SCSIDiskState @@ -107,10 +108,13 @@ static void scsi_cancel_io(SCSIRequest *req) static void scsi_read_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); int n; r->req.aiocb = NULL; + bdrv_acct_done(s->bs, &r->acct); + if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { return; @@ -161,6 +165,8 @@ static void scsi_read_data(SCSIRequest *req) r->iov.iov_len = n * 512; qemu_iovec_init_external(&r->qiov, &r->iov, 1); + + bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n, scsi_read_complete, r); if (r->req.aiocb == NULL) { @@ -207,11 +213,14 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) static void scsi_write_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t len; uint32_t n; r->req.aiocb = NULL; + bdrv_acct_done(s->bs, &r->acct); + if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { return; @@ -252,6 +261,8 @@ static void scsi_write_data(SCSIRequest *req) n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); + + bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { @@ -854,13 +865,19 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) buflen = 8; break; case SYNCHRONIZE_CACHE: + { + BlockAcctCookie acct; + + bdrv_acct_start(s->bs, &acct, 0, BDRV_ACCT_FLUSH); ret = bdrv_flush(s->bs); + bdrv_acct_done(s->bs, &acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { return -1; } } break; + } case GET_CONFIGURATION: memset(outbuf, 0, 8); /* ??? This should probably return much more information. For now diff --git a/hw/sh_pci.c b/hw/sh_pci.c index 76061bb756..36f39300d5 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -150,7 +150,7 @@ static int sh_pci_init_device(SysBusDevice *dev) PCI_DEVFN(0, 0), 4); memory_region_init_io(&s->memconfig_p4, &sh_pci_reg_ops, s, "sh_pci", 0x224); - memory_region_init_alias(&s->memconfig_a7, "sh_pci.2", &s->memconfig_a7, + memory_region_init_alias(&s->memconfig_a7, "sh_pci.2", &s->memconfig_p4, 0, 0x224); isa_mmio_setup(&s->isa, 0x40000); sysbus_init_mmio_cb2(dev, sh_pci_map, sh_pci_unmap); diff --git a/hw/sysbus.c b/hw/sysbus.c index f39768b6a2..c365d39d24 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -131,6 +131,11 @@ void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory) dev->mmio[n].memory = memory; } +MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n) +{ + return dev->mmio[n].memory; +} + void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) { pio_addr_t i; diff --git a/hw/sysbus.h b/hw/sysbus.h index b87c6c5aab..aa3d383277 100644 --- a/hw/sysbus.h +++ b/hw/sysbus.h @@ -50,6 +50,7 @@ void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, void sysbus_init_mmio_cb2(SysBusDevice *dev, mmio_mapfunc cb, mmio_mapfunc unmap); void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory); +MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n); void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); diff --git a/hw/tusb6010.c b/hw/tusb6010.c index b2bf35934d..de6ffc6133 100644 --- a/hw/tusb6010.c +++ b/hw/tusb6010.c @@ -23,9 +23,11 @@ #include "usb.h" #include "omap.h" #include "irq.h" -#include "tusb6010.h" +#include "devices.h" +#include "sysbus.h" -struct TUSBState { +typedef struct TUSBState { + SysBusDevice busdev; MemoryRegion iomem[2]; qemu_irq irq; MUSBState *musb; @@ -59,7 +61,7 @@ struct TUSBState { uint32_t pullup[2]; uint32_t control_config; uint32_t otg_timer_val; -}; +} TUSBState; #define TUSB_DEVCLOCK 60000000 /* 60 MHz */ @@ -234,16 +236,6 @@ struct TUSBState { #define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) #define TUSB_PROD_TEST_RESET_VAL 0xa596 -MemoryRegion *tusb6010_sync_io(TUSBState *s) -{ - return &s->iomem[0]; -} - -MemoryRegion *tusb6010_async_io(TUSBState *s) -{ - return &s->iomem[1]; -} - static void tusb_intr_update(TUSBState *s) { if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) @@ -723,9 +715,33 @@ static void tusb_musb_core_intr(void *opaque, int source, int level) } } -TUSBState *tusb6010_init(qemu_irq intr) +static void tusb6010_power(TUSBState *s, int on) { - TUSBState *s = g_malloc0(sizeof(*s)); + if (!on) { + s->power = 0; + } else if (!s->power && on) { + s->power = 1; + /* Pull the interrupt down after TUSB6010 comes up. */ + s->intr_ok = 0; + tusb_intr_update(s); + qemu_mod_timer(s->pwr_timer, + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2); + } +} + +static void tusb6010_irq(void *opaque, int source, int level) +{ + if (source) { + tusb_musb_core_intr(opaque, source - 1, level); + } else { + tusb6010_power(opaque, level); + } +} + +static void tusb6010_reset(DeviceState *dev) +{ + TUSBState *s = FROM_SYSBUS(TUSBState, sysbus_from_qdev(dev)); + int i; s->test_reset = TUSB_PROD_TEST_RESET_VAL; s->host_mode = 0; @@ -735,28 +751,59 @@ TUSBState *tusb6010_init(qemu_irq intr) s->mask = 0xffffffff; s->intr = 0x00000000; s->otg_timer_val = 0; - memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async", - UINT32_MAX); - s->irq = intr; + s->scratch = 0; + s->prcm_config = 0; + s->prcm_mngmt = 0; + s->intr_ok = 0; + s->usbip_intr = 0; + s->usbip_mask = 0; + s->gpio_intr = 0; + s->gpio_mask = 0; + s->gpio_config = 0; + s->dma_intr = 0; + s->dma_mask = 0; + s->dma_map = 0; + s->dma_config = 0; + s->ep0_config = 0; + s->wkup_mask = 0; + s->pullup[0] = s->pullup[1] = 0; + s->control_config = 0; + for (i = 0; i < 15; i++) { + s->rx_config[i] = s->tx_config[i] = 0; + } +} + +static int tusb6010_init(SysBusDevice *dev) +{ + TUSBState *s = FROM_SYSBUS(TUSBState, dev); + qemu_irq *musb_irqs; + int i; s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s); s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s); - s->musb = musb_init(qemu_allocate_irqs(tusb_musb_core_intr, s, - __musb_irq_max)); - - return s; + memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async", + UINT32_MAX); + sysbus_init_mmio_region(dev, &s->iomem[0]); + sysbus_init_mmio_region(dev, &s->iomem[1]); + sysbus_init_irq(dev, &s->irq); + qdev_init_gpio_in(&dev->qdev, tusb6010_irq, __musb_irq_max + 1); + musb_irqs = g_new0(qemu_irq, __musb_irq_max); + for (i = 0; i < __musb_irq_max; i++) { + musb_irqs[i] = qdev_get_gpio_in(&dev->qdev, i + 1); + } + s->musb = musb_init(musb_irqs); + return 0; } -void tusb6010_power(TUSBState *s, int on) -{ - if (!on) - s->power = 0; - else if (!s->power && on) { - s->power = 1; +static SysBusDeviceInfo tusb6010_info = { + .init = tusb6010_init, + .qdev.name = "tusb6010", + .qdev.size = sizeof(TUSBState), + .qdev.reset = tusb6010_reset, +}; - /* Pull the interrupt down after TUSB6010 comes up. */ - s->intr_ok = 0; - tusb_intr_update(s); - qemu_mod_timer(s->pwr_timer, - qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2); - } +static void tusb6010_register_device(void) +{ + sysbus_register_withprop(&tusb6010_info); } + +device_init(tusb6010_register_device) diff --git a/hw/tusb6010.h b/hw/tusb6010.h deleted file mode 100644 index b85ee86215..0000000000 --- a/hw/tusb6010.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * tusb6010 interfaces - * - * Copyright 2011 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Avi Kivity <avi@redhat.com> - * - * Derived from hw/devices.h. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef TUSB6010_H -#define TUSB6010_H - -#include "targphys.h" -#include "memory.h" - -typedef struct TUSBState TUSBState; -TUSBState *tusb6010_init(qemu_irq intr); -MemoryRegion *tusb6010_sync_io(TUSBState *s); -MemoryRegion *tusb6010_async_io(TUSBState *s); -void tusb6010_power(TUSBState *s, int on); - -#endif diff --git a/hw/vhost.c b/hw/vhost.c index 18860678ba..0870cb7d85 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -515,11 +515,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, }; struct VirtQueue *vvq = virtio_get_queue(vdev, idx); - if (!vdev->binding->set_host_notifier) { - fprintf(stderr, "binding does not support host notifiers\n"); - return -ENOSYS; - } - vq->num = state.num = virtio_queue_get_num(vdev, idx); r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); if (r) { @@ -567,12 +562,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, r = -errno; goto fail_alloc; } - r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, true); - if (r < 0) { - fprintf(stderr, "Error binding host notifier: %d\n", -r); - goto fail_host_notifier; - } - file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); if (r) { @@ -591,8 +580,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, fail_call: fail_kick: - vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false); -fail_host_notifier: fail_alloc: cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), 0, 0); @@ -618,12 +605,6 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev, .index = idx, }; int r; - r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false); - if (r < 0) { - fprintf(stderr, "vhost VQ %d host cleanup failed: %d\n", idx, r); - fflush(stderr); - } - assert (r >= 0); r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); if (r < 0) { fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); @@ -697,6 +678,60 @@ bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->force; } +/* Stop processing guest IO notifications in qemu. + * Start processing them in vhost in kernel. + */ +int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + int i, r; + if (!vdev->binding->set_host_notifier) { + fprintf(stderr, "binding does not support host notifiers\n"); + r = -ENOSYS; + goto fail; + } + + for (i = 0; i < hdev->nvqs; ++i) { + r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); + if (r < 0) { + fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); + goto fail_vq; + } + } + + return 0; +fail_vq: + while (--i >= 0) { + r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + if (r < 0) { + fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); + fflush(stderr); + } + assert (r >= 0); + } +fail: + return r; +} + +/* Stop processing guest IO notifications in vhost. + * Start processing them in qemu. + * This might actually run the qemu handlers right away, + * so virtio in qemu must be completely setup when this is called. + */ +void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + int i, r; + + for (i = 0; i < hdev->nvqs; ++i) { + r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + if (r < 0) { + fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); + fflush(stderr); + } + assert (r >= 0); + } +} + +/* Host notifiers must be enabled at this point. */ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r; @@ -762,6 +797,7 @@ fail: return r; } +/* Host notifiers must be enabled at this point. */ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r; diff --git a/hw/vhost.h b/hw/vhost.h index c8c595a147..c9452f0732 100644 --- a/hw/vhost.h +++ b/hw/vhost.h @@ -46,5 +46,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev); bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); +int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); +void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); #endif diff --git a/hw/vhost_net.c b/hw/vhost_net.c index a55981200d..950a6b8d99 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -139,16 +139,22 @@ int vhost_net_start(struct vhost_net *net, { struct vhost_vring_file file = { }; int r; + + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; + + r = vhost_dev_enable_notifiers(&net->dev, dev); + if (r < 0) { + goto fail_notifiers; + } if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr_mrg_rxbuf)); } - net->dev.nvqs = 2; - net->dev.vqs = net->vqs; r = vhost_dev_start(&net->dev, dev); if (r < 0) { - return r; + goto fail_start; } net->vc->info->poll(net->vc, false); @@ -173,6 +179,9 @@ fail: if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); } +fail_start: + vhost_dev_disable_notifiers(&net->dev, dev); +fail_notifiers: return r; } @@ -190,6 +199,7 @@ void vhost_net_stop(struct vhost_net *net, if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); } + vhost_dev_disable_notifiers(&net->dev, dev); } void vhost_net_cleanup(struct vhost_net *net) diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index dad8c0a6a2..2a8ccd0aa9 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -47,6 +47,7 @@ typedef struct VirtIOBlockReq struct virtio_scsi_inhdr *scsi; QEMUIOVector qiov; struct VirtIOBlockReq *next; + BlockAcctCookie acct; } VirtIOBlockReq; static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) @@ -58,8 +59,6 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); virtio_notify(&s->vdev, s->vq); - - g_free(req); } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, @@ -81,6 +80,8 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, vm_stop(VMSTOP_DISKFULL); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + bdrv_acct_done(s->bs, &req->acct); + g_free(req); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } @@ -100,6 +101,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret) } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + bdrv_acct_done(req->dev->bs, &req->acct); + g_free(req); } static void virtio_blk_flush_complete(void *opaque, int ret) @@ -113,6 +116,8 @@ static void virtio_blk_flush_complete(void *opaque, int ret) } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + bdrv_acct_done(req->dev->bs, &req->acct); + g_free(req); } static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) @@ -155,6 +160,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) */ if (req->elem.out_num < 2 || req->elem.in_num < 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + g_free(req); return; } @@ -163,6 +169,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) */ if (req->elem.out_num > 2 && req->elem.in_num > 3) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); return; } @@ -229,11 +236,13 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); + g_free(req); } #else static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); } #endif /* __linux__ */ @@ -266,6 +275,8 @@ static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) { BlockDriverAIOCB *acb; + bdrv_acct_start(req->dev->bs, &req->acct, 0, BDRV_ACCT_FLUSH); + /* * Make sure all outstanding writes are posted to the backing device. */ @@ -284,6 +295,8 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) sector = ldq_p(&req->out->sector); + bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); + trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); if (sector & req->dev->sector_mask) { @@ -317,6 +330,8 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) sector = ldq_p(&req->out->sector); + bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); + if (sector & req->dev->sector_mask) { virtio_blk_rw_complete(req, -EIO); return; @@ -370,6 +385,7 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, s->serial ? s->serial : "", MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + g_free(req); } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 31f91514f2..bd5c66916b 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -79,6 +79,7 @@ struct ioreq { struct XenBlkDev *blkdev; QLIST_ENTRY(ioreq) list; + BlockAcctCookie acct; }; struct XenBlkDev { @@ -401,6 +402,7 @@ static void qemu_aio_complete(void *opaque, int ret) ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; ioreq_unmap(ioreq); ioreq_finish(ioreq); + bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct); qemu_bh_schedule(ioreq->blkdev->bh); } @@ -419,6 +421,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) switch (ioreq->req.operation) { case BLKIF_OP_READ: + bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_READ); ioreq->aio_inflight++; bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, @@ -429,6 +432,8 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) if (!ioreq->req.nr_segments) { break; } + + bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_WRITE); ioreq->aio_inflight++; bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, |