|
@@ -0,0 +1,162 @@
|
|
|
+/*
|
|
|
+ * linux/arch/arm/mach-omap2/timer.c
|
|
|
+ *
|
|
|
+ * OMAP2 GP timer support.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2009 Nokia Corporation
|
|
|
+ *
|
|
|
+ * Update to use new clocksource/clockevent layers
|
|
|
+ * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
|
|
|
+ * Copyright (C) 2007 MontaVista Software, Inc.
|
|
|
+ *
|
|
|
+ * Original driver:
|
|
|
+ * Copyright (C) 2005 Nokia Corporation
|
|
|
+ * Author: Paul Mundt <paul.mundt@nokia.com>
|
|
|
+ * Juha Yrjölä <juha.yrjola@nokia.com>
|
|
|
+ * OMAP Dual-mode timer framework support by Timo Teras
|
|
|
+ *
|
|
|
+ * Some parts based off of TI's 24xx code:
|
|
|
+ *
|
|
|
+ * Copyright (C) 2004-2009 Texas Instruments, Inc.
|
|
|
+ *
|
|
|
+ * Roughly modelled after the OMAP1 MPU timer code.
|
|
|
+ * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
|
|
|
+ *
|
|
|
+ * 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/init.h>
|
|
|
+#include <linux/time.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/clk.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/irq.h>
|
|
|
+#include <linux/clocksource.h>
|
|
|
+#include <linux/clockchips.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/of.h>
|
|
|
+#include <linux/of_address.h>
|
|
|
+#include <linux/of_irq.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/platform_data/dmtimer-omap.h>
|
|
|
+
|
|
|
+#include <asm/mach/time.h>
|
|
|
+#include <asm/smp_twd.h>
|
|
|
+#include <asm/sched_clock.h>
|
|
|
+
|
|
|
+#include <asm/arch_timer.h>
|
|
|
+#include "omap_hwmod.h"
|
|
|
+#include "omap_device.h"
|
|
|
+#include <plat/counter-32k.h>
|
|
|
+#include <plat/dmtimer.h>
|
|
|
+#include "omap-pm.h"
|
|
|
+
|
|
|
+#include "soc.h"
|
|
|
+#include "common.h"
|
|
|
+#include "powerdomain.h"
|
|
|
+
|
|
|
+/* Parent clocks, eventually these will come from the clock framework */
|
|
|
+
|
|
|
+#define OMAP2_MPU_SOURCE "sys_ck"
|
|
|
+#define OMAP3_MPU_SOURCE OMAP2_MPU_SOURCE
|
|
|
+#define OMAP4_MPU_SOURCE "sys_clkin_ck"
|
|
|
+#define OMAP2_32K_SOURCE "func_32k_ck"
|
|
|
+#define OMAP3_32K_SOURCE "omap_32k_fck"
|
|
|
+#define OMAP4_32K_SOURCE "sys_32k_ck"
|
|
|
+
|
|
|
+#define REALTIME_COUNTER_BASE 0x48243200
|
|
|
+#define INCREMENTER_NUMERATOR_OFFSET 0x10
|
|
|
+#define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14
|
|
|
+#define NUMERATOR_DENUMERATOR_MASK 0xfffff000
|
|
|
+
|
|
|
+/* Clockevent code */
|
|
|
+
|
|
|
+static struct omap_dm_timer clkev;
|
|
|
+static struct clock_event_device clockevent_gpt;
|
|
|
+
|
|
|
+static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ struct clock_event_device *evt = &clockevent_gpt;
|
|
|
+
|
|
|
+ __omap_dm_timer_write_status(&clkev, OMAP_TIMER_INT_OVERFLOW);
|
|
|
+
|
|
|
+ evt->event_handler(evt);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+}
|
|
|
+
|
|
|
+static struct irqaction omap2_gp_timer_irq = {
|
|
|
+ .name = "gp_timer",
|
|
|
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
|
|
+ .handler = omap2_gp_timer_interrupt,
|
|
|
+};
|
|
|
+
|
|
|
+static int omap2_gp_timer_set_next_event(unsigned long cycles,
|
|
|
+ struct clock_event_device *evt)
|
|
|
+{
|
|
|
+ __omap_dm_timer_load_start(&clkev, OMAP_TIMER_CTRL_ST,
|
|
|
+ 0xffffffff - cycles, OMAP_TIMER_POSTED);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
|
|
|
+ struct clock_event_device *evt)
|
|
|
+{
|
|
|
+ u32 period;
|
|
|
+
|
|
|
+ __omap_dm_timer_stop(&clkev, OMAP_TIMER_POSTED, clkev.rate);
|
|
|
+
|
|
|
+ switch (mode) {
|
|
|
+ case CLOCK_EVT_MODE_PERIODIC:
|
|
|
+ period = clkev.rate / HZ;
|
|
|
+ period -= 1;
|
|
|
+ /* Looks like we need to first set the load value separately */
|
|
|
+ __omap_dm_timer_write(&clkev, OMAP_TIMER_LOAD_REG,
|
|
|
+ 0xffffffff - period, OMAP_TIMER_POSTED);
|
|
|
+ __omap_dm_timer_load_start(&clkev,
|
|
|
+ OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST,
|
|
|
+ 0xffffffff - period, OMAP_TIMER_POSTED);
|
|
|
+ break;
|
|
|
+ case CLOCK_EVT_MODE_ONESHOT:
|
|
|
+ break;
|
|
|
+ case CLOCK_EVT_MODE_UNUSED:
|
|
|
+ case CLOCK_EVT_MODE_SHUTDOWN:
|
|
|
+ case CLOCK_EVT_MODE_RESUME:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static struct clock_event_device clockevent_gpt = {
|
|
|
+ .name = "gp_timer",
|
|
|
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
|
+ .shift = 32,
|
|
|
+ .rating = 300,
|
|
|
+ .set_next_event = omap2_gp_timer_set_next_event,
|
|
|
+ .set_mode = omap2_gp_timer_set_mode,
|
|
|
+};
|
|
|
+
|
|
|
+static struct property device_disabled = {
|
|
|
+ .name = "status",
|
|
|
+ .length = sizeof("disabled"),
|
|
|
+ .value = "disabled",
|
|
|
+};
|
|
|
+
|
|
|
+static struct of_device_id omap_timer_match[] __initdata = {
|
|
|
+ { .compatible = "ti,omap2-timer", },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap_get_timer_dt - get a timer using device-tree
|
|
|
+ * @match - device-tree match structure for matching a device type
|
|
|
+ * @property - optional timer property to match
|
|
|
+ *
|
|
|
+ * Helper function to get a timer during early boot using device-tree for use
|
|
|
+ * as kernel system timer. Optionally, the property argument can be used to
|
|
|
+ * select a timer with a specific property. Once a timer is found then mark
|
|
|
+ * the timer node in device-tree as disabled, to prevent the kernel from
|
|
|
+ * registering this timer as a platform device and so no one else can use it.
|
|
|
+ */
|
|
|
+static struct device_node * __init omap_get_timer_dt(struct of_device_id *match,
|