diff options
Diffstat (limited to 'drivers/clocksource')
| -rw-r--r-- | drivers/clocksource/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/clocksource/acpi_pm.c | 6 | ||||
| -rw-r--r-- | drivers/clocksource/arm_arch_timer.c | 15 | ||||
| -rw-r--r-- | drivers/clocksource/em_sti.c | 7 | ||||
| -rw-r--r-- | drivers/clocksource/hyperv_timer.c | 61 | ||||
| -rw-r--r-- | drivers/clocksource/ingenic-ost.c | 10 | ||||
| -rw-r--r-- | drivers/clocksource/sh_cmt.c | 95 | ||||
| -rw-r--r-- | drivers/clocksource/sh_tmu.c | 7 | ||||
| -rw-r--r-- | drivers/clocksource/timer-microchip-pit64b.c | 12 | ||||
| -rw-r--r-- | drivers/clocksource/timer-npcm7xx.c | 10 | ||||
| -rw-r--r-- | drivers/clocksource/timer-riscv.c | 27 | ||||
| -rw-r--r-- | drivers/clocksource/timer-sp804.c | 6 | ||||
| -rw-r--r-- | drivers/clocksource/timer-sun4i.c | 3 | ||||
| -rw-r--r-- | drivers/clocksource/timer-ti-dm-systimer.c | 4 | ||||
| -rw-r--r-- | drivers/clocksource/timer-ti-dm.c | 21 |
15 files changed, 193 insertions, 95 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 4469e7f555e9..5fc8f0e7fb38 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -441,7 +441,7 @@ config CLKSRC_EXYNOS_MCT config CLKSRC_SAMSUNG_PWM bool "PWM timer driver for Samsung S3C, S5P" if COMPILE_TEST depends on HAS_IOMEM - depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || COMPILE_TEST + depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || COMPILE_TEST help This is a new clocksource driver for the PWM timer found in Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver @@ -706,7 +706,7 @@ config INGENIC_OST config MICROCHIP_PIT64B bool "Microchip PIT64B support" - depends on OF || COMPILE_TEST + depends on OF && ARM select TIMER_OF help This option enables Microchip PIT64B timer for Atmel diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index 279ddff81ab4..82338773602c 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -23,6 +23,7 @@ #include <linux/pci.h> #include <linux/delay.h> #include <asm/io.h> +#include <asm/time.h> /* * The I/O port the PMTMR resides at. @@ -210,8 +211,9 @@ static int __init init_acpi_pm_clocksource(void) return -ENODEV; } - return clocksource_register_hz(&clocksource_acpi_pm, - PMTMR_TICKS_PER_SEC); + if (tsc_clocksource_watchdog_disabled()) + clocksource_acpi_pm.flags |= CLOCK_SOURCE_MUST_VERIFY; + return clocksource_register_hz(&clocksource_acpi_pm, PMTMR_TICKS_PER_SEC); } /* We use fs_initcall because we want the PCI fixups to have run diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 933bb960490d..e09d4427f604 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -18,6 +18,7 @@ #include <linux/clocksource.h> #include <linux/clocksource_ids.h> #include <linux/interrupt.h> +#include <linux/kstrtox.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/io.h> @@ -97,7 +98,7 @@ static bool evtstrm_enable __ro_after_init = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EV static int __init early_evtstrm_cfg(char *buf) { - return strtobool(buf, &evtstrm_enable); + return kstrtobool(buf, &evtstrm_enable); } early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg); @@ -687,8 +688,8 @@ static irqreturn_t arch_timer_handler_virt_mem(int irq, void *dev_id) return timer_handler(ARCH_TIMER_MEM_VIRT_ACCESS, evt); } -static __always_inline int timer_shutdown(const int access, - struct clock_event_device *clk) +static __always_inline int arch_timer_shutdown(const int access, + struct clock_event_device *clk) { unsigned long ctrl; @@ -701,22 +702,22 @@ static __always_inline int timer_shutdown(const int access, static int arch_timer_shutdown_virt(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_VIRT_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_VIRT_ACCESS, clk); } static int arch_timer_shutdown_phys(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk); } static int arch_timer_shutdown_virt_mem(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk); } static int arch_timer_shutdown_phys_mem(struct clock_event_device *clk) { - return timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk); + return arch_timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk); } static __always_inline void set_next_event(const int access, unsigned long evt, diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index ab190dffb1ed..c04b47bd4868 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -333,11 +333,6 @@ static int em_sti_probe(struct platform_device *pdev) return 0; } -static int em_sti_remove(struct platform_device *pdev) -{ - return -EBUSY; /* cannot unregister clockevent and clocksource */ -} - static const struct of_device_id em_sti_dt_ids[] = { { .compatible = "renesas,em-sti", }, {}, @@ -346,10 +341,10 @@ MODULE_DEVICE_TABLE(of, em_sti_dt_ids); static struct platform_driver em_sti_device_driver = { .probe = em_sti_probe, - .remove = em_sti_remove, .driver = { .name = "em_sti", .of_match_table = em_sti_dt_ids, + .suppress_bind_attrs = true, } }; diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 18de1f439ffd..c0cef92b12b8 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -367,9 +367,18 @@ static union { u8 reserved[PAGE_SIZE]; } tsc_pg __aligned(PAGE_SIZE); +static struct ms_hyperv_tsc_page *tsc_page = &tsc_pg.page; +static unsigned long tsc_pfn; + +unsigned long hv_get_tsc_pfn(void) +{ + return tsc_pfn; +} +EXPORT_SYMBOL_GPL(hv_get_tsc_pfn); + struct ms_hyperv_tsc_page *hv_get_tsc_page(void) { - return &tsc_pg.page; + return tsc_page; } EXPORT_SYMBOL_GPL(hv_get_tsc_page); @@ -407,13 +416,12 @@ static void suspend_hv_clock_tsc(struct clocksource *arg) static void resume_hv_clock_tsc(struct clocksource *arg) { - phys_addr_t phys_addr = virt_to_phys(&tsc_pg); union hv_reference_tsc_msr tsc_msr; /* Re-enable the TSC page */ tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC); tsc_msr.enable = 1; - tsc_msr.pfn = HVPFN_DOWN(phys_addr); + tsc_msr.pfn = tsc_pfn; hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64); } @@ -497,14 +505,10 @@ static __always_inline void hv_setup_sched_clock(void *sched_clock) {} static bool __init hv_init_tsc_clocksource(void) { union hv_reference_tsc_msr tsc_msr; - phys_addr_t phys_addr; if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE)) return false; - if (hv_root_partition) - return false; - /* * If Hyper-V offers TSC_INVARIANT, then the virtualized TSC correctly * handles frequency and offset changes due to live migration, @@ -522,18 +526,30 @@ static bool __init hv_init_tsc_clocksource(void) } hv_read_reference_counter = read_hv_clock_tsc; - phys_addr = virt_to_phys(hv_get_tsc_page()); /* - * The Hyper-V TLFS specifies to preserve the value of reserved - * bits in registers. So read the existing value, preserve the - * low order 12 bits, and add in the guest physical address - * (which already has at least the low 12 bits set to zero since - * it is page aligned). Also set the "enable" bit, which is bit 0. + * TSC page mapping works differently in root compared to guest. + * - In guest partition the guest PFN has to be passed to the + * hypervisor. + * - In root partition it's other way around: it has to map the PFN + * provided by the hypervisor. + * But it can't be mapped right here as it's too early and MMU isn't + * ready yet. So, we only set the enable bit here and will remap the + * page later in hv_remap_tsc_clocksource(). + * + * It worth mentioning, that TSC clocksource read function + * (read_hv_clock_tsc) has a MSR-based fallback mechanism, used when + * TSC page is zeroed (which is the case until the PFN is remapped) and + * thus TSC clocksource will work even without the real TSC page + * mapped. */ tsc_msr.as_uint64 = hv_get_register(HV_REGISTER_REFERENCE_TSC); + if (hv_root_partition) + tsc_pfn = tsc_msr.pfn; + else + tsc_pfn = HVPFN_DOWN(virt_to_phys(tsc_page)); tsc_msr.enable = 1; - tsc_msr.pfn = HVPFN_DOWN(phys_addr); + tsc_msr.pfn = tsc_pfn; hv_set_register(HV_REGISTER_REFERENCE_TSC, tsc_msr.as_uint64); clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); @@ -566,3 +582,20 @@ void __init hv_init_clocksource(void) hv_sched_clock_offset = hv_read_reference_counter(); hv_setup_sched_clock(read_hv_sched_clock_msr); } + +void __init hv_remap_tsc_clocksource(void) +{ + if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE)) + return; + + if (!hv_root_partition) { + WARN(1, "%s: attempt to remap TSC page in guest partition\n", + __func__); + return; + } + + tsc_page = memremap(tsc_pfn << HV_HYP_PAGE_SHIFT, sizeof(tsc_pg), + MEMREMAP_WB); + if (!tsc_page) + pr_err("Failed to remap Hyper-V TSC page.\n"); +} diff --git a/drivers/clocksource/ingenic-ost.c b/drivers/clocksource/ingenic-ost.c index 06d25754e606..9f7c280a1336 100644 --- a/drivers/clocksource/ingenic-ost.c +++ b/drivers/clocksource/ingenic-ost.c @@ -141,7 +141,7 @@ static int __init ingenic_ost_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused ingenic_ost_suspend(struct device *dev) +static int ingenic_ost_suspend(struct device *dev) { struct ingenic_ost *ost = dev_get_drvdata(dev); @@ -150,14 +150,14 @@ static int __maybe_unused ingenic_ost_suspend(struct device *dev) return 0; } -static int __maybe_unused ingenic_ost_resume(struct device *dev) +static int ingenic_ost_resume(struct device *dev) { struct ingenic_ost *ost = dev_get_drvdata(dev); return clk_enable(ost->clk); } -static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = { +static const struct dev_pm_ops ingenic_ost_pm_ops = { /* _noirq: We want the OST clock to be gated last / ungated first */ .suspend_noirq = ingenic_ost_suspend, .resume_noirq = ingenic_ost_resume, @@ -181,9 +181,7 @@ static const struct of_device_id ingenic_ost_of_match[] = { static struct platform_driver ingenic_ost_driver = { .driver = { .name = "ingenic-ost", -#ifdef CONFIG_PM_SUSPEND - .pm = &ingenic_ost_pm_ops, -#endif + .pm = pm_sleep_ptr(&ingenic_ost_pm_ops), .of_match_table = ingenic_ost_of_match, }, }; diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 64dcb082d4cf..8b2e079d9df2 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/ioport.h> #include <linux/irq.h> #include <linux/module.h> @@ -116,6 +117,7 @@ struct sh_cmt_device { void __iomem *mapbase; struct clk *clk; unsigned long rate; + unsigned int reg_delay; raw_spinlock_t lock; /* Protect the shared start/stop register */ @@ -247,10 +249,17 @@ static inline u32 sh_cmt_read_cmstr(struct sh_cmt_channel *ch) static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, u32 value) { - if (ch->iostart) - ch->cmt->info->write_control(ch->iostart, 0, value); - else - ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); + u32 old_value = sh_cmt_read_cmstr(ch); + + if (value != old_value) { + if (ch->iostart) { + ch->cmt->info->write_control(ch->iostart, 0, value); + udelay(ch->cmt->reg_delay); + } else { + ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); + udelay(ch->cmt->reg_delay); + } + } } static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) @@ -260,7 +269,12 @@ static inline u32 sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, u32 value) { - ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); + u32 old_value = sh_cmt_read_cmcsr(ch); + + if (value != old_value) { + ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); + udelay(ch->cmt->reg_delay); + } } static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) @@ -268,14 +282,33 @@ static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) return ch->cmt->info->read_count(ch->ioctrl, CMCNT); } -static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value) +static inline int sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value) { + /* Tests showed that we need to wait 3 clocks here */ + unsigned int cmcnt_delay = DIV_ROUND_UP(3 * ch->cmt->reg_delay, 2); + u32 reg; + + if (ch->cmt->info->model > SH_CMT_16BIT) { + int ret = read_poll_timeout_atomic(sh_cmt_read_cmcsr, reg, + !(reg & SH_CMT32_CMCSR_WRFLG), + 1, cmcnt_delay, false, ch); + if (ret < 0) + return ret; + } + ch->cmt->info->write_count(ch->ioctrl, CMCNT, value); + udelay(cmcnt_delay); + return 0; } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, u32 value) { - ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); + u32 old_value = ch->cmt->info->read_count(ch->ioctrl, CMCOR); + + if (value != old_value) { + ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); + udelay(ch->cmt->reg_delay); + } } static u32 sh_cmt_get_counter(struct sh_cmt_channel *ch, u32 *has_wrapped) @@ -319,7 +352,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) static int sh_cmt_enable(struct sh_cmt_channel *ch) { - int k, ret; + int ret; dev_pm_syscore_device(&ch->cmt->pdev->dev, true); @@ -347,26 +380,9 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch) } sh_cmt_write_cmcor(ch, 0xffffffff); - sh_cmt_write_cmcnt(ch, 0); + ret = sh_cmt_write_cmcnt(ch, 0); - /* - * According to the sh73a0 user's manual, as CMCNT can be operated - * only by the RCLK (Pseudo 32 kHz), there's one restriction on - * modifying CMCNT register; two RCLK cycles are necessary before - * this register is either read or any modification of the value - * it holds is reflected in the LSI's actual operation. - * - * While at it, we're supposed to clear out the CMCNT as of this - * moment, so make sure it's processed properly here. This will - * take RCLKx2 at maximum. - */ - for (k = 0; k < 100; k++) { - if (!sh_cmt_read_cmcnt(ch)) - break; - udelay(1); - } - - if (sh_cmt_read_cmcnt(ch)) { + if (ret || sh_cmt_read_cmcnt(ch)) { dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n", ch->index); ret = -ETIMEDOUT; @@ -995,8 +1011,8 @@ MODULE_DEVICE_TABLE(of, sh_cmt_of_table); static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) { - unsigned int mask; - unsigned int i; + unsigned int mask, i; + unsigned long rate; int ret; cmt->pdev = pdev; @@ -1032,10 +1048,16 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) if (ret < 0) goto err_clk_unprepare; - if (cmt->info->width == 16) - cmt->rate = clk_get_rate(cmt->clk) / 512; - else - cmt->rate = clk_get_rate(cmt->clk) / 8; + rate = clk_get_rate(cmt->clk); + if (!rate) { + ret = -EINVAL; + goto err_clk_disable; + } + + /* We shall wait 2 input clks after register writes */ + if (cmt->info->model >= SH_CMT_48BIT) + cmt->reg_delay = DIV_ROUND_UP(2UL * USEC_PER_SEC, rate); + cmt->rate = rate / (cmt->info->width == 16 ? 512 : 8); /* Map the memory resource(s). */ ret = sh_cmt_map_memory(cmt); @@ -1123,17 +1145,12 @@ static int sh_cmt_probe(struct platform_device *pdev) return 0; } -static int sh_cmt_remove(struct platform_device *pdev) -{ - return -EBUSY; /* cannot unregister clockevent and clocksource */ -} - static struct platform_driver sh_cmt_device_driver = { .probe = sh_cmt_probe, - .remove = sh_cmt_remove, .driver = { .name = "sh_cmt", .of_match_table = of_match_ptr(sh_cmt_of_table), + .suppress_bind_attrs = true, }, .id_table = sh_cmt_id_table, }; diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index b00dec0655cb..932f31a7c5be 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -632,11 +632,6 @@ static int sh_tmu_probe(struct platform_device *pdev) return 0; } -static int sh_tmu_remove(struct platform_device *pdev) -{ - return -EBUSY; /* cannot unregister clockevent and clocksource */ -} - static const struct platform_device_id sh_tmu_id_table[] = { { "sh-tmu", SH_TMU }, { "sh-tmu-sh3", SH_TMU_SH3 }, @@ -652,10 +647,10 @@ MODULE_DEVICE_TABLE(of, sh_tmu_of_table); static struct platform_driver sh_tmu_device_driver = { .probe = sh_tmu_probe, - .remove = sh_tmu_remove, .driver = { .name = "sh_tmu", .of_match_table = of_match_ptr(sh_tmu_of_table), + .suppress_bind_attrs = true, }, .id_table = sh_tmu_id_table, }; diff --git a/drivers/clocksource/timer-microchip-pit64b.c b/drivers/clocksource/timer-microchip-pit64b.c index d5f1436f33d9..57209bb38c70 100644 --- a/drivers/clocksource/timer-microchip-pit64b.c +++ b/drivers/clocksource/timer-microchip-pit64b.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/clockchips.h> +#include <linux/delay.h> #include <linux/interrupt.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -92,6 +93,8 @@ struct mchp_pit64b_clksrc { static void __iomem *mchp_pit64b_cs_base; /* Default cycles for clockevent timer. */ static u64 mchp_pit64b_ce_cycles; +/* Delay timer. */ +static struct delay_timer mchp_pit64b_dt; static inline u64 mchp_pit64b_cnt_read(void __iomem *base) { @@ -169,6 +172,11 @@ static u64 notrace mchp_pit64b_sched_read_clk(void) return mchp_pit64b_cnt_read(mchp_pit64b_cs_base); } +static unsigned long notrace mchp_pit64b_dt_read(void) +{ + return mchp_pit64b_cnt_read(mchp_pit64b_cs_base); +} + static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev) { struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); @@ -376,6 +384,10 @@ static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer, sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate); + mchp_pit64b_dt.read_current_timer = mchp_pit64b_dt_read; + mchp_pit64b_dt.freq = clk_rate; + register_current_timer_delay(&mchp_pit64b_dt); + return 0; } diff --git a/drivers/clocksource/timer-npcm7xx.c b/drivers/clocksource/timer-npcm7xx.c index a00520cbb660..9af30af5f989 100644 --- a/drivers/clocksource/timer-npcm7xx.c +++ b/drivers/clocksource/timer-npcm7xx.c @@ -188,6 +188,7 @@ static void __init npcm7xx_clocksource_init(void) static int __init npcm7xx_timer_init(struct device_node *np) { + struct clk *clk; int ret; ret = timer_of_init(np, &npcm7xx_to); @@ -199,6 +200,15 @@ static int __init npcm7xx_timer_init(struct device_node *np) npcm7xx_to.of_clk.rate = npcm7xx_to.of_clk.rate / (NPCM7XX_Tx_MIN_PRESCALE + 1); + /* Enable the clock for timer1, if it exists */ + clk = of_clk_get(np, 1); + if (clk) { + if (!IS_ERR(clk)) + clk_prepare_enable(clk); + else + pr_warn("%pOF: Failed to get clock for timer1: %pe", np, clk); + } + npcm7xx_clocksource_init(); npcm7xx_clockevents_init(); diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index a0d66fabf073..5f0f10c7e222 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -28,6 +28,7 @@ #include <asm/timex.h> static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available); +static bool riscv_timer_cannot_wake_cpu; static int riscv_clock_next_event(unsigned long delta, struct clock_event_device *ce) @@ -73,10 +74,15 @@ static u64 notrace riscv_sched_clock(void) static struct clocksource riscv_clocksource = { .name = "riscv_clocksource", - .rating = 300, + .rating = 400, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, .read = riscv_clocksource_rdtime, +#if IS_ENABLED(CONFIG_GENERIC_GETTIMEOFDAY) + .vdso_clock_mode = VDSO_CLOCKMODE_ARCHTIMER, +#else + .vdso_clock_mode = VDSO_CLOCKMODE_NONE, +#endif }; static int riscv_timer_starting_cpu(unsigned int cpu) @@ -85,6 +91,8 @@ static int riscv_timer_starting_cpu(unsigned int cpu) ce->cpumask = cpumask_of(cpu); ce->irq = riscv_clock_event_irq; + if (riscv_timer_cannot_wake_cpu) + ce->features |= CLOCK_EVT_FEAT_C3STOP; clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff); enable_percpu_irq(riscv_clock_event_irq, @@ -139,6 +147,13 @@ static int __init riscv_timer_init_dt(struct device_node *n) if (cpuid != smp_processor_id()) return 0; + child = of_find_compatible_node(NULL, NULL, "riscv,timer"); + if (child) { + riscv_timer_cannot_wake_cpu = of_property_read_bool(child, + "riscv,timer-cannot-wake-cpu"); + of_node_put(child); + } + domain = NULL; child = of_get_compatible_child(n, "riscv,cpu-intc"); if (!child) { @@ -177,6 +192,11 @@ static int __init riscv_timer_init_dt(struct device_node *n) return error; } + if (riscv_isa_extension_available(NULL, SSTC)) { + pr_info("Timer interrupt in S-mode is available via sstc extension\n"); + static_branch_enable(&riscv_sstc_available); + } + error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING, "clockevents/riscv/timer:starting", riscv_timer_starting_cpu, riscv_timer_dying_cpu); @@ -184,11 +204,6 @@ static int __init riscv_timer_init_dt(struct device_node *n) pr_err("cpu hp setup state failed for RISCV timer [%d]\n", error); - if (riscv_isa_extension_available(NULL, SSTC)) { - pr_info("Timer interrupt in S-mode is available via sstc extension\n"); - static_branch_enable(&riscv_sstc_available); - } - return error; } diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c index e6a87f4af2b5..cd1916c05325 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -155,14 +155,14 @@ static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static inline void timer_shutdown(struct clock_event_device *evt) +static inline void evt_timer_shutdown(struct clock_event_device *evt) { writel(0, common_clkevt->ctrl); } static int sp804_shutdown(struct clock_event_device *evt) { - timer_shutdown(evt); + evt_timer_shutdown(evt); return 0; } @@ -171,7 +171,7 @@ static int sp804_set_periodic(struct clock_event_device *evt) unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; - timer_shutdown(evt); + evt_timer_shutdown(evt); writel(common_clkevt->reload, common_clkevt->load); writel(ctrl, common_clkevt->ctrl); return 0; diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c index e5a70aa1deb4..7bdcc60ad43c 100644 --- a/drivers/clocksource/timer-sun4i.c +++ b/drivers/clocksource/timer-sun4i.c @@ -144,7 +144,8 @@ static struct timer_of to = { .clkevt = { .name = "sun4i_tick", .rating = 350, - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ, .set_state_shutdown = sun4i_clkevt_shutdown, .set_state_periodic = sun4i_clkevt_set_periodic, .set_state_oneshot = sun4i_clkevt_set_oneshot, diff --git a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c index 2737407ff069..632523c1232f 100644 --- a/drivers/clocksource/timer-ti-dm-systimer.c +++ b/drivers/clocksource/timer-ti-dm-systimer.c @@ -345,8 +345,10 @@ static int __init dmtimer_systimer_init_clock(struct dmtimer_systimer *t, return error; r = clk_get_rate(clock); - if (!r) + if (!r) { + clk_disable_unprepare(clock); return -ENODEV; + } if (is_ick) t->ick = clock; diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index cad29ded3a48..b24b903a8822 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -633,6 +633,8 @@ static struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *n static int omap_dm_timer_free(struct omap_dm_timer *cookie) { struct dmtimer *timer; + struct device *dev; + int rc; timer = to_dmtimer(cookie); if (unlikely(!timer)) @@ -640,10 +642,21 @@ static int omap_dm_timer_free(struct omap_dm_timer *cookie) WARN_ON(!timer->reserved); timer->reserved = 0; + + dev = &timer->pdev->dev; + rc = pm_runtime_resume_and_get(dev); + if (rc) + return rc; + + /* Clear timer configuration */ + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, 0); + + pm_runtime_put_sync(dev); + return 0; } -int omap_dm_timer_get_irq(struct omap_dm_timer *cookie) +static int omap_dm_timer_get_irq(struct omap_dm_timer *cookie) { struct dmtimer *timer = to_dmtimer(cookie); if (timer) @@ -1135,6 +1148,10 @@ static int omap_dm_timer_probe(struct platform_device *pdev) goto err_disable; } __omap_dm_timer_init_regs(timer); + + /* Clear timer configuration */ + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, 0); + pm_runtime_put(dev); } @@ -1258,7 +1275,7 @@ static struct platform_driver omap_dm_timer_driver = { .remove = omap_dm_timer_remove, .driver = { .name = "omap_timer", - .of_match_table = of_match_ptr(omap_timer_match), + .of_match_table = omap_timer_match, .pm = &omap_dm_timer_pm_ops, }, }; |