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:
Peter Maydell
2023-01-05 21:04:52 +00:00
30 changed files with 1330 additions and 461 deletions
+1 -1
View File
@@ -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);
}
/*
+40 -1
View File
@@ -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]));
}
/*
+13 -15
View File
@@ -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;
+7 -8
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -2
View File
@@ -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
-6
View File
@@ -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);
+40 -9
View File
@@ -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)
+4 -4
View File
@@ -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) {
+223 -153
View File
@@ -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";
}
+25
View File
@@ -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);
}
+20
View File
@@ -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,
-3
View File
@@ -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 */
+2 -2
View File
@@ -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
+4 -4
View File
@@ -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;
};
+1
View File
@@ -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
+29 -6
View File
@@ -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)) {
+6
View File
@@ -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;
-6
View File
@@ -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"
+42
View File
@@ -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 },

Some files were not shown because too many files have changed in this diff Show More