mirror of https://github.com/xemu-project/xemu.git
target-arm queue:
* Implement AArch32 ARMv8-R support * Add Cortex-R52 CPU * fix handling of HLT semihosting in system mode * hw/timer/ixm_epit: cleanup and fix bug in compare handling * target/arm: Coding style fixes * target/arm: Clean up includes * nseries: minor code cleanups * target/arm: align exposed ID registers with Linux * hw/arm/smmu-common: remove unnecessary inlines * i.MX7D: Handle GPT timers * i.MX7D: Connect IRQs to GPIO devices * i.MX6UL: Add a specific GPT timer instance * hw/net: Fix read of uninitialized memory in imx_fec -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmO2/iYZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3sy1EACPsxR5R19BbfwuR3e2VKrA 3ltc1ZwiEiDzKE0YJ+VL8zSyzNKFs0OD4O+ZOBPu5PegwhLdH5QI5QrhkRwlWr6T XJfNDF+8oUUIlNYeD9iSOiZt+W7cnaWrHoM4Oga3O610eS6f0hGmfVxlXUxUfT/4 3x/MKcSXI4SnwXuXrxmqmTm7sVCXP8cbqrIZzN5VUo341B1uqQ5bp1hRmiLt+cvY pnCk3MgYCuZAXRQrLShJkFeu3lJ/W89DVAY5v5+VAMR3jD/tTvQ5bP4HdBMJP4RY AyoI/4cmlAnvOq4Yr8wKdWo7/fgkj9sTHV11sRWkiOdKhLZe9aNYnv1Bd2COhmvH gJcWZ8SNpJ364iRoQPy1PeKxuSMQaesUKWXkvkqjsaGKD9gr2QjTpI3yN6wU3O5+ lT4wGsDMHDhpQml2r19+D3XGm5oA+t2sr1/27WjKBDYopTtZF/KuJ1xVMnIRxzJW M+V3BcM4RPivmv0a+ICA6f1WwE59EeBBzOfZ+VjBpnQAfTv9HRN1yCIVWRN8hIiz cC/iuY6tGxpdZf965fYCIj5cZ2OmCbIw1mh5hUSLDIaCd9+qXl7cgT7stpLar7kA tYDazF2J3v+XqUeyWtPndzAFdgr4rLNH9Q9kDKS9fyXOspIFqv6bBhAMMxiiTbT5 zj5Y2K1lAyHLTTwWmcNruw== =b/pm -----END PGP SIGNATURE----- Merge tag 'pull-target-arm-20230105' of https://git.linaro.org/people/pmaydell/qemu-arm into staging target-arm queue: * Implement AArch32 ARMv8-R support * Add Cortex-R52 CPU * fix handling of HLT semihosting in system mode * hw/timer/ixm_epit: cleanup and fix bug in compare handling * target/arm: Coding style fixes * target/arm: Clean up includes * nseries: minor code cleanups * target/arm: align exposed ID registers with Linux * hw/arm/smmu-common: remove unnecessary inlines * i.MX7D: Handle GPT timers * i.MX7D: Connect IRQs to GPIO devices * i.MX6UL: Add a specific GPT timer instance * hw/net: Fix read of uninitialized memory in imx_fec # gpg: Signature made Thu 05 Jan 2023 16:43:18 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # gpg: aka "Peter Maydell <peter@archaic.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * tag 'pull-target-arm-20230105' of https://git.linaro.org/people/pmaydell/qemu-arm: (34 commits) hw/net: Fix read of uninitialized memory in imx_fec. i.MX7D: Connect IRQs to GPIO devices. i.MX6UL: Add a specific GPT timer instance for the i.MX6UL i.MX7D: Compute clock frequency for the fixed frequency clocks. i.MX7D: Connect GPT timers to IRQ hw/arm/smmu-common: Avoid using inlined functions with external linkage hw/arm/smmu-common: Reduce smmu_inv_notifiers_mr() scope target/arm: align exposed ID registers with Linux hw/arm/nseries: Silent -Wmissing-field-initializers warning hw/arm/nseries: Constify various read-only arrays hw/input/tsc2xxx: Constify set_transform()'s MouseTransformInfo arg target/arm: cleanup cpu includes target/arm: Remove unused includes from helper.c target/arm: Remove unused includes from m_helper.c target/arm: Fix checkpatch brace errors in helper.c target/arm: Fix checkpatch space errors in helper.c target/arm: Fix checkpatch comment style warnings in helper.c hw/timer/imx_epit: fix compare timer handling hw/timer/imx_epit: remove explicit fields cnt and freq hw/timer/imx_epit: factor out register write handlers ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
d365cb0b9d
|
@ -81,7 +81,7 @@ static void fsl_imx6ul_init(Object *obj)
|
|||
*/
|
||||
for (i = 0; i < FSL_IMX6UL_NUM_GPTS; i++) {
|
||||
snprintf(name, NAME_SIZE, "gpt%d", i);
|
||||
object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX7_GPT);
|
||||
object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX6UL_GPT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -219,9 +219,19 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
|||
FSL_IMX7_GPT4_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_GPTn_IRQ[FSL_IMX7_NUM_GPTS] = {
|
||||
FSL_IMX7_GPT1_IRQ,
|
||||
FSL_IMX7_GPT2_IRQ,
|
||||
FSL_IMX7_GPT3_IRQ,
|
||||
FSL_IMX7_GPT4_IRQ,
|
||||
};
|
||||
|
||||
s->gpt[i].ccm = IMX_CCM(&s->ccm);
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_GPTn_IRQ[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) {
|
||||
|
@ -235,8 +245,37 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
|||
FSL_IMX7_GPIO7_ADDR,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_GPIOn_LOW_IRQ[FSL_IMX7_NUM_GPIOS] = {
|
||||
FSL_IMX7_GPIO1_LOW_IRQ,
|
||||
FSL_IMX7_GPIO2_LOW_IRQ,
|
||||
FSL_IMX7_GPIO3_LOW_IRQ,
|
||||
FSL_IMX7_GPIO4_LOW_IRQ,
|
||||
FSL_IMX7_GPIO5_LOW_IRQ,
|
||||
FSL_IMX7_GPIO6_LOW_IRQ,
|
||||
FSL_IMX7_GPIO7_LOW_IRQ,
|
||||
};
|
||||
|
||||
static const int FSL_IMX7_GPIOn_HIGH_IRQ[FSL_IMX7_NUM_GPIOS] = {
|
||||
FSL_IMX7_GPIO1_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO2_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO3_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO4_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO5_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO6_HIGH_IRQ,
|
||||
FSL_IMX7_GPIO7_HIGH_IRQ,
|
||||
};
|
||||
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), &error_abort);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0,
|
||||
FSL_IMX7_GPIOn_ADDR[i]);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_GPIOn_LOW_IRQ[i]));
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1,
|
||||
qdev_get_gpio_in(DEVICE(&s->a7mpcore),
|
||||
FSL_IMX7_GPIOn_HIGH_IRQ[i]));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -230,13 +230,13 @@ static void n8x0_i2c_setup(struct n800_s *s)
|
|||
}
|
||||
|
||||
/* Touchscreen and keypad controller */
|
||||
static MouseTransformInfo n800_pointercal = {
|
||||
static const MouseTransformInfo n800_pointercal = {
|
||||
.x = 800,
|
||||
.y = 480,
|
||||
.a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
|
||||
};
|
||||
|
||||
static MouseTransformInfo n810_pointercal = {
|
||||
static const MouseTransformInfo n810_pointercal = {
|
||||
.x = 800,
|
||||
.y = 480,
|
||||
.a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 },
|
||||
|
@ -334,7 +334,7 @@ static void n810_key_event(void *opaque, int keycode)
|
|||
|
||||
#define M 0
|
||||
|
||||
static int n810_keys[0x80] = {
|
||||
static const int n810_keys[0x80] = {
|
||||
[0x01] = 16, /* Q */
|
||||
[0x02] = 37, /* K */
|
||||
[0x03] = 24, /* O */
|
||||
|
@ -810,7 +810,7 @@ static void n8x0_usb_setup(struct n800_s *s)
|
|||
/* Setup done before the main bootloader starts by some early setup code
|
||||
* - used when we want to run the main bootloader in emulation. This
|
||||
* isn't documented. */
|
||||
static uint32_t n800_pinout[104] = {
|
||||
static const uint32_t n800_pinout[104] = {
|
||||
0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0,
|
||||
0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808,
|
||||
0x08080808, 0x180800c4, 0x00b80000, 0x08080808,
|
||||
|
@ -1060,7 +1060,7 @@ static void n8x0_boot_init(void *opaque)
|
|||
#define OMAP_TAG_CBUS 0x4e03
|
||||
#define OMAP_TAG_EM_ASIC_BB5 0x4e04
|
||||
|
||||
static struct omap_gpiosw_info_s {
|
||||
static const struct omap_gpiosw_info_s {
|
||||
const char *name;
|
||||
int line;
|
||||
int type;
|
||||
|
@ -1078,7 +1078,7 @@ static struct omap_gpiosw_info_s {
|
|||
"headphone", N8X0_HEADPHONE_GPIO,
|
||||
OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED,
|
||||
},
|
||||
{ NULL }
|
||||
{ /* end of list */ }
|
||||
}, n810_gpiosw_info[] = {
|
||||
{
|
||||
"gps_reset", N810_GPS_RESET_GPIO,
|
||||
|
@ -1099,10 +1099,10 @@ static struct omap_gpiosw_info_s {
|
|||
"slide", N810_SLIDE_GPIO,
|
||||
OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
|
||||
},
|
||||
{ NULL }
|
||||
{ /* end of list */ }
|
||||
};
|
||||
|
||||
static struct omap_partition_info_s {
|
||||
static const struct omap_partition_info_s {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
int mask;
|
||||
|
@ -1113,27 +1113,25 @@ static struct omap_partition_info_s {
|
|||
{ 0x00080000, 0x00200000, 0x0, "kernel" },
|
||||
{ 0x00280000, 0x00200000, 0x3, "initfs" },
|
||||
{ 0x00480000, 0x0fb80000, 0x3, "rootfs" },
|
||||
|
||||
{ 0, 0, 0, NULL }
|
||||
{ /* end of list */ }
|
||||
}, n810_part_info[] = {
|
||||
{ 0x00000000, 0x00020000, 0x3, "bootloader" },
|
||||
{ 0x00020000, 0x00060000, 0x0, "config" },
|
||||
{ 0x00080000, 0x00220000, 0x0, "kernel" },
|
||||
{ 0x002a0000, 0x00400000, 0x0, "initfs" },
|
||||
{ 0x006a0000, 0x0f960000, 0x0, "rootfs" },
|
||||
|
||||
{ 0, 0, 0, NULL }
|
||||
{ /* end of list */ }
|
||||
};
|
||||
|
||||
static uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR };
|
||||
static const uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR };
|
||||
|
||||
static int n8x0_atag_setup(void *p, int model)
|
||||
{
|
||||
uint8_t *b;
|
||||
uint16_t *w;
|
||||
uint32_t *l;
|
||||
struct omap_gpiosw_info_s *gpiosw;
|
||||
struct omap_partition_info_s *partition;
|
||||
const struct omap_gpiosw_info_s *gpiosw;
|
||||
const struct omap_partition_info_s *partition;
|
||||
const char *tag;
|
||||
|
||||
w = p;
|
||||
|
|
|
@ -116,7 +116,7 @@ void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new)
|
|||
g_hash_table_insert(bs->iotlb, key, new);
|
||||
}
|
||||
|
||||
inline void smmu_iotlb_inv_all(SMMUState *s)
|
||||
void smmu_iotlb_inv_all(SMMUState *s)
|
||||
{
|
||||
trace_smmu_iotlb_inv_all();
|
||||
g_hash_table_remove_all(s->iotlb);
|
||||
|
@ -146,9 +146,8 @@ static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value,
|
|||
((entry->iova & ~info->mask) == info->iova);
|
||||
}
|
||||
|
||||
inline void
|
||||
smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages, uint8_t ttl)
|
||||
void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||
uint8_t tg, uint64_t num_pages, uint8_t ttl)
|
||||
{
|
||||
/* if tg is not set we use 4KB range invalidation */
|
||||
uint8_t granule = tg ? tg * 2 + 10 : 12;
|
||||
|
@ -174,7 +173,7 @@ smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
|||
&info);
|
||||
}
|
||||
|
||||
inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
|
||||
void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
|
||||
{
|
||||
trace_smmu_iotlb_inv_asid(asid);
|
||||
g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid);
|
||||
|
@ -374,8 +373,8 @@ error:
|
|||
*
|
||||
* return 0 on success
|
||||
*/
|
||||
inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm,
|
||||
SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm,
|
||||
SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info)
|
||||
{
|
||||
if (!cfg->aa64) {
|
||||
/*
|
||||
|
@ -483,7 +482,7 @@ static void smmu_unmap_notifier_range(IOMMUNotifier *n)
|
|||
}
|
||||
|
||||
/* Unmap all notifiers attached to @mr */
|
||||
inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr)
|
||||
static void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr)
|
||||
{
|
||||
IOMMUNotifier *n;
|
||||
|
||||
|
|
|
@ -523,7 +523,7 @@ void *tsc2005_init(qemu_irq pintdav)
|
|||
* from the touchscreen. Assuming 12-bit precision was used during
|
||||
* tslib calibration.
|
||||
*/
|
||||
void tsc2005_set_transform(void *opaque, MouseTransformInfo *info)
|
||||
void tsc2005_set_transform(void *opaque, const MouseTransformInfo *info)
|
||||
{
|
||||
TSC2005State *s = (TSC2005State *) opaque;
|
||||
|
||||
|
|
|
@ -1176,8 +1176,7 @@ I2SCodec *tsc210x_codec(uWireSlave *chip)
|
|||
* from the touchscreen. Assuming 12-bit precision was used during
|
||||
* tslib calibration.
|
||||
*/
|
||||
void tsc210x_set_transform(uWireSlave *chip,
|
||||
MouseTransformInfo *info)
|
||||
void tsc210x_set_transform(uWireSlave *chip, const MouseTransformInfo *info)
|
||||
{
|
||||
TSC210xState *s = (TSC210xState *) chip->opaque;
|
||||
#if 0
|
||||
|
|
|
@ -522,12 +522,6 @@ static uint32_t imx6ul_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
|||
case CLK_32k:
|
||||
freq = CKIL_FREQ;
|
||||
break;
|
||||
case CLK_HIGH:
|
||||
freq = CKIH_FREQ;
|
||||
break;
|
||||
case CLK_HIGH_DIV:
|
||||
freq = CKIH_FREQ / 8;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
|
||||
TYPE_IMX6UL_CCM, __func__, clock);
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#include "hw/misc/imx7_ccm.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
#define CKIH_FREQ 24000000 /* 24MHz crystal input */
|
||||
|
||||
static void imx7_analog_reset(DeviceState *dev)
|
||||
{
|
||||
IMX7AnalogState *s = IMX7_ANALOG(dev);
|
||||
|
@ -219,16 +223,43 @@ static const VMStateDescription vmstate_imx7_ccm = {
|
|||
static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
{
|
||||
/*
|
||||
* This function is "consumed" by GPT emulation code, however on
|
||||
* i.MX7 each GPT block can have their own clock root. This means
|
||||
* that this functions needs somehow to know requester's identity
|
||||
* and the way to pass it: be it via additional IMXClk constants
|
||||
* or by adding another argument to this method needs to be
|
||||
* figured out
|
||||
* This function is "consumed" by GPT emulation code. Some clocks
|
||||
* have fixed frequencies and we can provide requested frequency
|
||||
* easily. However for CCM provided clocks (like IPG) each GPT
|
||||
* timer can have its own clock root.
|
||||
* This means we need additionnal information when calling this
|
||||
* function to know the requester's identity.
|
||||
*/
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n",
|
||||
TYPE_IMX7_CCM, __func__);
|
||||
return 0;
|
||||
uint32_t freq = 0;
|
||||
|
||||
switch (clock) {
|
||||
case CLK_NONE:
|
||||
break;
|
||||
case CLK_32k:
|
||||
freq = CKIL_FREQ;
|
||||
break;
|
||||
case CLK_HIGH:
|
||||
freq = CKIH_FREQ;
|
||||
break;
|
||||
case CLK_IPG:
|
||||
case CLK_IPG_HIGH:
|
||||
/*
|
||||
* For now we don't have a way to figure out the device this
|
||||
* function is called for. Until then the IPG derived clocks
|
||||
* are left unimplemented.
|
||||
*/
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n",
|
||||
TYPE_IMX7_CCM, __func__, clock);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
|
||||
TYPE_IMX7_CCM, __func__, clock);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_ccm_clock_freq(clock, freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void imx7_ccm_class_init(ObjectClass *klass, void *data)
|
||||
|
|
|
@ -1068,9 +1068,9 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* 4 bytes for the CRC. */
|
||||
size += 4;
|
||||
crc = cpu_to_be32(crc32(~0, buf, size));
|
||||
/* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
|
||||
size += 4;
|
||||
crc_ptr = (uint8_t *) &crc;
|
||||
|
||||
/* Huge frames are truncated. */
|
||||
|
@ -1164,9 +1164,9 @@ static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* 4 bytes for the CRC. */
|
||||
size += 4;
|
||||
crc = cpu_to_be32(crc32(~0, buf, size));
|
||||
/* Increase size by 4, loop below reads the last 4 bytes from crc_ptr. */
|
||||
size += 4;
|
||||
crc_ptr = (uint8_t *) &crc;
|
||||
|
||||
if (shift16) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Originally written by Hans Jiang
|
||||
* Updated by Peter Chubb
|
||||
* Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
* Updated by Axel Heider
|
||||
*
|
||||
* This code is licensed under GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
|
@ -66,73 +67,54 @@ static const IMXClk imx_epit_clocks[] = {
|
|||
*/
|
||||
static void imx_epit_update_int(IMXEPITState *s)
|
||||
{
|
||||
if (s->sr && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) {
|
||||
if ((s->sr & SR_OCIF) && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called from within a ptimer_transaction_begin/commit block
|
||||
* for both s->timer_cmp and s->timer_reload.
|
||||
*/
|
||||
static void imx_epit_set_freq(IMXEPITState *s)
|
||||
static uint32_t imx_epit_get_freq(IMXEPITState *s)
|
||||
{
|
||||
uint32_t clksrc;
|
||||
uint32_t prescaler;
|
||||
|
||||
clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
|
||||
prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
|
||||
|
||||
s->freq = imx_ccm_get_clock_frequency(s->ccm,
|
||||
imx_epit_clocks[clksrc]) / prescaler;
|
||||
|
||||
DPRINTF("Setting ptimer frequency to %u\n", s->freq);
|
||||
|
||||
if (s->freq) {
|
||||
ptimer_set_freq(s->timer_reload, s->freq);
|
||||
ptimer_set_freq(s->timer_cmp, s->freq);
|
||||
}
|
||||
uint32_t clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, CR_CLKSRC_BITS);
|
||||
uint32_t prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, CR_PRESCALE_BITS);
|
||||
uint32_t f_in = imx_ccm_get_clock_frequency(s->ccm, imx_epit_clocks[clksrc]);
|
||||
uint32_t freq = f_in / prescaler;
|
||||
DPRINTF("ptimer frequency is %u\n", freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void imx_epit_reset(DeviceState *dev)
|
||||
/*
|
||||
* This is called both on hardware (device) reset and software reset.
|
||||
*/
|
||||
static void imx_epit_reset(IMXEPITState *s, bool is_hard_reset)
|
||||
{
|
||||
IMXEPITState *s = IMX_EPIT(dev);
|
||||
|
||||
/*
|
||||
* Soft reset doesn't touch some bits; hard reset clears them
|
||||
*/
|
||||
s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN);
|
||||
/* Soft reset doesn't touch some bits; hard reset clears them */
|
||||
if (is_hard_reset) {
|
||||
s->cr = 0;
|
||||
} else {
|
||||
s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN);
|
||||
}
|
||||
s->sr = 0;
|
||||
s->lr = EPIT_TIMER_MAX;
|
||||
s->cmp = 0;
|
||||
s->cnt = 0;
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
/* stop both timers */
|
||||
|
||||
/*
|
||||
* The reset switches off the input clock, so even if the CR.EN is still
|
||||
* set, the timers are no longer running.
|
||||
*/
|
||||
assert(imx_epit_get_freq(s) == 0);
|
||||
ptimer_stop(s->timer_cmp);
|
||||
ptimer_stop(s->timer_reload);
|
||||
/* compute new frequency */
|
||||
imx_epit_set_freq(s);
|
||||
/* init both timers to EPIT_TIMER_MAX */
|
||||
ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
|
||||
ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
|
||||
if (s->freq && (s->cr & CR_EN)) {
|
||||
/* if the timer is still enabled, restart it */
|
||||
ptimer_run(s->timer_reload, 0);
|
||||
}
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
ptimer_transaction_commit(s->timer_reload);
|
||||
}
|
||||
|
||||
static uint32_t imx_epit_update_count(IMXEPITState *s)
|
||||
{
|
||||
s->cnt = ptimer_get_count(s->timer_reload);
|
||||
|
||||
return s->cnt;
|
||||
}
|
||||
|
||||
static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IMXEPITState *s = IMX_EPIT(opaque);
|
||||
|
@ -156,8 +138,7 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
|
|||
break;
|
||||
|
||||
case 4: /* CNT */
|
||||
imx_epit_update_count(s);
|
||||
reg_value = s->cnt;
|
||||
reg_value = ptimer_get_count(s->timer_reload);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -171,144 +152,219 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
|
|||
return reg_value;
|
||||
}
|
||||
|
||||
/* Must be called from ptimer_transaction_begin/commit block for s->timer_cmp */
|
||||
static void imx_epit_reload_compare_timer(IMXEPITState *s)
|
||||
/*
|
||||
* Must be called from a ptimer_transaction_begin/commit block for
|
||||
* s->timer_cmp, but outside of a transaction block of s->timer_reload,
|
||||
* so the proper counter value is read.
|
||||
*/
|
||||
static void imx_epit_update_compare_timer(IMXEPITState *s)
|
||||
{
|
||||
if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) {
|
||||
/* if the compare feature is on and timers are running */
|
||||
uint32_t tmp = imx_epit_update_count(s);
|
||||
uint64_t next;
|
||||
if (tmp > s->cmp) {
|
||||
/* It'll fire in this round of the timer */
|
||||
next = tmp - s->cmp;
|
||||
} else { /* catch it next time around */
|
||||
next = tmp - s->cmp + ((s->cr & CR_RLD) ? EPIT_TIMER_MAX : s->lr);
|
||||
uint64_t counter = 0;
|
||||
bool is_oneshot = false;
|
||||
/*
|
||||
* The compare timer only has to run if the timer peripheral is active
|
||||
* and there is an input clock, Otherwise it can be switched off.
|
||||
*/
|
||||
bool is_active = (s->cr & CR_EN) && imx_epit_get_freq(s);
|
||||
if (is_active) {
|
||||
/*
|
||||
* Calculate next timeout for compare timer. Reading the reload
|
||||
* counter returns proper results only if pending transactions
|
||||
* on it are committed here. Otherwise stale values are be read.
|
||||
*/
|
||||
counter = ptimer_get_count(s->timer_reload);
|
||||
uint64_t limit = ptimer_get_limit(s->timer_cmp);
|
||||
/*
|
||||
* The compare timer is a periodic timer if the limit is at least
|
||||
* the compare value. Otherwise it may fire at most once in the
|
||||
* current round.
|
||||
*/
|
||||
bool is_oneshot = (limit >= s->cmp);
|
||||
if (counter >= s->cmp) {
|
||||
/* The compare timer fires in the current round. */
|
||||
counter -= s->cmp;
|
||||
} else if (!is_oneshot) {
|
||||
/*
|
||||
* The compare timer fires after a reload, as it is below the
|
||||
* compare value already in this round. Note that the counter
|
||||
* value calculated below can be above the 32-bit limit, which
|
||||
* is legal here because the compare timer is an internal
|
||||
* helper ptimer only.
|
||||
*/
|
||||
counter += limit - s->cmp;
|
||||
} else {
|
||||
/*
|
||||
* The compare timer won't fire in this round, and the limit is
|
||||
* set to a value below the compare value. This practically means
|
||||
* it will never fire, so it can be switched off.
|
||||
*/
|
||||
is_active = false;
|
||||
}
|
||||
ptimer_set_count(s->timer_cmp, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the compare timer and let it run, or stop it. This is agnostic
|
||||
* of CR.OCIEN bit, as this bit affects interrupt generation only. The
|
||||
* compare timer needs to run even if no interrupts are to be generated,
|
||||
* because the SR.OCIF bit must be updated also.
|
||||
* Note that the timer might already be stopped or be running with
|
||||
* counter values. However, finding out when an update is needed and
|
||||
* when not is not trivial. It's much easier applying the setting again,
|
||||
* as this does not harm either and the overhead is negligible.
|
||||
*/
|
||||
if (is_active) {
|
||||
ptimer_set_count(s->timer_cmp, counter);
|
||||
ptimer_run(s->timer_cmp, is_oneshot ? 1 : 0);
|
||||
} else {
|
||||
ptimer_stop(s->timer_cmp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void imx_epit_write_cr(IMXEPITState *s, uint32_t value)
|
||||
{
|
||||
uint32_t oldcr = s->cr;
|
||||
|
||||
s->cr = value & 0x03ffffff;
|
||||
|
||||
if (s->cr & CR_SWR) {
|
||||
/*
|
||||
* Reset clears CR.SWR again. It does not touch CR.EN, but the timers
|
||||
* are still stopped because the input clock is disabled.
|
||||
*/
|
||||
imx_epit_reset(s, false);
|
||||
} else {
|
||||
uint32_t freq;
|
||||
uint32_t toggled_cr_bits = oldcr ^ s->cr;
|
||||
/* re-initialize the limits if CR.RLD has changed */
|
||||
bool set_limit = toggled_cr_bits & CR_RLD;
|
||||
/* set the counter if the timer got just enabled and CR.ENMOD is set */
|
||||
bool is_switched_on = (toggled_cr_bits & s->cr) & CR_EN;
|
||||
bool set_counter = is_switched_on && (s->cr & CR_ENMOD);
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
freq = imx_epit_get_freq(s);
|
||||
if (freq) {
|
||||
ptimer_set_freq(s->timer_reload, freq);
|
||||
ptimer_set_freq(s->timer_cmp, freq);
|
||||
}
|
||||
|
||||
if (set_limit || set_counter) {
|
||||
uint64_t limit = (s->cr & CR_RLD) ? s->lr : EPIT_TIMER_MAX;
|
||||
ptimer_set_limit(s->timer_reload, limit, set_counter ? 1 : 0);
|
||||
if (set_limit) {
|
||||
ptimer_set_limit(s->timer_cmp, limit, 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If there is an input clock and the peripheral is enabled, then
|
||||
* ensure the wall clock timer is ticking. Otherwise stop the timers.
|
||||
* The compare timer will be updated later.
|
||||
*/
|
||||
if (freq && (s->cr & CR_EN)) {
|
||||
ptimer_run(s->timer_reload, 0);
|
||||
} else {
|
||||
ptimer_stop(s->timer_reload);
|
||||
}
|
||||
/* Commit changes to reload timer, so they can propagate. */
|
||||
ptimer_transaction_commit(s->timer_reload);
|
||||
/* Update compare timer based on the committed reload timer value. */
|
||||
imx_epit_update_compare_timer(s);
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The interrupt state can change due to:
|
||||
* - reset clears both SR.OCIF and CR.OCIE
|
||||
* - write to CR.EN or CR.OCIE
|
||||
*/
|
||||
imx_epit_update_int(s);
|
||||
}
|
||||
|
||||
static void imx_epit_write_sr(IMXEPITState *s, uint32_t value)
|
||||
{
|
||||
/* writing 1 to SR.OCIF clears this bit and turns the interrupt off */
|
||||
if (value & SR_OCIF) {
|
||||
s->sr = 0; /* SR.OCIF is the only bit in this register anyway */
|
||||
imx_epit_update_int(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_epit_write_lr(IMXEPITState *s, uint32_t value)
|
||||
{
|
||||
s->lr = value;
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
if (s->cr & CR_RLD) {
|
||||
/* Also set the limit if the LRD bit is set */
|
||||
/* If IOVW bit is set then set the timer value */
|
||||
ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW);
|
||||
ptimer_set_limit(s->timer_cmp, s->lr, 0);
|
||||
} else if (s->cr & CR_IOVW) {
|
||||
/* If IOVW bit is set then set the timer value */
|
||||
ptimer_set_count(s->timer_reload, s->lr);
|
||||
}
|
||||
/* Commit the changes to s->timer_reload, so they can propagate. */
|
||||
ptimer_transaction_commit(s->timer_reload);
|
||||
/* Update the compare timer based on the committed reload timer value. */
|
||||
imx_epit_update_compare_timer(s);
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
}
|
||||
|
||||
static void imx_epit_write_cmp(IMXEPITState *s, uint32_t value)
|
||||
{
|
||||
s->cmp = value;
|
||||
|
||||
/* Update the compare timer based on the committed reload timer value. */
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
imx_epit_update_compare_timer(s);
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
}
|
||||
|
||||
static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
IMXEPITState *s = IMX_EPIT(opaque);
|
||||
uint64_t oldcr;
|
||||
|
||||
DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
|
||||
(uint32_t)value);
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* CR */
|
||||
|
||||
oldcr = s->cr;
|
||||
s->cr = value & 0x03ffffff;
|
||||
if (s->cr & CR_SWR) {
|
||||
/* handle the reset */
|
||||
imx_epit_reset(DEVICE(s));
|
||||
/*
|
||||
* TODO: could we 'break' here? following operations appear
|
||||
* to duplicate the work imx_epit_reset() already did.
|
||||
*/
|
||||
}
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
|
||||
if (!(s->cr & CR_SWR)) {
|
||||
imx_epit_set_freq(s);
|
||||
}
|
||||
|
||||
if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) {
|
||||
if (s->cr & CR_ENMOD) {
|
||||
if (s->cr & CR_RLD) {
|
||||
ptimer_set_limit(s->timer_reload, s->lr, 1);
|
||||
ptimer_set_limit(s->timer_cmp, s->lr, 1);
|
||||
} else {
|
||||
ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
|
||||
ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
|
||||
}
|
||||
}
|
||||
|
||||
imx_epit_reload_compare_timer(s);
|
||||
ptimer_run(s->timer_reload, 0);
|
||||
if (s->cr & CR_OCIEN) {
|
||||
ptimer_run(s->timer_cmp, 0);
|
||||
} else {
|
||||
ptimer_stop(s->timer_cmp);
|
||||
}
|
||||
} else if (!(s->cr & CR_EN)) {
|
||||
/* stop both timers */
|
||||
ptimer_stop(s->timer_reload);
|
||||
ptimer_stop(s->timer_cmp);
|
||||
} else if (s->cr & CR_OCIEN) {
|
||||
if (!(oldcr & CR_OCIEN)) {
|
||||
imx_epit_reload_compare_timer(s);
|
||||
ptimer_run(s->timer_cmp, 0);
|
||||
}
|
||||
} else {
|
||||
ptimer_stop(s->timer_cmp);
|
||||
}
|
||||
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
ptimer_transaction_commit(s->timer_reload);
|
||||
imx_epit_write_cr(s, (uint32_t)value);
|
||||
break;
|
||||
|
||||
case 1: /* SR - ACK*/
|
||||
/* writing 1 to OCIF clear the OCIF bit */
|
||||
if (value & 0x01) {
|
||||
s->sr = 0;
|
||||
imx_epit_update_int(s);
|
||||
}
|
||||
case 1: /* SR */
|
||||
imx_epit_write_sr(s, (uint32_t)value);
|
||||
break;
|
||||
|
||||
case 2: /* LR - set ticks */
|
||||
s->lr = value;
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
if (s->cr & CR_RLD) {
|
||||
/* Also set the limit if the LRD bit is set */
|
||||
/* If IOVW bit is set then set the timer value */
|
||||
ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW);
|
||||
ptimer_set_limit(s->timer_cmp, s->lr, 0);
|
||||
} else if (s->cr & CR_IOVW) {
|
||||
/* If IOVW bit is set then set the timer value */
|
||||
ptimer_set_count(s->timer_reload, s->lr);
|
||||
}
|
||||
/*
|
||||
* Commit the change to s->timer_reload, so it can propagate. Otherwise
|
||||
* the timer interrupt may not fire properly. The commit must happen
|
||||
* before calling imx_epit_reload_compare_timer(), which reads
|
||||
* s->timer_reload internally again.
|
||||
*/
|
||||
ptimer_transaction_commit(s->timer_reload);
|
||||
imx_epit_reload_compare_timer(s);
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
case 2: /* LR */
|
||||
imx_epit_write_lr(s, (uint32_t)value);
|
||||
break;
|
||||
|
||||
case 3: /* CMP */
|
||||
s->cmp = value;
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
imx_epit_reload_compare_timer(s);
|
||||
ptimer_transaction_commit(s->timer_cmp);
|
||||
|
||||
imx_epit_write_cmp(s, (uint32_t)value);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_epit_cmp(void *opaque)
|
||||
{
|
||||
IMXEPITState *s = IMX_EPIT(opaque);
|
||||
|
||||
DPRINTF("sr was %d\n", s->sr);
|
||||
/* The cmp ptimer can't be running when the peripheral is disabled */
|
||||
assert(s->cr & CR_EN);
|
||||
|
||||
s->sr = 1;
|
||||
DPRINTF("sr was %d\n", s->sr);
|
||||
/* Set interrupt status bit SR.OCIF and update the interrupt state */
|
||||
s->sr |= SR_OCIF;
|
||||
imx_epit_update_int(s);
|
||||
}
|
||||
|
||||
|
@ -325,15 +381,13 @@ static const MemoryRegionOps imx_epit_ops = {
|
|||
|
||||
static const VMStateDescription vmstate_imx_timer_epit = {
|
||||
.name = TYPE_IMX_EPIT,
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 3,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(cr, IMXEPITState),
|
||||
VMSTATE_UINT32(sr, IMXEPITState),
|
||||
VMSTATE_UINT32(lr, IMXEPITState),
|
||||
VMSTATE_UINT32(cmp, IMXEPITState),
|
||||
VMSTATE_UINT32(cnt, IMXEPITState),
|
||||
VMSTATE_UINT32(freq, IMXEPITState),
|
||||
VMSTATE_PTIMER(timer_reload, IMXEPITState),
|
||||
VMSTATE_PTIMER(timer_cmp, IMXEPITState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
|
@ -352,17 +406,33 @@ static void imx_epit_realize(DeviceState *dev, Error **errp)
|
|||
0x00001000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
/*
|
||||
* The reload timer keeps running when the peripheral is enabled. It is a
|
||||
* kind of wall clock that does not generate any interrupts. The callback
|
||||
* needs to be provided, but it does nothing as the ptimer already supports
|
||||
* all necessary reloading functionality.
|
||||
*/
|
||||
s->timer_reload = ptimer_init(imx_epit_reload, s, PTIMER_POLICY_LEGACY);
|
||||
|
||||
/*
|
||||
* The compare timer is running only when the peripheral configuration is
|
||||
* in a state that will generate compare interrupts.
|
||||
*/
|
||||
s->timer_cmp = ptimer_init(imx_epit_cmp, s, PTIMER_POLICY_LEGACY);
|
||||
}
|
||||
|
||||
static void imx_epit_dev_reset(DeviceState *dev)
|
||||
{
|
||||
IMXEPITState *s = IMX_EPIT(dev);
|
||||
imx_epit_reset(s, true);
|
||||
}
|
||||
|
||||
static void imx_epit_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = imx_epit_realize;
|
||||
dc->reset = imx_epit_reset;
|
||||
dc->reset = imx_epit_dev_reset;
|
||||
dc->vmsd = &vmstate_imx_timer_epit;
|
||||
dc->desc = "i.MX periodic timer";
|
||||
}
|
||||
|
|
|
@ -115,6 +115,17 @@ static const IMXClk imx6_gpt_clocks[] = {
|
|||
CLK_HIGH, /* 111 reference clock */
|
||||
};
|
||||
|
||||
static const IMXClk imx6ul_gpt_clocks[] = {
|
||||
CLK_NONE, /* 000 No clock source */
|
||||
CLK_IPG, /* 001 ipg_clk, 532MHz*/
|
||||
CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */
|
||||
CLK_EXT, /* 011 External clock */
|
||||
CLK_32k, /* 100 ipg_clk_32k */
|
||||
CLK_NONE, /* 101 not defined */
|
||||
CLK_NONE, /* 110 not defined */
|
||||
CLK_NONE, /* 111 not defined */
|
||||
};
|
||||
|
||||
static const IMXClk imx7_gpt_clocks[] = {
|
||||
CLK_NONE, /* 000 No clock source */
|
||||
CLK_IPG, /* 001 ipg_clk, 532MHz*/
|
||||
|
@ -539,6 +550,13 @@ static void imx6_gpt_init(Object *obj)
|
|||
s->clocks = imx6_gpt_clocks;
|
||||
}
|
||||
|
||||
static void imx6ul_gpt_init(Object *obj)
|
||||
{
|
||||
IMXGPTState *s = IMX_GPT(obj);
|
||||
|
||||
s->clocks = imx6ul_gpt_clocks;
|
||||
}
|
||||
|
||||
static void imx7_gpt_init(Object *obj)
|
||||
{
|
||||
IMXGPTState *s = IMX_GPT(obj);
|
||||
|
@ -566,6 +584,12 @@ static const TypeInfo imx6_gpt_info = {
|
|||
.instance_init = imx6_gpt_init,
|
||||
};
|
||||
|
||||
static const TypeInfo imx6ul_gpt_info = {
|
||||
.name = TYPE_IMX6UL_GPT,
|
||||
.parent = TYPE_IMX25_GPT,
|
||||
.instance_init = imx6ul_gpt_init,
|
||||
};
|
||||
|
||||
static const TypeInfo imx7_gpt_info = {
|
||||
.name = TYPE_IMX7_GPT,
|
||||
.parent = TYPE_IMX25_GPT,
|
||||
|
@ -577,6 +601,7 @@ static void imx_gpt_register_types(void)
|
|||
type_register_static(&imx25_gpt_info);
|
||||
type_register_static(&imx31_gpt_info);
|
||||
type_register_static(&imx6_gpt_info);
|
||||
type_register_static(&imx6ul_gpt_info);
|
||||
type_register_static(&imx7_gpt_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,26 @@ enum FslIMX7IRQs {
|
|||
FSL_IMX7_USB2_IRQ = 42,
|
||||
FSL_IMX7_USB3_IRQ = 40,
|
||||
|
||||
FSL_IMX7_GPT1_IRQ = 55,
|
||||
FSL_IMX7_GPT2_IRQ = 54,
|
||||
FSL_IMX7_GPT3_IRQ = 53,
|
||||
FSL_IMX7_GPT4_IRQ = 52,
|
||||
|
||||
FSL_IMX7_GPIO1_LOW_IRQ = 64,
|
||||
FSL_IMX7_GPIO1_HIGH_IRQ = 65,
|
||||
FSL_IMX7_GPIO2_LOW_IRQ = 66,
|
||||
FSL_IMX7_GPIO2_HIGH_IRQ = 67,
|
||||
FSL_IMX7_GPIO3_LOW_IRQ = 68,
|
||||
FSL_IMX7_GPIO3_HIGH_IRQ = 69,
|
||||
FSL_IMX7_GPIO4_LOW_IRQ = 70,
|
||||
FSL_IMX7_GPIO4_HIGH_IRQ = 71,
|
||||
FSL_IMX7_GPIO5_LOW_IRQ = 72,
|
||||
FSL_IMX7_GPIO5_HIGH_IRQ = 73,
|
||||
FSL_IMX7_GPIO6_LOW_IRQ = 74,
|
||||
FSL_IMX7_GPIO6_HIGH_IRQ = 75,
|
||||
FSL_IMX7_GPIO7_LOW_IRQ = 76,
|
||||
FSL_IMX7_GPIO7_HIGH_IRQ = 77,
|
||||
|
||||
FSL_IMX7_WDOG1_IRQ = 78,
|
||||
FSL_IMX7_WDOG2_IRQ = 79,
|
||||
FSL_IMX7_WDOG3_IRQ = 10,
|
||||
|
|
|
@ -173,7 +173,4 @@ void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
|||
/* Unmap the range of all the notifiers registered to any IOMMU mr */
|
||||
void smmu_inv_notifiers_all(SMMUState *s);
|
||||
|
||||
/* Unmap the range of all the notifiers registered to @mr */
|
||||
void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr);
|
||||
|
||||
#endif /* HW_ARM_SMMU_COMMON_H */
|
||||
|
|
|
@ -30,12 +30,12 @@ uWireSlave *tsc2102_init(qemu_irq pint);
|
|||
uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav);
|
||||
I2SCodec *tsc210x_codec(uWireSlave *chip);
|
||||
uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
|
||||
void tsc210x_set_transform(uWireSlave *chip, MouseTransformInfo *info);
|
||||
void tsc210x_set_transform(uWireSlave *chip, const MouseTransformInfo *info);
|
||||
void tsc210x_key_event(uWireSlave *chip, int key, int down);
|
||||
|
||||
/* tsc2005.c */
|
||||
void *tsc2005_init(qemu_irq pintdav);
|
||||
uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
|
||||
void tsc2005_set_transform(void *opaque, MouseTransformInfo *info);
|
||||
void tsc2005_set_transform(void *opaque, const MouseTransformInfo *info);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#define CR_OCIEN (1 << 2)
|
||||
#define CR_RLD (1 << 3)
|
||||
#define CR_PRESCALE_SHIFT (4)
|
||||
#define CR_PRESCALE_MASK (0xfff)
|
||||
#define CR_PRESCALE_BITS (12)
|
||||
#define CR_SWR (1 << 16)
|
||||
#define CR_IOVW (1 << 17)
|
||||
#define CR_DBGEN (1 << 18)
|
||||
|
@ -51,7 +51,9 @@
|
|||
#define CR_DOZEN (1 << 20)
|
||||
#define CR_STOPEN (1 << 21)
|
||||
#define CR_CLKSRC_SHIFT (24)
|
||||
#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
|
||||
#define CR_CLKSRC_BITS (2)
|
||||
|
||||
#define SR_OCIF (1 << 0)
|
||||
|
||||
#define EPIT_TIMER_MAX 0XFFFFFFFFUL
|
||||
|
||||
|
@ -72,9 +74,7 @@ struct IMXEPITState {
|
|||
uint32_t sr;
|
||||
uint32_t lr;
|
||||
uint32_t cmp;
|
||||
uint32_t cnt;
|
||||
|
||||
uint32_t freq;
|
||||
qemu_irq irq;
|
||||
};
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#define TYPE_IMX25_GPT "imx25.gpt"
|
||||
#define TYPE_IMX31_GPT "imx31.gpt"
|
||||
#define TYPE_IMX6_GPT "imx6.gpt"
|
||||
#define TYPE_IMX6UL_GPT "imx6ul.gpt"
|
||||
#define TYPE_IMX7_GPT "imx7.gpt"
|
||||
|
||||
#define TYPE_IMX_GPT TYPE_IMX25_GPT
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "target/arm/idau.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "cpu.h"
|
||||
#ifdef CONFIG_TCG
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
|
@ -309,6 +308,10 @@ static void arm_cpu_reset_hold(Object *obj)
|
|||
env->cp15.cpacr_el1 = FIELD_DP64(env->cp15.cpacr_el1,
|
||||
CPACR, CP11, 3);
|
||||
#endif
|
||||
if (arm_feature(env, ARM_FEATURE_V8)) {
|
||||
env->cp15.rvbar = cpu->rvbar_prop;
|
||||
env->regs[15] = cpu->rvbar_prop;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
|
@ -487,6 +490,14 @@ static void arm_cpu_reset_hold(Object *obj)
|
|||
sizeof(*env->pmsav7.dracr) * cpu->pmsav7_dregion);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->pmsav8r_hdregion > 0) {
|
||||
memset(env->pmsav8.hprbar, 0,
|
||||
sizeof(*env->pmsav8.hprbar) * cpu->pmsav8r_hdregion);
|
||||
memset(env->pmsav8.hprlar, 0,
|
||||
sizeof(*env->pmsav8.hprlar) * cpu->pmsav8r_hdregion);
|
||||
}
|
||||
|
||||
env->pmsav7.rnr[M_REG_NS] = 0;
|
||||
env->pmsav7.rnr[M_REG_S] = 0;
|
||||
env->pmsav8.mair0[M_REG_NS] = 0;
|
||||
|
@ -1345,7 +1356,7 @@ void arm_cpu_post_init(Object *obj)
|
|||
qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property);
|
||||
}
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_V8)) {
|
||||
object_property_add_uint64_ptr(obj, "rvbar",
|
||||
&cpu->rvbar_prop,
|
||||
OBJ_PROP_FLAG_READWRITE);
|
||||
|
@ -1998,11 +2009,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||
/* MPU can be configured out of a PMSA CPU either by setting has-mpu
|
||||
* to false or by setting pmsav7-dregion to 0.
|
||||
*/
|
||||
if (!cpu->has_mpu) {
|
||||
cpu->pmsav7_dregion = 0;
|
||||
}
|
||||
if (cpu->pmsav7_dregion == 0) {
|
||||
if (!cpu->has_mpu || cpu->pmsav7_dregion == 0) {
|
||||
cpu->has_mpu = false;
|
||||
cpu->pmsav7_dregion = 0;
|
||||
cpu->pmsav8r_hdregion = 0;
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_PMSA) &&
|
||||
|
@ -2029,6 +2039,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||
env->pmsav7.dracr = g_new0(uint32_t, nr);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->pmsav8r_hdregion > 0xff) {
|
||||
error_setg(errp, "PMSAv8 MPU EL2 #regions invalid %" PRIu32,
|
||||
cpu->pmsav8r_hdregion);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->pmsav8r_hdregion) {
|
||||
env->pmsav8.hprbar = g_new0(uint32_t,
|
||||
cpu->pmsav8r_hdregion);
|
||||
env->pmsav8.hprlar = g_new0(uint32_t,
|
||||
cpu->pmsav8r_hdregion);
|
||||
}
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
|
||||
|
|
|
@ -309,6 +309,7 @@ typedef struct CPUArchState {
|
|||
};
|
||||
uint64_t sctlr_el[4];
|
||||
};
|
||||
uint64_t vsctlr; /* Virtualization System control register. */
|
||||
uint64_t cpacr_el1; /* Architectural feature access control register */
|
||||
uint64_t cptr_el[4]; /* ARMv8 feature trap registers */
|
||||
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
|
||||
|
@ -745,8 +746,11 @@ typedef struct CPUArchState {
|
|||
*/
|
||||
uint32_t *rbar[M_REG_NUM_BANKS];
|
||||
uint32_t *rlar[M_REG_NUM_BANKS];
|
||||
uint32_t *hprbar;
|
||||
uint32_t *hprlar;
|
||||
uint32_t mair0[M_REG_NUM_BANKS];
|
||||
uint32_t mair1[M_REG_NUM_BANKS];
|
||||
uint32_t hprselr;
|
||||
} pmsav8;
|
||||
|
||||
/* v8M SAU */
|
||||
|
@ -906,6 +910,8 @@ struct ArchCPU {
|
|||
bool has_mpu;
|
||||
/* PMSAv7 MPU number of supported regions */
|
||||
uint32_t pmsav7_dregion;
|
||||
/* PMSAv8 MPU number of supported hyp regions */
|
||||
uint32_t pmsav8r_hdregion;
|
||||
/* v8M SAU number of supported regions */
|
||||
uint32_t sau_sregion;
|
||||
|
||||
|
|
|
@ -21,13 +21,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#ifdef CONFIG_TCG
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#endif /* CONFIG_TCG */
|
||||
#include "qemu/module.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/loader.h"
|
||||
#endif
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "kvm_arm.h"
|
||||
|
|
|
@ -854,6 +854,47 @@ static void cortex_r5_initfn(Object *obj)
|
|||
define_arm_cp_regs(cpu, cortexr5_cp_reginfo);
|
||||
}
|
||||
|
||||
static void cortex_r52_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_EL2);
|
||||
set_feature(&cpu->env, ARM_FEATURE_PMSA);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
|
||||
cpu->midr = 0x411fd133; /* r1p3 */
|
||||
cpu->revidr = 0x00000000;
|
||||
cpu->reset_fpsid = 0x41034023;
|
||||
cpu->isar.mvfr0 = 0x10110222;
|
||||
cpu->isar.mvfr1 = 0x12111111;
|
||||
cpu->isar.mvfr2 = 0x00000043;
|
||||
cpu->ctr = 0x8144c004;
|
||||
cpu->reset_sctlr = 0x30c50838;
|
||||
cpu->isar.id_pfr0 = 0x00000131;
|
||||
cpu->isar.id_pfr1 = 0x10111001;
|
||||
cpu->isar.id_dfr0 = 0x03010006;
|
||||
cpu->id_afr0 = 0x00000000;
|
||||
cpu->isar.id_mmfr0 = 0x00211040;
|
||||
cpu->isar.id_mmfr1 = 0x40000000;
|
||||
cpu->isar.id_mmfr2 = 0x01200000;
|
||||
cpu->isar.id_mmfr3 = 0xf0102211;
|
||||
cpu->isar.id_mmfr4 = 0x00000010;
|
||||
cpu->isar.id_isar0 = 0x02101110;
|
||||
cpu->isar.id_isar1 = 0x13112111;
|
||||
cpu->isar.id_isar2 = 0x21232142;
|
||||
cpu->isar.id_isar3 = 0x01112131;
|
||||
cpu->isar.id_isar4 = 0x00010142;
|
||||
cpu->isar.id_isar5 = 0x00010001;
|
||||
cpu->isar.dbgdidr = 0x77168000;
|
||||
cpu->clidr = (1 << 27) | (1 << 24) | 0x3;
|
||||
cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */
|
||||
cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */
|
||||
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->pmsav8r_hdregion = 16;
|
||||
}
|
||||
|
||||
static void cortex_r5f_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
@ -1163,6 +1204,7 @@ static const ARMCPUInfo arm_tcg_cpus[] = {
|
|||
.class_init = arm_v7m_class_init },
|
||||
{ .name = "cortex-r5", .initfn = cortex_r5_initfn },
|
||||
{ .name = "cortex-r5f", .initfn = cortex_r5f_initfn },
|
||||
{ .name = "cortex-r52", .initfn = cortex_r52_initfn },
|
||||
{ .name = "ti925t", .initfn = ti925t_initfn },
|
||||
{ .name = "sa1100", .initfn = sa1100_initfn },
|
||||
{ .name = "sa1110", .initfn = sa1110_initfn },
|
||||
|
|
|
@ -437,6 +437,9 @@ static uint32_t arm_debug_exception_fsr(CPUARMState *env)
|
|||
|
||||
if (target_el == 2 || arm_el_is_aa64(env, target_el)) {
|
||||
using_lpae = true;
|
||||
} else if (arm_feature(env, ARM_FEATURE_PMSA) &&
|
||||
arm_feature(env, ARM_FEATURE_V8)) {
|
||||
using_lpae = true;
|
||||
} else {
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE) &&
|
||||
(env->cp15.tcr_el[target_el] & TTBCR_EAE)) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -257,6 +257,10 @@ unsigned int arm_pamax(ARMCPU *cpu);
|
|||
static inline bool extended_addresses_enabled(CPUARMState *env)
|
||||
{
|
||||
uint64_t tcr = env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1];
|
||||
if (arm_feature(env, ARM_FEATURE_PMSA) &&
|
||||
arm_feature(env, ARM_FEATURE_V8)) {
|
||||
return true;
|
||||
}
|
||||
return arm_el_is_aa64(env, 1) ||
|
||||
(arm_feature(env, ARM_FEATURE_LPAE) && (tcr & TTBCR_EAE));
|
||||
}
|
||||
|
|
|
@ -7,30 +7,14 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "target/arm/idau.h"
|
||||
#include "trace.h"
|
||||
#include "cpu.h"
|
||||
#include "internals.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include <zlib.h> /* For crc32 */
|
||||
#include "semihosting/semihost.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qapi/qapi-commands-machine-target.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#ifdef CONFIG_TCG
|
||||
#include "arm_ldst.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "semihosting/common-semi.h"
|
||||
#endif
|
||||
|
|
|
@ -487,6 +487,30 @@ static bool pmsav8_needed(void *opaque)
|
|||
arm_feature(env, ARM_FEATURE_V8);
|
||||
}
|
||||
|
||||
static bool pmsav8r_needed(void *opaque)
|
||||
{
|
||||
ARMCPU *cpu = opaque;
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
return arm_feature(env, ARM_FEATURE_PMSA) &&
|
||||
arm_feature(env, ARM_FEATURE_V8) &&
|
||||
!arm_feature(env, ARM_FEATURE_M);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pmsav8r = {
|
||||
.name = "cpu/pmsav8/pmsav8r",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = pmsav8r_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_VARRAY_UINT32(env.pmsav8.hprbar, ARMCPU,
|
||||
pmsav8r_hdregion, 0, vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_VARRAY_UINT32(env.pmsav8.hprlar, ARMCPU,
|
||||
pmsav8r_hdregion, 0, vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_pmsav8 = {
|
||||
.name = "cpu/pmsav8",
|
||||
.version_id = 1,
|
||||
|
@ -500,6 +524,10 @@ static const VMStateDescription vmstate_pmsav8 = {
|
|||
VMSTATE_UINT32(env.pmsav8.mair0[M_REG_NS], ARMCPU),
|
||||
VMSTATE_UINT32(env.pmsav8.mair1[M_REG_NS], ARMCPU),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_pmsav8r,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
|
152
target/arm/ptw.c
152
target/arm/ptw.c
|
@ -1758,9 +1758,13 @@ static bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx,
|
|||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
return env->v7m.mpu_ctrl[is_secure] & R_V7M_MPU_CTRL_PRIVDEFENA_MASK;
|
||||
} else {
|
||||
return regime_sctlr(env, mmu_idx) & SCTLR_BR;
|
||||
}
|
||||
|
||||
if (mmu_idx == ARMMMUIdx_Stage2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return regime_sctlr(env, mmu_idx) & SCTLR_BR;
|
||||
}
|
||||
|
||||
static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
||||
|
@ -1952,6 +1956,26 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
|
|||
return !(result->f.prot & (1 << access_type));
|
||||
}
|
||||
|
||||
static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||
uint32_t secure)
|
||||
{
|
||||
if (regime_el(env, mmu_idx) == 2) {
|
||||
return env->pmsav8.hprbar;
|
||||
} else {
|
||||
return env->pmsav8.rbar[secure];
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t *regime_rlar(CPUARMState *env, ARMMMUIdx mmu_idx,
|
||||
uint32_t secure)
|
||||
{
|
||||
if (regime_el(env, mmu_idx) == 2) {
|
||||
return env->pmsav8.hprlar;
|
||||
} else {
|
||||
return env->pmsav8.rlar[secure];
|
||||
}
|
||||
}
|
||||
|
||||
bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
||||
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
||||
bool secure, GetPhysAddrResult *result,
|
||||
|
@ -1974,6 +1998,13 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
bool hit = false;
|
||||
uint32_t addr_page_base = address & TARGET_PAGE_MASK;
|
||||
uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1);
|
||||
int region_counter;
|
||||
|
||||
if (regime_el(env, mmu_idx) == 2) {
|
||||
region_counter = cpu->pmsav8r_hdregion;
|
||||
} else {
|
||||
region_counter = cpu->pmsav7_dregion;
|
||||
}
|
||||
|
||||
result->f.lg_page_size = TARGET_PAGE_BITS;
|
||||
result->f.phys_addr = address;
|
||||
|
@ -1982,6 +2013,10 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
*mregion = -1;
|
||||
}
|
||||
|
||||
if (mmu_idx == ARMMMUIdx_Stage2) {
|
||||
fi->stage2 = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike the ARM ARM pseudocode, we don't need to check whether this
|
||||
* was an exception vector read from the vector table (which is always
|
||||
|
@ -1998,17 +2033,26 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
hit = true;
|
||||
}
|
||||
|
||||
for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) {
|
||||
uint32_t bitmask;
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
bitmask = 0x1f;
|
||||
} else {
|
||||
bitmask = 0x3f;
|
||||
fi->level = 0;
|
||||
}
|
||||
|
||||
for (n = region_counter - 1; n >= 0; n--) {
|
||||
/* region search */
|
||||
/*
|
||||
* Note that the base address is bits [31:5] from the register
|
||||
* with bits [4:0] all zeroes, but the limit address is bits
|
||||
* [31:5] from the register with bits [4:0] all ones.
|
||||
* Note that the base address is bits [31:x] from the register
|
||||
* with bits [x-1:0] all zeroes, but the limit address is bits
|
||||
* [31:x] from the register with bits [x:0] all ones. Where x is
|
||||
* 5 for Cortex-M and 6 for Cortex-R
|
||||
*/
|
||||
uint32_t base = env->pmsav8.rbar[secure][n] & ~0x1f;
|
||||
uint32_t limit = env->pmsav8.rlar[secure][n] | 0x1f;
|
||||
uint32_t base = regime_rbar(env, mmu_idx, secure)[n] & ~bitmask;
|
||||
uint32_t limit = regime_rlar(env, mmu_idx, secure)[n] | bitmask;
|
||||
|
||||
if (!(env->pmsav8.rlar[secure][n] & 0x1)) {
|
||||
if (!(regime_rlar(env, mmu_idx, secure)[n] & 0x1)) {
|
||||
/* Region disabled */
|
||||
continue;
|
||||
}
|
||||
|
@ -2042,7 +2086,9 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
* PMSAv7 where highest-numbered-region wins)
|
||||
*/
|
||||
fi->type = ARMFault_Permission;
|
||||
fi->level = 1;
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
fi->level = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2052,8 +2098,11 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
}
|
||||
|
||||
if (!hit) {
|
||||
/* background fault */
|
||||
fi->type = ARMFault_Background;
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
fi->type = ARMFault_Background;
|
||||
} else {
|
||||
fi->type = ARMFault_Permission;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2061,12 +2110,14 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
/* hit using the background region */
|
||||
get_phys_addr_pmsav7_default(env, mmu_idx, address, &result->f.prot);
|
||||
} else {
|
||||
uint32_t ap = extract32(env->pmsav8.rbar[secure][matchregion], 1, 2);
|
||||
uint32_t xn = extract32(env->pmsav8.rbar[secure][matchregion], 0, 1);
|
||||
uint32_t matched_rbar = regime_rbar(env, mmu_idx, secure)[matchregion];
|
||||
uint32_t matched_rlar = regime_rlar(env, mmu_idx, secure)[matchregion];
|
||||
uint32_t ap = extract32(matched_rbar, 1, 2);
|
||||
uint32_t xn = extract32(matched_rbar, 0, 1);
|
||||
bool pxn = false;
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_V8_1M)) {
|
||||
pxn = extract32(env->pmsav8.rlar[secure][matchregion], 4, 1);
|
||||
pxn = extract32(matched_rlar, 4, 1);
|
||||
}
|
||||
|
||||
if (m_is_system_region(env, address)) {
|
||||
|
@ -2074,21 +2125,46 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,
|
|||
xn = 1;
|
||||
}
|
||||
|
||||
result->f.prot = simple_ap_to_rw_prot(env, mmu_idx, ap);
|
||||
if (regime_el(env, mmu_idx) == 2) {
|
||||
result->f.prot = simple_ap_to_rw_prot_is_user(ap,
|
||||
mmu_idx != ARMMMUIdx_E2);
|
||||
} else {
|
||||
result->f.prot = simple_ap_to_rw_prot(env, mmu_idx, ap);
|
||||
}
|
||||
|
||||
if (!arm_feature(env, ARM_FEATURE_M)) {
|
||||
uint8_t attrindx = extract32(matched_rlar, 1, 3);
|
||||
uint64_t mair = env->cp15.mair_el[regime_el(env, mmu_idx)];
|
||||
uint8_t sh = extract32(matched_rlar, 3, 2);
|
||||
|
||||
if (regime_sctlr(env, mmu_idx) & SCTLR_WXN &&
|
||||
result->f.prot & PAGE_WRITE && mmu_idx != ARMMMUIdx_Stage2) {
|
||||
xn = 0x1;
|
||||
}
|
||||
|
||||
if ((regime_el(env, mmu_idx) == 1) &&
|
||||
regime_sctlr(env, mmu_idx) & SCTLR_UWXN && ap == 0x1) {
|
||||
pxn = 0x1;
|
||||
}
|
||||
|
||||
result->cacheattrs.is_s2_format = false;
|
||||
result->cacheattrs.attrs = extract64(mair, attrindx * 8, 8);
|
||||
result->cacheattrs.shareability = sh;
|
||||
}
|
||||
|
||||
if (result->f.prot && !xn && !(pxn && !is_user)) {
|
||||
result->f.prot |= PAGE_EXEC;
|
||||
}
|
||||
/*
|
||||
* We don't need to look the attribute up in the MAIR0/MAIR1
|
||||
* registers because that only tells us about cacheability.
|
||||
*/
|
||||
|
||||
if (mregion) {
|
||||
*mregion = matchregion;
|
||||
}
|
||||
}
|
||||
|
||||
fi->type = ARMFault_Permission;
|
||||
fi->level = 1;
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
fi->level = 1;
|
||||
}
|
||||
return !(result->f.prot & (1 << access_type));
|
||||
}
|
||||
|
||||
|
@ -2361,7 +2437,11 @@ static uint8_t combined_attrs_nofwb(uint64_t hcr,
|
|||
{
|
||||
uint8_t s1lo, s2lo, s1hi, s2hi, s2_mair_attrs, ret_attrs;
|
||||
|
||||
s2_mair_attrs = convert_stage2_attrs(hcr, s2.attrs);
|
||||
if (s2.is_s2_format) {
|
||||
s2_mair_attrs = convert_stage2_attrs(hcr, s2.attrs);
|
||||
} else {
|
||||
s2_mair_attrs = s2.attrs;
|
||||
}
|
||||
|
||||
s1lo = extract32(s1.attrs, 0, 4);
|
||||
s2lo = extract32(s2_mair_attrs, 0, 4);
|
||||
|
@ -2418,6 +2498,8 @@ static uint8_t force_cacheattr_nibble_wb(uint8_t attr)
|
|||
*/
|
||||
static uint8_t combined_attrs_fwb(ARMCacheAttrs s1, ARMCacheAttrs s2)
|
||||
{
|
||||
assert(s2.is_s2_format && !s1.is_s2_format);
|
||||
|
||||
switch (s2.attrs) {
|
||||
case 7:
|
||||
/* Use stage 1 attributes */
|
||||
|
@ -2467,7 +2549,7 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr,
|
|||
ARMCacheAttrs ret;
|
||||
bool tagged = false;
|
||||
|
||||
assert(s2.is_s2_format && !s1.is_s2_format);
|
||||
assert(!s1.is_s2_format);
|
||||
ret.is_s2_format = false;
|
||||
|
||||
if (s1.attrs == 0xf0) {
|
||||
|
@ -2643,7 +2725,13 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
|
|||
cacheattrs1 = result->cacheattrs;
|
||||
memset(result, 0, sizeof(*result));
|
||||
|
||||
ret = get_phys_addr_lpae(env, ptw, ipa, access_type, is_el0, result, fi);
|
||||
if (arm_feature(env, ARM_FEATURE_PMSA)) {
|
||||
ret = get_phys_addr_pmsav8(env, ipa, access_type,
|
||||
ptw->in_mmu_idx, is_secure, result, fi);
|
||||
} else {
|
||||
ret = get_phys_addr_lpae(env, ptw, ipa, access_type,
|
||||
is_el0, result, fi);
|
||||
}
|
||||
fi->s2addr = ipa;
|
||||
|
||||
/* Combine the S1 and S2 perms. */
|
||||
|
@ -2655,10 +2743,20 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
|
|||
}
|
||||
|
||||
/*
|
||||
* Use the maximum of the S1 & S2 page size, so that invalidation
|
||||
* of pages > TARGET_PAGE_SIZE works correctly.
|
||||
* If either S1 or S2 returned a result smaller than TARGET_PAGE_SIZE,
|
||||
* this means "don't put this in the TLB"; in this case, return a
|
||||
* result with lg_page_size == 0 to achieve that. Otherwise,
|
||||
* use the maximum of the S1 & S2 page size, so that invalidation
|
||||
* of pages > TARGET_PAGE_SIZE works correctly. (This works even though
|
||||
* we know the combined result permissions etc only cover the minimum
|
||||
* of the S1 and S2 page size, because we know that the common TLB code
|
||||
* never actually creates TLB entries bigger than TARGET_PAGE_SIZE,
|
||||
* and passing a larger page size value only affects invalidations.)
|
||||
*/
|
||||
if (result->f.lg_page_size < s1_lgpgsz) {
|
||||
if (result->f.lg_page_size < TARGET_PAGE_BITS ||
|
||||
s1_lgpgsz < TARGET_PAGE_BITS) {
|
||||
result->f.lg_page_size = 0;
|
||||
} else if (result->f.lg_page_size < s1_lgpgsz) {
|
||||
result->f.lg_page_size = s1_lgpgsz;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx)
|
|||
if (el == 2 || arm_el_is_aa64(env, el)) {
|
||||
return true;
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_PMSA) &&
|
||||
arm_feature(env, ARM_FEATURE_V8)) {
|
||||
return true;
|
||||
}
|
||||
if (arm_feature(env, ARM_FEATURE_LPAE)
|
||||
&& (regime_tcr(env, mmu_idx) & TTBCR_EAE)) {
|
||||
return true;
|
||||
|
|
|
@ -1184,7 +1184,7 @@ static inline void gen_hlt(DisasContext *s, int imm)
|
|||
* semihosting, to provide some semblance of security
|
||||
* (and for consistency with our 32-bit semihosting).
|
||||
*/
|
||||
if (semihosting_enabled(s->current_el != 0) &&
|
||||
if (semihosting_enabled(s->current_el == 0) &&
|
||||
(imm == (s->thumb ? 0x3c : 0xf000))) {
|
||||
gen_exception_internal_insn(s, EXCP_SEMIHOST);
|
||||
return;
|
||||
|
|
|
@ -23,7 +23,8 @@ config-cc.mak: Makefile
|
|||
$(call cc-option,-march=armv8.1-a+sve2, CROSS_CC_HAS_SVE2); \
|
||||
$(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3); \
|
||||
$(call cc-option,-mbranch-protection=standard, CROSS_CC_HAS_ARMV8_BTI); \
|
||||
$(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE)) 3> config-cc.mak
|
||||
$(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \
|
||||
$(call cc-option,-march=armv9-a+sme, CROSS_CC_HAS_ARMV9_SME)) 3> config-cc.mak
|
||||
-include config-cc.mak
|
||||
|
||||
# Pauth Tests
|
||||
|
@ -53,7 +54,11 @@ endif
|
|||
ifneq ($(CROSS_CC_HAS_SVE),)
|
||||
# System Registers Tests
|
||||
AARCH64_TESTS += sysregs
|
||||
ifneq ($(CROSS_CC_HAS_ARMV9_SME),)
|
||||
sysregs: CFLAGS+=-march=armv9-a+sme -DHAS_ARMV9_SME
|
||||
else
|
||||
sysregs: CFLAGS+=-march=armv8.1-a+sve
|
||||
endif
|
||||
|
||||
# SVE ioctl test
|
||||
AARCH64_TESTS += sve-ioctls
|
||||
|
|
|
@ -22,6 +22,13 @@
|
|||
#define HWCAP_CPUID (1 << 11)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Older assemblers don't recognize newer system register names,
|
||||
* but we can still access them by the Sn_n_Cn_Cn_n syntax.
|
||||
*/
|
||||
#define SYS_ID_AA64ISAR2_EL1 S3_0_C0_C6_2
|
||||
#define SYS_ID_AA64MMFR2_EL1 S3_0_C0_C7_2
|
||||
|
||||
int failed_bit_count;
|
||||
|
||||
/* Read and print system register `id' value */
|
||||
|
@ -112,18 +119,23 @@ int main(void)
|
|||
* minimum valid fields - for the purposes of this check allowed
|
||||
* to have non-zero values.
|
||||
*/
|
||||
get_cpu_reg_check_mask(id_aa64isar0_el1, _m(00ff,ffff,f0ff,fff0));
|
||||
get_cpu_reg_check_mask(id_aa64isar1_el1, _m(0000,00f0,ffff,ffff));
|
||||
get_cpu_reg_check_mask(id_aa64isar0_el1, _m(f0ff,ffff,f0ff,fff0));
|
||||
get_cpu_reg_check_mask(id_aa64isar1_el1, _m(00ff,f0ff,ffff,ffff));
|
||||
get_cpu_reg_check_mask(SYS_ID_AA64ISAR2_EL1, _m(0000,0000,0000,ffff));
|
||||
/* TGran4 & TGran64 as pegged to -1 */
|
||||
get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(0000,0000,ff00,0000));
|
||||
get_cpu_reg_check_zero(id_aa64mmfr1_el1);
|
||||
get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(f000,0000,ff00,0000));
|
||||
get_cpu_reg_check_mask(id_aa64mmfr1_el1, _m(0000,f000,0000,0000));
|
||||
get_cpu_reg_check_mask(SYS_ID_AA64MMFR2_EL1, _m(0000,000f,0000,0000));
|
||||
/* EL1/EL0 reported as AA64 only */
|
||||
get_cpu_reg_check_mask(id_aa64pfr0_el1, _m(000f,000f,00ff,0011));
|
||||
get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0000,00f0));
|
||||
get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0f00,0fff));
|
||||
/* all hidden, DebugVer fixed to 0x6 (ARMv8 debug architecture) */
|
||||
get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006));
|
||||
get_cpu_reg_check_zero(id_aa64dfr1_el1);
|
||||
get_cpu_reg_check_zero(id_aa64zfr0_el1);
|
||||
get_cpu_reg_check_mask(id_aa64zfr0_el1, _m(0ff0,ff0f,00ff,00ff));
|
||||
#ifdef HAS_ARMV9_SME
|
||||
get_cpu_reg_check_mask(id_aa64smfr0_el1, _m(80f1,00fd,0000,0000));
|
||||
#endif
|
||||
|
||||
get_cpu_reg_check_zero(id_aa64afr0_el1);
|
||||
get_cpu_reg_check_zero(id_aa64afr1_el1);
|
||||
|
|
Loading…
Reference in New Issue