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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJW+/87AAoJEDwlJe0UNgzeKowP/3L/X4iOsmd4liFUyMxtp4QX
 7XeG3OfOPKn6H8R/PtHQFT5/s4pyIsVnt3l00itLSqF89cvMh1dIwS51EYkgr7IT
 G+kF4mPEfsaOGixDChrc4DgEE8DpqSXRaTSK/lX7E81ZXYiObMKlAe6oNbsQkDv8
 vr6MDaPrNZPf4FlSiePyAwP4NuyHim1csRMHzXGMoGuAm/yZiAK9vtM58FfZHtt7
 QFka7Kaly71sctxaCnDZJ3h+3UABTNDiFWwkWkVPA4IdWtB328iG7RLSuy2z5jgJ
 qIRPkUKwxXxAYzgqkHO5GmqB3No1Tm7NFyTf05Qvl9wlIMQqBYV0rA/EDLptlNUg
 M1DnXYR9mnyFt82TVeEAPJckyOvE2XSym74MFlwukOBxgaF7usYNvMTjPB/ix1th
 ZlkrrFAo7xsOltYpwiZDL1M+JCePspGMk7VlgKjMQutTzfJGwcPym9zaEJZhsQv9
 Z6u0b8QhyEOoWOv3b3/8IGBy7Qrh1r34plqZDOKzp7q6Wrpx1q2mqhj0dyavLB+T
 uWKh10FcjeI/DyM+HhFdnrGQwLvdrWZVd8tRnVwEo164jkfZEe8bVbCbtFtHbeVx
 ++B7NVaDbjxF3c1vv7F4PyWz2F68Sa+Z+t2okK2+icBHtmqEVonH2zOOSgQymz+E
 9QKHN2YogpQFq7m5YWt8
 =mfnJ
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2016-03-30 17:32:11 +01:00
commit 4468d4e0f3
14 changed files with 620 additions and 25 deletions

View File

@ -111,3 +111,4 @@ CONFIG_I82801B11=y
CONFIG_ACPI=y
CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y
CONFIG_GPIO_KEY=y

View File

@ -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);

View File

@ -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;
}

View File

@ -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

104
hw/gpio/gpio_key.c Normal file
View File

@ -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)

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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'] }

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

84
target-arm/monitor.c Normal file
View File

@ -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;
}