diff options
author | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2022-10-03 17:04:02 +0100 |
---|---|---|
committer | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2022-10-03 17:04:02 +0100 |
commit | 97acb6a8fcc4e5c2cdc2693a35acdc5a7461aaa3 (patch) | |
tree | c4f1a18b38d655b7806a72515992bd9aae14ef53 /drivers/clocksource | |
parent | 6fa964c045a6bc3321a9186e87bfbcfd1059b0f1 (diff) | |
parent | 7860d720a84c74b2761c6b7995392a798ab0a3cb (diff) |
Merge drm/drm-next into drm-intel-gt-next
Daniele needs 84d4333c1e28 ("misc/mei: Add NULL check to component match
callback functions") in order to merge the DG2 HuC patches.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Diffstat (limited to 'drivers/clocksource')
27 files changed, 1240 insertions, 723 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 1589ae7d5abb6..4f2bb7315b67a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,7 +22,7 @@ config CLKEVT_I8253 config I8253_LOCK bool -config OMAP_DM_TIMER +config OMAP_DM_SYSTIMER bool select TIMER_OF @@ -56,6 +56,13 @@ config DIGICOLOR_TIMER help Enables the support for the digicolor timer driver. +config OMAP_DM_TIMER + bool "OMAP dual-mode timer driver" if ARCH_K3 || COMPILE_TEST + default y if ARCH_K3 + select TIMER_OF + help + Enables the support for the TI dual-mode timer driver. + config DW_APB_TIMER bool "DW APB timer driver" if COMPILE_TEST help @@ -80,7 +87,7 @@ config IXP4XX_TIMER bool "Intel XScale IXP4xx timer driver" if COMPILE_TEST depends on HAS_IOMEM select CLKSRC_MMIO - select TIMER_OF if OF + select TIMER_OF help Enables support for the Intel XScale IXP4xx SoC timer. @@ -150,6 +157,14 @@ config TEGRA_TIMER help Enables support for the Tegra driver. +config TEGRA186_TIMER + bool "NVIDIA Tegra186 timer driver" + depends on ARCH_TEGRA || COMPILE_TEST + depends on WATCHDOG && WATCHDOG_CORE + help + Enables support for the timers and watchdogs found on NVIDIA + Tegra186 and later SoCs. + config VT8500_TIMER bool "VT8500 timer driver" if COMPILE_TEST depends on HAS_IOMEM @@ -367,7 +382,7 @@ config ARM_GT_INITIAL_PRESCALER_VAL depends on ARM_GLOBAL_TIMER help When the ARM global timer initializes, its current rate is declared - to the kernel and maintained forever. Should it's parent clock + to the kernel and maintained forever. Should its parent clock change, the driver tries to fix the timer's internal prescaler. On some machs (i.e. Zynq) the initial prescaler value thus poses bounds about how much the parent clock is allowed to decrease or @@ -567,26 +582,6 @@ config CLKSRC_PXA This enables OST0 support available on PXA and SA-11x0 platforms. -config H8300_TMR8 - bool "Clockevent timer for the H8300 platform" if COMPILE_TEST - depends on HAS_IOMEM - help - This enables the 8 bits timer for the H8300 platform. - -config H8300_TMR16 - bool "Clockevent timer for the H83069 platform" if COMPILE_TEST - depends on HAS_IOMEM - help - This enables the 16 bits timer for the H8300 platform with the - H83069 CPU. - -config H8300_TPU - bool "Clocksource for the H8300 platform" if COMPILE_TEST - depends on HAS_IOMEM - help - This enables the clocksource for the H8300 platform with the - H8S2678 CPU. - config CLKSRC_IMX_GPT bool "Clocksource using i.MX GPT" if COMPILE_TEST depends on (ARM || ARM64) && HAVE_CLK @@ -617,6 +612,14 @@ config CLKSRC_ST_LPC Enable this option to use the Low Power controller timer as clocksource. +config GXP_TIMER + bool "GXP timer driver" if COMPILE_TEST && !ARCH_HPE + default ARCH_HPE + select TIMER_OF if OF + help + Provides a driver for the timer control found on HPE + GXP SOCs. This is required for all GXP SOCs. + config RISCV_TIMER bool "Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV && RISCV_SBI @@ -711,4 +714,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 9c85ee2bb3735..64ab547de97b9 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_CLKSRC_MMIO) += mmio.o obj-$(CONFIG_DAVINCI_TIMER) += timer-davinci.o obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o -obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm-systimer.o +obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o @@ -36,6 +36,7 @@ obj-$(CONFIG_SUN4I_TIMER) += timer-sun4i.o obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o obj-$(CONFIG_MESON6_TIMER) += timer-meson6.o obj-$(CONFIG_TEGRA_TIMER) += timer-tegra.o +obj-$(CONFIG_TEGRA186_TIMER) += timer-tegra186.o obj-$(CONFIG_VT8500_TIMER) += timer-vt8500.o obj-$(CONFIG_NSPIRE_TIMER) += timer-zevio.o obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o @@ -73,9 +74,6 @@ obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o obj-$(CONFIG_CLKSRC_IMX_TPM) += timer-imx-tpm.o obj-$(CONFIG_TIMER_IMX_SYS_CTR) += timer-imx-sysctr.o obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o -obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o -obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o -obj-$(CONFIG_H8300_TPU) += h8300_tpu.o obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o obj-$(CONFIG_INGENIC_SYSOST) += ingenic-sysost.o obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o @@ -88,3 +86,5 @@ 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 +obj-$(CONFIG_GXP_TIMER) += timer-gxp.o diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index a50ab5c2154fa..39f172d7e29ef 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2012 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 Broadcom Corporation #include <linux/init.h> #include <linux/irq.h> diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c deleted file mode 100644 index 86ca91451b2ec..0000000000000 --- a/drivers/clocksource/h8300_timer16.c +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * H8/300 16bit Timer driver - * - * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp> - */ - -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/clocksource.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#define TSTR 0 -#define TISRC 6 - -#define TCR 0 -#define TCNT 2 - -#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a)) -#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a)) - -struct timer16_priv { - struct clocksource cs; - unsigned long total_cycles; - void __iomem *mapbase; - void __iomem *mapcommon; - unsigned short cs_enabled; - unsigned char enb; - unsigned char ovf; - unsigned char ovie; -}; - -static unsigned long timer16_get_counter(struct timer16_priv *p) -{ - unsigned short v1, v2, v3; - unsigned char o1, o2; - - o1 = ioread8(p->mapcommon + TISRC) & p->ovf; - - /* Make sure the timer value is stable. Stolen from acpi_pm.c */ - do { - o2 = o1; - v1 = ioread16be(p->mapbase + TCNT); - v2 = ioread16be(p->mapbase + TCNT); - v3 = ioread16be(p->mapbase + TCNT); - o1 = ioread8(p->mapcommon + TISRC) & p->ovf; - } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) - || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); - - if (likely(!o1)) - return v2; - else - return v2 + 0x10000; -} - - -static irqreturn_t timer16_interrupt(int irq, void *dev_id) -{ - struct timer16_priv *p = (struct timer16_priv *)dev_id; - - bclr(p->ovf, p->mapcommon + TISRC); - p->total_cycles += 0x10000; - - return IRQ_HANDLED; -} - -static inline struct timer16_priv *cs_to_priv(struct clocksource *cs) -{ - return container_of(cs, struct timer16_priv, cs); -} - -static u64 timer16_clocksource_read(struct clocksource *cs) -{ - struct timer16_priv *p = cs_to_priv(cs); - unsigned long raw, value; - - value = p->total_cycles; - raw = timer16_get_counter(p); - - return value + raw; -} - -static int timer16_enable(struct clocksource *cs) -{ - struct timer16_priv *p = cs_to_priv(cs); - - WARN_ON(p->cs_enabled); - - p->total_cycles = 0; - iowrite16be(0x0000, p->mapbase + TCNT); - iowrite8(0x83, p->mapbase + TCR); - bset(p->ovie, p->mapcommon + TISRC); - bset(p->enb, p->mapcommon + TSTR); - - p->cs_enabled = true; - return 0; -} - -static void timer16_disable(struct clocksource *cs) -{ - struct timer16_priv *p = cs_to_priv(cs); - - WARN_ON(!p->cs_enabled); - - bclr(p->ovie, p->mapcommon + TISRC); - bclr(p->enb, p->mapcommon + TSTR); - - p->cs_enabled = false; -} - -static struct timer16_priv timer16_priv = { - .cs = { - .name = "h8300_16timer", - .rating = 200, - .read = timer16_clocksource_read, - .enable = timer16_enable, - .disable = timer16_disable, - .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - }, -}; - -#define REG_CH 0 -#define REG_COMM 1 - -static int __init h8300_16timer_init(struct device_node *node) -{ - void __iomem *base[2]; - int ret, irq; - unsigned int ch; - struct clk *clk; - - clk = of_clk_get(node, 0); - if (IS_ERR(clk)) { - pr_err("failed to get clock for clocksource\n"); - return PTR_ERR(clk); - } - - ret = -ENXIO; - base[REG_CH] = of_iomap(node, 0); - if (!base[REG_CH]) { - pr_err("failed to map registers for clocksource\n"); - goto free_clk; - } - - base[REG_COMM] = of_iomap(node, 1); - if (!base[REG_COMM]) { - pr_err("failed to map registers for clocksource\n"); - goto unmap_ch; - } - - ret = -EINVAL; - irq = irq_of_parse_and_map(node, 0); - if (!irq) { - pr_err("failed to get irq for clockevent\n"); - goto unmap_comm; - } - - of_property_read_u32(node, "renesas,channel", &ch); - - timer16_priv.mapbase = base[REG_CH]; - timer16_priv.mapcommon = base[REG_COMM]; - timer16_priv.enb = ch; - timer16_priv.ovf = ch; - timer16_priv.ovie = 4 + ch; - - ret = request_irq(irq, timer16_interrupt, - IRQF_TIMER, timer16_priv.cs.name, &timer16_priv); - if (ret < 0) { - pr_err("failed to request irq %d of clocksource\n", irq); - goto unmap_comm; - } - - clocksource_register_hz(&timer16_priv.cs, - clk_get_rate(clk) / 8); - return 0; - -unmap_comm: - iounmap(base[REG_COMM]); -unmap_ch: - iounmap(base[REG_CH]); -free_clk: - clk_put(clk); - return ret; -} - -TIMER_OF_DECLARE(h8300_16bit, "renesas,16bit-timer", - h8300_16timer_init); diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c deleted file mode 100644 index 47114c2a7cb54..0000000000000 --- a/drivers/clocksource/h8300_timer8.c +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/arch/h8300/kernel/cpu/timer/timer8.c - * - * Yoshinori Sato <ysato@users.sourcefoge.jp> - * - * 8bit Timer driver - * - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/clockchips.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#define _8TCR 0 -#define _8TCSR 2 -#define TCORA 4 -#define TCORB 6 -#define _8TCNT 8 - -#define CMIEA 6 -#define CMFA 6 - -#define FLAG_STARTED (1 << 3) - -#define SCALE 64 - -#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a)) -#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a)) - -struct timer8_priv { - struct clock_event_device ced; - void __iomem *mapbase; - unsigned long flags; - unsigned int rate; -}; - -static irqreturn_t timer8_interrupt(int irq, void *dev_id) -{ - struct timer8_priv *p = dev_id; - - if (clockevent_state_oneshot(&p->ced)) - iowrite16be(0x0000, p->mapbase + _8TCR); - - p->ced.event_handler(&p->ced); - - bclr(CMFA, p->mapbase + _8TCSR); - - return IRQ_HANDLED; -} - -static void timer8_set_next(struct timer8_priv *p, unsigned long delta) -{ - if (delta >= 0x10000) - pr_warn("delta out of range\n"); - bclr(CMIEA, p->mapbase + _8TCR); - iowrite16be(delta, p->mapbase + TCORA); - iowrite16be(0x0000, p->mapbase + _8TCNT); - bclr(CMFA, p->mapbase + _8TCSR); - bset(CMIEA, p->mapbase + _8TCR); -} - -static int timer8_enable(struct timer8_priv *p) -{ - iowrite16be(0xffff, p->mapbase + TCORA); - iowrite16be(0x0000, p->mapbase + _8TCNT); - iowrite16be(0x0c02, p->mapbase + _8TCR); - - return 0; -} - -static int timer8_start(struct timer8_priv *p) -{ - int ret; - - if ((p->flags & FLAG_STARTED)) - return 0; - - ret = timer8_enable(p); - if (!ret) - p->flags |= FLAG_STARTED; - - return ret; -} - -static void timer8_stop(struct timer8_priv *p) -{ - iowrite16be(0x0000, p->mapbase + _8TCR); -} - -static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced) -{ - return container_of(ced, struct timer8_priv, ced); -} - -static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta) -{ - timer8_start(p); - timer8_set_next(p, delta); -} - -static int timer8_clock_event_shutdown(struct clock_event_device *ced) -{ - timer8_stop(ced_to_priv(ced)); - return 0; -} - -static int timer8_clock_event_periodic(struct clock_event_device *ced) -{ - struct timer8_priv *p = ced_to_priv(ced); - - pr_info("%s: used for periodic clock events\n", ced->name); - timer8_stop(p); - timer8_clock_event_start(p, (p->rate + HZ/2) / HZ); - - return 0; -} - -static int timer8_clock_event_oneshot(struct clock_event_device *ced) -{ - struct timer8_priv *p = ced_to_priv(ced); - - pr_info("%s: used for oneshot clock events\n", ced->name); - timer8_stop(p); - timer8_clock_event_start(p, 0x10000); - - return 0; -} - -static int timer8_clock_event_next(unsigned long delta, - struct clock_event_device *ced) -{ - struct timer8_priv *p = ced_to_priv(ced); - - BUG_ON(!clockevent_state_oneshot(ced)); - timer8_set_next(p, delta - 1); - - return 0; -} - -static struct timer8_priv timer8_priv = { - .ced = { - .name = "h8300_8timer", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .rating = 200, - .set_next_event = timer8_clock_event_next, - .set_state_shutdown = timer8_clock_event_shutdown, - .set_state_periodic = timer8_clock_event_periodic, - .set_state_oneshot = timer8_clock_event_oneshot, - }, -}; - -static int __init h8300_8timer_init(struct device_node *node) -{ - void __iomem *base; - int irq, ret; - struct clk *clk; - - clk = of_clk_get(node, 0); - if (IS_ERR(clk)) { - pr_err("failed to get clock for clockevent\n"); - return PTR_ERR(clk); - } - - ret = -ENXIO; - base = of_iomap(node, 0); - if (!base) { - pr_err("failed to map registers for clockevent\n"); - goto free_clk; - } - - ret = -EINVAL; - irq = irq_of_parse_and_map(node, 0); - if (!irq) { - pr_err("failed to get irq for clockevent\n"); - goto unmap_reg; - } - - timer8_priv.mapbase = base; - - timer8_priv.rate = clk_get_rate(clk) / SCALE; - if (!timer8_priv.rate) { - pr_err("Failed to get rate for the clocksource\n"); - goto unmap_reg; - } - - if (request_irq(irq, timer8_interrupt, IRQF_TIMER, - timer8_priv.ced.name, &timer8_priv) < 0) { - pr_err("failed to request irq %d for clockevent\n", irq); - goto unmap_reg; - } - - clockevents_config_and_register(&timer8_priv.ced, - timer8_priv.rate, 1, 0x0000ffff); - - return 0; -unmap_reg: - iounmap(base); -free_clk: - clk_put(clk); - return ret; -} - -TIMER_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init); diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c deleted file mode 100644 index 17d4ab0f6ad19..0000000000000 --- a/drivers/clocksource/h8300_tpu.c +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * H8S TPU Driver - * - * Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp> - * - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/clocksource.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#define TCR 0x0 -#define TSR 0x5 -#define TCNT 0x6 - -#define TCFV 0x10 - -struct tpu_priv { - struct clocksource cs; - void __iomem *mapbase1; - void __iomem *mapbase2; - raw_spinlock_t lock; - unsigned int cs_enabled; -}; - -static inline unsigned long read_tcnt32(struct tpu_priv *p) -{ - unsigned long tcnt; - - tcnt = ioread16be(p->mapbase1 + TCNT) << 16; - tcnt |= ioread16be(p->mapbase2 + TCNT); - return tcnt; -} - -static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val) -{ - unsigned long v1, v2, v3; - int o1, o2; - - o1 = ioread8(p->mapbase1 + TSR) & TCFV; - - /* Make sure the timer value is stable. Stolen from acpi_pm.c */ - do { - o2 = o1; - v1 = read_tcnt32(p); - v2 = read_tcnt32(p); - v3 = read_tcnt32(p); - o1 = ioread8(p->mapbase1 + TSR) & TCFV; - } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) - || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); - - *val = v2; - return o1; -} - -static inline struct tpu_priv *cs_to_priv(struct clocksource *cs) -{ - return container_of(cs, struct tpu_priv, cs); -} - -static u64 tpu_clocksource_read(struct clocksource *cs) -{ - struct tpu_priv *p = cs_to_priv(cs); - unsigned long flags; - unsigned long long value; - - raw_spin_lock_irqsave(&p->lock, flags); - if (tpu_get_counter(p, &value)) - value += 0x100000000; - raw_spin_unlock_irqrestore(&p->lock, flags); - - return value; -} - -static int tpu_clocksource_enable(struct clocksource *cs) -{ - struct tpu_priv *p = cs_to_priv(cs); - - WARN_ON(p->cs_enabled); - - iowrite16be(0, p->mapbase1 + TCNT); - iowrite16be(0, p->mapbase2 + TCNT); - iowrite8(0x0f, p->mapbase1 + TCR); - iowrite8(0x03, p->mapbase2 + TCR); - - p->cs_enabled = true; - return 0; -} - -static void tpu_clocksource_disable(struct clocksource *cs) -{ - struct tpu_priv *p = cs_to_priv(cs); - - WARN_ON(!p->cs_enabled); - - iowrite8(0, p->mapbase1 + TCR); - iowrite8(0, p->mapbase2 + TCR); - p->cs_enabled = false; -} - -static struct tpu_priv tpu_priv = { - .cs = { - .name = "H8S_TPU", - .rating = 200, - .read = tpu_clocksource_read, - .enable = tpu_clocksource_enable, - .disable = tpu_clocksource_disable, - .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - }, -}; - -#define CH_L 0 -#define CH_H 1 - -static int __init h8300_tpu_init(struct device_node *node) -{ - void __iomem *base[2]; - struct clk *clk; - int ret = -ENXIO; - - clk = of_clk_get(node, 0); - if (IS_ERR(clk)) { - pr_err("failed to get clock for clocksource\n"); - return PTR_ERR(clk); - } - - base[CH_L] = of_iomap(node, CH_L); - if (!base[CH_L]) { - pr_err("failed to map registers for clocksource\n"); - goto free_clk; - } - base[CH_H] = of_iomap(node, CH_H); - if (!base[CH_H]) { - pr_err("failed to map registers for clocksource\n"); - goto unmap_L; - } - - tpu_priv.mapbase1 = base[CH_L]; - tpu_priv.mapbase2 = base[CH_H]; - - return clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64); - -unmap_L: - iounmap(base[CH_H]); -free_clk: - clk_put(clk); - return ret; -} - -TIMER_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init); diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index ff188ab68496e..bb47610bbd1c4 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -565,4 +565,3 @@ void __init hv_init_clocksource(void) hv_sched_clock_offset = hv_read_reference_counter(); hv_setup_sched_clock(read_hv_sched_clock_msr); } -EXPORT_SYMBOL_GPL(hv_init_clocksource); diff --git a/drivers/clocksource/jcore-pit.c b/drivers/clocksource/jcore-pit.c index 5d3d88e0fc8c0..a4a991101fa31 100644 --- a/drivers/clocksource/jcore-pit.c +++ b/drivers/clocksource/jcore-pit.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * J-Core SoC PIT/clocksource driver * * Copyright (C) 2015-2016 Smart Energy Instruments, Inc. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #include <linux/kernel.h> diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c index be4175f415ba5..b3ae38f367205 100644 --- a/drivers/clocksource/mips-gic-timer.c +++ b/drivers/clocksource/mips-gic-timer.c @@ -1,10 +1,5 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. - */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. #define pr_fmt(fmt) "mips-gic-timer: " fmt diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index dd0956ad969c1..64dcb082d4cf6 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -981,6 +981,14 @@ static const struct of_device_id sh_cmt_of_table[] __maybe_unused = { .compatible = "renesas,rcar-gen3-cmt1", .data = &sh_cmt_info[SH_CMT1_RCAR_GEN2] }, + { + .compatible = "renesas,rcar-gen4-cmt0", + .data = &sh_cmt_info[SH_CMT0_RCAR_GEN2] + }, + { + .compatible = "renesas,rcar-gen4-cmt1", + .data = &sh_cmt_info[SH_CMT1_RCAR_GEN2] + }, { } }; MODULE_DEVICE_TABLE(of, sh_cmt_of_table); diff --git a/drivers/clocksource/timer-armada-370-xp.c b/drivers/clocksource/timer-armada-370-xp.c index e3acc3c631b72..6ec565d6939ad 100644 --- a/drivers/clocksource/timer-armada-370-xp.c +++ b/drivers/clocksource/timer-armada-370-xp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Marvell Armada 370/XP SoC timer handling. * @@ -7,10 +8,6 @@ * Gregory CLEMENT <gregory.clement@free-electrons.com> * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * Timer 0 is used as free-running clocksource, while timer 1 is * used as clock_event_device. * diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c index 1e984a4d8ad00..559aa96089c34 100644 --- a/drivers/clocksource/timer-digicolor.c +++ b/drivers/clocksource/timer-digicolor.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Conexant Digicolor timer driver * @@ -11,10 +12,6 @@ * Copyright (C) 2013 Maxime Ripard * * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ /* diff --git a/drivers/clocksource/timer-goldfish.c b/drivers/clocksource/timer-goldfish.c new file mode 100644 index 0000000000000..0512d5eabc82a --- /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/clocksource/timer-gxp.c b/drivers/clocksource/timer-gxp.c new file mode 100644 index 0000000000000..8b38b32123880 --- /dev/null +++ b/drivers/clocksource/timer-gxp.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ + +#include <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/interrupt.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/sched_clock.h> + +#define TIMER0_FREQ 1000000 +#define GXP_TIMER_CNT_OFS 0x00 +#define GXP_TIMESTAMP_OFS 0x08 +#define GXP_TIMER_CTRL_OFS 0x14 + +/* TCS Stands for Timer Control/Status: these are masks to be used in */ +/* the Timer Count Registers */ +#define MASK_TCS_ENABLE 0x01 +#define MASK_TCS_PERIOD 0x02 +#define MASK_TCS_RELOAD 0x04 +#define MASK_TCS_TC 0x80 + +struct gxp_timer { + void __iomem *counter; + void __iomem *control; + struct clock_event_device evt; +}; + +static struct gxp_timer *gxp_timer; + +static void __iomem *system_clock __ro_after_init; + +static inline struct gxp_timer *to_gxp_timer(struct clock_event_device *evt_dev) +{ + return container_of(evt_dev, struct gxp_timer, evt); +} + +static u64 notrace gxp_sched_read(void) +{ + return readl_relaxed(system_clock); +} + +static int gxp_time_set_next_event(unsigned long event, struct clock_event_device *evt_dev) +{ + struct gxp_timer *timer = to_gxp_timer(evt_dev); + + /* Stop counting and disable interrupt before updating */ + writeb_relaxed(MASK_TCS_TC, timer->control); + writel_relaxed(event, timer->counter); + writeb_relaxed(MASK_TCS_TC | MASK_TCS_ENABLE, timer->control); + + return 0; +} + +static irqreturn_t gxp_timer_interrupt(int irq, void *dev_id) +{ + struct gxp_timer *timer = (struct gxp_timer *)dev_id; + + if (!(readb_relaxed(timer->control) & MASK_TCS_TC)) + return IRQ_NONE; + + writeb_relaxed(MASK_TCS_TC, timer->control); + + timer->evt.event_handler(&timer->evt); + + return IRQ_HANDLED; +} + +static int __init gxp_timer_init(struct device_node *node) +{ + void __iomem *base; + struct clk *clk; + u32 freq; + int ret, irq; + + gxp_timer = kzalloc(sizeof(*gxp_timer), GFP_KERNEL); + if (!gxp_timer) { + ret = -ENOMEM; + pr_err("Can't allocate gxp_timer"); + return ret; + } + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + ret = (int)PTR_ERR(clk); + pr_err("%pOFn clock not found: %d\n", node, ret); + goto err_free; + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("%pOFn clock enable failed: %d\n", node, ret); + goto err_clk_enable; + } + + base = of_iomap(node, 0); + if (!base) { + ret = -ENXIO; + pr_err("Can't map timer base registers"); + goto err_iomap; + } + + /* Set the offsets to the clock register and timer registers */ + gxp_timer->counter = base + GXP_TIMER_CNT_OFS; + gxp_timer->control = base + GXP_TIMER_CTRL_OFS; + system_clock = base + GXP_TIMESTAMP_OFS; + + gxp_timer->evt.name = node->name; + gxp_timer->evt.rating = 300; + gxp_timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; + gxp_timer->evt.set_next_event = gxp_time_set_next_event; + gxp_timer->evt.cpumask = cpumask_of(0); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + ret = -EINVAL; + pr_err("GXP Timer Can't parse IRQ %d", irq); + goto err_exit; + } + + freq = clk_get_rate(clk); + + ret = clocksource_mmio_init(system_clock, node->name, freq, + 300, 32, clocksource_mmio_readl_up); + if (ret) { + pr_err("%pOFn init clocksource failed: %d", node, ret); + goto err_exit; + } + + sched_clock_register(gxp_sched_read, 32, freq); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + ret = -EINVAL; + pr_err("%pOFn Can't parse IRQ %d", node, irq); + goto err_exit; + } + + clockevents_config_and_register(&gxp_timer->evt, TIMER0_FREQ, + 0xf, 0xffffffff); + + ret = request_irq(irq, gxp_timer_interrupt, IRQF_TIMER | IRQF_SHARED, + node->name, gxp_timer); + if (ret) { + pr_err("%pOFn request_irq() failed: %d", node, ret); + goto err_exit; + } + + pr_debug("gxp: system timer (irq = %d)\n", irq); + return 0; + +err_exit: + iounmap(base); +err_iomap: + clk_disable_unprepare(clk); +err_clk_enable: + clk_put(clk); +err_free: + kfree(gxp_timer); + return ret; +} + +/* + * This probe gets called after the timer is already up and running. This will create + * the watchdog device as a child since the registers are shared. + */ + +static int gxp_timer_probe(struct platform_device *pdev) +{ + struct platform_device *gxp_watchdog_device; + struct device *dev = &pdev->dev; + + if (!gxp_timer) { + pr_err("Gxp Timer not initialized, cannot create watchdog"); + return -ENOMEM; + } + + gxp_watchdog_device = platform_device_alloc("gxp-wdt", -1); + if (!gxp_watchdog_device) { + pr_err("Timer failed to allocate gxp-wdt"); + return -ENOMEM; + } + + /* Pass the base address (counter) as platform data and nothing else */ + gxp_watchdog_device->dev.platform_data = gxp_timer->counter; + gxp_watchdog_device->dev.parent = dev; + + return platform_device_add(gxp_watchdog_device); +} + +static const struct of_device_id gxp_timer_of_match[] = { + { .compatible = "hpe,gxp-timer", }, + {}, +}; + +static struct platform_driver gxp_timer_driver = { + .probe = gxp_timer_probe, + .driver = { + .name = "gxp-timer", + .of_match_table = gxp_timer_of_match, + .suppress_bind_attrs = true, + }, +}; + +builtin_platform_driver(gxp_timer_driver); + +TIMER_OF_DECLARE(gxp, "hpe,gxp-timer", gxp_timer_init); diff --git a/drivers/clocksource/timer-ixp4xx.c b/drivers/clocksource/timer-ixp4xx.c index cbb184953510b..720ed70a2964f 100644 --- a/drivers/clocksource/timer-ixp4xx.c +++ b/drivers/clocksource/timer-ixp4xx.c @@ -19,8 +19,6 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> -/* Goes away with OF conversion */ -#include <linux/platform_data/timer-ixp4xx.h> /* * Constants to make it easy to access Timer Control/Status registers @@ -263,28 +261,6 @@ static struct platform_driver ixp4xx_timer_driver = { }; builtin_platform_driver(ixp4xx_timer_driver); -/** - * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles - * @timerbase: physical base of timer block - * @timer_irq: Linux IRQ number for the timer - * @timer_freq: Fixed frequency of the timer - */ -void __init ixp4xx_timer_setup(resource_size_t timerbase, - int timer_irq, - unsigned int timer_freq) -{ - void __iomem *base; - - base = ioremap(timerbase, 0x100); - if (!base) { - pr_crit("IXP4xx: can't remap timer\n"); - return; - } - ixp4xx_timer_register(base, timer_irq, timer_freq); -} -EXPORT_SYMBOL_GPL(ixp4xx_timer_setup); - -#ifdef CONFIG_OF static __init int ixp4xx_of_timer_init(struct device_node *np) { void __iomem *base; @@ -315,4 +291,3 @@ out_unmap: return ret; } TIMER_OF_DECLARE(ixp4xx, "intel,ixp4xx-timer", ixp4xx_of_timer_init); -#endif diff --git a/drivers/clocksource/timer-lpc32xx.c b/drivers/clocksource/timer-lpc32xx.c index d51a62a79ef76..68eae6378bf33 100644 --- a/drivers/clocksource/timer-lpc32xx.c +++ b/drivers/clocksource/timer-lpc32xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Clocksource driver for NXP LPC32xx/18xx/43xx timer * @@ -6,11 +7,6 @@ * Based on: * time-efm32 Copyright (C) 2013 Pengutronix * mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * */ #define pr_fmt(fmt) "%s: " fmt, __func__ diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c index 7bcb4a3f26fb0..d5b29fd03ca2a 100644 --- a/drivers/clocksource/timer-mediatek.c +++ b/drivers/clocksource/timer-mediatek.c @@ -22,6 +22,19 @@ #define TIMER_SYNC_TICKS (3) +/* cpux mcusys wrapper */ +#define CPUX_CON_REG 0x0 +#define CPUX_IDX_REG 0x4 + +/* cpux */ +#define CPUX_IDX_GLOBAL_CTRL 0x0 + #define CPUX_ENABLE BIT(0) + #define CPUX_CLK_DIV_MASK GENMASK(10, 8) + #define CPUX_CLK_DIV1 BIT(8) + #define CPUX_CLK_DIV2 BIT(9) + #define CPUX_CLK_DIV4 BIT(10) +#define CPUX_IDX_GLOBAL_IRQ 0x30 + /* gpt */ #define GPT_IRQ_EN_REG 0x00 #define GPT_IRQ_ENABLE(val) BIT((val) - 1) @@ -72,6 +85,52 @@ static void __iomem *gpt_sched_reg __read_mostly; +static u32 mtk_cpux_readl(u32 reg_idx, struct timer_of *to) +{ + writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG); + return readl(timer_of_base(to) + CPUX_CON_REG); +} + +static void mtk_cpux_writel(u32 val, u32 reg_idx, struct timer_of *to) +{ + writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG); + writel(val, timer_of_base(to) + CPUX_CON_REG); +} + +static void mtk_cpux_set_irq(struct timer_of *to, bool enable) +{ + const unsigned long *irq_mask = cpumask_bits(cpu_possible_mask); + u32 val; + + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_IRQ, to); + + if (enable) + val |= *irq_mask; + else + val &= ~(*irq_mask); + + mtk_cpux_writel(val, CPUX_IDX_GLOBAL_IRQ, to); +} + +static int mtk_cpux_clkevt_shutdown(struct clock_event_device *clkevt) +{ + /* Clear any irq */ + mtk_cpux_set_irq(to_timer_of(clkevt), false); + + /* + * Disabling CPUXGPT timer will crash the platform, especially + * if Trusted Firmware is using it (usually, for sleep states), + * so we only mask the IRQ and call it a day. + */ + return 0; +} + +static int mtk_cpux_clkevt_resume(struct clock_event_device *clkevt) +{ + mtk_cpux_set_irq(to_timer_of(clkevt), true); + return 0; +} + static void mtk_syst_ack_irq(struct timer_of *to) { /* Clear and disable interrupt */ @@ -281,6 +340,60 @@ static struct timer_of to = { }, }; +static int __init mtk_cpux_init(struct device_node *node) +{ + static struct timer_of to_cpux; + u32 freq, val; + int ret; + + /* + * There are per-cpu interrupts for the CPUX General Purpose Timer + * but since this timer feeds the AArch64 System Timer we can rely + * on the CPU timer PPIs as well, so we don't declare TIMER_OF_IRQ. + */ + to_cpux.flags = TIMER_OF_BASE | TIMER_OF_CLOCK; + to_cpux.clkevt.name = "mtk-cpuxgpt"; + to_cpux.clkevt.rating = 10; + to_cpux.clkevt.cpumask = cpu_possible_mask; + to_cpux.clkevt.set_state_shutdown = mtk_cpux_clkevt_shutdown; + to_cpux.clkevt.tick_resume = mtk_cpux_clkevt_resume; + + /* If this fails, bad things are about to happen... */ + ret = timer_of_init(node, &to_cpux); + if (ret) { + WARN(1, "Cannot start CPUX timers.\n"); + return ret; + } + + /* + * Check if we're given a clock with the right frequency for this + * timer, otherwise warn but keep going with the setup anyway, as + * that makes it possible to still boot the kernel, even though + * it may not work correctly (random lockups, etc). + * The reason behind this is that having an early UART may not be + * possible for everyone and this gives a chance to retrieve kmsg + * for eventual debugging even on consumer devices. + */ + freq = timer_of_rate(&to_cpux); + if (freq > 13000000) + WARN(1, "Requested unsupported timer frequency %u\n", freq); + + /* Clock input is 26MHz, set DIV2 to achieve 13MHz clock */ + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux); + val &= ~CPUX_CLK_DIV_MASK; + val |= CPUX_CLK_DIV2; + mtk_cpux_writel(val, CPUX_IDX_GLOBAL_CTRL, &to_cpux); + + /* Enable all CPUXGPT timers */ + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux); + mtk_cpux_writel(val | CPUX_ENABLE, CPUX_IDX_GLOBAL_CTRL, &to_cpux); + + clockevents_config_and_register(&to_cpux.clkevt, timer_of_rate(&to_cpux), + TIMER_SYNC_TICKS, 0xffffffff); + + return 0; +} + static int __init mtk_syst_init(struct device_node *node) { int ret; @@ -339,3 +452,4 @@ static int __init mtk_gpt_init(struct device_node *node) } TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init); +TIMER_OF_DECLARE(mtk_mt6795, "mediatek,mt6795-systimer", mtk_cpux_init); diff --git a/drivers/clocksource/timer-microchip-pit64b.c b/drivers/clocksource/timer-microchip-pit64b.c index abce83d2f00bd..d5f1436f33d96 100644 --- a/drivers/clocksource/timer-microchip-pit64b.c +++ b/drivers/clocksource/timer-microchip-pit64b.c @@ -61,7 +61,7 @@ struct mchp_pit64b_timer { }; /** - * mchp_pit64b_clkevt - PIT64B clockevent data structure + * struct mchp_pit64b_clkevt - PIT64B clockevent data structure * @timer: PIT64B timer * @clkevt: clockevent */ @@ -75,7 +75,7 @@ struct mchp_pit64b_clkevt { struct mchp_pit64b_clkevt, clkevt)) /** - * mchp_pit64b_clksrc - PIT64B clocksource data structure + * struct mchp_pit64b_clksrc - PIT64B clocksource data structure * @timer: PIT64B timer * @clksrc: clocksource */ @@ -173,7 +173,8 @@ static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev) { struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); - writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR); + if (!clockevent_state_detached(cedev)) + mchp_pit64b_suspend(timer); return 0; } @@ -182,35 +183,37 @@ static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev) { struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); + if (clockevent_state_shutdown(cedev)) + mchp_pit64b_resume(timer); + mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT, MCHP_PIT64B_IER_PERIOD); return 0; } -static int mchp_pit64b_clkevt_set_next_event(unsigned long evt, - struct clock_event_device *cedev) +static int mchp_pit64b_clkevt_set_oneshot(struct clock_event_device *cedev) { struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); - mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT, + if (clockevent_state_shutdown(cedev)) + mchp_pit64b_resume(timer); + + mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_ONE_SHOT, MCHP_PIT64B_IER_PERIOD); return 0; } -static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev) +static int mchp_pit64b_clkevt_set_next_event(unsigned long evt, + struct clock_event_device *cedev) { struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); - mchp_pit64b_suspend(timer); -} - -static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev) -{ - struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev); + mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT, + MCHP_PIT64B_IER_PERIOD); - mchp_pit64b_resume(timer); + return 0; } static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id) @@ -242,8 +245,10 @@ static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate, } /** - * mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at - * runtime; this includes prescaler and SGCLK bit + * mchp_pit64b_init_mode() - prepare PIT64B mode register value to be used at + * runtime; this includes prescaler and SGCLK bit + * @timer: pointer to pit64b timer to init + * @max_rate: maximum rate that timer's clock could use * * PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to * be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate @@ -341,6 +346,7 @@ static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer, if (!cs) return -ENOMEM; + mchp_pit64b_resume(timer); mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0); mchp_pit64b_cs_base = timer->base; @@ -362,8 +368,7 @@ static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer, pr_debug("clksrc: Failed to register PIT64B clocksource!\n"); /* Stop timer. */ - writel_relaxed(MCHP_PIT64B_CR_SWRST, - timer->base + MCHP_PIT64B_CR); + mchp_pit64b_suspend(timer); kfree(cs); return ret; @@ -395,9 +400,8 @@ static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer, ce->clkevt.rating = 150; ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown; ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic; + ce->clkevt.set_state_oneshot = mchp_pit64b_clkevt_set_oneshot; ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event; - ce->clkevt.suspend = mchp_pit64b_clkevt_suspend; - ce->clkevt.resume = mchp_pit64b_clkevt_resume; ce->clkevt.cpumask = cpumask_of(0); ce->clkevt.irq = irq; @@ -448,19 +452,10 @@ static int __init mchp_pit64b_dt_init_timer(struct device_node *node, if (ret) goto irq_unmap; - ret = clk_prepare_enable(timer.pclk); - if (ret) - goto irq_unmap; - - if (timer.mode & MCHP_PIT64B_MR_SGCLK) { - ret = clk_prepare_enable(timer.gclk); - if (ret) - goto pclk_unprepare; - + if (timer.mode & MCHP_PIT64B_MR_SGCLK) clk_rate = clk_get_rate(timer.gclk); - } else { + else clk_rate = clk_get_rate(timer.pclk); - } clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1); if (clkevt) @@ -469,15 +464,10 @@ static int __init mchp_pit64b_dt_init_timer(struct device_node *node, ret = mchp_pit64b_init_clksrc(&timer, clk_rate); if (ret) - goto gclk_unprepare; + goto irq_unmap; return 0; -gclk_unprepare: - if (timer.mode & MCHP_PIT64B_MR_SGCLK) - clk_disable_unprepare(timer.gclk); -pclk_unprepare: - clk_disable_unprepare(timer.pclk); irq_unmap: irq_dispose_mapping(irq); io_unmap: diff --git a/drivers/clocksource/timer-orion.c b/drivers/clocksource/timer-orion.c index 5101e834d78ff..49e86cb70a7a8 100644 --- a/drivers/clocksource/timer-orion.c +++ b/drivers/clocksource/timer-orion.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Marvell Orion SoC timer handling. * * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * Timer 0 is used as free-running clocksource, while timer 1 is * used as clock_event_device. */ diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c index 56c0cc32d0ac6..d514b44e67dd1 100644 --- a/drivers/clocksource/timer-oxnas-rps.c +++ b/drivers/clocksource/timer-oxnas-rps.c @@ -236,7 +236,7 @@ static int __init oxnas_rps_timer_init(struct device_node *np) } rps->irq = irq_of_parse_and_map(np, 0); - if (rps->irq < 0) { + if (!rps->irq) { ret = -EINVAL; goto err_iomap; } diff --git a/drivers/clocksource/timer-pistachio.c b/drivers/clocksource/timer-pistachio.c index 69c069e6f0a21..57b2197a0b67b 100644 --- a/drivers/clocksource/timer-pistachio.c +++ b/drivers/clocksource/timer-pistachio.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Pistachio clocksource based on general-purpose timers * * Copyright (C) 2015 Imagination Technologies - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ #define pr_fmt(fmt) "%s: " fmt, __func__ diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index 1767f8bf20133..969a552da8d29 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -7,6 +7,9 @@ * either be read from the "time" and "timeh" CSRs, and can use the SBI to * setup events, or directly accessed using MMIO registers. */ + +#define pr_fmt(fmt) "riscv-timer: " fmt + #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/cpu.h> @@ -20,21 +23,35 @@ #include <linux/of_irq.h> #include <clocksource/timer-riscv.h> #include <asm/smp.h> +#include <asm/hwcap.h> #include <asm/sbi.h> #include <asm/timex.h> +static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available); + static int riscv_clock_next_event(unsigned long delta, struct clock_event_device *ce) { + u64 next_tval = get_cycles64() + delta; + csr_set(CSR_IE, IE_TIE); - sbi_set_timer(get_cycles64() + delta); + if (static_branch_likely(&riscv_sstc_available)) { +#if defined(CONFIG_32BIT) + csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF); + csr_write(CSR_STIMECMPH, next_tval >> 32); +#else + csr_write(CSR_STIMECMP, next_tval); +#endif + } else + sbi_set_timer(next_tval); + return 0; } static unsigned int riscv_clock_event_irq; static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = { .name = "riscv_timer_clockevent", - .features = CLOCK_EVT_FEAT_ONESHOT, + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP, .rating = 100, .set_next_event = riscv_clock_next_event, }; @@ -101,20 +118,21 @@ static irqreturn_t riscv_timer_interrupt(int irq, void *dev_id) static int __init riscv_timer_init_dt(struct device_node *n) { - int cpuid, hartid, error; + int cpuid, error; + unsigned long hartid; struct device_node *child; struct irq_domain *domain; - hartid = riscv_of_processor_hartid(n); - if (hartid < 0) { - pr_warn("Not valid hartid for node [%pOF] error = [%d]\n", + error = riscv_of_processor_hartid(n, &hartid); + if (error < 0) { + pr_warn("Not valid hartid for node [%pOF] error = [%lu]\n", n, hartid); - return hartid; + return error; } cpuid = riscv_hartid_to_cpuid(hartid); if (cpuid < 0) { - pr_warn("Invalid cpuid for hartid [%d]\n", hartid); + pr_warn("Invalid cpuid for hartid [%lu]\n", hartid); return cpuid; } @@ -140,7 +158,7 @@ static int __init riscv_timer_init_dt(struct device_node *n) return -ENODEV; } - pr_info("%s: Registering clocksource cpuid [%d] hartid [%d]\n", + pr_info("%s: Registering clocksource cpuid [%d] hartid [%lu]\n", __func__, cpuid, hartid); error = clocksource_register_hz(&riscv_clocksource, riscv_timebase); if (error) { @@ -165,6 +183,12 @@ static int __init riscv_timer_init_dt(struct device_node *n) if (error) 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 401d592e85f5a..e6a87f4af2b50 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -259,6 +259,11 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time struct clk *clk1, *clk2; const char *name = of_get_property(np, "compatible", NULL); + if (initialized) { + pr_debug("%pOF: skipping further SP804 timer device\n", np); + return 0; + } + base = of_iomap(np, 0); if (!base) return -ENXIO; @@ -270,11 +275,6 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time writel(0, timer1_base + timer->ctrl); writel(0, timer2_base + timer->ctrl); - if (initialized || !of_device_is_available(np)) { - ret = -EINVAL; - goto err; - } - clk1 = of_clk_get(np, 0); if (IS_ERR(clk1)) clk1 = NULL; diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c index 0ba8155b8287d..94dc6e42e983d 100644 --- a/drivers/clocksource/timer-sun4i.c +++ b/drivers/clocksource/timer-sun4i.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Allwinner A1X SoCs timer handling. * @@ -8,10 +9,6 @@ * Based on code from * Allwinner Technology Co., Ltd. <www.allwinnertech.com> * Benn Huang <benn@allwinnertech.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> @@ -131,7 +128,7 @@ static void sun4i_timer_clear_interrupt(void __iomem *base) static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = (struct clock_event_device *)dev_id; + struct clock_event_device *evt = dev_id; struct timer_of *to = to_timer_of(evt); sun4i_timer_clear_interrupt(timer_of_base(to)); diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 552c5254390cb..7d5fa90699061 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Allwinner SoCs hstimer driver. * * Copyright (C) 2013 Maxime Ripard * * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> @@ -145,7 +142,7 @@ static int sun5i_clkevt_next_event(unsigned long evt, static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) { - struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id; + struct sun5i_timer_clkevt *ce = dev_id; writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG); ce->clkevt.event_handler(&ce->clkevt); diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c new file mode 100644 index 0000000000000..ea742889ee067 --- /dev/null +++ b/drivers/clocksource/timer-tegra186.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved. + */ + +#include <linux/clocksource.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +/* shared registers */ +#define TKETSC0 0x000 +#define TKETSC1 0x004 +#define TKEUSEC 0x008 +#define TKEOSC 0x00c + +#define TKEIE(x) (0x100 + ((x) * 4)) +#define TKEIE_WDT_MASK(x, y) ((y) << (16 + 4 * (x))) + +/* timer registers */ +#define TMRCR 0x000 +#define TMRCR_ENABLE BIT(31) +#define TMRCR_PERIODIC BIT(30) +#define TMRCR_PTV(x) ((x) & 0x0fffffff) + +#define TMRSR 0x004 +#define TMRSR_INTR_CLR BIT(30) + +#define TMRCSSR 0x008 +#define TMRCSSR_SRC_USEC (0 << 0) + +/* watchdog registers */ +#define WDTCR 0x000 +#define WDTCR_SYSTEM_POR_RESET_ENABLE BIT(16) +#define WDTCR_SYSTEM_DEBUG_RESET_ENABLE BIT(15) +#define WDTCR_REMOTE_INT_ENABLE BIT(14) +#define WDTCR_LOCAL_FIQ_ENABLE BIT(13) +#define WDTCR_LOCAL_INT_ENABLE BIT(12) +#define WDTCR_PERIOD_MASK (0xff << 4) +#define WDTCR_PERIOD(x) (((x) & 0xff) << 4) +#define WDTCR_TIMER_SOURCE_MASK 0xf +#define WDTCR_TIMER_SOURCE(x) ((x) & 0xf) + +#define WDTCMDR 0x008 +#define WDTCMDR_DISABLE_COUNTER BIT(1) +#define WDTCMDR_START_COUNTER BIT(0) + +#define WDTUR 0x00c +#define WDTUR_UNLOCK_PATTERN 0x0000c45a + +struct tegra186_timer_soc { + unsigned int num_timers; + unsigned int num_wdts; +}; + +struct tegra186_tmr { + struct tegra186_timer *parent; + void __iomem *regs; + unsigned int index; + unsigned int hwirq; +}; + +struct tegra186_wdt { + struct watchdog_device base; + + void __iomem *regs; + unsigned int index; + bool locked; + + struct tegra186_tmr *tmr; +}; + +static inline struct tegra186_wdt *to_tegra186_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct tegra186_wdt, base); +} + +struct tegra186_timer { + const struct tegra186_timer_soc *soc; + struct device *dev; + void __iomem *regs; + + struct tegra186_wdt *wdt; + struct clocksource usec; + struct clocksource tsc; + struct clocksource osc; +}; + +static void tmr_writel(struct tegra186_tmr *tmr, u32 value, unsigned int offset) +{ + writel_relaxed(value, tmr->regs + offset); +} + +static void wdt_writel(struct tegra186_wdt *wdt, u32 value, unsigned int offset) +{ + writel_relaxed(value, wdt->regs + offset); +} + +static u32 wdt_readl(struct tegra186_wdt *wdt, unsigned int offset) +{ + return readl_relaxed(wdt->regs + offset); +} + +static struct tegra186_tmr *tegra186_tmr_create(struct tegra186_timer *tegra, + unsigned int index) +{ + unsigned int offset = 0x10000 + index * 0x10000; + struct tegra186_tmr *tmr; + + tmr = devm_kzalloc(tegra->dev, sizeof(*tmr), GFP_KERNEL); + if (!tmr) + return ERR_PTR(-ENOMEM); + + tmr->parent = tegra; + tmr->regs = tegra->regs + offset; + tmr->index = index; + tmr->hwirq = 0; + + return tmr; +} + +static const struct watchdog_info tegra186_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "NVIDIA Tegra186 WDT", +}; + +static void tegra186_wdt_disable(struct tegra186_wdt *wdt) +{ + /* unlock and disable the watchdog */ + wdt_writel(wdt, WDTUR_UNLOCK_PATTERN, WDTUR); + wdt_writel(wdt, WDTCMDR_DISABLE_COUNTER, WDTCMDR); + + /* disable timer */ + tmr_writel(wdt->tmr, 0, TMRCR); +} + +static void tegra186_wdt_enable(struct tegra186_wdt *wdt) +{ + struct tegra186_timer *tegra = wdt->tmr->parent; + u32 value; + + /* unmask hardware IRQ, this may have been lost across powergate */ + value = TKEIE_WDT_MASK(wdt->index, 1); + writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq)); + + /* clear interrupt */ + tmr_writel(wdt->tmr, TMRSR_INTR_CLR, TMRSR); + + /* select microsecond source */ + tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR); + + /* configure timer (system reset happens on the fifth expiration) */ + value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) | + TMRCR_PERIODIC | TMRCR_ENABLE; + tmr_writel(wdt->tmr, value, TMRCR); + + if (!wdt->locked) { + value = wdt_readl(wdt, WDTCR); + + /* select the proper timer source */ + value &= ~WDTCR_TIMER_SOURCE_MASK; + value |= WDTCR_TIMER_SOURCE(wdt->tmr->index); + + /* single timer period since that's already configured */ + value &= ~WDTCR_PERIOD_MASK; + value |= WDTCR_PERIOD(1); + + /* enable local interrupt for WDT petting */ + value |= WDTCR_LOCAL_INT_ENABLE; + + /* enable local FIQ and remote interrupt for debug dump */ + if (0) + value |= WDTCR_REMOTE_INT_ENABLE | + WDTCR_LOCAL_FIQ_ENABLE; + + /* enable system debug reset (doesn't properly reboot) */ + if (0) + value |= WDTCR_SYSTEM_DEBUG_RESET_ENABLE; + + /* enable system POR reset */ + value |= WDTCR_SYSTEM_POR_RESET_ENABLE; + + wdt_writel(wdt, value, WDTCR); + } + + wdt_writel(wdt, WDTCMDR_START_COUNTER, WDTCMDR); +} + +static int tegra186_wdt_start(struct watchdog_device *wdd) +{ + struct tegra186_wdt *wdt = to_tegra186_wdt(wdd); + + tegra186_wdt_enable(wdt); + + return 0; +} + +static int tegra186_wdt_stop(struct watchdog_device *wdd) +{ + struct tegra186_wdt *wdt = to_tegra186_wdt(wdd); + + tegra186_wdt_disable(wdt); + + return 0; +} + +static int tegra186_wdt_ping(struct watchdog_device *wdd) +{ + struct tegra186_wdt *wdt = to_tegra186_wdt(wdd); + + tegra186_wdt_disable(wdt); + tegra186_wdt_enable(wdt); + + return 0; +} + +static int tegra186_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct tegra186_wdt *wdt = to_tegra186_wdt(wdd); + + if (watchdog_active(&wdt->base)) + tegra186_wdt_disable(wdt); + + wdt->base.timeout = timeout; + + if (watchdog_active(&wdt->base)) + tegra186_wdt_enable(wdt); + + return 0; +} + +static const struct watchdog_ops tegra186_wdt_ops = { + .owner = THIS_MODULE, + .start = tegra186_wdt_start, + .stop = tegra186_wdt_stop, + .ping = tegra186_wdt_ping, + .set_timeout = tegra186_wdt_set_timeout, +}; + +static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra, + unsigned int index) +{ + unsigned int offset = 0x10000, source; + struct tegra186_wdt *wdt; + u32 value; + int err; + + offset += tegra->soc->num_timers * 0x10000 + index * 0x10000; + + wdt = devm_kzalloc(tegra->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return ERR_PTR(-ENOMEM); + + wdt->regs = tegra->regs + offset; + wdt->index = index; + + /* read the watchdog configuration since it might be locked down */ + value = wdt_readl(wdt, WDTCR); + + if (value & WDTCR_LOCAL_INT_ENABLE) + wdt->locked = true; + + source = value & WDTCR_TIMER_SOURCE_MASK; + + wdt->tmr = tegra186_tmr_create(tegra, source); + if (IS_ERR(wdt->tmr)) + return ERR_CAST(wdt->tmr); + + wdt->base.info = &tegra186_wdt_info; + wdt->base.ops = &tegra186_wdt_ops; + wdt->base.min_timeout = 1; + wdt->base.max_timeout = 255; + wdt->base.parent = tegra->dev; + + err = watchdog_init_timeout(&wdt->base, 5, tegra->dev); + if (err < 0) { + dev_err(tegra->dev, "failed to initialize timeout: %d\n", err); + return ERR_PTR(err); + } + + err = devm_watchdog_register_device(tegra->dev, &wdt->base); + if (err < 0) { + dev_err(tegra->dev, "failed to register WDT: %d\n", err); + return ERR_PTR(err); + } + + return wdt; +} + +static u64 tegra186_timer_tsc_read(struct clocksource *cs) +{ + struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer, + tsc); + u32 hi, lo, ss; + + hi = readl_relaxed(tegra->regs + TKETSC1); + + /* + * The 56-bit value of the TSC is spread across two registers that are + * not synchronized. In order to read them atomically, ensure that the + * high 24 bits match before and after reading the low 32 bits. + */ + do { + /* snapshot the high 24 bits */ + ss = hi; + + lo = readl_relaxed(tegra->regs + TKETSC0); + hi = readl_relaxed(tegra->regs + TKETSC1); + } while (hi != ss); + + return (u64)hi << 32 | lo; +} + +static int tegra186_timer_tsc_init(struct tegra186_timer *tegra) +{ + tegra->tsc.name = "tsc"; + tegra->tsc.rating = 300; + tegra->tsc.read = tegra186_timer_tsc_read; + tegra->tsc.mask = CLOCKSOURCE_MASK(56); + tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + return clocksource_register_hz(&tegra->tsc, 31250000); +} + +static u64 tegra186_timer_osc_read(struct clocksource *cs) +{ + struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer, + osc); + + return readl_relaxed(tegra->regs + TKEOSC); +} + +static int tegra186_timer_osc_init(struct tegra186_timer *tegra) +{ + tegra->osc.name = "osc"; + tegra->osc.rating = 300; + tegra->osc.read = tegra186_timer_osc_read; + tegra->osc.mask = CLOCKSOURCE_MASK(32); + tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + return clocksource_register_hz(&tegra->osc, 38400000); +} + +static u64 tegra186_timer_usec_read(struct clocksource *cs) +{ + struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer, + usec); + + return readl_relaxed(tegra->regs + TKEUSEC); +} + +static int tegra186_timer_usec_init(struct tegra186_timer *tegra) +{ + tegra->usec.name = "usec"; + tegra->usec.rating = 300; + tegra->usec.read = tegra186_timer_usec_read; + tegra->usec.mask = CLOCKSOURCE_MASK(32); + tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS; + + return clocksource_register_hz(&tegra->usec, USEC_PER_SEC); +} + +static irqreturn_t tegra186_timer_irq(int irq, void *data) +{ + struct tegra186_timer *tegra = data; + + if (watchdog_active(&tegra->wdt->base)) { + tegra186_wdt_disable(tegra->wdt); + tegra186_wdt_enable(tegra->wdt); + } + + return IRQ_HANDLED; +} + +static int tegra186_timer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra186_timer *tegra; + unsigned int irq; + int err; + + tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return -ENOMEM; + + tegra->soc = of_device_get_match_data(dev); + dev_set_drvdata(dev, tegra); + tegra->dev = dev; + + tegra->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tegra->regs)) + return PTR_ERR(tegra->regs); + + err = platform_get_irq(pdev, 0); + if (err < 0) + return err; + + irq = 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; + } + + err = tegra186_timer_tsc_init(tegra); + if (err < 0) { + dev_err(dev, "failed to register TSC counter: %d\n", err); + return err; + } + + err = tegra186_timer_osc_init(tegra); + if (err < 0) { + dev_err(dev, "failed to register OSC counter: %d\n", err); + goto unregister_tsc; + } + + err = tegra186_timer_usec_init(tegra); + if (err < 0) { + dev_err(dev, "failed to register USEC counter: %d\n", err); + goto unregister_osc; + } + + err = devm_request_irq(dev, irq, tegra186_timer_irq, 0, + "tegra186-timer", tegra); + if (err < 0) { + dev_err(dev, "failed to request IRQ#%u: %d\n", irq, err); + goto unregister_usec; + } + + return 0; + +unregister_usec: + clocksource_unregister(&tegra->usec); +unregister_osc: + clocksource_unregister(&tegra->osc); +unregister_tsc: + clocksource_unregister(&tegra->tsc); + return err; +} + +static int tegra186_timer_remove(struct platform_device *pdev) +{ + struct tegra186_timer *tegra = platform_get_drvdata(pdev); + + clocksource_unregister(&tegra->usec); + clocksource_unregister(&tegra->osc); + clocksource_unregister(&tegra->tsc); + + return 0; +} + +static int __maybe_unused tegra186_timer_suspend(struct device *dev) +{ + struct tegra186_timer *tegra = dev_get_drvdata(dev); + + if (watchdog_active(&tegra->wdt->base)) + tegra186_wdt_disable(tegra->wdt); + + return 0; +} + +static int __maybe_unused tegra186_timer_resume(struct device *dev) +{ + struct tegra186_timer *tegra = dev_get_drvdata(dev); + + if (watchdog_active(&tegra->wdt->base)) + tegra186_wdt_enable(tegra->wdt); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend, + tegra186_timer_resume); + +static const struct tegra186_timer_soc tegra186_timer = { + .num_timers = 10, + .num_wdts = 3, +}; + +static const struct tegra186_timer_soc tegra234_timer = { + .num_timers = 16, + .num_wdts = 3, +}; + +static const struct of_device_id tegra186_timer_of_match[] = { + { .compatible = "nvidia,tegra186-timer", .data = &tegra186_timer }, + { .compatible = "nvidia,tegra234-timer", .data = &tegra234_timer }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra186_timer_of_match); + +static struct platform_driver tegra186_wdt_driver = { + .driver = { + .name = "tegra186-timer", + .pm = &tegra186_timer_pm_ops, + .of_match_table = tegra186_timer_of_match, + }, + .probe = tegra186_timer_probe, + .remove = tegra186_timer_remove, +}; +module_platform_driver(tegra186_wdt_driver); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 3e52c5226c4d9..469f7c91564b6 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -44,6 +44,121 @@ enum { REQUEST_BY_NODE, }; +static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg, + int posted) +{ + if (posted) + while (readl_relaxed(timer->pend) & (reg >> WPSHIFT)) + cpu_relax(); + + return readl_relaxed(timer->func_base + (reg & 0xff)); +} + +static inline void __omap_dm_timer_write(struct omap_dm_timer *timer, + u32 reg, u32 val, int posted) +{ + if (posted) + while (readl_relaxed(timer->pend) & (reg >> WPSHIFT)) + cpu_relax(); + + writel_relaxed(val, timer->func_base + (reg & 0xff)); +} + +static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer) +{ + u32 tidr; + + /* Assume v1 ip if bits [31:16] are zero */ + tidr = readl_relaxed(timer->io_base); + if (!(tidr >> 16)) { + timer->revision = 1; + timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET; + timer->irq_ena = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET; + timer->irq_dis = timer->io_base + OMAP_TIMER_V1_INT_EN_OFFSET; + timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET; + timer->func_base = timer->io_base; + } else { + timer->revision = 2; + timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS; + timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET; + timer->irq_dis = timer->io_base + OMAP_TIMER_V2_IRQENABLE_CLR; + timer->pend = timer->io_base + + _OMAP_TIMER_WRITE_PEND_OFFSET + + OMAP_TIMER_V2_FUNC_OFFSET; + timer->func_base = timer->io_base + OMAP_TIMER_V2_FUNC_OFFSET; + } +} + +/* + * __omap_dm_timer_enable_posted - enables write posted mode + * @timer: pointer to timer instance handle + * + * Enables the write posted mode for the timer. When posted mode is enabled + * writes to certain timer registers are immediately acknowledged by the + * internal bus and hence prevents stalling the CPU waiting for the write to + * complete. Enabling this feature can improve performance for writing to the + * timer registers. + */ +static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer) +{ + if (timer->posted) + return; + + if (timer->errata & OMAP_TIMER_ERRATA_I103_I767) { + timer->posted = OMAP_TIMER_NONPOSTED; + __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, 0, 0); + return; + } + + __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG, + OMAP_TIMER_CTRL_POSTED, 0); + timer->context.tsicr = OMAP_TIMER_CTRL_POSTED; + timer->posted = OMAP_TIMER_POSTED; +} + +static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer, + int posted, unsigned long rate) +{ + u32 l; + + l = __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted); + if (l & OMAP_TIMER_CTRL_ST) { + l &= ~0x1; + __omap_dm_timer_write(timer, OMAP_TIMER_CTRL_REG, l, posted); +#ifdef CONFIG_ARCH_OMAP2PLUS + /* Readback to make sure write has completed */ + __omap_dm_timer_read(timer, OMAP_TIMER_CTRL_REG, posted); + /* + * Wait for functional clock period x 3.5 to make sure that + * timer is stopped + */ + udelay(3500000 / rate + 1); +#endif + } + + /* Ack possibly pending interrupt */ + writel_relaxed(OMAP_TIMER_INT_OVERFLOW, timer->irq_stat); +} + +static inline void __omap_dm_timer_int_enable(struct omap_dm_timer *timer, + unsigned int value) +{ + writel_relaxed(value, timer->irq_ena); + __omap_dm_timer_write(timer, OMAP_TIMER_WAKEUP_EN_REG, value, 0); +} + +static inline unsigned int +__omap_dm_timer_read_counter(struct omap_dm_timer *timer, int posted) +{ + return __omap_dm_timer_read(timer, OMAP_TIMER_COUNTER_REG, posted); +} + +static inline void __omap_dm_timer_write_status(struct omap_dm_timer *timer, + unsigned int value) +{ + writel_relaxed(value, timer->irq_stat); +} + /** * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode * @timer: timer pointer over which read operation to perform @@ -433,7 +548,7 @@ int omap_dm_timer_get_irq(struct omap_dm_timer *timer) } #if defined(CONFIG_ARCH_OMAP1) -#include <mach/hardware.h> +#include <linux/soc/ti/omap1-io.h> static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) { @@ -828,8 +943,7 @@ static int omap_dm_timer_probe(struct platform_device *pdev) cpu_pm_register_notifier(&timer->nb); } - if (pdata) - timer->errata = pdata->timer_errata; + timer->errata = pdata->timer_errata; timer->pdev = pdev; @@ -922,6 +1036,10 @@ static const struct dmtimer_platform_data omap3plus_pdata = { .timer_ops = &dmtimer_ops, }; +static const struct dmtimer_platform_data am6_pdata = { + .timer_ops = &dmtimer_ops, +}; + static const struct of_device_id omap_timer_match[] = { { .compatible = "ti,omap2420-timer", @@ -950,6 +1068,10 @@ static const struct of_device_id omap_timer_match[] = { .compatible = "ti,dm816-timer", .data = &omap3plus_pdata, }, + { + .compatible = "ti,am654-timer", + .data = &am6_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_timer_match); |