aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-03-30 17:32:11 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-03-30 17:32:11 +0100
commit4468d4e0f383f09c7a4df2480a2bf87fafcbe20b (patch)
tree033500f975fe01bd5277631202d0730cb1db0521
parent489ef4c810033e63af570c8a430af8b9858bfa5f (diff)
parentdb31e49a565fc4c165fd98201721b313c3412c1f (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160330-1' into staging
target-arm queue: * virt: fix the virtual power button by adding a modelled "key press for 100ms" device * various improvements to m25p80 flash devices * implement new QMP query-gic-capability command to let the management layer know what versions of GIC we support # gpg: Signature made Wed 30 Mar 2016 17:30:51 BST using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" * remotes/pmaydell/tags/pull-target-arm-20160330-1: arm: implement query-gic-capabilities kvm: add kvm_device_supported() helper function arm: enhance kvm_arm_create_scratch_host_vcpu arm: qmp: add query-gic-capabilities interface block: m25p80: at25128a/at25256a models block: m25p80: n25q256a/n25q512a models block: m25p80: Implemented FSR register block: m25p80: Fast read and 4bytes commands block: m25p80: Dummy cycles for N25Q256/512 block: m25p80: Add configuration registers block: m25p80: 4byte address mode block: m25p80: Extend address mode block: m25p80: Widen flags variable block: m25p80: RESET_ENABLE and RESET_MEMORY commands block: m25p80: Removed unused variable ARM: Virt: Use gpio_key for power button hw/gpio: Add the emulation of gpio_key Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--hw/arm/virt.c7
-rw-r--r--hw/block/m25p80.c330
-rw-r--r--hw/gpio/Makefile.objs1
-rw-r--r--hw/gpio/gpio_key.c104
-rw-r--r--include/sysemu/kvm.h9
-rw-r--r--kvm-all.c15
-rw-r--r--monitor.c8
-rw-r--r--qapi-schema.json36
-rw-r--r--qmp-commands.hx27
-rw-r--r--target-arm/Makefile.objs2
-rw-r--r--target-arm/kvm.c14
-rw-r--r--target-arm/kvm_arm.h7
-rw-r--r--target-arm/monitor.c84
14 files changed, 620 insertions, 25 deletions
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 2bcd23656b..c63cdd073d 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -111,3 +111,4 @@ CONFIG_I82801B11=y
CONFIG_ACPI=y
CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y
+CONFIG_GPIO_KEY=y
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index a5e787dec6..56d35c7716 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -582,11 +582,11 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic)
g_free(nodename);
}
-static DeviceState *pl061_dev;
+static DeviceState *gpio_key_dev;
static void virt_powerdown_req(Notifier *n, void *opaque)
{
/* use gpio Pin 3 for power button event */
- qemu_set_irq(qdev_get_gpio_in(pl061_dev, 3), 1);
+ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
}
static Notifier virt_system_powerdown_notifier = {
@@ -596,6 +596,7 @@ static Notifier virt_system_powerdown_notifier = {
static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
{
char *nodename;
+ DeviceState *pl061_dev;
hwaddr base = vbi->memmap[VIRT_GPIO].base;
hwaddr size = vbi->memmap[VIRT_GPIO].size;
int irq = vbi->irqmap[VIRT_GPIO];
@@ -618,6 +619,8 @@ static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk");
qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle);
+ gpio_key_dev = sysbus_create_simple("gpio-key", -1,
+ qdev_get_gpio_in(pl061_dev, 3));
qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys");
qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys");
qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0);
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index de24f427dc..906b71257e 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -26,6 +26,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/ssi/ssi.h"
+#include "qemu/bitops.h"
#ifndef M25P80_ERR_DEBUG
#define M25P80_ERR_DEBUG 0
@@ -46,7 +47,10 @@
/* set to allow the page program command to write 0s back to 1. Useful for
* modelling EEPROM with SPI flash command set
*/
-#define WR_1 0x100
+#define EEPROM 0x100
+
+/* 16 MiB max in 3 byte address mode */
+#define MAX_3BYTES_SIZE 0x1000000
typedef struct FlashPartInfo {
const char *part_name;
@@ -61,7 +65,7 @@ typedef struct FlashPartInfo {
uint32_t sector_size;
uint32_t n_sectors;
uint32_t page_size;
- uint8_t flags;
+ uint16_t flags;
} FlashPartInfo;
/* adapted from linux */
@@ -79,6 +83,30 @@ typedef struct FlashPartInfo {
#define JEDEC_WINBOND 0xEF
#define JEDEC_SPANSION 0x01
+/* Numonyx (Micron) Configuration register macros */
+#define VCFG_DUMMY 0x1
+#define VCFG_WRAP_SEQUENTIAL 0x2
+#define NVCFG_XIP_MODE_DISABLED (7 << 9)
+#define NVCFG_XIP_MODE_MASK (7 << 9)
+#define VCFG_XIP_MODE_ENABLED (1 << 3)
+#define CFG_DUMMY_CLK_LEN 4
+#define NVCFG_DUMMY_CLK_POS 12
+#define VCFG_DUMMY_CLK_POS 4
+#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7
+#define EVCFG_VPP_ACCELERATOR (1 << 3)
+#define EVCFG_RESET_HOLD_ENABLED (1 << 4)
+#define NVCFG_DUAL_IO_MASK (1 << 2)
+#define EVCFG_DUAL_IO_ENABLED (1 << 6)
+#define NVCFG_QUAD_IO_MASK (1 << 3)
+#define EVCFG_QUAD_IO_ENABLED (1 << 7)
+#define NVCFG_4BYTE_ADDR_MASK (1 << 0)
+#define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
+#define CFG_UPPER_128MB_SEG_ENABLED 0x3
+
+/* Numonyx (Micron) Flag Status Register macros */
+#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
+#define FSR_FLASH_READY (1 << 7)
+
static const FlashPartInfo known_devices[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) },
@@ -95,6 +123,12 @@ static const FlashPartInfo known_devices[] = {
{ INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) },
+ /* Atmel EEPROMS - it is assumed, that don't care bit in command
+ * is set to 0. Block protection is not supported.
+ */
+ { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) },
+ { INFO("at25256a-nonjedec", 0x0, 0, 1, 262144, EEPROM) },
+
/* EON -- en25xxx */
{ INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) },
{ INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) },
@@ -206,8 +240,9 @@ static const FlashPartInfo known_devices[] = {
{ INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) },
- /* Numonyx -- n25q128 */
{ INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
+ { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
+ { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
};
typedef enum {
@@ -218,21 +253,49 @@ typedef enum {
WREN = 0x6,
JEDEC_READ = 0x9f,
BULK_ERASE = 0xc7,
+ READ_FSR = 0x70,
- READ = 0x3,
- FAST_READ = 0xb,
+ READ = 0x03,
+ READ4 = 0x13,
+ FAST_READ = 0x0b,
+ FAST_READ4 = 0x0c,
DOR = 0x3b,
+ DOR4 = 0x3c,
QOR = 0x6b,
+ QOR4 = 0x6c,
DIOR = 0xbb,
+ DIOR4 = 0xbc,
QIOR = 0xeb,
+ QIOR4 = 0xec,
- PP = 0x2,
+ PP = 0x02,
+ PP4 = 0x12,
DPP = 0xa2,
QPP = 0x32,
ERASE_4K = 0x20,
+ ERASE4_4K = 0x21,
ERASE_32K = 0x52,
ERASE_SECTOR = 0xd8,
+ ERASE4_SECTOR = 0xdc,
+
+ EN_4BYTE_ADDR = 0xB7,
+ EX_4BYTE_ADDR = 0xE9,
+
+ EXTEND_ADDR_READ = 0xC8,
+ EXTEND_ADDR_WRITE = 0xC5,
+
+ RESET_ENABLE = 0x66,
+ RESET_MEMORY = 0x99,
+
+ RNVCR = 0xB5,
+ WNVCR = 0xB1,
+
+ RVCR = 0x85,
+ WVCR = 0x81,
+
+ REVCR = 0x65,
+ WEVCR = 0x61,
} FlashCMD;
typedef enum {
@@ -246,8 +309,6 @@ typedef enum {
typedef struct Flash {
SSISlave parent_obj;
- uint32_t r;
-
BlockBackend *blk;
uint8_t *storage;
@@ -261,7 +322,13 @@ typedef struct Flash {
uint8_t needed_bytes;
uint8_t cmd_in_progress;
uint64_t cur_addr;
+ uint32_t nonvolatile_cfg;
+ uint32_t volatile_cfg;
+ uint32_t enh_volatile_cfg;
bool write_enable;
+ bool four_bytes_address_mode;
+ bool reset_enable;
+ uint8_t ear;
int64_t dirty_page;
@@ -333,6 +400,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
switch (cmd) {
case ERASE_4K:
+ case ERASE4_4K:
len = 4 << 10;
capa_to_assert = ER_4K;
break;
@@ -341,6 +409,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
capa_to_assert = ER_32K;
break;
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
len = s->pi->sector_size;
break;
case BULK_ERASE:
@@ -387,7 +456,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
" -> %" PRIx8 "\n", addr, prev, data);
}
- if (s->pi->flags & WR_1) {
+ if (s->pi->flags & EEPROM) {
s->storage[s->cur_addr] = data;
} else {
s->storage[s->cur_addr] &= data;
@@ -397,11 +466,43 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
s->dirty_page = page;
}
+static inline int get_addr_length(Flash *s)
+{
+ /* check if eeprom is in use */
+ if (s->pi->flags == EEPROM) {
+ return 2;
+ }
+
+ switch (s->cmd_in_progress) {
+ case PP4:
+ case READ4:
+ case QIOR4:
+ case ERASE4_4K:
+ case ERASE4_SECTOR:
+ case FAST_READ4:
+ case DOR4:
+ case QOR4:
+ case DIOR4:
+ return 4;
+ default:
+ return s->four_bytes_address_mode ? 4 : 3;
+ }
+}
+
static void complete_collecting_data(Flash *s)
{
- s->cur_addr = s->data[0] << 16;
- s->cur_addr |= s->data[1] << 8;
- s->cur_addr |= s->data[2];
+ int i;
+
+ s->cur_addr = 0;
+
+ for (i = 0; i < get_addr_length(s); ++i) {
+ s->cur_addr <<= 8;
+ s->cur_addr |= s->data[i];
+ }
+
+ if (get_addr_length(s) == 3) {
+ s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE;
+ }
s->state = STATE_IDLE;
@@ -409,19 +510,28 @@ static void complete_collecting_data(Flash *s)
case DPP:
case QPP:
case PP:
+ case PP4:
s->state = STATE_PAGE_PROGRAM;
break;
case READ:
+ case READ4:
case FAST_READ:
+ case FAST_READ4:
case DOR:
+ case DOR4:
case QOR:
+ case QOR4:
case DIOR:
+ case DIOR4:
case QIOR:
+ case QIOR4:
s->state = STATE_READ;
break;
case ERASE_4K:
+ case ERASE4_4K:
case ERASE_32K:
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
flash_erase(s, s->cur_addr, s->cmd_in_progress);
break;
case WRSR:
@@ -429,49 +539,128 @@ static void complete_collecting_data(Flash *s)
s->write_enable = false;
}
break;
+ case EXTEND_ADDR_WRITE:
+ s->ear = s->data[0];
+ break;
+ case WNVCR:
+ s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
+ break;
+ case WVCR:
+ s->volatile_cfg = s->data[0];
+ break;
+ case WEVCR:
+ s->enh_volatile_cfg = s->data[0];
+ break;
default:
break;
}
}
+static void reset_memory(Flash *s)
+{
+ s->cmd_in_progress = NOP;
+ s->cur_addr = 0;
+ s->ear = 0;
+ s->four_bytes_address_mode = false;
+ s->len = 0;
+ s->needed_bytes = 0;
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ s->write_enable = false;
+ s->reset_enable = false;
+
+ if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
+ s->volatile_cfg = 0;
+ s->volatile_cfg |= VCFG_DUMMY;
+ s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL;
+ if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK)
+ != NVCFG_XIP_MODE_DISABLED) {
+ s->volatile_cfg |= VCFG_XIP_MODE_ENABLED;
+ }
+ s->volatile_cfg |= deposit32(s->volatile_cfg,
+ VCFG_DUMMY_CLK_POS,
+ CFG_DUMMY_CLK_LEN,
+ extract32(s->nonvolatile_cfg,
+ NVCFG_DUMMY_CLK_POS,
+ CFG_DUMMY_CLK_LEN)
+ );
+
+ s->enh_volatile_cfg = 0;
+ s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF;
+ s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR;
+ s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED;
+ if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) {
+ s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED;
+ }
+ if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) {
+ s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED;
+ }
+ if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) {
+ s->four_bytes_address_mode = true;
+ }
+ if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) {
+ s->ear = CFG_UPPER_128MB_SEG_ENABLED;
+ }
+ }
+
+ DB_PRINT_L(0, "Reset done.\n");
+}
+
static void decode_new_cmd(Flash *s, uint32_t value)
{
s->cmd_in_progress = value;
DB_PRINT_L(0, "decoded new command:%x\n", value);
+ if (value != RESET_MEMORY) {
+ s->reset_enable = false;
+ }
+
switch (value) {
case ERASE_4K:
+ case ERASE4_4K:
case ERASE_32K:
case ERASE_SECTOR:
+ case ERASE4_SECTOR:
case READ:
+ case READ4:
case DPP:
case QPP:
case PP:
- s->needed_bytes = 3;
+ case PP4:
+ s->needed_bytes = get_addr_length(s);
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
break;
case FAST_READ:
+ case FAST_READ4:
case DOR:
+ case DOR4:
case QOR:
- s->needed_bytes = 4;
+ case QOR4:
+ s->needed_bytes = get_addr_length(s);
+ if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
+ }
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
break;
case DIOR:
+ case DIOR4:
switch ((s->pi->jedec >> 16) & 0xFF) {
case JEDEC_WINBOND:
case JEDEC_SPANSION:
s->needed_bytes = 4;
break;
- case JEDEC_NUMONYX:
default:
- s->needed_bytes = 5;
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
}
s->pos = 0;
s->len = 0;
@@ -479,14 +668,16 @@ static void decode_new_cmd(Flash *s, uint32_t value)
break;
case QIOR:
+ case QIOR4:
switch ((s->pi->jedec >> 16) & 0xFF) {
case JEDEC_WINBOND:
case JEDEC_SPANSION:
s->needed_bytes = 6;
break;
- case JEDEC_NUMONYX:
default:
- s->needed_bytes = 8;
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
}
s->pos = 0;
s->len = 0;
@@ -516,6 +707,16 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
+ case READ_FSR:
+ s->data[0] = FSR_FLASH_READY;
+ if (s->four_bytes_address_mode) {
+ s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED;
+ }
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+
case JEDEC_READ:
DB_PRINT_L(0, "populated jedec code\n");
s->data[0] = (s->pi->jedec >> 16) & 0xff;
@@ -543,6 +744,77 @@ static void decode_new_cmd(Flash *s, uint32_t value)
break;
case NOP:
break;
+ case EN_4BYTE_ADDR:
+ s->four_bytes_address_mode = true;
+ break;
+ case EX_4BYTE_ADDR:
+ s->four_bytes_address_mode = false;
+ break;
+ case EXTEND_ADDR_READ:
+ s->data[0] = s->ear;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case EXTEND_ADDR_WRITE:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RNVCR:
+ s->data[0] = s->nonvolatile_cfg & 0xFF;
+ s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
+ s->pos = 0;
+ s->len = 2;
+ s->state = STATE_READING_DATA;
+ break;
+ case WNVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 2;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RVCR:
+ s->data[0] = s->volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case REVCR:
+ s->data[0] = s->enh_volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WEVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RESET_ENABLE:
+ s->reset_enable = true;
+ break;
+ case RESET_MEMORY:
+ if (s->reset_enable) {
+ reset_memory(s);
+ }
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
break;
@@ -649,14 +921,26 @@ static int m25p80_init(SSISlave *ss)
return 0;
}
+static void m25p80_reset(DeviceState *d)
+{
+ Flash *s = M25P80(d);
+
+ reset_memory(s);
+}
+
static void m25p80_pre_save(void *opaque)
{
flash_sync_dirty((Flash *)opaque, -1);
}
+static Property m25p80_properties[] = {
+ DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static const VMStateDescription vmstate_m25p80 = {
.name = "xilinx_spi",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.pre_save = m25p80_pre_save,
.fields = (VMStateField[]) {
@@ -668,6 +952,12 @@ static const VMStateDescription vmstate_m25p80 = {
VMSTATE_UINT8(cmd_in_progress, Flash),
VMSTATE_UINT64(cur_addr, Flash),
VMSTATE_BOOL(write_enable, Flash),
+ VMSTATE_BOOL_V(reset_enable, Flash, 2),
+ VMSTATE_UINT8_V(ear, Flash, 2),
+ VMSTATE_BOOL_V(four_bytes_address_mode, Flash, 2),
+ VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2),
+ VMSTATE_UINT32_V(volatile_cfg, Flash, 2),
+ VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2),
VMSTATE_END_OF_LIST()
}
};
@@ -683,6 +973,8 @@ static void m25p80_class_init(ObjectClass *klass, void *data)
k->set_cs = m25p80_cs;
k->cs_polarity = SSI_CS_LOW;
dc->vmsd = &vmstate_m25p80;
+ dc->props = m25p80_properties;
+ dc->reset = m25p80_reset;
mc->pi = data;
}
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 52233f7e2f..a43c7cf442 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -3,6 +3,7 @@ common-obj-$(CONFIG_PL061) += pl061.o
common-obj-$(CONFIG_PUV3) += puv3_gpio.o
common-obj-$(CONFIG_ZAURUS) += zaurus.o
common-obj-$(CONFIG_E500) += mpc8xxx.o
+common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o
obj-$(CONFIG_OMAP) += omap_gpio.o
obj-$(CONFIG_IMX) += imx_gpio.o
diff --git a/hw/gpio/gpio_key.c b/hw/gpio/gpio_key.c
new file mode 100644
index 0000000000..ef287727b6
--- /dev/null
+++ b/hw/gpio/gpio_key.c
@@ -0,0 +1,104 @@
+/*
+ * GPIO key
+ *
+ * Copyright (c) 2016 Linaro Limited
+ *
+ * Author: Shannon Zhao <shannon.zhao@linaro.org>
+ *
+ * Emulate a (human) keypress -- when the key is triggered by
+ * setting the incoming gpio line, the outbound irq line is
+ * raised for 100ms before being dropped again.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+
+#define TYPE_GPIOKEY "gpio-key"
+#define GPIOKEY(obj) OBJECT_CHECK(GPIOKEYState, (obj), TYPE_GPIOKEY)
+#define GPIO_KEY_LATENCY 100 /* 100ms */
+
+typedef struct GPIOKEYState {
+ SysBusDevice parent_obj;
+
+ QEMUTimer *timer;
+ qemu_irq irq;
+} GPIOKEYState;
+
+static const VMStateDescription vmstate_gpio_key = {
+ .name = "gpio-key",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(timer, GPIOKEYState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void gpio_key_reset(DeviceState *dev)
+{
+ GPIOKEYState *s = GPIOKEY(dev);
+
+ timer_del(s->timer);
+}
+
+static void gpio_key_timer_expired(void *opaque)
+{
+ GPIOKEYState *s = (GPIOKEYState *)opaque;
+
+ qemu_set_irq(s->irq, 0);
+ timer_del(s->timer);
+}
+
+static void gpio_key_set_irq(void *opaque, int irq, int level)
+{
+ GPIOKEYState *s = (GPIOKEYState *)opaque;
+
+ qemu_set_irq(s->irq, 1);
+ timer_mod(s->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + GPIO_KEY_LATENCY);
+}
+
+static void gpio_key_realize(DeviceState *dev, Error **errp)
+{
+ GPIOKEYState *s = GPIOKEY(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(dev, gpio_key_set_irq, 1);
+ s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s);
+}
+
+static void gpio_key_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = gpio_key_realize;
+ dc->vmsd = &vmstate_gpio_key;
+ dc->reset = &gpio_key_reset;
+}
+
+static const TypeInfo gpio_key_info = {
+ .name = TYPE_GPIOKEY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GPIOKEYState),
+ .class_init = gpio_key_class_init,
+};
+
+static void gpio_key_register_types(void)
+{
+ type_register_static(&gpio_key_info);
+}
+
+type_init(gpio_key_register_types)
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index 6695fa7cfd..0e18f15c94 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -306,6 +306,15 @@ void kvm_device_access(int fd, int group, uint64_t attr,
*/
int kvm_create_device(KVMState *s, uint64_t type, bool test);
+/**
+ * kvm_device_supported - probe whether KVM supports specific device
+ *
+ * @vmfd: The fd handler for VM
+ * @type: type of device
+ *
+ * @return: true if supported, otherwise false.
+ */
+bool kvm_device_supported(int vmfd, uint64_t type);
/* Arch specific hooks */
diff --git a/kvm-all.c b/kvm-all.c
index 44c0464261..e7b66df197 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -2339,6 +2339,21 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test)
return test ? 0 : create_dev.fd;
}
+bool kvm_device_supported(int vmfd, uint64_t type)
+{
+ struct kvm_create_device create_dev = {
+ .type = type,
+ .fd = -1,
+ .flags = KVM_CREATE_DEVICE_TEST,
+ };
+
+ if (ioctl(vmfd, KVM_CHECK_EXTENSION, KVM_CAP_DEVICE_CTRL) <= 0) {
+ return false;
+ }
+
+ return (ioctl(vmfd, KVM_CREATE_DEVICE, &create_dev) >= 0);
+}
+
int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source)
{
struct kvm_one_reg reg;
diff --git a/monitor.c b/monitor.c
index 955ed35303..d1c193013e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4259,3 +4259,11 @@ void qmp_dump_skeys(const char *filename, Error **errp)
error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
}
#endif
+
+#ifndef TARGET_ARM
+GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "query-gic-capabilities");
+ return NULL;
+}
+#endif
diff --git a/qapi-schema.json b/qapi-schema.json
index 8907790daa..e58f6a9a12 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4134,3 +4134,39 @@
##
{ 'enum': 'ReplayMode',
'data': [ 'none', 'record', 'play' ] }
+
+##
+# @GICCapability:
+#
+# The struct describes capability for a specific GIC (Generic
+# Interrupt Controller) version. These bits are not only decided by
+# QEMU/KVM software version, but also decided by the hardware that
+# the program is running upon.
+#
+# @version: version of GIC to be described. Currently, only 2 and 3
+# are supported.
+#
+# @emulated: whether current QEMU/hardware supports emulated GIC
+# device in user space.
+#
+# @kernel: whether current QEMU/hardware supports hardware
+# accelerated GIC device in kernel.
+#
+# Since: 2.6
+##
+{ 'struct': 'GICCapability',
+ 'data': { 'version': 'int',
+ 'emulated': 'bool',
+ 'kernel': 'bool' } }
+
+##
+# @query-gic-capabilities:
+#
+# This command is ARM-only. It will return a list of GICCapability
+# objects that describe its capability bits.
+#
+# Returns: a list of GICCapability objects.
+#
+# Since: 2.6
+##
+{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 9e05365ccf..de896a5a31 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -4853,3 +4853,30 @@ Example:
{"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840,
"pop-vlan": 1, "id": 251658240}
]}
+
+EQMP
+
+#if defined TARGET_ARM
+ {
+ .name = "query-gic-capabilities",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_query_gic_capabilities,
+ },
+#endif
+
+SQMP
+query-gic-capabilities
+---------------
+
+Return a list of GICCapability objects, describing supported GIC
+(Generic Interrupt Controller) versions.
+
+Arguments: None
+
+Example:
+
+-> { "execute": "query-gic-capabilities" }
+<- { "return": [{ "version": 2, "emulated": true, "kernel": false },
+ { "version": 3, "emulated": false, "kernel": true } ] }
+
+EQMP
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index a80eb39743..82cbe6bbad 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += arm-semi.o
-obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o
+obj-$(CONFIG_SOFTMMU) += machine.o psci.o arch_dump.o monitor.o
obj-$(CONFIG_KVM) += kvm.o
obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 969ab0bab5..36710320f0 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -62,13 +62,18 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
goto err;
}
+ if (!init) {
+ /* Caller doesn't want the VCPU to be initialized, so skip it */
+ goto finish;
+ }
+
ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, init);
if (ret >= 0) {
ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init);
if (ret < 0) {
goto err;
}
- } else {
+ } else if (cpus_to_try) {
/* Old kernel which doesn't know about the
* PREFERRED_TARGET ioctl: we know it will only support
* creating one kind of guest CPU which is its preferred
@@ -85,8 +90,15 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
if (ret < 0) {
goto err;
}
+ } else {
+ /* Treat a NULL cpus_to_try argument the same as an empty
+ * list, which means we will fail the call since this must
+ * be an old kernel which doesn't support PREFERRED_TARGET.
+ */
+ goto err;
}
+finish:
fdarray[0] = kvmfd;
fdarray[1] = vmfd;
fdarray[2] = cpufd;
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index 07f0c72199..345233c18b 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -124,9 +124,12 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu);
* kvm_arm_create_scratch_host_vcpu:
* @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with
* QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not
- * know the PREFERRED_TARGET ioctl
+ * know the PREFERRED_TARGET ioctl. Passing NULL is the same as passing
+ * an empty array.
* @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order
- * @init: filled in with the necessary values for creating a host vcpu
+ * @init: filled in with the necessary values for creating a host
+ * vcpu. If NULL is provided, will not init the vCPU (though the cpufd
+ * will still be set up).
*
* Create a scratch vcpu in its own VM of the type preferred by the host
* kernel (as would be used for '-cpu host'), for purposes of probing it
diff --git a/target-arm/monitor.c b/target-arm/monitor.c
new file mode 100644
index 0000000000..1ee59a2e45
--- /dev/null
+++ b/target-arm/monitor.c
@@ -0,0 +1,84 @@
+/*
+ * QEMU monitor.c for ARM.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qmp-commands.h"
+#include "hw/boards.h"
+#include "kvm_arm.h"
+
+static GICCapability *gic_cap_new(int version)
+{
+ GICCapability *cap = g_new0(GICCapability, 1);
+ cap->version = version;
+ /* by default, support none */
+ cap->emulated = false;
+ cap->kernel = false;
+ return cap;
+}
+
+static GICCapabilityList *gic_cap_list_add(GICCapabilityList *head,
+ GICCapability *cap)
+{
+ GICCapabilityList *item = g_new0(GICCapabilityList, 1);
+ item->value = cap;
+ item->next = head;
+ return item;
+}
+
+static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3)
+{
+#ifdef CONFIG_KVM
+ int fdarray[3];
+
+ if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) {
+ return;
+ }
+
+ /* Test KVM GICv2 */
+ if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) {
+ v2->kernel = true;
+ }
+
+ /* Test KVM GICv3 */
+ if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) {
+ v3->kernel = true;
+ }
+
+ kvm_arm_destroy_scratch_host_vcpu(fdarray);
+#endif
+}
+
+GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
+{
+ GICCapabilityList *head = NULL;
+ GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3);
+
+ v2->emulated = true;
+ /* TODO: we'd change to true after we get emulated GICv3. */
+ v3->emulated = false;
+
+ gic_cap_kvm_probe(v2, v3);
+
+ head = gic_cap_list_add(head, v2);
+ head = gic_cap_list_add(head, v3);
+
+ return head;
+}