1bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer/*
2bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * arch/arm/mach-netx/time.c
3bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer *
4bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
5bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer *
6bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * This program is free software; you can redistribute it and/or modify
7bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * it under the terms of the GNU General Public License version 2
8bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * as published by the Free Software Foundation.
9bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer *
10bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * This program is distributed in the hope that it will be useful,
11bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * but WITHOUT ANY WARRANTY; without even the implied warranty of
12bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * GNU General Public License for more details.
14bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer *
15bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * You should have received a copy of the GNU General Public License
16bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * along with this program; if not, write to the Free Software
17bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer */
19bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
20bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer#include <linux/init.h>
21bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer#include <linux/interrupt.h>
221a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer#include <linux/irq.h>
231a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer#include <linux/clocksource.h>
242fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König#include <linux/clockchips.h>
25fced80c735941fa518ac67c0b61bbe153fb8c050Russell King#include <linux/io.h>
26bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
27a09e64fbc0094e3073dbb09c3b4bfe4ab669244bRussell King#include <mach/hardware.h>
28bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer#include <asm/mach/time.h>
29a09e64fbc0094e3073dbb09c3b4bfe4ab669244bRussell King#include <mach/netx-regs.h>
30bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
315e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König#define NETX_CLOCK_FREQ 100000000
325e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König#define NETX_LATCH DIV_ROUND_CLOSEST(NETX_CLOCK_FREQ, HZ)
335e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König
342fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König#define TIMER_CLOCKEVENT 0
3524e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König#define TIMER_CLOCKSOURCE 1
3624e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König
372fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-Königstatic void netx_set_mode(enum clock_event_mode mode,
382fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		struct clock_event_device *clk)
392fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König{
402fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	u32 tmode;
412fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
422fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	/* disable timer */
432fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
442fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
452fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	switch (mode) {
462fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	case CLOCK_EVT_MODE_PERIODIC:
475e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König		writel(NETX_LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
482fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		tmode = NETX_GPIO_COUNTER_CTRL_RST_EN |
492fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König			NETX_GPIO_COUNTER_CTRL_IRQ_EN |
502fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König			NETX_GPIO_COUNTER_CTRL_RUN;
512fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		break;
522fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
532fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	case CLOCK_EVT_MODE_ONESHOT:
542fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		writel(0, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
552fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		tmode = NETX_GPIO_COUNTER_CTRL_IRQ_EN |
562fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König			NETX_GPIO_COUNTER_CTRL_RUN;
572fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		break;
582fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
592fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	default:
602fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		WARN(1, "%s: unhandled mode %d\n", __func__, mode);
612fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		/* fall through */
622fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
632fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	case CLOCK_EVT_MODE_SHUTDOWN:
642fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	case CLOCK_EVT_MODE_UNUSED:
652fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	case CLOCK_EVT_MODE_RESUME:
662fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		tmode = 0;
672fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		break;
682fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	}
692fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
702fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	writel(tmode, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
712fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König}
722fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
732fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-Königstatic int netx_set_next_event(unsigned long evt,
742fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König		struct clock_event_device *clk)
752fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König{
762fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	writel(0 - evt, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKEVENT));
772fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	return 0;
782fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König}
792fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
802fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-Königstatic struct clock_event_device netx_clockevent = {
812fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	.name = "netx-timer" __stringify(TIMER_CLOCKEVENT),
822fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
832fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	.set_next_event = netx_set_next_event,
842fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	.set_mode = netx_set_mode,
852fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König};
862fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
87bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer/*
88bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * IRQ handler for the timer
89bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer */
90bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauerstatic irqreturn_t
910cd61b68c340a4f901a06e8bb5e0dea4353161c0Linus Torvaldsnetx_timer_interrupt(int irq, void *dev_id)
92bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer{
932fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	struct clock_event_device *evt = &netx_clockevent;
941a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer
95bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	/* acknowledge interrupt */
96bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
97bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
982fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	evt->event_handler(evt);
992fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
100bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	return IRQ_HANDLED;
101bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer}
102bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
103bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauerstatic struct irqaction netx_timer_irq = {
1049853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	.name		= "NetX Timer Tick",
10578f6db99522bbdd2f2a90c963744bd51c8602990Michael Opdenacker	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
1069853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	.handler	= netx_timer_interrupt,
107bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer};
108bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
109bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer/*
110bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer * Set up timer interrupt
111bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer */
1126bb27d7349db51b50c40534710fe164ca0d58902Stephen Warrenvoid __init netx_timer_init(void)
113bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer{
114bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	/* disable timer initially */
115bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(0, NETX_GPIO_COUNTER_CTRL(0));
116bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
117bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	/* Reset the timer value to zero */
118bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(0, NETX_GPIO_COUNTER_CURRENT(0));
119bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
1205e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König	writel(NETX_LATCH, NETX_GPIO_COUNTER_MAX(0));
121bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
122bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	/* acknowledge interrupt */
123bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
124bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
1259853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	/* Enable the interrupt in the specific timer
1269853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	 * register and start timer
1279853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	 */
128bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(COUNTER_BIT(0), NETX_GPIO_IRQ_ENABLE);
129bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	writel(NETX_GPIO_COUNTER_CTRL_IRQ_EN | NETX_GPIO_COUNTER_CTRL_RUN,
1309853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König			NETX_GPIO_COUNTER_CTRL(0));
131bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer
132bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer	setup_irq(NETX_IRQ_TIMER0, &netx_timer_irq);
1331a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer
1341a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer	/* Setup timer one for clocksource */
13524e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König	writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE));
13624e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König	writel(0, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE));
13724e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König	writel(0xffffffff, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKSOURCE));
1381a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer
1399853848860d7ece7d84ac43cfde5390b2638eb89Uwe Kleine-König	writel(NETX_GPIO_COUNTER_CTRL_RUN,
14024e7857677fe3cb87f1dd7fa1418a73795e9f4c5Uwe Kleine-König			NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE));
1411a815aed1e03c73fcd0390109c7da9c69dc97490Sascha Hauer
142234b6ceddb4fc2a4bc5b9a7670f070f6e69e0868Russell King	clocksource_mmio_init(NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE),
1435e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König		"netx_timer", NETX_CLOCK_FREQ, 200, 32, clocksource_mmio_readl_up);
1442fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König
1452fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	/* with max_delta_ns >= delta2ns(0x800) the system currently runs fine.
1462fcfe6b872b21639dcffbaf3ca2a84ec01d104e0Uwe Kleine-König	 * Adding some safety ... */
1472927926707d07f846154842bc12bed6d817d9412Russell King	netx_clockevent.cpumask = cpumask_of(0);
1485e3e27637959155bf3b06f52c5fbe9e9b0ffbdfeUwe Kleine-König	clockevents_config_and_register(&netx_clockevent, NETX_CLOCK_FREQ,
149838a2ae80a6ab52139fb1bf0a93ea8c5eff94488Shawn Guo					0xa00, 0xfffffffe);
150bb6d8c8828123e01e2ae6c9d9c4870477889fd94Sascha Hauer}
151