18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2013 by John Crispin <john@phrozen.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
108c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/reset.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/time.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/mach-ralink/ralink_regs.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define SYSTICK_FREQ		(50 * 1000)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define SYSTICK_CONFIG		0x00
248c2ecf20Sopenharmony_ci#define SYSTICK_COMPARE		0x04
258c2ecf20Sopenharmony_ci#define SYSTICK_COUNT		0x08
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* route systick irq to mips irq 7 instead of the r4k-timer */
288c2ecf20Sopenharmony_ci#define CFG_EXT_STK_EN		0x2
298c2ecf20Sopenharmony_ci/* enable the counter */
308c2ecf20Sopenharmony_ci#define CFG_CNT_EN		0x1
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct systick_device {
338c2ecf20Sopenharmony_ci	void __iomem *membase;
348c2ecf20Sopenharmony_ci	struct clock_event_device dev;
358c2ecf20Sopenharmony_ci	int irq_requested;
368c2ecf20Sopenharmony_ci	int freq_scale;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int systick_set_oneshot(struct clock_event_device *evt);
408c2ecf20Sopenharmony_cistatic int systick_shutdown(struct clock_event_device *evt);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int systick_next_event(unsigned long delta,
438c2ecf20Sopenharmony_ci				struct clock_event_device *evt)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct systick_device *sdev;
468c2ecf20Sopenharmony_ci	u32 count;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	sdev = container_of(evt, struct systick_device, dev);
498c2ecf20Sopenharmony_ci	count = ioread32(sdev->membase + SYSTICK_COUNT);
508c2ecf20Sopenharmony_ci	count = (count + delta) % SYSTICK_FREQ;
518c2ecf20Sopenharmony_ci	iowrite32(count, sdev->membase + SYSTICK_COMPARE);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void systick_event_handler(struct clock_event_device *dev)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	/* noting to do here */
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic irqreturn_t systick_interrupt(int irq, void *dev_id)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct clock_event_device *dev = (struct clock_event_device *) dev_id;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	dev->event_handler(dev);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct systick_device systick = {
718c2ecf20Sopenharmony_ci	.dev = {
728c2ecf20Sopenharmony_ci		/*
738c2ecf20Sopenharmony_ci		 * cevt-r4k uses 300, make sure systick
748c2ecf20Sopenharmony_ci		 * gets used if available
758c2ecf20Sopenharmony_ci		 */
768c2ecf20Sopenharmony_ci		.rating			= 310,
778c2ecf20Sopenharmony_ci		.features		= CLOCK_EVT_FEAT_ONESHOT,
788c2ecf20Sopenharmony_ci		.set_next_event		= systick_next_event,
798c2ecf20Sopenharmony_ci		.set_state_shutdown	= systick_shutdown,
808c2ecf20Sopenharmony_ci		.set_state_oneshot	= systick_set_oneshot,
818c2ecf20Sopenharmony_ci		.event_handler		= systick_event_handler,
828c2ecf20Sopenharmony_ci	},
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int systick_shutdown(struct clock_event_device *evt)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct systick_device *sdev;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	sdev = container_of(evt, struct systick_device, dev);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (sdev->irq_requested)
928c2ecf20Sopenharmony_ci		free_irq(systick.dev.irq, &systick.dev);
938c2ecf20Sopenharmony_ci	sdev->irq_requested = 0;
948c2ecf20Sopenharmony_ci	iowrite32(0, systick.membase + SYSTICK_CONFIG);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int systick_set_oneshot(struct clock_event_device *evt)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	const char *name = systick.dev.name;
1028c2ecf20Sopenharmony_ci	struct systick_device *sdev;
1038c2ecf20Sopenharmony_ci	int irq = systick.dev.irq;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	sdev = container_of(evt, struct systick_device, dev);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!sdev->irq_requested) {
1088c2ecf20Sopenharmony_ci		if (request_irq(irq, systick_interrupt,
1098c2ecf20Sopenharmony_ci				IRQF_PERCPU | IRQF_TIMER, name, &systick.dev))
1108c2ecf20Sopenharmony_ci			pr_err("Failed to request irq %d (%s)\n", irq, name);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci	sdev->irq_requested = 1;
1138c2ecf20Sopenharmony_ci	iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN,
1148c2ecf20Sopenharmony_ci		  systick.membase + SYSTICK_CONFIG);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int __init ralink_systick_init(struct device_node *np)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	systick.membase = of_iomap(np, 0);
1248c2ecf20Sopenharmony_ci	if (!systick.membase)
1258c2ecf20Sopenharmony_ci		return -ENXIO;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	systick.dev.name = np->name;
1288c2ecf20Sopenharmony_ci	clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
1298c2ecf20Sopenharmony_ci	systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
1308c2ecf20Sopenharmony_ci	systick.dev.max_delta_ticks = 0x7fff;
1318c2ecf20Sopenharmony_ci	systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
1328c2ecf20Sopenharmony_ci	systick.dev.min_delta_ticks = 0x3;
1338c2ecf20Sopenharmony_ci	systick.dev.irq = irq_of_parse_and_map(np, 0);
1348c2ecf20Sopenharmony_ci	if (!systick.dev.irq) {
1358c2ecf20Sopenharmony_ci		pr_err("%pOFn: request_irq failed", np);
1368c2ecf20Sopenharmony_ci		return -EINVAL;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
1408c2ecf20Sopenharmony_ci				    SYSTICK_FREQ, 301, 16,
1418c2ecf20Sopenharmony_ci				    clocksource_mmio_readl_up);
1428c2ecf20Sopenharmony_ci	if (ret)
1438c2ecf20Sopenharmony_ci		return ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	clockevents_register_device(&systick.dev);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	pr_info("%pOFn: running - mult: %d, shift: %d\n",
1488c2ecf20Sopenharmony_ci			np, systick.dev.mult, systick.dev.shift);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
154