diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml index f1853daec2f9..3e2725c56995 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml +++ b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml @@ -15,9 +15,13 @@ properties: oneOf: - const: allwinner,sun5i-a13-hstimer - const: allwinner,sun7i-a20-hstimer + - const: allwinner,sun20i-d1-hstimer - items: - const: allwinner,sun6i-a31-hstimer - const: allwinner,sun7i-a20-hstimer + - items: + - const: allwinner,sun50i-h616-hstimer + - const: allwinner,sun20i-d1-hstimer reg: maxItems: 1 @@ -45,7 +49,10 @@ required: if: properties: compatible: - const: allwinner,sun5i-a13-hstimer + anyOf: + - const: allwinner,sun5i-a13-hstimer + - contains: + const: allwinner,sun20i-d1-hstimer then: properties: diff --git a/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml b/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml index c5fc3b6c8bd0..c65e48a155ab 100644 --- a/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml +++ b/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml @@ -10,13 +10,8 @@ maintainers: - Marc Zyngier - Mark Rutland description: |+ - ARM cores may have a per-core architected timer, which provides per-cpu timers, - or a memory mapped architected timer, which provides up to 8 frames with a - physical and optional virtual timer per frame. - - The per-core architected timer is attached to a GIC to deliver its - per-processor interrupts via PPIs. The memory mapped timer is attached to a GIC - to deliver its interrupts via SPIs. + The per-core architected timer is expected to deliver per-CPU interrupts + (commonly to a GIC to deliver its per-processor interrupts as PPIs). properties: compatible: @@ -33,13 +28,13 @@ properties: - const: arm,armv7-timer interrupts: - minItems: 1 + minItems: 2 items: - - description: secure timer irq - - description: non-secure timer irq - - description: virtual timer irq - - description: hypervisor timer irq - - description: hypervisor virtual timer irq + - description: EL1 secure physical timer irq, if EL3 is implemented + - description: EL1 non-secure physical timer irq + - description: EL1 virtual timer irq + - description: EL2 physical timer irq, if EL2 is implemented + - description: EL2 virtual timer irq, if FEAT_VHE is implemented interrupt-names: oneOf: diff --git a/Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml b/Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml index 3ad10c5b66ba..ecff2912d812 100644 --- a/Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml +++ b/Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml @@ -112,6 +112,8 @@ properties: - renesas,r9a07g043-mtu3 # RZ/{G2UL,Five} - renesas,r9a07g044-mtu3 # RZ/G2{L,LC} - renesas,r9a07g054-mtu3 # RZ/V2L + - renesas,r9a09g077-mtu3 # RZ/T2H + - renesas,r9a09g087-mtu3 # RZ/N2H - const: renesas,rz-mtu3 reg: @@ -162,7 +164,6 @@ properties: - description: MTU8.TGRC input capture/compare match - description: MTU8.TGRD input capture/compare match - description: MTU8.TCNT overflow - - description: MTU8.TCNT underflow interrupt-names: items: @@ -209,7 +210,6 @@ properties: - const: tgic8 - const: tgid8 - const: tciv8 - - const: tciu8 clocks: maxItems: 1 @@ -233,7 +233,22 @@ required: - interrupt-names - clocks - power-domains - - resets + +allOf: + - if: + properties: + compatible: + contains: + enum: + - renesas,r9a07g043-mtu3 + - renesas,r9a07g044-mtu3 + - renesas,r9a07g054-mtu3 + then: + required: + - resets + else: + properties: + resets: false additionalProperties: false @@ -287,8 +302,7 @@ examples: , , , - , - ; + ; interrupt-names = "tgia0", "tgib0", "tgic0", "tgid0", "tciv0", "tgie0", "tgif0", "tgia1", "tgib1", "tciv1", "tciu1", @@ -298,7 +312,7 @@ examples: "tgiu5", "tgiv5", "tgiw5", "tgia6", "tgib6", "tgic6", "tgid6", "tciv6", "tgia7", "tgib7", "tgic7", "tgid7", "tciv7", - "tgia8", "tgib8", "tgic8", "tgid8", "tciv8", "tciu8"; + "tgia8", "tgib8", "tgic8", "tgid8", "tciv8"; clocks = <&cpg CPG_MOD R9A07G044_MTU_X_MCK_MTU3>; power-domains = <&cpg>; resets = <&cpg R9A07G044_MTU_X_PRESET_MTU3>; diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index 3c16b260db04..67cea8edb59f 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -38,6 +38,7 @@ properties: - starfive,jh7100-clint # StarFive JH7100 - starfive,jh7110-clint # StarFive JH7110 - starfive,jh8100-clint # StarFive JH8100 + - starfive,jhb100-clint # StarFive JHB100 - tenstorrent,blackhole-clint # Tenstorrent Blackhole - const: sifive,clint0 # SiFive CLINT v0 IP block - items: @@ -72,22 +73,6 @@ properties: minItems: 1 maxItems: 4095 - sifive,fine-ctr-bits: - maximum: 15 - description: The width in bits of the fine counter. - -if: - properties: - compatible: - contains: - const: sifive,clint2 -then: - required: - - sifive,fine-ctr-bits -else: - properties: - sifive,fine-ctr-bits: false - additionalProperties: false required: diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 6ea1bd55acf8..a361840d7a04 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -227,27 +227,6 @@ config SOC_VF610 help This enables support for Freescale Vybrid VF610 processor. -choice - prompt "Clocksource for scheduler clock" - depends on SOC_VF610 - default VF_USE_ARM_GLOBAL_TIMER - - config VF_USE_ARM_GLOBAL_TIMER - bool "Use ARM Global Timer" - depends on ARCH_MULTI_V7 - select ARM_GLOBAL_TIMER - select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK - help - Use the ARM Global Timer as clocksource - - config VF_USE_PIT_TIMER - bool "Use PIT timer" - select NXP_PIT_TIMER - help - Use SoC Periodic Interrupt Timer (PIT) as clocksource - -endchoice - endif endif diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c index ffc867bac2d6..00158c8aa6d9 100644 --- a/drivers/acpi/arm64/gtdt.c +++ b/drivers/acpi/arm64/gtdt.c @@ -34,14 +34,33 @@ struct acpi_gtdt_descriptor { void *platform_timer; }; +struct gtdt_v3 { + struct acpi_table_gtdt gtdt_v2; + struct acpi_gtdt_el2 el2_vtimer; +}; + static struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata; +static __init struct acpi_gtdt_el2 *gtdt_to_el2_vtimer(struct acpi_table_gtdt *gtdt) +{ + if (gtdt->header.revision < 3) + return NULL; + + return &container_of(gtdt, struct gtdt_v3, gtdt_v2)->el2_vtimer; +} + static __init bool platform_timer_valid(void *platform_timer) { struct acpi_gtdt_header *gh = platform_timer; + void *platform_timer_begin; - return (platform_timer >= (void *)(acpi_gtdt_desc.gtdt + 1) && - platform_timer < acpi_gtdt_desc.gtdt_end && + if (acpi_gtdt_desc.gtdt->header.revision >= 3) + platform_timer_begin = container_of(acpi_gtdt_desc.gtdt, struct gtdt_v3, gtdt_v2) + 1; + else + platform_timer_begin = acpi_gtdt_desc.gtdt + 1; + + return (platform_timer >= platform_timer_begin && + platform_timer + sizeof(*gh) <= acpi_gtdt_desc.gtdt_end && gh->length != 0 && platform_timer + gh->length <= acpi_gtdt_desc.gtdt_end); } @@ -101,6 +120,7 @@ static int __init map_gt_gsi(u32 interrupt, u32 flags) int __init acpi_gtdt_map_ppi(int type) { struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + struct acpi_gtdt_el2 *el2_vtimer = gtdt_to_el2_vtimer(gtdt); switch (type) { case ARCH_TIMER_PHYS_NONSECURE_PPI: @@ -113,6 +133,12 @@ int __init acpi_gtdt_map_ppi(int type) case ARCH_TIMER_HYP_PPI: return map_gt_gsi(gtdt->non_secure_el2_interrupt, gtdt->non_secure_el2_flags); + case ARCH_TIMER_HYP_VIRT_PPI: + if (el2_vtimer && el2_vtimer->virtual_el2_timer_gsiv) + return map_gt_gsi(el2_vtimer->virtual_el2_timer_gsiv, + el2_vtimer->virtual_el2_timer_flags); + + return 0; default: pr_err("Failed to map timer interrupt: invalid type.\n"); } @@ -130,6 +156,7 @@ int __init acpi_gtdt_map_ppi(int type) bool __init acpi_gtdt_c3stop(int type) { struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + struct acpi_gtdt_el2 *el2_vtimer = gtdt_to_el2_vtimer(gtdt); switch (type) { case ARCH_TIMER_PHYS_NONSECURE_PPI: @@ -141,6 +168,10 @@ bool __init acpi_gtdt_c3stop(int type) case ARCH_TIMER_HYP_PPI: return !(gtdt->non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON); + case ARCH_TIMER_HYP_VIRT_PPI: + return el2_vtimer && el2_vtimer->virtual_el2_timer_gsiv && + !(el2_vtimer->virtual_el2_timer_flags & ACPI_GTDT_ALWAYS_ON); + default: pr_err("Failed to get c3stop info: invalid type.\n"); } @@ -166,6 +197,13 @@ int __init acpi_gtdt_init(struct acpi_table_header *table, u32 cnt = 0; gtdt = container_of(table, struct acpi_table_gtdt, header); + + if ((gtdt->header.revision >= 3 && gtdt->header.length < sizeof(struct gtdt_v3)) || + (gtdt->header.revision == 2 && gtdt->header.length < sizeof(*gtdt))) { + pr_err(FW_BUG "GTDT with invalid size %d\n", gtdt->header.length); + return -EINVAL; + } + acpi_gtdt_desc.gtdt = gtdt; acpi_gtdt_desc.gtdt_end = (void *)table + table->length; acpi_gtdt_desc.platform_timer = NULL; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index d1a33a231a44..d9c76dd443f8 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -793,4 +793,35 @@ config RTK_SYSTIMER this option only when building for a Realtek platform or for compilation testing. +choice + prompt "NXP clocksource for scheduler clock" + depends on SOC_VF610 || ARCH_S32 + # Default to Global Timer for Vybrid (32-bit) + default VF_USE_ARM_GLOBAL_TIMER if SOC_VF610 + # Default to None for S32 (64-bit) + default VF_TIMER_NONE if ARCH_S32 + + config VF_USE_ARM_GLOBAL_TIMER + bool "Use NXP Vybrid Global Timer" + depends on ARCH_MULTI_V7 && SOC_VF610 + select ARM_GLOBAL_TIMER + select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK + help + Use the NXP Vybrid Global Timer as clocksource. + + config VF_USE_PIT_TIMER + bool "Use NXP PIT timer" + select NXP_PIT_TIMER + help + Use NXP Periodic Interrupt Timer (PIT) as clocksource. + + config VF_TIMER_NONE + bool "None (Use standard Arch Timer)" + depends on ARCH_S32 + help + Do not use any specific NXP timer driver. Use the standard + ARM Architected Timer instead. + +endchoice + endmenu diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 90aeff44a276..4adf756423de 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -688,6 +688,7 @@ static void __arch_timer_setup(struct clock_event_device *clk) clk->irq = arch_timer_ppi[arch_timer_uses_ppi]; switch (arch_timer_uses_ppi) { case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: clk->set_state_shutdown = arch_timer_shutdown_virt; clk->set_state_oneshot_stopped = arch_timer_shutdown_virt; sne = erratum_handler(set_next_event_virt); @@ -879,7 +880,7 @@ static void __init arch_timer_banner(void) pr_info("cp15 timer running at %lu.%02luMHz (%s).\n", (unsigned long)arch_timer_rate / 1000000, (unsigned long)(arch_timer_rate / 10000) % 100, - (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys"); + arch_timer_ppi_names[arch_timer_uses_ppi]); } u32 arch_timer_get_rate(void) @@ -912,7 +913,8 @@ static void __init arch_counter_register(void) int width; if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) || - arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) { + arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI || + arch_timer_uses_ppi == ARCH_TIMER_HYP_VIRT_PPI) { if (arch_timer_counter_has_wa()) { rd = arch_counter_get_cntvct_stable; scr = raw_counter_get_cntvct_stable; @@ -1023,6 +1025,7 @@ static int __init arch_timer_register(void) ppi = arch_timer_ppi[arch_timer_uses_ppi]; switch (arch_timer_uses_ppi) { case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: err = request_percpu_irq(ppi, arch_timer_handler_virt, "arch_timer", arch_timer_evt); break; @@ -1090,25 +1093,34 @@ static int __init arch_timer_common_init(void) /** * arch_timer_select_ppi() - Select suitable PPI for the current system. * - * If HYP mode is available, we know that the physical timer - * has been configured to be accessible from PL1. Use it, so - * that a guest can use the virtual timer instead. + * On AArch32, if HYP mode is available, we know that the physical + * timer has been configured to be accessible from PL1. Use it, so + * that a guest can use the virtual timer instead (though KVM host + * support has long been removed). * - * On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE - * accesses to CNTP_*_EL1 registers are silently redirected to - * their CNTHP_*_EL2 counterparts, and use a different PPI - * number. + * On ARMv8.1 with FEAT_VHE, the kernel runs in EL2. Accesses to + * CNTV_*_EL1 registers are silently redirected to their CNTHV_*_EL2 + * counterparts, and the timer uses a different PPI number. Similar + * thing happen when using the EL2 physical timer. Note that a bunch + * of DTs out there omit the virtual EL2 timer, so fallback gracefully + * on the physical timer. + * + * Without VHE, if no interrupt provided for virtual timer, we'll have + * to stick to the physical timer. It'd better be accessible... * - * If no interrupt provided for virtual timer, we'll have to - * stick to the physical timer. It'd better be accessible... * For arm64 we never use the secure interrupt. * * Return: a suitable PPI type for the current system. */ static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void) { - if (is_kernel_in_hyp_mode()) + if (is_kernel_in_hyp_mode()) { + if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI]) + return ARCH_TIMER_HYP_VIRT_PPI; + + pr_warn_once(FW_BUG "VHE-capable CPU without EL2 virtual timer interrupt\n"); return ARCH_TIMER_HYP_PPI; + } if (!is_hyp_mode_available() && arch_timer_ppi[ARCH_TIMER_VIRT_PPI]) return ARCH_TIMER_VIRT_PPI; @@ -1200,14 +1212,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) if (ret) return ret; - arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_PHYS_NONSECURE_PPI); - - arch_timer_ppi[ARCH_TIMER_VIRT_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_VIRT_PPI); - - arch_timer_ppi[ARCH_TIMER_HYP_PPI] = - acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI); + /* The GTDT parser can't be bothered with the secure timer */ + for (int i = ARCH_TIMER_PHYS_NONSECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++) + arch_timer_ppi[i] = acpi_gtdt_map_ppi(i); arch_timer_populate_kvm_info(); @@ -1253,10 +1260,14 @@ int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *ts, if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC_DISCOVERY)) return -EOPNOTSUPP; - if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) + switch (arch_timer_uses_ppi) { + case ARCH_TIMER_VIRT_PPI: + case ARCH_TIMER_HYP_VIRT_PPI: ptp_counter = KVM_PTP_VIRT_COUNTER; - else + break; + default: ptp_counter = KVM_PTP_PHYS_COUNTER; + } arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, ptp_counter, &hvc_res); diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index d7e012992170..6ab300d22621 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -18,21 +18,30 @@ #include #include -#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN_REG 0x00 #define TIMER_IRQ_EN(val) BIT(val) -#define TIMER_IRQ_ST_REG 0x04 -#define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) +#define TIMER_IRQ_ST_REG 0x04 +#define TIMER_CTL_REG(val, offset) (0x20 * (val) + 0x10 + (offset)) #define TIMER_CTL_ENABLE BIT(0) #define TIMER_CTL_RELOAD BIT(1) #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) #define TIMER_CTL_ONESHOT BIT(7) -#define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) -#define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) -#define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) -#define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) +#define TIMER_INTVAL_LO_REG(val, offset) (0x20 * (val) + 0x14 + (offset)) +#define TIMER_INTVAL_HI_REG(val, offset) (0x20 * (val) + 0x18 + (offset)) +#define TIMER_CNTVAL_LO_REG(val, offset) (0x20 * (val) + 0x1c + (offset)) +#define TIMER_CNTVAL_HI_REG(val, offset) (0x20 * (val) + 0x20 + (offset)) #define TIMER_SYNC_TICKS 3 +/** + * struct sunxi_timer_quirks - Differences between SoC variants. + * + * @from_ctl_base_offset: offset applied from ctl register onwards + */ +struct sunxi_timer_quirks { + u32 from_ctl_base_offset; +}; + struct sun5i_timer { void __iomem *base; struct clk *clk; @@ -40,6 +49,7 @@ struct sun5i_timer { u32 ticks_per_jiffy; struct clocksource clksrc; struct clock_event_device clkevt; + const struct sunxi_timer_quirks *quirks; }; #define nb_to_sun5i_timer(x) \ @@ -57,28 +67,36 @@ struct sun5i_timer { */ static void sun5i_clkevt_sync(struct sun5i_timer *ce) { - u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset)); - while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) + while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset))) < + TIMER_SYNC_TICKS) cpu_relax(); } static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) { - u32 val = readl(ce->base + TIMER_CTL_REG(timer)); - writel(val & ~TIMER_CTL_ENABLE, ce->base + TIMER_CTL_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset)); + + writel(val & ~TIMER_CTL_ENABLE, + ce->base + TIMER_CTL_REG(timer, offset)); sun5i_clkevt_sync(ce); } static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) { - writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + + writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer, offset)); } static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) { - u32 val = readl(ce->base + TIMER_CTL_REG(timer)); + u32 offset = ce->quirks->from_ctl_base_offset; + u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset)); if (periodic) val &= ~TIMER_CTL_ONESHOT; @@ -86,7 +104,7 @@ static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool perio val |= TIMER_CTL_ONESHOT; writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - ce->base + TIMER_CTL_REG(timer)); + ce->base + TIMER_CTL_REG(timer, offset)); } static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) @@ -141,8 +159,9 @@ static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) static u64 sun5i_clksrc_read(struct clocksource *clksrc) { struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); + u32 offset = cs->quirks->from_ctl_base_offset; - return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1)); + return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1, offset)); } static int sun5i_rate_cb(struct notifier_block *nb, @@ -173,12 +192,13 @@ static int sun5i_setup_clocksource(struct platform_device *pdev, unsigned long rate) { struct sun5i_timer *cs = platform_get_drvdata(pdev); + u32 offset = cs->quirks->from_ctl_base_offset; void __iomem *base = cs->base; int ret; - writel(~0, base + TIMER_INTVAL_LO_REG(1)); + writel(~0, base + TIMER_INTVAL_LO_REG(1, offset)); writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - base + TIMER_CTL_REG(1)); + base + TIMER_CTL_REG(1, offset)); cs->clksrc.name = pdev->dev.of_node->name; cs->clksrc.rating = 340; @@ -237,6 +257,7 @@ static int sun5i_setup_clockevent(struct platform_device *pdev, static int sun5i_timer_probe(struct platform_device *pdev) { + const struct sunxi_timer_quirks *quirks; struct device *dev = &pdev->dev; struct sun5i_timer *st; struct reset_control *rstc; @@ -273,11 +294,18 @@ static int sun5i_timer_probe(struct platform_device *pdev) return -EINVAL; } + quirks = of_device_get_match_data(&pdev->dev); + if (!quirks) { + dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); + return -ENODEV; + } + st->base = timer_base; st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); st->clk = clk; st->clk_rate_cb.notifier_call = sun5i_rate_cb; st->clk_rate_cb.next = NULL; + st->quirks = quirks; ret = devm_clk_notifier_register(dev, clk, &st->clk_rate_cb); if (ret) { @@ -314,9 +342,27 @@ static void sun5i_timer_remove(struct platform_device *pdev) clocksource_unregister(&st->clksrc); } +static const struct sunxi_timer_quirks sun5i_sun7i_hstimer_quirks = { + .from_ctl_base_offset = 0x0, +}; + +static const struct sunxi_timer_quirks sun20i_d1_hstimer_quirks = { + .from_ctl_base_offset = 0x10, +}; + static const struct of_device_id sun5i_timer_of_match[] = { - { .compatible = "allwinner,sun5i-a13-hstimer" }, - { .compatible = "allwinner,sun7i-a20-hstimer" }, + { + .compatible = "allwinner,sun5i-a13-hstimer", + .data = &sun5i_sun7i_hstimer_quirks, + }, + { + .compatible = "allwinner,sun7i-a20-hstimer", + .data = &sun5i_sun7i_hstimer_quirks, + }, + { + .compatible = "allwinner,sun20i-d1-hstimer", + .data = &sun20i_d1_hstimer_quirks, + }, {}, }; MODULE_DEVICE_TABLE(of, sun5i_timer_of_match); diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c index 355558893e5f..78600ddeb1c6 100644 --- a/drivers/clocksource/timer-tegra186.c +++ b/drivers/clocksource/timer-tegra186.c @@ -57,6 +57,15 @@ #define WDTUR 0x00c #define WDTUR_UNLOCK_PATTERN 0x0000c45a +#define TEGRA186_KERNEL_WDT_TIMEOUT 120 + +/* WDT security configuration registers */ +#define WDTSCR(x) (0xf02c + (x) * 4) +#define WDTSCR_SEC_WEN BIT(28) +#define WDTSCR_SEC_REN BIT(27) +#define WDTSCR_SEC_G1W BIT(9) +#define WDTSCR_SEC_G1R BIT(1) + struct tegra186_timer_soc { unsigned int num_timers; unsigned int num_wdts; @@ -75,6 +84,7 @@ struct tegra186_wdt { void __iomem *regs; unsigned int index; bool locked; + bool is_kernel_wdt; struct tegra186_tmr *tmr; }; @@ -89,7 +99,7 @@ struct tegra186_timer { struct device *dev; void __iomem *regs; - struct tegra186_wdt *wdt; + struct tegra186_wdt **wdts; struct clocksource usec; struct clocksource tsc; struct clocksource osc; @@ -149,7 +159,8 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) u32 value; /* unmask hardware IRQ, this may have been lost across powergate */ - value = TKEIE_WDT_MASK(wdt->index, 1); + value = readl(tegra->regs + TKEIE(wdt->tmr->hwirq)); + value |= TKEIE_WDT_MASK(wdt->index, 1); writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq)); /* clear interrupt */ @@ -174,6 +185,10 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt) value &= ~WDTCR_PERIOD_MASK; value |= WDTCR_PERIOD(1); + /* enable local interrupt for kernel watchdog */ + if (wdt->is_kernel_wdt) + value |= WDTCR_LOCAL_INT_ENABLE; + /* enable system POR reset */ value |= WDTCR_SYSTEM_POR_RESET_ENABLE; @@ -211,6 +226,16 @@ static int tegra186_wdt_ping(struct watchdog_device *wdd) return 0; } +static irqreturn_t tegra186_wdt_irq(int irq, void *data) +{ + struct tegra186_wdt *wdt = data; + + tegra186_wdt_disable(wdt); + tegra186_wdt_enable(wdt); + + return IRQ_HANDLED; +} + static int tegra186_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { @@ -297,6 +322,23 @@ static const struct watchdog_ops tegra186_wdt_ops = { .get_timeleft = tegra186_wdt_get_timeleft, }; +static bool tegra186_wdt_is_accessible(struct tegra186_timer *tegra, unsigned int index) +{ + u32 value; + + value = readl_relaxed(tegra->regs + WDTSCR(index)); + + /* Check OS write access if write blocking is enabled. */ + if ((value & WDTSCR_SEC_WEN) && !(value & WDTSCR_SEC_G1W)) + return false; + + /* Check OS read access if read blocking is enabled. */ + if ((value & WDTSCR_SEC_REN) && !(value & WDTSCR_SEC_G1R)) + return false; + + return true; +} + static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, unsigned int index) { @@ -336,10 +378,6 @@ static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, if (err < 0) return ERR_PTR(err); - err = devm_watchdog_register_device(tegra->dev, &wdt->base); - if (err < 0) - return ERR_PTR(err); - return wdt; } @@ -421,8 +459,11 @@ static int tegra186_timer_usec_init(struct tegra186_timer *tegra) static int tegra186_timer_probe(struct platform_device *pdev) { + struct tegra186_wdt *kernel_wdt = NULL; struct device *dev = &pdev->dev; struct tegra186_timer *tegra; + unsigned int i; + int irq; int err; tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL); @@ -441,12 +482,33 @@ static int tegra186_timer_probe(struct platform_device *pdev) if (err < 0) return err; - /* create a watchdog using a preconfigured timer */ - tegra->wdt = tegra186_wdt_create(tegra, 0); - if (IS_ERR(tegra->wdt)) { - err = PTR_ERR(tegra->wdt); - dev_err(dev, "failed to create WDT: %d\n", err); - return err; + irq = err; + + tegra->wdts = devm_kcalloc(dev, tegra->soc->num_wdts, sizeof(*tegra->wdts), GFP_KERNEL); + if (!tegra->wdts) + return -ENOMEM; + + for (i = 0; i < tegra->soc->num_wdts; i++) { + if (!tegra186_wdt_is_accessible(tegra, i)) { + dev_warn(dev, "WDT%u is not accessible\n", i); + continue; + } + + tegra->wdts[i] = tegra186_wdt_create(tegra, i); + if (IS_ERR(tegra->wdts[i])) + return dev_err_probe(dev, PTR_ERR(tegra->wdts[i]), + "failed to create WDT%u\n", i); + + /* Reserve the first accessible WDT for the Kernel. */ + if (!kernel_wdt) { + kernel_wdt = tegra->wdts[i]; + kernel_wdt->is_kernel_wdt = true; + } else { + err = devm_watchdog_register_device(dev, &tegra->wdts[i]->base); + if (err < 0) + return dev_err_probe(dev, err, + "failed to register WDT%u\n", i); + } } err = tegra186_timer_tsc_init(tegra); @@ -467,8 +529,22 @@ static int tegra186_timer_probe(struct platform_device *pdev) goto unregister_osc; } + if (kernel_wdt) { + err = devm_request_irq(dev, irq, tegra186_wdt_irq, 0, + dev_name(dev), kernel_wdt); + if (err < 0) { + dev_err(dev, "failed to request kernel WDT IRQ: %d\n", err); + goto unregister_usec; + } + + tegra186_wdt_set_timeout(&kernel_wdt->base, TEGRA186_KERNEL_WDT_TIMEOUT); + tegra186_wdt_enable(kernel_wdt); + } + return 0; +unregister_usec: + clocksource_unregister(&tegra->usec); unregister_osc: clocksource_unregister(&tegra->osc); unregister_tsc: @@ -488,9 +564,14 @@ static void tegra186_timer_remove(struct platform_device *pdev) static int __maybe_unused tegra186_timer_suspend(struct device *dev) { struct tegra186_timer *tegra = dev_get_drvdata(dev); + unsigned int i; - if (watchdog_active(&tegra->wdt->base)) - tegra186_wdt_disable(tegra->wdt); + for (i = 0; i < tegra->soc->num_wdts; i++) { + struct tegra186_wdt *wdt = tegra->wdts[i]; + + if (wdt && (wdt->is_kernel_wdt || watchdog_active(&wdt->base))) + tegra186_wdt_disable(wdt); + } return 0; } @@ -498,9 +579,14 @@ static int __maybe_unused tegra186_timer_suspend(struct device *dev) static int __maybe_unused tegra186_timer_resume(struct device *dev) { struct tegra186_timer *tegra = dev_get_drvdata(dev); + unsigned int i; - if (watchdog_active(&tegra->wdt->base)) - tegra186_wdt_enable(tegra->wdt); + for (i = 0; i < tegra->soc->num_wdts; i++) { + struct tegra186_wdt *wdt = tegra->wdts[i]; + + if (wdt && (wdt->is_kernel_wdt || watchdog_active(&wdt->base))) + tegra186_wdt_enable(wdt); + } return 0; } @@ -510,12 +596,12 @@ static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend, static const struct tegra186_timer_soc tegra186_timer = { .num_timers = 10, - .num_wdts = 3, + .num_wdts = 2, }; static const struct tegra186_timer_soc tegra234_timer = { .num_timers = 16, - .num_wdts = 3, + .num_wdts = 2, }; static const struct of_device_id tegra186_timer_of_match[] = { diff --git a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c index eb0dfe4b9b7c..3804c1234522 100644 --- a/drivers/clocksource/timer-ti-dm-systimer.c +++ b/drivers/clocksource/timer-ti-dm-systimer.c @@ -226,7 +226,7 @@ static bool __init dmtimer_is_preferred(struct device_node *np) * Some omap3 boards with unreliable oscillator must not use the counter_32k * or dmtimer1 with 32 KiHz source. Additionally, the boards with unreliable * oscillator should really set counter_32k as disabled, and delete dmtimer1 - * ti,always-on property, but let's not count on it. For these quirky cases, + * ti,timer-alwon property, but let's not count on it. For these quirky cases, * we prefer using the always-on secure dmtimer12 with the internal 32 KiHz * clock as the clocksource, and any available dmtimer as clockevent. * diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 793e7cdcb1b1..bd06afb7d522 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -20,8 +20,11 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -29,6 +32,7 @@ #include #include #include +#include #include #include @@ -148,6 +152,21 @@ static u32 omap_reserved_systimers; static LIST_HEAD(omap_timer_list); static DEFINE_SPINLOCK(dm_timer_lock); +struct dmtimer_clocksource { + struct clocksource dev; + struct dmtimer *timer; + unsigned int loadval; +}; + +struct omap_dm_timer_clockevent { + struct clock_event_device dev; + struct dmtimer *timer; + u32 period; +}; + +static bool omap_dm_timer_clockevent_setup; +static void __iomem *omap_dm_timer_sched_clock_counter; + enum { REQUEST_ANY = 0, REQUEST_BY_ID, @@ -1185,6 +1204,192 @@ static const struct dev_pm_ops omap_dm_timer_pm_ops = { static const struct of_device_id omap_timer_match[]; +static struct dmtimer_clocksource *omap_dm_timer_to_clocksource(struct clocksource *cs) +{ + return container_of(cs, struct dmtimer_clocksource, dev); +} + +static u64 omap_dm_timer_read_cycles(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + return (u64)__omap_dm_timer_read_counter(timer); +} + +static u64 notrace omap_dm_timer_read_sched_clock(void) +{ + /* Posted mode is not active here, so we can read directly */ + return readl_relaxed(omap_dm_timer_sched_clock_counter); +} + +static void omap_dm_timer_clocksource_suspend(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + clksrc->loadval = __omap_dm_timer_read_counter(timer); + __omap_dm_timer_stop(timer); +} + +static void omap_dm_timer_clocksource_resume(struct clocksource *cs) +{ + struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs); + struct dmtimer *timer = clksrc->timer; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clksrc->loadval); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR); +} + +static void omap_dm_timer_clocksource_unregister(void *data) +{ + struct clocksource *cs = data; + + clocksource_unregister(cs); +} + +static int omap_dm_timer_setup_clocksource(struct dmtimer *timer) +{ + struct device *dev = &timer->pdev->dev; + struct dmtimer_clocksource *clksrc; + int err; + + __omap_dm_timer_init_regs(timer); + + timer->reserved = 1; + + clksrc = devm_kzalloc(dev, sizeof(*clksrc), GFP_KERNEL); + if (!clksrc) + return -ENOMEM; + + clksrc->timer = timer; + + clksrc->dev.name = "omap_dm_timer"; + clksrc->dev.rating = 300; + clksrc->dev.read = omap_dm_timer_read_cycles; + clksrc->dev.mask = CLOCKSOURCE_MASK(32); + clksrc->dev.flags = CLOCK_SOURCE_IS_CONTINUOUS; + clksrc->dev.suspend = omap_dm_timer_clocksource_suspend; + clksrc->dev.resume = omap_dm_timer_clocksource_resume; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0); + dmtimer_write(timer, OMAP_TIMER_LOAD_REG, 0); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR); + + omap_dm_timer_sched_clock_counter = timer->func_base + _OMAP_TIMER_COUNTER_OFFSET; + sched_clock_register(omap_dm_timer_read_sched_clock, 32, timer->fclk_rate); + + err = clocksource_register_hz(&clksrc->dev, timer->fclk_rate); + if (err) + return dev_err_probe(dev, err, "Could not register as clocksource\n"); + + err = devm_add_action_or_reset(dev, omap_dm_timer_clocksource_unregister, &clksrc->dev); + if (err) + return dev_err_probe(dev, err, "Could not register clocksource_unregister action\n"); + + return 0; +} + +static struct omap_dm_timer_clockevent *to_dm_timer_clockevent(struct clock_event_device *evt) +{ + return container_of(evt, struct omap_dm_timer_clockevent, dev); +} + +static int omap_dm_timer_evt_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0xffffffff - cycles); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST); + + return 0; +} + +static int omap_dm_timer_evt_shutdown(struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + __omap_dm_timer_stop(timer); + + return 0; +} + +static int omap_dm_timer_evt_set_periodic(struct clock_event_device *evt) +{ + struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt); + struct dmtimer *timer = clkevt->timer; + + omap_dm_timer_evt_shutdown(evt); + + omap_dm_timer_set_load(&timer->cookie, clkevt->period); + dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clkevt->period); + dmtimer_write(timer, OMAP_TIMER_CTRL_REG, + OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST); + + return 0; +} + +static irqreturn_t omap_dm_timer_evt_interrupt(int irq, void *dev_id) +{ + struct omap_dm_timer_clockevent *clkevt = dev_id; + struct dmtimer *timer = clkevt->timer; + + __omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW); + + clkevt->dev.event_handler(&clkevt->dev); + + return IRQ_HANDLED; +} + +static int omap_dm_timer_setup_clockevent(struct dmtimer *timer) +{ + struct device *dev = &timer->pdev->dev; + struct omap_dm_timer_clockevent *clkevt; + int ret; + + clkevt = devm_kzalloc(dev, sizeof(*clkevt), GFP_KERNEL); + if (!clkevt) + return -ENOMEM; + + timer->reserved = 1; + clkevt->timer = timer; + + clkevt->dev.name = "omap_dm_timer"; + clkevt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + clkevt->dev.rating = 300; + clkevt->dev.set_next_event = omap_dm_timer_evt_set_next_event; + clkevt->dev.set_state_shutdown = omap_dm_timer_evt_shutdown; + clkevt->dev.set_state_periodic = omap_dm_timer_evt_set_periodic; + clkevt->dev.set_state_oneshot = omap_dm_timer_evt_shutdown; + clkevt->dev.set_state_oneshot_stopped = omap_dm_timer_evt_shutdown; + clkevt->dev.tick_resume = omap_dm_timer_evt_shutdown; + clkevt->dev.cpumask = cpu_possible_mask; + clkevt->period = 0xffffffff - DIV_ROUND_CLOSEST(timer->fclk_rate, HZ); + + __omap_dm_timer_init_regs(timer); + __omap_dm_timer_stop(timer); + __omap_dm_timer_enable_posted(timer); + + ret = devm_request_irq(dev, timer->irq, omap_dm_timer_evt_interrupt, + IRQF_TIMER, "omap_dm_timer_clockevent", clkevt); + if (ret) { + dev_err(dev, "Failed to request interrupt: %d\n", ret); + return ret; + } + + __omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW); + + clockevents_config_and_register(&clkevt->dev, timer->fclk_rate, + 3, + 0xffffffff); + + omap_dm_timer_clockevent_setup = true; + return 0; +} + /** * omap_dm_timer_probe - probe function called for every registered device * @pdev: pointer to current timer platform device @@ -1272,6 +1477,18 @@ static int omap_dm_timer_probe(struct platform_device *pdev) timer->pdev = pdev; + if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk)) { + if (!omap_dm_timer_sched_clock_counter) { + ret = omap_dm_timer_setup_clocksource(timer); + if (ret) + return ret; + } else if (!omap_dm_timer_clockevent_setup) { + ret = omap_dm_timer_setup_clockevent(timer); + if (ret) + return ret; + } + } + pm_runtime_enable(dev); if (!timer->reserved) {