18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * arch/arm/plat-spear/time.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2010 ST Microelectronics
58c2ecf20Sopenharmony_ci * Shiraz Hashim<shiraz.linux.kernel@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
88c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
148c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/ioport.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/time.h>
248c2ecf20Sopenharmony_ci#include <linux/irq.h>
258c2ecf20Sopenharmony_ci#include <asm/mach/time.h>
268c2ecf20Sopenharmony_ci#include "generic.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * We would use TIMER0 and TIMER1 as clockevent and clocksource.
308c2ecf20Sopenharmony_ci * Timer0 and Timer1 both belong to same gpt block in cpu subbsystem. Further
318c2ecf20Sopenharmony_ci * they share same functional clock. Any change in one's functional clock will
328c2ecf20Sopenharmony_ci * also affect other timer.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define CLKEVT	0	/* gpt0, channel0 as clockevent */
368c2ecf20Sopenharmony_ci#define CLKSRC	1	/* gpt0, channel1 as clocksource */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Register offsets, x is channel number */
398c2ecf20Sopenharmony_ci#define CR(x)		((x) * 0x80 + 0x80)
408c2ecf20Sopenharmony_ci#define IR(x)		((x) * 0x80 + 0x84)
418c2ecf20Sopenharmony_ci#define LOAD(x)		((x) * 0x80 + 0x88)
428c2ecf20Sopenharmony_ci#define COUNT(x)	((x) * 0x80 + 0x8C)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Reg bit definitions */
458c2ecf20Sopenharmony_ci#define CTRL_INT_ENABLE		0x0100
468c2ecf20Sopenharmony_ci#define CTRL_ENABLE		0x0020
478c2ecf20Sopenharmony_ci#define CTRL_ONE_SHOT		0x0010
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define CTRL_PRESCALER1		0x0
508c2ecf20Sopenharmony_ci#define CTRL_PRESCALER2		0x1
518c2ecf20Sopenharmony_ci#define CTRL_PRESCALER4		0x2
528c2ecf20Sopenharmony_ci#define CTRL_PRESCALER8		0x3
538c2ecf20Sopenharmony_ci#define CTRL_PRESCALER16	0x4
548c2ecf20Sopenharmony_ci#define CTRL_PRESCALER32	0x5
558c2ecf20Sopenharmony_ci#define CTRL_PRESCALER64	0x6
568c2ecf20Sopenharmony_ci#define CTRL_PRESCALER128	0x7
578c2ecf20Sopenharmony_ci#define CTRL_PRESCALER256	0x8
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define INT_STATUS		0x1
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Minimum clocksource/clockevent timer range in seconds
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ci#define SPEAR_MIN_RANGE 4
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic __iomem void *gpt_base;
678c2ecf20Sopenharmony_cistatic struct clk *gpt_clk;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int clockevent_next_event(unsigned long evt,
708c2ecf20Sopenharmony_ci				 struct clock_event_device *clk_event_dev);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void __init spear_clocksource_init(void)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u32 tick_rate;
758c2ecf20Sopenharmony_ci	u16 val;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* program the prescaler (/256)*/
788c2ecf20Sopenharmony_ci	writew(CTRL_PRESCALER256, gpt_base + CR(CLKSRC));
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* find out actual clock driving Timer */
818c2ecf20Sopenharmony_ci	tick_rate = clk_get_rate(gpt_clk);
828c2ecf20Sopenharmony_ci	tick_rate >>= CTRL_PRESCALER256;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	writew(0xFFFF, gpt_base + LOAD(CLKSRC));
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	val = readw(gpt_base + CR(CLKSRC));
878c2ecf20Sopenharmony_ci	val &= ~CTRL_ONE_SHOT;	/* autoreload mode */
888c2ecf20Sopenharmony_ci	val |= CTRL_ENABLE ;
898c2ecf20Sopenharmony_ci	writew(val, gpt_base + CR(CLKSRC));
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* register the clocksource */
928c2ecf20Sopenharmony_ci	clocksource_mmio_init(gpt_base + COUNT(CLKSRC), "tmr1", tick_rate,
938c2ecf20Sopenharmony_ci		200, 16, clocksource_mmio_readw_up);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic inline void timer_shutdown(struct clock_event_device *evt)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	u16 val = readw(gpt_base + CR(CLKEVT));
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* stop the timer */
1018c2ecf20Sopenharmony_ci	val &= ~CTRL_ENABLE;
1028c2ecf20Sopenharmony_ci	writew(val, gpt_base + CR(CLKEVT));
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int spear_shutdown(struct clock_event_device *evt)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	timer_shutdown(evt);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int spear_set_oneshot(struct clock_event_device *evt)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	u16 val;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* stop the timer */
1178c2ecf20Sopenharmony_ci	timer_shutdown(evt);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	val = readw(gpt_base + CR(CLKEVT));
1208c2ecf20Sopenharmony_ci	val |= CTRL_ONE_SHOT;
1218c2ecf20Sopenharmony_ci	writew(val, gpt_base + CR(CLKEVT));
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int spear_set_periodic(struct clock_event_device *evt)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	u32 period;
1298c2ecf20Sopenharmony_ci	u16 val;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* stop the timer */
1328c2ecf20Sopenharmony_ci	timer_shutdown(evt);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	period = clk_get_rate(gpt_clk) / HZ;
1358c2ecf20Sopenharmony_ci	period >>= CTRL_PRESCALER16;
1368c2ecf20Sopenharmony_ci	writew(period, gpt_base + LOAD(CLKEVT));
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	val = readw(gpt_base + CR(CLKEVT));
1398c2ecf20Sopenharmony_ci	val &= ~CTRL_ONE_SHOT;
1408c2ecf20Sopenharmony_ci	val |= CTRL_ENABLE | CTRL_INT_ENABLE;
1418c2ecf20Sopenharmony_ci	writew(val, gpt_base + CR(CLKEVT));
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct clock_event_device clkevt = {
1478c2ecf20Sopenharmony_ci	.name = "tmr0",
1488c2ecf20Sopenharmony_ci	.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
1498c2ecf20Sopenharmony_ci	.set_state_shutdown = spear_shutdown,
1508c2ecf20Sopenharmony_ci	.set_state_periodic = spear_set_periodic,
1518c2ecf20Sopenharmony_ci	.set_state_oneshot = spear_set_oneshot,
1528c2ecf20Sopenharmony_ci	.tick_resume = spear_shutdown,
1538c2ecf20Sopenharmony_ci	.set_next_event = clockevent_next_event,
1548c2ecf20Sopenharmony_ci	.shift = 0,	/* to be computed */
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int clockevent_next_event(unsigned long cycles,
1588c2ecf20Sopenharmony_ci				 struct clock_event_device *clk_event_dev)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	u16 val = readw(gpt_base + CR(CLKEVT));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (val & CTRL_ENABLE)
1638c2ecf20Sopenharmony_ci		writew(val & ~CTRL_ENABLE, gpt_base + CR(CLKEVT));
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	writew(cycles, gpt_base + LOAD(CLKEVT));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	val |= CTRL_ENABLE | CTRL_INT_ENABLE;
1688c2ecf20Sopenharmony_ci	writew(val, gpt_base + CR(CLKEVT));
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic irqreturn_t spear_timer_interrupt(int irq, void *dev_id)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct clock_event_device *evt = &clkevt;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	writew(INT_STATUS, gpt_base + IR(CLKEVT));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	evt->event_handler(evt);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void __init spear_clockevent_init(int irq)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	u32 tick_rate;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* program the prescaler */
1898c2ecf20Sopenharmony_ci	writew(CTRL_PRESCALER16, gpt_base + CR(CLKEVT));
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	tick_rate = clk_get_rate(gpt_clk);
1928c2ecf20Sopenharmony_ci	tick_rate >>= CTRL_PRESCALER16;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	clkevt.cpumask = cpumask_of(0);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	clockevents_config_and_register(&clkevt, tick_rate, 3, 0xfff0);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (request_irq(irq, spear_timer_interrupt, IRQF_TIMER, "timer", NULL))
1998c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (timer)\n", irq);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic const struct of_device_id timer_of_match[] __initconst = {
2038c2ecf20Sopenharmony_ci	{ .compatible = "st,spear-timer", },
2048c2ecf20Sopenharmony_ci	{ },
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_civoid __init spear_setup_of_timer(void)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct device_node *np;
2108c2ecf20Sopenharmony_ci	int irq, ret;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, timer_of_match);
2138c2ecf20Sopenharmony_ci	if (!np) {
2148c2ecf20Sopenharmony_ci		pr_err("%s: No timer passed via DT\n", __func__);
2158c2ecf20Sopenharmony_ci		return;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(np, 0);
2198c2ecf20Sopenharmony_ci	if (!irq) {
2208c2ecf20Sopenharmony_ci		pr_err("%s: No irq passed for timer via DT\n", __func__);
2218c2ecf20Sopenharmony_ci		return;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	gpt_base = of_iomap(np, 0);
2258c2ecf20Sopenharmony_ci	if (!gpt_base) {
2268c2ecf20Sopenharmony_ci		pr_err("%s: of iomap failed\n", __func__);
2278c2ecf20Sopenharmony_ci		return;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	gpt_clk = clk_get_sys("gpt0", NULL);
2318c2ecf20Sopenharmony_ci	if (IS_ERR(gpt_clk)) {
2328c2ecf20Sopenharmony_ci		pr_err("%s:couldn't get clk for gpt\n", __func__);
2338c2ecf20Sopenharmony_ci		goto err_iomap;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(gpt_clk);
2378c2ecf20Sopenharmony_ci	if (ret < 0) {
2388c2ecf20Sopenharmony_ci		pr_err("%s:couldn't prepare-enable gpt clock\n", __func__);
2398c2ecf20Sopenharmony_ci		goto err_prepare_enable_clk;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	spear_clockevent_init(irq);
2438c2ecf20Sopenharmony_ci	spear_clocksource_init();
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cierr_prepare_enable_clk:
2488c2ecf20Sopenharmony_ci	clk_put(gpt_clk);
2498c2ecf20Sopenharmony_cierr_iomap:
2508c2ecf20Sopenharmony_ci	iounmap(gpt_base);
2518c2ecf20Sopenharmony_ci}
252