Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3438081imu; Mon, 28 Jan 2019 05:00:42 -0800 (PST) X-Google-Smtp-Source: ALg8bN7WhX5dxHdBQSgLpvF1tAmY66RycH0g+TsiA/Bugad7Uywi7cQowLNGAMKYh7nP7O/zfEAv X-Received: by 2002:a17:902:20c6:: with SMTP id v6mr21911711plg.156.1548680442488; Mon, 28 Jan 2019 05:00:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548680442; cv=none; d=google.com; s=arc-20160816; b=v+iZqnLYTx2eb8RAAXSIRCE+vYjBJngbw5yBF3BkawP62FezPPmtUBF/B6Zoh7Hz0R CKsGDToygoPTW/Q+riTBu7YFBlDyHCLgEgE5+D75tFOUPNr/4z6mfk2XpD6LvY6fDovq lXDAGJ2o40g2KkfPFJYab9+PHxvmMOp9yhmetC08qQE2wdp0Qv6sg0mqrEoeDb2QUEsG PoxUz4nJxo1rYM8Dn2Xze5roE9Cp1aFOkyo7KgBLtVUsloMSo3ZgtB4jqFtfYVe/NIWQ KINideWbh7FZ2VOTl3ywOGx8N4V5hBEVVLCNEwpObU4lM3NwyJ5//ree7jwt3ntuRMB0 QTOA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject:dkim-signature; bh=F1kXDxN9SgU3Gy4DpvLOYRSgLn1+0pgCe+JxFuZTAzk=; b=DJmenj1Q6GykrkxxN4RsPX7PVbnI1jiReG9xQhutCI2gzZSNlBaToxdsQ7KQY2Pjrn 4PMnzKzTwrlN877uP0GRG1TKRbYIr6fENpZxgeUL8aRPH4QrBUIqyh+Q7144OJ8NZd6K GjGCeUw8n2lnrRkg9FPR5+tJQnfjxm+f8i2rSuGRVywhu9XkJ+oX69Os2xvdu8yQ/R24 7T3cjjazzSjwlCChliqL4WrruWz7nWlMcGiwztnqNEeHy82TaTijhelbuaSPBbdA8RRt gf2oe3JdOB7VeoLQ7CCkVv9qf3XZuQY4YHqM5C6jLEZ6VeLJH1i1FeC/Db7AZGMCeK0l H5VQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=SiE0cST9; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 15si30464174pgv.351.2019.01.28.05.00.26; Mon, 28 Jan 2019 05:00:42 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=SiE0cST9; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726750AbfA1NAH (ORCPT + 99 others); Mon, 28 Jan 2019 08:00:07 -0500 Received: from mail-wr1-f68.google.com ([209.85.221.68]:42417 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726672AbfA1NAH (ORCPT ); Mon, 28 Jan 2019 08:00:07 -0500 Received: by mail-wr1-f68.google.com with SMTP id q18so17926906wrx.9 for ; Mon, 28 Jan 2019 05:00:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=F1kXDxN9SgU3Gy4DpvLOYRSgLn1+0pgCe+JxFuZTAzk=; b=SiE0cST9qoxXY5YKpQ6cO8bV6ODwDGRiLjNJEnhvZJxiqu/Ft/HeClD9KhraxlGhbu QSbqe9MRVhSd5WFqy9JzoZkfFF65PR5c5cairH9vzuwuvjSvkwnqR8jHoE/55XsW1LcD yNwa16sSzcNcox+giTRIcNM2sUXda2EMvD5aA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=F1kXDxN9SgU3Gy4DpvLOYRSgLn1+0pgCe+JxFuZTAzk=; b=D2JLgHoIUkjN9nK4JvxlPIXDwyxN9XX9VfM3ceAVn4lx8dpRVq0MvmYnoMRo9Gj6NX nk2rtkw+406vD/tFrHCcFtBiyOVVncMpIs/5fV53p/WhI0oMuaQZJn/Kxbv26BC/ZRi+ ZorCnRHbDoWxQtDbFnIB8XGBnQu73xaBhvkmFGH88VdoDb+d90Y0hYCEHkfbEIKyPU3H xiyZ7O36sFqiwD8d86rt/sxSC5mMvQbzbFYKLyVAWf8LjYtVw3ob+SELLHLJU/r8egCG 4zR0lUON4hwhFlom1gDYlc09mz+K6bIlzqwBPQWaWs+Vq6s16FzEYZREW+WYxOtyfN2F OJ2w== X-Gm-Message-State: AJcUukeAqHw9yNiH40lSogiRtJG59V0TOpfkRzGoWMmke/iovsJyx1B+ 7vD5hOhKcbYWmbSUnJJVwOQyNf7zv9E= X-Received: by 2002:adf:92a4:: with SMTP id 33mr21532140wrn.11.1548680403537; Mon, 28 Jan 2019 05:00:03 -0800 (PST) Received: from [192.168.0.41] (167.126.130.77.rev.sfr.net. [77.130.126.167]) by smtp.googlemail.com with ESMTPSA id r200sm6317760wmb.36.2019.01.28.05.00.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 28 Jan 2019 05:00:02 -0800 (PST) Subject: Re: [PATCH V2 2/6] clocksource: tegra: add Tegra210 timer driver To: Joseph Lo , Thierry Reding , Jonathan Hunter Cc: linux-tegra@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Thomas Gleixner , linux-kernel@vger.kernel.org References: <20190128091815.7040-1-josephl@nvidia.com> <20190128091815.7040-3-josephl@nvidia.com> From: Daniel Lezcano Message-ID: Date: Mon, 28 Jan 2019 14:00:01 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 MIME-Version: 1.0 In-Reply-To: <20190128091815.7040-3-josephl@nvidia.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 28/01/2019 10:18, Joseph Lo wrote: > Add support for the Tegra210 timer that runs at oscillator clock > (TMR10-TMR13). We need these timers to work as clock event device and to > replace the ARMv8 architected timer due to it can't survive across the > power cycle of the CPU core or CPUPORESET signal. So it can't be a wake-up > source when CPU suspends in power down state. > > Based on the work of Antti P Miettinen > > Cc: Daniel Lezcano > Cc: Thomas Gleixner > Cc: linux-kernel@vger.kernel.org > Signed-off-by: Joseph Lo > --- > v2: > * add error clean-up code > --- > drivers/clocksource/Kconfig | 3 + > drivers/clocksource/Makefile | 1 + > drivers/clocksource/timer-tegra210.c | 268 +++++++++++++++++++++++++++ > include/linux/cpuhotplug.h | 1 + > 4 files changed, 273 insertions(+) > create mode 100644 drivers/clocksource/timer-tegra210.c > > diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig > index a9e26f6a81a1..e6e3e64b6320 100644 > --- a/drivers/clocksource/Kconfig > +++ b/drivers/clocksource/Kconfig > @@ -135,6 +135,9 @@ config TEGRA_TIMER > help > Enables support for the Tegra driver. > > +config TEGRA210_TIMER > + def_bool ARCH_TEGRA_210_SOC > + Please make the option consistent with the option below (VT8500_TIMER or similar). > config VT8500_TIMER > bool "VT8500 timer driver" if COMPILE_TEST > depends on HAS_IOMEM > diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile > index cdd210ff89ea..95de59c8a47b 100644 > --- a/drivers/clocksource/Makefile > +++ b/drivers/clocksource/Makefile > @@ -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-tegra20.o > +obj-$(CONFIG_TEGRA210_TIMER) += timer-tegra210.o > obj-$(CONFIG_VT8500_TIMER) += timer-vt8500.o > obj-$(CONFIG_NSPIRE_TIMER) += timer-zevio.o > obj-$(CONFIG_BCM_KONA_TIMER) += bcm_kona_timer.o > diff --git a/drivers/clocksource/timer-tegra210.c b/drivers/clocksource/timer-tegra210.c > new file mode 100644 > index 000000000000..477b164e540b > --- /dev/null > +++ b/drivers/clocksource/timer-tegra210.c > @@ -0,0 +1,268 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static u32 tegra210_timer_freq; > +static void __iomem *tegra210_timer_reg_base; > +static u32 usec_config; > + > +#define TIMER_PTV 0x0 > +#define TIMER_PTV_EN BIT(31) > +#define TIMER_PTV_PER BIT(30) > +#define TIMER_PCR 0x4 > +#define TIMER_PCR_INTR_CLR BIT(30) > +#define TIMERUS_CNTR_1US 0x10 > +#define TIMERUS_USEC_CFG 0x14 > + > +#define TIMER10_OFFSET 0x90 > +#define TIMER10_IRQ_IDX 10 > + > +#define TIMER_FOR_CPU(cpu) (TIMER10_OFFSET + (cpu) * 8) > +#define IRQ_IDX_FOR_CPU(cpu) (TIMER10_IRQ_IDX + cpu) > + > +struct tegra210_clockevent { > + struct clock_event_device evt; > + char name[20]; > + void __iomem *reg_base; > +}; > +#define to_tegra_cevt(p) (container_of(p, struct tegra210_clockevent, evt)) > + > +static struct tegra210_clockevent __percpu *tegra210_evt; > + > +static int tegra210_timer_set_next_event(unsigned long cycles, > + struct clock_event_device *evt) > +{ > + struct tegra210_clockevent *tevt; > + > + tevt = to_tegra_cevt(evt); > + writel(TIMER_PTV_EN | > + ((cycles > 1) ? (cycles - 1) : 0), /* n+1 scheme */ > + tevt->reg_base + TIMER_PTV); > + > + return 0; > +} > + > +static inline void timer_shutdown(struct tegra210_clockevent *tevt) > +{ > + writel(0, tevt->reg_base + TIMER_PTV); > +} > + > +static int tegra210_timer_shutdown(struct clock_event_device *evt) > +{ > + struct tegra210_clockevent *tevt; > + > + tevt = to_tegra_cevt(evt); > + timer_shutdown(tevt); > + > + return 0; > +} > + > +static int tegra210_timer_set_periodic(struct clock_event_device *evt) > +{ > + struct tegra210_clockevent *tevt; > + > + tevt = to_tegra_cevt(evt); > + writel(TIMER_PTV_EN | TIMER_PTV_PER | ((tegra210_timer_freq / HZ) - 1), > + tevt->reg_base + TIMER_PTV); > + > + return 0; > +} > + > +static irqreturn_t tegra210_timer_isr(int irq, void *dev_id) > +{ > + struct tegra210_clockevent *tevt; > + > + tevt = dev_id; > + writel(TIMER_PCR_INTR_CLR, tevt->reg_base + TIMER_PCR); > + tevt->evt.event_handler(&tevt->evt); > + > + return IRQ_HANDLED; > +} > + > +static int tegra210_timer_setup(unsigned int cpu) > +{ > + struct tegra210_clockevent *tevt = per_cpu_ptr(tegra210_evt, cpu); > + > + irq_force_affinity(tevt->evt.irq, cpumask_of(cpu)); > + enable_irq(tevt->evt.irq); > + > + clockevents_config_and_register(&tevt->evt, tegra210_timer_freq, > + 1, /* min */ > + 0x1fffffff); /* 29 bits */ > + > + return 0; > +} > + > +static int tegra210_timer_stop(unsigned int cpu) > +{ > + struct tegra210_clockevent *tevt = per_cpu_ptr(tegra210_evt, cpu); > + > + tevt->evt.set_state_shutdown(&tevt->evt); > + disable_irq_nosync(tevt->evt.irq); > + > + return 0; > +} > + > +static int tegra_timer_suspend(void) > +{ > + int cpu; > + > + for_each_possible_cpu(cpu) { > + void __iomem *reg_base = tegra210_timer_reg_base + > + TIMER_FOR_CPU(cpu); > + writel(TIMER_PCR_INTR_CLR, reg_base + TIMER_PCR); > + } > + > + return 0; > +} > + > +static void tegra_timer_resume(void) > +{ > + writel(usec_config, tegra210_timer_reg_base + TIMERUS_USEC_CFG); > +} > + > +static struct syscore_ops tegra_timer_syscore_ops = { > + .suspend = tegra_timer_suspend, > + .resume = tegra_timer_resume, > +}; > + > +static int __init tegra210_timer_init(struct device_node *np) > +{ > + int cpu, ret = 0; > + struct tegra210_clockevent *tevt; > + struct clk *clk; > + > + tegra210_evt = alloc_percpu(struct tegra210_clockevent); > + if (!tegra210_evt) { > + ret = -ENOMEM; > + goto out; > + } > + > + tegra210_timer_reg_base = of_iomap(np, 0); > + if (!tegra210_timer_reg_base) { > + ret = -ENXIO; > + goto out_free_mem; > + } > + > + clk = of_clk_get(np, 0); > + if (IS_ERR(clk)) { > + ret = -EINVAL; > + goto out_iounmap; > + } > + > + clk_prepare_enable(clk); > + tegra210_timer_freq = clk_get_rate(clk); Use the timer-of API for each CPU. > + for_each_possible_cpu(cpu) { No cpu loop for the initialization, use the tegra210_timer_setup for this (cf. armada 370 timer). > + tevt = per_cpu_ptr(tegra210_evt, cpu); > + tevt->reg_base = tegra210_timer_reg_base + TIMER_FOR_CPU(cpu); > + tevt->evt.irq = irq_of_parse_and_map(np, IRQ_IDX_FOR_CPU(cpu)); > + if (!tevt->evt.irq) { > + pr_err("%s: can't map IRQ for CPU%d\n", > + __func__, cpu); > + ret = -EINVAL; > + goto out_clk; > + } > + > + snprintf(tevt->name, ARRAY_SIZE(tevt->name), > + "tegra210_timer%d", cpu); > + tevt->evt.name = tevt->name; > + tevt->evt.cpumask = cpumask_of(cpu); > + tevt->evt.set_next_event = tegra210_timer_set_next_event; > + tevt->evt.set_state_shutdown = tegra210_timer_shutdown; > + tevt->evt.set_state_periodic = tegra210_timer_set_periodic; > + tevt->evt.set_state_oneshot = tegra210_timer_shutdown; > + tevt->evt.tick_resume = tegra210_timer_shutdown; > + tevt->evt.features = CLOCK_EVT_FEAT_PERIODIC | > + CLOCK_EVT_FEAT_ONESHOT; > + tevt->evt.rating = 460; > + irq_set_status_flags(tevt->evt.irq, IRQ_NOAUTOEN); Why do you need to prevent the interrupt to be enabled ? If the timer is stopped and cleared, no spurious interrupt will occur no ? > + ret = request_irq(tevt->evt.irq, tegra210_timer_isr, > + IRQF_TIMER | IRQF_NOBALANCING, > + tevt->name, tevt); > + if (ret) { > + pr_err("%s: cannot setup irq %d for CPU%d\n", > + __func__, tevt->evt.irq, cpu); > + ret = -EINVAL; > + goto out_irq; > + } > + } > + > + /* > + * Configure microsecond timers to have 1MHz clock > + * Config register is 0xqqww, where qq is "dividend", ww is "divisor" Did you mean 0xwwqq ? > + * Uses n+1 scheme > + */ > + switch (tegra210_timer_freq) { > + case 12000000: > + usec_config = 0x000b; /* (11+1)/(0+1) */ > + break; > + case 12800000: > + usec_config = 0x043f; /* (63+1)/(4+1) */ > + break; > + case 13000000: > + usec_config = 0x000c; /* (12+1)/(0+1) */ > + break; > + case 16800000: > + usec_config = 0x0453; /* (83+1)/(4+1) */ > + break; > + case 19200000: > + usec_config = 0x045f; /* (95+1)/(4+1) */ > + break; > + case 26000000: > + usec_config = 0x0019; /* (25+1)/(0+1) */ > + break; > + case 38400000: > + usec_config = 0x04bf; /* (191+1)/(4+1) */ > + break; > + case 48000000: > + usec_config = 0x002f; /* (47+1)/(0+1) */ > + break; > + default: > + ret = -EINVAL; > + goto out_irq; > + } > + > + writel(usec_config, tegra210_timer_reg_base + TIMERUS_USEC_CFG); > + > + cpuhp_setup_state(CPUHP_AP_TEGRA_TIMER_STARTING, > + "AP_TEGRA_TIMER_STARTING", tegra210_timer_setup, > + tegra210_timer_stop); > + > + register_syscore_ops(&tegra_timer_syscore_ops); > + > + return ret; > + > +out_irq: > + for_each_possible_cpu(cpu) { > + tevt = per_cpu_ptr(tegra210_evt, cpu); > + if (tevt->evt.irq) { > + free_irq(tevt->evt.irq, tevt); > + irq_dispose_mapping(tevt->evt.irq); > + } > + } > +out_clk: > + clk_put(clk); > +out_iounmap: > + iounmap(tegra210_timer_reg_base); > +out_free_mem: > + free_percpu(tegra210_evt); > +out: > + return ret; > +} > + > +TIMER_OF_DECLARE(tegra210_timer, "nvidia,tegra210-timer", tegra210_timer_init); > diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h > index fd586d0301e7..e78281d07b70 100644 > --- a/include/linux/cpuhotplug.h > +++ b/include/linux/cpuhotplug.h > @@ -121,6 +121,7 @@ enum cpuhp_state { > CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING, > CPUHP_AP_ARM_TWD_STARTING, > CPUHP_AP_QCOM_TIMER_STARTING, > + CPUHP_AP_TEGRA_TIMER_STARTING, > CPUHP_AP_ARMADA_TIMER_STARTING, > CPUHP_AP_MARCO_TIMER_STARTING, > CPUHP_AP_MIPS_GIC_TIMER_STARTING, > -- Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog