[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RE: [PATCH v1 2/3] hw/timer/aspeed: Add AST2700 Support
From: |
Jamin Lin |
Subject: |
RE: [PATCH v1 2/3] hw/timer/aspeed: Add AST2700 Support |
Date: |
Tue, 24 Dec 2024 01:22:47 +0000 |
H Cedric,
> Subject: Re: [PATCH v1 2/3] hw/timer/aspeed: Add AST2700 Support
>
> On 12/16/24 08:53, Jamin Lin wrote:
> > The timer controller include 8 sets of 32-bit decrement counters,
> > based on either PCLK or 1MHZ clock and the design of timer controller
> > between AST2600 and AST2700 are almost the same.
> >
> > The different is that the register set have a significant change in AST2700.
>
> You can drop the sentence above ^. It seems redundant.
>
> > TIMER0 – TIMER7 has their own individua control and interrupt status
> register.
>
> individual
>
> > In other words, users are able to set timer control in register TMC10
> > with different TIMER base address and clear timer control and
> > interrupt status in register TMC14 with different TIMER base address.
> >
> > Both "aspeed_timer_read" and "aspeed_timer_write" callback functions
> > are not compatible AST2700. Introduce new "aspeed_2700_timer_read" and
> > "aspeed_2700_timer_write" callback functions and
> > "aspeed_2700_timer_ops" memory region operation for AST2700. Introduce
> a new ast2700 class to support AST2700.
> >
> > The base address of TIMER0 to TIMER7 as following.
> > Base Address of Timer 0 = 0x12C1_0000
> > Base Address of Timer 1 = 0x12C1_0040
> > Base Address of Timer 2 = 0x12C1_0080
> > Base Address of Timer 3 = 0x12C1_00C0
> > Base Address of Timer 4 = 0x12C1_0100
> > Base Address of Timer 5 = 0x12C1_0140
> > Base Address of Timer 6 = 0x12C1_0180
> > Base Address of Timer 7 = 0x12C1_01C0
> >
> > The register address space of each TIMER is "0x40" , so uses the
> > following
>
> I think this change would improve reading :
>
> " , so uses" -> "and uses"
>
> > formula to get the index and register of each TIMER.
> >
> > timer_index = offset >> 6;
> > timer_offset = offset & 0x3f;
> >
> > The TMC010 is a counter control set and interrupt status register.
> > Write "1" to TMC10[3:0] will set the specific bits to "1". Introduce a
> > new "aspeed_2700_timer_set_ctrl" function to handle this register behavior.
> >
> > The TMC014 is a counter control clear and interrupt status register,
> > to clear the specific bits to "0", it should write "1" to TMC14[3:0]
> > on the same bit position. Introduce a new
> > "aspeed_2700_timer_clear_ctrl" function to handle this register behavior.
> TMC014 does not support read operation.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
>
> The rest looks OK. So,
>
> Acked-by: Cédric Le Goater <clg@redhat.com>
>
> I would prefer Andrew to take look before merging this change though.
>
Got it.
Thanks for your review, suggestion and kindly support.
Jamin
> Thanks,
>
> C.
>
>
> > ---
> > hw/timer/aspeed_timer.c | 224
> ++++++++++++++++++++++++++++++++
> > include/hw/timer/aspeed_timer.h | 1 +
> > 2 files changed, 225 insertions(+)
> >
> > diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index
> > 970bf1d79d..de4d78a0fb 100644
> > --- a/hw/timer/aspeed_timer.c
> > +++ b/hw/timer/aspeed_timer.c
> > @@ -593,6 +593,214 @@ static void
> aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
> > }
> > }
> >
> > +static void aspeed_2700_timer_set_ctrl(AspeedTimerCtrlState *s, int index,
> > + uint32_t reg) {
> > + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt);
> > + const uint8_t external_clock_mask = BIT(op_external_clock);
> > + const uint8_t pulse_enable_mask = BIT(op_pulse_enable);
> > + const uint8_t enable_mask = BIT(op_enable);
> > + AspeedTimer *t;
> > + uint8_t t_old;
> > + uint8_t t_new;
> > + int shift;
> > +
> > + /*
> > + * Only 1 will set the specific bits to 1
> > + * Handle a dependency between the 'enable' and remaining three
> > + * configuration bits - i.e. if more than one bit in the control set
> > has
> > + * set, including the 'enable' bit, perform configuration and then
> > + * enable the timer.
> > + * Interrupt Status bit should not be set.
> > + */
> > +
> > + t = &s->timers[index];
> > + shift = index * TIMER_CTRL_BITS;
> > +
> > + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
> > + t_new = reg & TIMER_CTRL_MASK;
> > +
> > + if (!(t_old & external_clock_mask) &&
> > + (t_new & external_clock_mask)) {
> > + aspeed_timer_ctrl_external_clock(t, true);
> > + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 1);
> > + }
> > +
> > + if (!(t_old & overflow_interrupt_mask) &&
> > + (t_new & overflow_interrupt_mask)) {
> > + aspeed_timer_ctrl_overflow_interrupt(t, true);
> > + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 1);
> > + }
> > +
> > +
> > + if (!(t_old & pulse_enable_mask) &&
> > + (t_new & pulse_enable_mask)) {
> > + aspeed_timer_ctrl_pulse_enable(t, true);
> > + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 1);
> > + }
> > +
> > + /* If we are enabling, do so last */
> > + if (!(t_old & enable_mask) &&
> > + (t_new & enable_mask)) {
> > + aspeed_timer_ctrl_enable(t, true);
> > + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 1);
> > + }
> > +}
> > +
> > +static void aspeed_2700_timer_clear_ctrl(AspeedTimerCtrlState *s, int
> index,
> > + uint32_t reg) {
> > + const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt);
> > + const uint8_t external_clock_mask = BIT(op_external_clock);
> > + const uint8_t pulse_enable_mask = BIT(op_pulse_enable);
> > + const uint8_t enable_mask = BIT(op_enable);
> > + AspeedTimer *t;
> > + uint8_t t_old;
> > + uint8_t t_new;
> > + int shift;
> > +
> > + /*
> > + * Only 1 will clear the specific bits to 0
> > + * Handle a dependency between the 'enable' and remaining three
> > + * configuration bits - i.e. if more than one bit in the control set
> > has
> > + * clear, including the 'enable' bit, then disable the timer and
> > perform
> > + * configuration
> > + */
> > +
> > + t = &s->timers[index];
> > + shift = index * TIMER_CTRL_BITS;
> > +
> > + t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
> > + t_new = reg & TIMER_CTRL_MASK;
> > +
> > + /* If we are disabling, do so first */
> > + if ((t_old & enable_mask) &&
> > + (t_new & enable_mask)) {
> > + aspeed_timer_ctrl_enable(t, false);
> > + s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 0);
> > + }
> > +
> > + if ((t_old & external_clock_mask) &&
> > + (t_new & external_clock_mask)) {
> > + aspeed_timer_ctrl_external_clock(t, false);
> > + s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 0);
> > + }
> > +
> > + if ((t_old & overflow_interrupt_mask) &&
> > + (t_new & overflow_interrupt_mask)) {
> > + aspeed_timer_ctrl_overflow_interrupt(t, false);
> > + s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 0);
> > + }
> > +
> > + if ((t_old & pulse_enable_mask) &&
> > + (t_new & pulse_enable_mask)) {
> > + aspeed_timer_ctrl_pulse_enable(t, false);
> > + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 0);
> > + }
> > +
> > + if ((t_old & pulse_enable_mask) &&
> > + (t_new & pulse_enable_mask)) {
> > + aspeed_timer_ctrl_pulse_enable(t, false);
> > + s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 0);
> > + }
> > +
> > + /* Clear interrupt status */
> > + if (reg & 0x10000) {
> > + s->irq_sts = deposit32(s->irq_sts, index, 1, 0);
> > + }
> > +}
> > +
> > +static uint64_t aspeed_2700_timer_read(void *opaque, hwaddr offset,
> > + uint32_t size) {
> > + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque);
> > + uint32_t timer_offset = offset & 0x3f;
> > + int timer_index = offset >> 6;
> > + uint64_t value = 0;
> > +
> > + if (timer_index >= ASPEED_TIMER_NR_TIMERS) {
> > + qemu_log_mask(LOG_GUEST_ERROR,
> > + "%s: offset 0x%" PRIx64 " out of bounds\n",
> > + __func__, offset);
> > + return 0;
> > + }
> > +
> > + switch (timer_offset) {
> > + /*
> > + * Counter Status
> > + * Counter Reload
> > + * Counter First Matching
> > + * Counter Second Matching
> > + */
> > + case 0x00 ... 0x0C:
> > + value = aspeed_timer_get_value(&s->timers[timer_index],
> > + timer_offset >> 2);
> > + break;
> > + /* Counter Control and Interrupt Status */
> > + case 0x10:
> > + value = deposit64(value, 0, 4,
> > + extract32(s->ctrl, timer_index * 4, 4));
> > + value = deposit64(value, 16, 1,
> > + extract32(s->irq_sts, timer_index, 1));
> > + break;
> > + default:
> > + qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset
> 0x%"
> > + PRIx64"\n", __func__, offset);
> > + return 0;
> > + }
> > + trace_aspeed_timer_read(offset, size, value);
> > + return value;
> > +}
> > +
> > +static void aspeed_2700_timer_write(void *opaque, hwaddr offset,
> > + uint64_t value, uint32_t size) {
> > + const uint32_t timer_value = (uint32_t)(value & 0xFFFFFFFF);
> > + AspeedTimerCtrlState *s = ASPEED_TIMER(opaque);
> > + uint32_t timer_offset = offset & 0x3f;
> > + int timer_index = offset >> 6;
> > +
> > + if (timer_index >= ASPEED_TIMER_NR_TIMERS) {
> > + qemu_log_mask(LOG_GUEST_ERROR,
> > + "%s: offset 0x%" PRIx64 " out of bounds\n",
> > + __func__, offset);
> > + }
> > +
> > + switch (timer_offset) {
> > + /*
> > + * Counter Status
> > + * Counter Reload
> > + * Counter First Matching
> > + * Counter Second Matching
> > + */
> > + case 0x00 ... 0x0C:
> > + aspeed_timer_set_value(s, timer_index, timer_offset >> 2,
> > + timer_value);
> > + break;
> > + /* Counter Control Set and Interrupt Status */
> > + case 0x10:
> > + aspeed_2700_timer_set_ctrl(s, timer_index, timer_value);
> > + break;
> > + /* Counter Control Clear and Interrupr Status */
> > + case 0x14:
> > + aspeed_2700_timer_clear_ctrl(s, timer_index, timer_value);
> > + break;
> > + default:
> > + qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset
> 0x%"
> > + PRIx64"\n", __func__, offset);
> > + break;
> > + }
> > +}
> > +
> > +static const MemoryRegionOps aspeed_2700_timer_ops = {
> > + .read = aspeed_2700_timer_read,
> > + .write = aspeed_2700_timer_write,
> > + .endianness = DEVICE_LITTLE_ENDIAN,
> > + .valid.min_access_size = 4,
> > + .valid.max_access_size = 4,
> > + .valid.unaligned = false,
> > +};
> > +
> > static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id)
> > {
> > AspeedTimer *t = &s->timers[id]; @@ -769,6 +977,21 @@ static
> > const TypeInfo aspeed_1030_timer_info = {
> > .class_init = aspeed_1030_timer_class_init,
> > };
> >
> > +static void aspeed_2700_timer_class_init(ObjectClass *klass, void
> > +*data) {
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > + AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
> > +
> > + dc->desc = "ASPEED 2700 Timer";
> > + awc->reg_ops = &aspeed_2700_timer_ops; }
> > +
> > +static const TypeInfo aspeed_2700_timer_info = {
> > + .name = TYPE_ASPEED_2700_TIMER,
> > + .parent = TYPE_ASPEED_TIMER,
> > + .class_init = aspeed_2700_timer_class_init, };
> > +
> > static void aspeed_timer_register_types(void)
> > {
> > type_register_static(&aspeed_timer_info);
> > @@ -776,6 +999,7 @@ static void aspeed_timer_register_types(void)
> > type_register_static(&aspeed_2500_timer_info);
> > type_register_static(&aspeed_2600_timer_info);
> > type_register_static(&aspeed_1030_timer_info);
> > + type_register_static(&aspeed_2700_timer_info);
> > }
> >
> > type_init(aspeed_timer_register_types)
> > diff --git a/include/hw/timer/aspeed_timer.h
> > b/include/hw/timer/aspeed_timer.h index 8d0b15f055..2247dc20ab 100644
> > --- a/include/hw/timer/aspeed_timer.h
> > +++ b/include/hw/timer/aspeed_timer.h
> > @@ -32,6 +32,7 @@ OBJECT_DECLARE_TYPE(AspeedTimerCtrlState,
> AspeedTimerClass, ASPEED_TIMER)
> > #define TYPE_ASPEED_2500_TIMER TYPE_ASPEED_TIMER "-ast2500"
> > #define TYPE_ASPEED_2600_TIMER TYPE_ASPEED_TIMER "-ast2600"
> > #define TYPE_ASPEED_1030_TIMER TYPE_ASPEED_TIMER "-ast1030"
> > +#define TYPE_ASPEED_2700_TIMER TYPE_ASPEED_TIMER "-ast2700"
> >
> > #define ASPEED_TIMER_NR_TIMERS 8
> >