Messages in this thread | | | Date | Wed, 16 Feb 2022 15:31:12 +0100 | Subject | Re: [PATCH v14 4/5] clocksource/drivers: Add a goldfish-timer clocksource | From | Laurent Vivier <> |
| |
Daniel,
as you reviewed the v12, could you review this version to see if I have addressed your comments?
Thanks, Laurent
Le 30/01/2022 à 15:33, Laurent Vivier a écrit : > Add a clocksource based on the goldfish-rtc device. > > Move the timer register definition to <clocksource/timer-goldfish.h> > > This kernel implementation is based on the QEMU upstream implementation: > > https://git.qemu.org/?p=qemu.git;a=blob_plain;f=hw/rtc/goldfish_rtc.c > > Details related to Goldfish devices can be found in: > > https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT > > Signed-off-by: Laurent Vivier <laurent@vivier.eu> > --- > drivers/clocksource/Kconfig | 7 ++ > drivers/clocksource/Makefile | 1 + > drivers/clocksource/timer-goldfish.c | 153 +++++++++++++++++++++++++++ > drivers/rtc/rtc-goldfish.c | 13 +-- > include/clocksource/timer-goldfish.h | 31 ++++++ > 5 files changed, 193 insertions(+), 12 deletions(-) > create mode 100644 drivers/clocksource/timer-goldfish.c > create mode 100644 include/clocksource/timer-goldfish.h > > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig > index cfb8ea0df3b1..94f00374cebb 100644 > --- a/drivers/clocksource/Kconfig > +++ b/drivers/clocksource/Kconfig > @@ -721,4 +721,11 @@ config MICROCHIP_PIT64B > modes and high resolution. It is used as a clocksource > and a clockevent. > > +config GOLDFISH_TIMER > + bool "Clocksource using goldfish-rtc" > + depends on M68K || COMPILE_TEST > + depends on RTC_DRV_GOLDFISH > + help > + Support for the timer/counter of goldfish-rtc > + > endmenu > diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile > index fa5f624eadb6..12f5d7e8cc2d 100644 > --- a/drivers/clocksource/Makefile > +++ b/drivers/clocksource/Makefile > @@ -89,3 +89,4 @@ obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o > obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o > obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o > obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o > +obj-$(CONFIG_GOLDFISH_TIMER) += timer-goldfish.o > diff --git a/drivers/clocksource/timer-goldfish.c b/drivers/clocksource/timer-goldfish.c > new file mode 100644 > index 000000000000..0512d5eabc82 > --- /dev/null > +++ b/drivers/clocksource/timer-goldfish.c > @@ -0,0 +1,153 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/interrupt.h> > +#include <linux/ioport.h> > +#include <linux/clocksource.h> > +#include <linux/clockchips.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/goldfish.h> > +#include <clocksource/timer-goldfish.h> > + > +struct goldfish_timer { > + struct clocksource cs; > + struct clock_event_device ced; > + struct resource res; > + void __iomem *base; > +}; > + > +static struct goldfish_timer *ced_to_gf(struct clock_event_device *ced) > +{ > + return container_of(ced, struct goldfish_timer, ced); > +} > + > +static struct goldfish_timer *cs_to_gf(struct clocksource *cs) > +{ > + return container_of(cs, struct goldfish_timer, cs); > +} > + > +static u64 goldfish_timer_read(struct clocksource *cs) > +{ > + struct goldfish_timer *timerdrv = cs_to_gf(cs); > + void __iomem *base = timerdrv->base; > + u32 time_low, time_high; > + u64 ticks; > + > + /* > + * time_low: get low bits of current time and update time_high > + * time_high: get high bits of time at last time_low read > + */ > + time_low = gf_ioread32(base + TIMER_TIME_LOW); > + time_high = gf_ioread32(base + TIMER_TIME_HIGH); > + > + ticks = ((u64)time_high << 32) | time_low; > + > + return ticks; > +} > + > +static int goldfish_timer_set_oneshot(struct clock_event_device *evt) > +{ > + struct goldfish_timer *timerdrv = ced_to_gf(evt); > + void __iomem *base = timerdrv->base; > + > + gf_iowrite32(0, base + TIMER_ALARM_HIGH); > + gf_iowrite32(0, base + TIMER_ALARM_LOW); > + gf_iowrite32(1, base + TIMER_IRQ_ENABLED); > + > + return 0; > +} > + > +static int goldfish_timer_shutdown(struct clock_event_device *evt) > +{ > + struct goldfish_timer *timerdrv = ced_to_gf(evt); > + void __iomem *base = timerdrv->base; > + > + gf_iowrite32(0, base + TIMER_IRQ_ENABLED); > + > + return 0; > +} > + > +static int goldfish_timer_next_event(unsigned long delta, > + struct clock_event_device *evt) > +{ > + struct goldfish_timer *timerdrv = ced_to_gf(evt); > + void __iomem *base = timerdrv->base; > + u64 now; > + > + now = goldfish_timer_read(&timerdrv->cs); > + > + now += delta; > + > + gf_iowrite32(upper_32_bits(now), base + TIMER_ALARM_HIGH); > + gf_iowrite32(lower_32_bits(now), base + TIMER_ALARM_LOW); > + > + return 0; > +} > + > +static irqreturn_t goldfish_timer_irq(int irq, void *dev_id) > +{ > + struct goldfish_timer *timerdrv = dev_id; > + struct clock_event_device *evt = &timerdrv->ced; > + void __iomem *base = timerdrv->base; > + > + gf_iowrite32(1, base + TIMER_CLEAR_INTERRUPT); > + > + evt->event_handler(evt); > + > + return IRQ_HANDLED; > +} > + > +int __init goldfish_timer_init(int irq, void __iomem *base) > +{ > + struct goldfish_timer *timerdrv; > + int ret; > + > + timerdrv = kzalloc(sizeof(*timerdrv), GFP_KERNEL); > + if (!timerdrv) > + return -ENOMEM; > + > + timerdrv->base = base; > + > + timerdrv->ced = (struct clock_event_device){ > + .name = "goldfish_timer", > + .features = CLOCK_EVT_FEAT_ONESHOT, > + .set_state_shutdown = goldfish_timer_shutdown, > + .set_state_oneshot = goldfish_timer_set_oneshot, > + .set_next_event = goldfish_timer_next_event, > + }; > + > + timerdrv->res = (struct resource){ > + .name = "goldfish_timer", > + .start = (unsigned long)base, > + .end = (unsigned long)base + 0xfff, > + }; > + > + ret = request_resource(&iomem_resource, &timerdrv->res); > + if (ret) { > + pr_err("Cannot allocate '%s' resource\n", timerdrv->res.name); > + return ret; > + } > + > + timerdrv->cs = (struct clocksource){ > + .name = "goldfish_timer", > + .rating = 400, > + .read = goldfish_timer_read, > + .mask = CLOCKSOURCE_MASK(64), > + .flags = 0, > + .max_idle_ns = LONG_MAX, > + }; > + > + clocksource_register_hz(&timerdrv->cs, NSEC_PER_SEC); > + > + ret = request_irq(irq, goldfish_timer_irq, IRQF_TIMER, > + "goldfish_timer", timerdrv); > + if (ret) { > + pr_err("Couldn't register goldfish-timer interrupt\n"); > + return ret; > + } > + > + clockevents_config_and_register(&timerdrv->ced, NSEC_PER_SEC, > + 1, 0xffffffff); > + > + return 0; > +} > diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c > index eb1929b0cbb6..59c0f38cc08d 100644 > --- a/drivers/rtc/rtc-goldfish.c > +++ b/drivers/rtc/rtc-goldfish.c > @@ -11,18 +11,7 @@ > #include <linux/platform_device.h> > #include <linux/rtc.h> > #include <linux/goldfish.h> > - > -#define TIMER_TIME_LOW 0x00 /* get low bits of current time */ > - /* and update TIMER_TIME_HIGH */ > -#define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */ > - /* TIMER_TIME_LOW read */ > -#define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */ > - /* activate it */ > -#define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */ > -#define TIMER_IRQ_ENABLED 0x10 > -#define TIMER_CLEAR_ALARM 0x14 > -#define TIMER_ALARM_STATUS 0x18 > -#define TIMER_CLEAR_INTERRUPT 0x1c > +#include <clocksource/timer-goldfish.h> > > struct goldfish_rtc { > void __iomem *base; > diff --git a/include/clocksource/timer-goldfish.h b/include/clocksource/timer-goldfish.h > new file mode 100644 > index 000000000000..d39097729b1d > --- /dev/null > +++ b/include/clocksource/timer-goldfish.h > @@ -0,0 +1,31 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * goldfish-timer clocksource > + * Registers definition for the goldfish-timer device > + */ > + > +#ifndef _CLOCKSOURCE_TIMER_GOLDFISH_H > +#define _CLOCKSOURCE_TIMER_GOLDFISH_H > + > +/* > + * TIMER_TIME_LOW get low bits of current time and update TIMER_TIME_HIGH > + * TIMER_TIME_HIGH get high bits of time at last TIMER_TIME_LOW read > + * TIMER_ALARM_LOW set low bits of alarm and activate it > + * TIMER_ALARM_HIGH set high bits of next alarm > + * TIMER_IRQ_ENABLED enable alarm interrupt > + * TIMER_CLEAR_ALARM disarm an existin alarm > + * TIMER_ALARM_STATUS alarm status (running or not) > + * TIMER_CLEAR_INTERRUPT clear interrupt > + */ > +#define TIMER_TIME_LOW 0x00 > +#define TIMER_TIME_HIGH 0x04 > +#define TIMER_ALARM_LOW 0x08 > +#define TIMER_ALARM_HIGH 0x0c > +#define TIMER_IRQ_ENABLED 0x10 > +#define TIMER_CLEAR_ALARM 0x14 > +#define TIMER_ALARM_STATUS 0x18 > +#define TIMER_CLEAR_INTERRUPT 0x1c > + > +extern int goldfish_timer_init(int irq, void __iomem *base); > + > +#endif /* _CLOCKSOURCE_TIMER_GOLDFISH_H */
| |