3c1f6e358d
Patches are generated using the "format-patch" command from the following location: *https://www.codeaurora.org/cgit/quic/kernel/galak-msm/log/?h=apq_ipq_base *rev=0771849495b4128cac2faf7d49c85c729fc48b20 Patches numbered 76/77/102/103 have already been integrated in 3.14.12, so they're not in this list. All these patches are either integrated are pending integration into kernel.org, therefore these patches should go away once the kernel gets upgraded to 3.16. Support is currently limited to AP148 board but can be extended to other platforms in the future. These changes do not cover ethernet connectivity. Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> SVN-Revision: 42334
789 lines
20 KiB
Diff
789 lines
20 KiB
Diff
From 00009eabeb2074bef5c89e576a7a6d827c12c3d9 Mon Sep 17 00:00:00 2001
|
|
From: Kumar Gala <galak@codeaurora.org>
|
|
Date: Wed, 29 Jan 2014 16:17:30 -0600
|
|
Subject: [PATCH 004/182] clocksource: qcom: Move clocksource code out of
|
|
mach-msm
|
|
|
|
We intend to share the clocksource code for MSM platforms between legacy
|
|
and multiplatform supported qcom SoCs.
|
|
|
|
Acked-by: Olof Johansson <olof@lixom.net>
|
|
Signed-off-by: Kumar Gala <galak@codeaurora.org>
|
|
---
|
|
arch/arm/mach-msm/Kconfig | 13 +-
|
|
arch/arm/mach-msm/Makefile | 1 -
|
|
arch/arm/mach-msm/timer.c | 333 --------------------------------------
|
|
drivers/clocksource/Kconfig | 3 +
|
|
drivers/clocksource/Makefile | 1 +
|
|
drivers/clocksource/qcom-timer.c | 329 +++++++++++++++++++++++++++++++++++++
|
|
6 files changed, 338 insertions(+), 342 deletions(-)
|
|
delete mode 100644 arch/arm/mach-msm/timer.c
|
|
create mode 100644 drivers/clocksource/qcom-timer.c
|
|
|
|
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
|
|
index 9625cf3..3c4eca7 100644
|
|
--- a/arch/arm/mach-msm/Kconfig
|
|
+++ b/arch/arm/mach-msm/Kconfig
|
|
@@ -21,7 +21,7 @@ config ARCH_MSM8X60
|
|
select CPU_V7
|
|
select HAVE_SMP
|
|
select MSM_SCM if SMP
|
|
- select MSM_TIMER
|
|
+ select CLKSRC_QCOM
|
|
|
|
config ARCH_MSM8960
|
|
bool "Enable support for MSM8960"
|
|
@@ -29,7 +29,7 @@ config ARCH_MSM8960
|
|
select CPU_V7
|
|
select HAVE_SMP
|
|
select MSM_SCM if SMP
|
|
- select MSM_TIMER
|
|
+ select CLKSRC_QCOM
|
|
|
|
config ARCH_MSM8974
|
|
bool "Enable support for MSM8974"
|
|
@@ -54,7 +54,7 @@ config ARCH_MSM7X00A
|
|
select MACH_TROUT if !MACH_HALIBUT
|
|
select MSM_PROC_COMM
|
|
select MSM_SMD
|
|
- select MSM_TIMER
|
|
+ select CLKSRC_QCOM
|
|
select MSM_SMD_PKG3
|
|
|
|
config ARCH_MSM7X30
|
|
@@ -66,7 +66,7 @@ config ARCH_MSM7X30
|
|
select MSM_GPIOMUX
|
|
select MSM_PROC_COMM
|
|
select MSM_SMD
|
|
- select MSM_TIMER
|
|
+ select CLKSRC_QCOM
|
|
select MSM_VIC
|
|
|
|
config ARCH_QSD8X50
|
|
@@ -78,7 +78,7 @@ config ARCH_QSD8X50
|
|
select MSM_GPIOMUX
|
|
select MSM_PROC_COMM
|
|
select MSM_SMD
|
|
- select MSM_TIMER
|
|
+ select CLKSRC_QCOM
|
|
select MSM_VIC
|
|
|
|
endchoice
|
|
@@ -153,7 +153,4 @@ config MSM_GPIOMUX
|
|
config MSM_SCM
|
|
bool
|
|
|
|
-config MSM_TIMER
|
|
- bool
|
|
-
|
|
endif
|
|
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
|
|
index 8327f60..04b1bee 100644
|
|
--- a/arch/arm/mach-msm/Makefile
|
|
+++ b/arch/arm/mach-msm/Makefile
|
|
@@ -1,4 +1,3 @@
|
|
-obj-$(CONFIG_MSM_TIMER) += timer.o
|
|
obj-$(CONFIG_MSM_PROC_COMM) += clock.o
|
|
|
|
obj-$(CONFIG_MSM_VIC) += irq-vic.o
|
|
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
|
|
deleted file mode 100644
|
|
index fd16449..0000000
|
|
--- a/arch/arm/mach-msm/timer.c
|
|
+++ /dev/null
|
|
@@ -1,333 +0,0 @@
|
|
-/*
|
|
- *
|
|
- * Copyright (C) 2007 Google, Inc.
|
|
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
|
|
- *
|
|
- * This software is licensed under the terms of the GNU General Public
|
|
- * License version 2, as published by the Free Software Foundation, and
|
|
- * may be copied, distributed, and modified under those terms.
|
|
- *
|
|
- * This program is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- */
|
|
-
|
|
-#include <linux/clocksource.h>
|
|
-#include <linux/clockchips.h>
|
|
-#include <linux/cpu.h>
|
|
-#include <linux/init.h>
|
|
-#include <linux/interrupt.h>
|
|
-#include <linux/irq.h>
|
|
-#include <linux/io.h>
|
|
-#include <linux/of.h>
|
|
-#include <linux/of_address.h>
|
|
-#include <linux/of_irq.h>
|
|
-#include <linux/sched_clock.h>
|
|
-
|
|
-#include <asm/mach/time.h>
|
|
-
|
|
-#include "common.h"
|
|
-
|
|
-#define TIMER_MATCH_VAL 0x0000
|
|
-#define TIMER_COUNT_VAL 0x0004
|
|
-#define TIMER_ENABLE 0x0008
|
|
-#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
|
|
-#define TIMER_ENABLE_EN BIT(0)
|
|
-#define TIMER_CLEAR 0x000C
|
|
-#define DGT_CLK_CTL 0x10
|
|
-#define DGT_CLK_CTL_DIV_4 0x3
|
|
-#define TIMER_STS_GPT0_CLR_PEND BIT(10)
|
|
-
|
|
-#define GPT_HZ 32768
|
|
-
|
|
-#define MSM_DGT_SHIFT 5
|
|
-
|
|
-static void __iomem *event_base;
|
|
-static void __iomem *sts_base;
|
|
-
|
|
-static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
|
|
-{
|
|
- struct clock_event_device *evt = dev_id;
|
|
- /* Stop the timer tick */
|
|
- if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
|
|
- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
- ctrl &= ~TIMER_ENABLE_EN;
|
|
- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
- }
|
|
- evt->event_handler(evt);
|
|
- return IRQ_HANDLED;
|
|
-}
|
|
-
|
|
-static int msm_timer_set_next_event(unsigned long cycles,
|
|
- struct clock_event_device *evt)
|
|
-{
|
|
- u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
-
|
|
- ctrl &= ~TIMER_ENABLE_EN;
|
|
- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
-
|
|
- writel_relaxed(ctrl, event_base + TIMER_CLEAR);
|
|
- writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
|
|
-
|
|
- if (sts_base)
|
|
- while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
|
|
- cpu_relax();
|
|
-
|
|
- writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void msm_timer_set_mode(enum clock_event_mode mode,
|
|
- struct clock_event_device *evt)
|
|
-{
|
|
- u32 ctrl;
|
|
-
|
|
- ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
- ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
|
|
-
|
|
- switch (mode) {
|
|
- case CLOCK_EVT_MODE_RESUME:
|
|
- case CLOCK_EVT_MODE_PERIODIC:
|
|
- break;
|
|
- case CLOCK_EVT_MODE_ONESHOT:
|
|
- /* Timer is enabled in set_next_event */
|
|
- break;
|
|
- case CLOCK_EVT_MODE_UNUSED:
|
|
- case CLOCK_EVT_MODE_SHUTDOWN:
|
|
- break;
|
|
- }
|
|
- writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
-}
|
|
-
|
|
-static struct clock_event_device __percpu *msm_evt;
|
|
-
|
|
-static void __iomem *source_base;
|
|
-
|
|
-static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
|
|
-{
|
|
- return readl_relaxed(source_base + TIMER_COUNT_VAL);
|
|
-}
|
|
-
|
|
-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
|
|
-{
|
|
- /*
|
|
- * Shift timer count down by a constant due to unreliable lower bits
|
|
- * on some targets.
|
|
- */
|
|
- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
|
|
-}
|
|
-
|
|
-static struct clocksource msm_clocksource = {
|
|
- .name = "dg_timer",
|
|
- .rating = 300,
|
|
- .read = msm_read_timer_count,
|
|
- .mask = CLOCKSOURCE_MASK(32),
|
|
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
-};
|
|
-
|
|
-static int msm_timer_irq;
|
|
-static int msm_timer_has_ppi;
|
|
-
|
|
-static int msm_local_timer_setup(struct clock_event_device *evt)
|
|
-{
|
|
- int cpu = smp_processor_id();
|
|
- int err;
|
|
-
|
|
- evt->irq = msm_timer_irq;
|
|
- evt->name = "msm_timer";
|
|
- evt->features = CLOCK_EVT_FEAT_ONESHOT;
|
|
- evt->rating = 200;
|
|
- evt->set_mode = msm_timer_set_mode;
|
|
- evt->set_next_event = msm_timer_set_next_event;
|
|
- evt->cpumask = cpumask_of(cpu);
|
|
-
|
|
- clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
|
|
-
|
|
- if (msm_timer_has_ppi) {
|
|
- enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
|
|
- } else {
|
|
- err = request_irq(evt->irq, msm_timer_interrupt,
|
|
- IRQF_TIMER | IRQF_NOBALANCING |
|
|
- IRQF_TRIGGER_RISING, "gp_timer", evt);
|
|
- if (err)
|
|
- pr_err("request_irq failed\n");
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static void msm_local_timer_stop(struct clock_event_device *evt)
|
|
-{
|
|
- evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
|
|
- disable_percpu_irq(evt->irq);
|
|
-}
|
|
-
|
|
-static int msm_timer_cpu_notify(struct notifier_block *self,
|
|
- unsigned long action, void *hcpu)
|
|
-{
|
|
- /*
|
|
- * Grab cpu pointer in each case to avoid spurious
|
|
- * preemptible warnings
|
|
- */
|
|
- switch (action & ~CPU_TASKS_FROZEN) {
|
|
- case CPU_STARTING:
|
|
- msm_local_timer_setup(this_cpu_ptr(msm_evt));
|
|
- break;
|
|
- case CPU_DYING:
|
|
- msm_local_timer_stop(this_cpu_ptr(msm_evt));
|
|
- break;
|
|
- }
|
|
-
|
|
- return NOTIFY_OK;
|
|
-}
|
|
-
|
|
-static struct notifier_block msm_timer_cpu_nb = {
|
|
- .notifier_call = msm_timer_cpu_notify,
|
|
-};
|
|
-
|
|
-static u64 notrace msm_sched_clock_read(void)
|
|
-{
|
|
- return msm_clocksource.read(&msm_clocksource);
|
|
-}
|
|
-
|
|
-static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
|
|
- bool percpu)
|
|
-{
|
|
- struct clocksource *cs = &msm_clocksource;
|
|
- int res = 0;
|
|
-
|
|
- msm_timer_irq = irq;
|
|
- msm_timer_has_ppi = percpu;
|
|
-
|
|
- msm_evt = alloc_percpu(struct clock_event_device);
|
|
- if (!msm_evt) {
|
|
- pr_err("memory allocation failed for clockevents\n");
|
|
- goto err;
|
|
- }
|
|
-
|
|
- if (percpu)
|
|
- res = request_percpu_irq(irq, msm_timer_interrupt,
|
|
- "gp_timer", msm_evt);
|
|
-
|
|
- if (res) {
|
|
- pr_err("request_percpu_irq failed\n");
|
|
- } else {
|
|
- res = register_cpu_notifier(&msm_timer_cpu_nb);
|
|
- if (res) {
|
|
- free_percpu_irq(irq, msm_evt);
|
|
- goto err;
|
|
- }
|
|
-
|
|
- /* Immediately configure the timer on the boot CPU */
|
|
- msm_local_timer_setup(__this_cpu_ptr(msm_evt));
|
|
- }
|
|
-
|
|
-err:
|
|
- writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
|
|
- res = clocksource_register_hz(cs, dgt_hz);
|
|
- if (res)
|
|
- pr_err("clocksource_register failed\n");
|
|
- sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
|
|
-}
|
|
-
|
|
-#ifdef CONFIG_OF
|
|
-static void __init msm_dt_timer_init(struct device_node *np)
|
|
-{
|
|
- u32 freq;
|
|
- int irq;
|
|
- struct resource res;
|
|
- u32 percpu_offset;
|
|
- void __iomem *base;
|
|
- void __iomem *cpu0_base;
|
|
-
|
|
- base = of_iomap(np, 0);
|
|
- if (!base) {
|
|
- pr_err("Failed to map event base\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- /* We use GPT0 for the clockevent */
|
|
- irq = irq_of_parse_and_map(np, 1);
|
|
- if (irq <= 0) {
|
|
- pr_err("Can't get irq\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- /* We use CPU0's DGT for the clocksource */
|
|
- if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
|
|
- percpu_offset = 0;
|
|
-
|
|
- if (of_address_to_resource(np, 0, &res)) {
|
|
- pr_err("Failed to parse DGT resource\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
|
|
- if (!cpu0_base) {
|
|
- pr_err("Failed to map source base\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- if (of_property_read_u32(np, "clock-frequency", &freq)) {
|
|
- pr_err("Unknown frequency\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- event_base = base + 0x4;
|
|
- sts_base = base + 0x88;
|
|
- source_base = cpu0_base + 0x24;
|
|
- freq /= 4;
|
|
- writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
|
|
-
|
|
- msm_timer_init(freq, 32, irq, !!percpu_offset);
|
|
-}
|
|
-CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
|
|
-CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
|
|
-#endif
|
|
-
|
|
-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
|
|
- u32 sts)
|
|
-{
|
|
- void __iomem *base;
|
|
-
|
|
- base = ioremap(addr, SZ_256);
|
|
- if (!base) {
|
|
- pr_err("Failed to map timer base\n");
|
|
- return -ENOMEM;
|
|
- }
|
|
- event_base = base + event;
|
|
- source_base = base + source;
|
|
- if (sts)
|
|
- sts_base = base + sts;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-void __init msm7x01_timer_init(void)
|
|
-{
|
|
- struct clocksource *cs = &msm_clocksource;
|
|
-
|
|
- if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
|
|
- return;
|
|
- cs->read = msm_read_timer_count_shift;
|
|
- cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
|
|
- /* 600 KHz */
|
|
- msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
|
|
- false);
|
|
-}
|
|
-
|
|
-void __init msm7x30_timer_init(void)
|
|
-{
|
|
- if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
|
|
- return;
|
|
- msm_timer_init(24576000 / 4, 32, 1, false);
|
|
-}
|
|
-
|
|
-void __init qsd8x50_timer_init(void)
|
|
-{
|
|
- if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
|
|
- return;
|
|
- msm_timer_init(19200000 / 4, 32, 7, false);
|
|
-}
|
|
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
|
|
index cd6950f..6510ec4 100644
|
|
--- a/drivers/clocksource/Kconfig
|
|
+++ b/drivers/clocksource/Kconfig
|
|
@@ -140,3 +140,6 @@ config VF_PIT_TIMER
|
|
bool
|
|
help
|
|
Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
|
|
+
|
|
+config CLKSRC_QCOM
|
|
+ bool
|
|
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
|
|
index c7ca50a..2e0c0cc 100644
|
|
--- a/drivers/clocksource/Makefile
|
|
+++ b/drivers/clocksource/Makefile
|
|
@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
|
|
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
|
|
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
|
|
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
|
|
+obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
|
|
|
|
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
|
|
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
|
|
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
|
|
new file mode 100644
|
|
index 0000000..dca829e
|
|
--- /dev/null
|
|
+++ b/drivers/clocksource/qcom-timer.c
|
|
@@ -0,0 +1,329 @@
|
|
+/*
|
|
+ *
|
|
+ * Copyright (C) 2007 Google, Inc.
|
|
+ * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
|
|
+ *
|
|
+ * This software is licensed under the terms of the GNU General Public
|
|
+ * License version 2, as published by the Free Software Foundation, and
|
|
+ * may be copied, distributed, and modified under those terms.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/clocksource.h>
|
|
+#include <linux/clockchips.h>
|
|
+#include <linux/cpu.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/sched_clock.h>
|
|
+
|
|
+#define TIMER_MATCH_VAL 0x0000
|
|
+#define TIMER_COUNT_VAL 0x0004
|
|
+#define TIMER_ENABLE 0x0008
|
|
+#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
|
|
+#define TIMER_ENABLE_EN BIT(0)
|
|
+#define TIMER_CLEAR 0x000C
|
|
+#define DGT_CLK_CTL 0x10
|
|
+#define DGT_CLK_CTL_DIV_4 0x3
|
|
+#define TIMER_STS_GPT0_CLR_PEND BIT(10)
|
|
+
|
|
+#define GPT_HZ 32768
|
|
+
|
|
+#define MSM_DGT_SHIFT 5
|
|
+
|
|
+static void __iomem *event_base;
|
|
+static void __iomem *sts_base;
|
|
+
|
|
+static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
|
|
+{
|
|
+ struct clock_event_device *evt = dev_id;
|
|
+ /* Stop the timer tick */
|
|
+ if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
|
|
+ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
+ ctrl &= ~TIMER_ENABLE_EN;
|
|
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
+ }
|
|
+ evt->event_handler(evt);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int msm_timer_set_next_event(unsigned long cycles,
|
|
+ struct clock_event_device *evt)
|
|
+{
|
|
+ u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
+
|
|
+ ctrl &= ~TIMER_ENABLE_EN;
|
|
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
+
|
|
+ writel_relaxed(ctrl, event_base + TIMER_CLEAR);
|
|
+ writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
|
|
+
|
|
+ if (sts_base)
|
|
+ while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
|
|
+ cpu_relax();
|
|
+
|
|
+ writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void msm_timer_set_mode(enum clock_event_mode mode,
|
|
+ struct clock_event_device *evt)
|
|
+{
|
|
+ u32 ctrl;
|
|
+
|
|
+ ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
|
+ ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
|
|
+
|
|
+ switch (mode) {
|
|
+ case CLOCK_EVT_MODE_RESUME:
|
|
+ case CLOCK_EVT_MODE_PERIODIC:
|
|
+ break;
|
|
+ case CLOCK_EVT_MODE_ONESHOT:
|
|
+ /* Timer is enabled in set_next_event */
|
|
+ break;
|
|
+ case CLOCK_EVT_MODE_UNUSED:
|
|
+ case CLOCK_EVT_MODE_SHUTDOWN:
|
|
+ break;
|
|
+ }
|
|
+ writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
|
+}
|
|
+
|
|
+static struct clock_event_device __percpu *msm_evt;
|
|
+
|
|
+static void __iomem *source_base;
|
|
+
|
|
+static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
|
|
+{
|
|
+ return readl_relaxed(source_base + TIMER_COUNT_VAL);
|
|
+}
|
|
+
|
|
+static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
|
|
+{
|
|
+ /*
|
|
+ * Shift timer count down by a constant due to unreliable lower bits
|
|
+ * on some targets.
|
|
+ */
|
|
+ return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
|
|
+}
|
|
+
|
|
+static struct clocksource msm_clocksource = {
|
|
+ .name = "dg_timer",
|
|
+ .rating = 300,
|
|
+ .read = msm_read_timer_count,
|
|
+ .mask = CLOCKSOURCE_MASK(32),
|
|
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
+};
|
|
+
|
|
+static int msm_timer_irq;
|
|
+static int msm_timer_has_ppi;
|
|
+
|
|
+static int msm_local_timer_setup(struct clock_event_device *evt)
|
|
+{
|
|
+ int cpu = smp_processor_id();
|
|
+ int err;
|
|
+
|
|
+ evt->irq = msm_timer_irq;
|
|
+ evt->name = "msm_timer";
|
|
+ evt->features = CLOCK_EVT_FEAT_ONESHOT;
|
|
+ evt->rating = 200;
|
|
+ evt->set_mode = msm_timer_set_mode;
|
|
+ evt->set_next_event = msm_timer_set_next_event;
|
|
+ evt->cpumask = cpumask_of(cpu);
|
|
+
|
|
+ clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
|
|
+
|
|
+ if (msm_timer_has_ppi) {
|
|
+ enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
|
|
+ } else {
|
|
+ err = request_irq(evt->irq, msm_timer_interrupt,
|
|
+ IRQF_TIMER | IRQF_NOBALANCING |
|
|
+ IRQF_TRIGGER_RISING, "gp_timer", evt);
|
|
+ if (err)
|
|
+ pr_err("request_irq failed\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void msm_local_timer_stop(struct clock_event_device *evt)
|
|
+{
|
|
+ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
|
|
+ disable_percpu_irq(evt->irq);
|
|
+}
|
|
+
|
|
+static int msm_timer_cpu_notify(struct notifier_block *self,
|
|
+ unsigned long action, void *hcpu)
|
|
+{
|
|
+ /*
|
|
+ * Grab cpu pointer in each case to avoid spurious
|
|
+ * preemptible warnings
|
|
+ */
|
|
+ switch (action & ~CPU_TASKS_FROZEN) {
|
|
+ case CPU_STARTING:
|
|
+ msm_local_timer_setup(this_cpu_ptr(msm_evt));
|
|
+ break;
|
|
+ case CPU_DYING:
|
|
+ msm_local_timer_stop(this_cpu_ptr(msm_evt));
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
+static struct notifier_block msm_timer_cpu_nb = {
|
|
+ .notifier_call = msm_timer_cpu_notify,
|
|
+};
|
|
+
|
|
+static u64 notrace msm_sched_clock_read(void)
|
|
+{
|
|
+ return msm_clocksource.read(&msm_clocksource);
|
|
+}
|
|
+
|
|
+static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
|
|
+ bool percpu)
|
|
+{
|
|
+ struct clocksource *cs = &msm_clocksource;
|
|
+ int res = 0;
|
|
+
|
|
+ msm_timer_irq = irq;
|
|
+ msm_timer_has_ppi = percpu;
|
|
+
|
|
+ msm_evt = alloc_percpu(struct clock_event_device);
|
|
+ if (!msm_evt) {
|
|
+ pr_err("memory allocation failed for clockevents\n");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (percpu)
|
|
+ res = request_percpu_irq(irq, msm_timer_interrupt,
|
|
+ "gp_timer", msm_evt);
|
|
+
|
|
+ if (res) {
|
|
+ pr_err("request_percpu_irq failed\n");
|
|
+ } else {
|
|
+ res = register_cpu_notifier(&msm_timer_cpu_nb);
|
|
+ if (res) {
|
|
+ free_percpu_irq(irq, msm_evt);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* Immediately configure the timer on the boot CPU */
|
|
+ msm_local_timer_setup(__this_cpu_ptr(msm_evt));
|
|
+ }
|
|
+
|
|
+err:
|
|
+ writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
|
|
+ res = clocksource_register_hz(cs, dgt_hz);
|
|
+ if (res)
|
|
+ pr_err("clocksource_register failed\n");
|
|
+ sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static void __init msm_dt_timer_init(struct device_node *np)
|
|
+{
|
|
+ u32 freq;
|
|
+ int irq;
|
|
+ struct resource res;
|
|
+ u32 percpu_offset;
|
|
+ void __iomem *base;
|
|
+ void __iomem *cpu0_base;
|
|
+
|
|
+ base = of_iomap(np, 0);
|
|
+ if (!base) {
|
|
+ pr_err("Failed to map event base\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* We use GPT0 for the clockevent */
|
|
+ irq = irq_of_parse_and_map(np, 1);
|
|
+ if (irq <= 0) {
|
|
+ pr_err("Can't get irq\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* We use CPU0's DGT for the clocksource */
|
|
+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
|
|
+ percpu_offset = 0;
|
|
+
|
|
+ if (of_address_to_resource(np, 0, &res)) {
|
|
+ pr_err("Failed to parse DGT resource\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
|
|
+ if (!cpu0_base) {
|
|
+ pr_err("Failed to map source base\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (of_property_read_u32(np, "clock-frequency", &freq)) {
|
|
+ pr_err("Unknown frequency\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ event_base = base + 0x4;
|
|
+ sts_base = base + 0x88;
|
|
+ source_base = cpu0_base + 0x24;
|
|
+ freq /= 4;
|
|
+ writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
|
|
+
|
|
+ msm_timer_init(freq, 32, irq, !!percpu_offset);
|
|
+}
|
|
+CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
|
|
+CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
|
|
+#endif
|
|
+
|
|
+static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
|
|
+ u32 sts)
|
|
+{
|
|
+ void __iomem *base;
|
|
+
|
|
+ base = ioremap(addr, SZ_256);
|
|
+ if (!base) {
|
|
+ pr_err("Failed to map timer base\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ event_base = base + event;
|
|
+ source_base = base + source;
|
|
+ if (sts)
|
|
+ sts_base = base + sts;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void __init msm7x01_timer_init(void)
|
|
+{
|
|
+ struct clocksource *cs = &msm_clocksource;
|
|
+
|
|
+ if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
|
|
+ return;
|
|
+ cs->read = msm_read_timer_count_shift;
|
|
+ cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
|
|
+ /* 600 KHz */
|
|
+ msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
|
|
+ false);
|
|
+}
|
|
+
|
|
+void __init msm7x30_timer_init(void)
|
|
+{
|
|
+ if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
|
|
+ return;
|
|
+ msm_timer_init(24576000 / 4, 32, 1, false);
|
|
+}
|
|
+
|
|
+void __init qsd8x50_timer_init(void)
|
|
+{
|
|
+ if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
|
|
+ return;
|
|
+ msm_timer_init(19200000 / 4, 32, 7, false);
|
|
+}
|
|
--
|
|
1.7.10.4
|
|
|