11f2acc5aSJohn Crispin /*
21f2acc5aSJohn Crispin * This file is subject to the terms and conditions of the GNU General Public
31f2acc5aSJohn Crispin * License. See the file "COPYING" in the main directory of this archive
41f2acc5aSJohn Crispin * for more details.
51f2acc5aSJohn Crispin *
697b92108SJohn Crispin * Copyright (C) 2013 by John Crispin <john@phrozen.org>
71f2acc5aSJohn Crispin */
81f2acc5aSJohn Crispin
91f2acc5aSJohn Crispin #include <linux/clockchips.h>
101f2acc5aSJohn Crispin #include <linux/clocksource.h>
111f2acc5aSJohn Crispin #include <linux/interrupt.h>
121f2acc5aSJohn Crispin #include <linux/reset.h>
131f2acc5aSJohn Crispin #include <linux/init.h>
141f2acc5aSJohn Crispin #include <linux/time.h>
151f2acc5aSJohn Crispin #include <linux/of.h>
161f2acc5aSJohn Crispin #include <linux/of_irq.h>
171f2acc5aSJohn Crispin #include <linux/of_address.h>
181f2acc5aSJohn Crispin
191f2acc5aSJohn Crispin #include <asm/mach-ralink/ralink_regs.h>
201f2acc5aSJohn Crispin
211f2acc5aSJohn Crispin #define SYSTICK_FREQ (50 * 1000)
221f2acc5aSJohn Crispin
231f2acc5aSJohn Crispin #define SYSTICK_CONFIG 0x00
241f2acc5aSJohn Crispin #define SYSTICK_COMPARE 0x04
251f2acc5aSJohn Crispin #define SYSTICK_COUNT 0x08
261f2acc5aSJohn Crispin
271f2acc5aSJohn Crispin /* route systick irq to mips irq 7 instead of the r4k-timer */
281f2acc5aSJohn Crispin #define CFG_EXT_STK_EN 0x2
291f2acc5aSJohn Crispin /* enable the counter */
301f2acc5aSJohn Crispin #define CFG_CNT_EN 0x1
311f2acc5aSJohn Crispin
321f2acc5aSJohn Crispin struct systick_device {
331f2acc5aSJohn Crispin void __iomem *membase;
341f2acc5aSJohn Crispin struct clock_event_device dev;
351f2acc5aSJohn Crispin int irq_requested;
361f2acc5aSJohn Crispin int freq_scale;
371f2acc5aSJohn Crispin };
381f2acc5aSJohn Crispin
3959113d93SViresh Kumar static int systick_set_oneshot(struct clock_event_device *evt);
4059113d93SViresh Kumar static int systick_shutdown(struct clock_event_device *evt);
411f2acc5aSJohn Crispin
systick_next_event(unsigned long delta,struct clock_event_device * evt)421f2acc5aSJohn Crispin static int systick_next_event(unsigned long delta,
431f2acc5aSJohn Crispin struct clock_event_device *evt)
441f2acc5aSJohn Crispin {
451f2acc5aSJohn Crispin struct systick_device *sdev;
461f2acc5aSJohn Crispin u32 count;
471f2acc5aSJohn Crispin
481f2acc5aSJohn Crispin sdev = container_of(evt, struct systick_device, dev);
491f2acc5aSJohn Crispin count = ioread32(sdev->membase + SYSTICK_COUNT);
501f2acc5aSJohn Crispin count = (count + delta) % SYSTICK_FREQ;
5137bcc03fSJohn Crispin iowrite32(count, sdev->membase + SYSTICK_COMPARE);
521f2acc5aSJohn Crispin
531f2acc5aSJohn Crispin return 0;
541f2acc5aSJohn Crispin }
551f2acc5aSJohn Crispin
systick_event_handler(struct clock_event_device * dev)561f2acc5aSJohn Crispin static void systick_event_handler(struct clock_event_device *dev)
571f2acc5aSJohn Crispin {
581f2acc5aSJohn Crispin /* noting to do here */
591f2acc5aSJohn Crispin }
601f2acc5aSJohn Crispin
systick_interrupt(int irq,void * dev_id)611f2acc5aSJohn Crispin static irqreturn_t systick_interrupt(int irq, void *dev_id)
621f2acc5aSJohn Crispin {
631f2acc5aSJohn Crispin struct clock_event_device *dev = (struct clock_event_device *) dev_id;
641f2acc5aSJohn Crispin
651f2acc5aSJohn Crispin dev->event_handler(dev);
661f2acc5aSJohn Crispin
671f2acc5aSJohn Crispin return IRQ_HANDLED;
681f2acc5aSJohn Crispin }
691f2acc5aSJohn Crispin
701f2acc5aSJohn Crispin static struct systick_device systick = {
711f2acc5aSJohn Crispin .dev = {
721f2acc5aSJohn Crispin /*
731f2acc5aSJohn Crispin * cevt-r4k uses 300, make sure systick
741f2acc5aSJohn Crispin * gets used if available
751f2acc5aSJohn Crispin */
761f2acc5aSJohn Crispin .rating = 310,
771f2acc5aSJohn Crispin .features = CLOCK_EVT_FEAT_ONESHOT,
781f2acc5aSJohn Crispin .set_next_event = systick_next_event,
7959113d93SViresh Kumar .set_state_shutdown = systick_shutdown,
8059113d93SViresh Kumar .set_state_oneshot = systick_set_oneshot,
811f2acc5aSJohn Crispin .event_handler = systick_event_handler,
821f2acc5aSJohn Crispin },
831f2acc5aSJohn Crispin };
841f2acc5aSJohn Crispin
systick_shutdown(struct clock_event_device * evt)8559113d93SViresh Kumar static int systick_shutdown(struct clock_event_device *evt)
861f2acc5aSJohn Crispin {
871f2acc5aSJohn Crispin struct systick_device *sdev;
881f2acc5aSJohn Crispin
891f2acc5aSJohn Crispin sdev = container_of(evt, struct systick_device, dev);
901f2acc5aSJohn Crispin
9159113d93SViresh Kumar if (sdev->irq_requested)
92*ac8fd122Safzal mohammed free_irq(systick.dev.irq, &systick.dev);
9359113d93SViresh Kumar sdev->irq_requested = 0;
9459113d93SViresh Kumar iowrite32(0, systick.membase + SYSTICK_CONFIG);
9559113d93SViresh Kumar
9659113d93SViresh Kumar return 0;
9759113d93SViresh Kumar }
9859113d93SViresh Kumar
systick_set_oneshot(struct clock_event_device * evt)9959113d93SViresh Kumar static int systick_set_oneshot(struct clock_event_device *evt)
10059113d93SViresh Kumar {
101*ac8fd122Safzal mohammed const char *name = systick.dev.name;
10259113d93SViresh Kumar struct systick_device *sdev;
103*ac8fd122Safzal mohammed int irq = systick.dev.irq;
10459113d93SViresh Kumar
10559113d93SViresh Kumar sdev = container_of(evt, struct systick_device, dev);
10659113d93SViresh Kumar
107*ac8fd122Safzal mohammed if (!sdev->irq_requested) {
108*ac8fd122Safzal mohammed if (request_irq(irq, systick_interrupt,
109*ac8fd122Safzal mohammed IRQF_PERCPU | IRQF_TIMER, name, &systick.dev))
110*ac8fd122Safzal mohammed pr_err("Failed to request irq %d (%s)\n", irq, name);
111*ac8fd122Safzal mohammed }
1121f2acc5aSJohn Crispin sdev->irq_requested = 1;
1131f2acc5aSJohn Crispin iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN,
1141f2acc5aSJohn Crispin systick.membase + SYSTICK_CONFIG);
1151f2acc5aSJohn Crispin
11659113d93SViresh Kumar return 0;
1171f2acc5aSJohn Crispin }
1181f2acc5aSJohn Crispin
ralink_systick_init(struct device_node * np)1192712616fSDaniel Lezcano static int __init ralink_systick_init(struct device_node *np)
1201f2acc5aSJohn Crispin {
1212712616fSDaniel Lezcano int ret;
1222712616fSDaniel Lezcano
1231f2acc5aSJohn Crispin systick.membase = of_iomap(np, 0);
1241f2acc5aSJohn Crispin if (!systick.membase)
1252712616fSDaniel Lezcano return -ENXIO;
1261f2acc5aSJohn Crispin
1271f2acc5aSJohn Crispin systick.dev.name = np->name;
1281f2acc5aSJohn Crispin clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
1291f2acc5aSJohn Crispin systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
130e4db9253SNicolai Stange systick.dev.max_delta_ticks = 0x7fff;
1311f2acc5aSJohn Crispin systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
132e4db9253SNicolai Stange systick.dev.min_delta_ticks = 0x3;
1331f2acc5aSJohn Crispin systick.dev.irq = irq_of_parse_and_map(np, 0);
1341f2acc5aSJohn Crispin if (!systick.dev.irq) {
1359475e90fSRob Herring pr_err("%pOFn: request_irq failed", np);
1362712616fSDaniel Lezcano return -EINVAL;
1371f2acc5aSJohn Crispin }
1381f2acc5aSJohn Crispin
1392712616fSDaniel Lezcano ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
1402712616fSDaniel Lezcano SYSTICK_FREQ, 301, 16,
1412712616fSDaniel Lezcano clocksource_mmio_readl_up);
1422712616fSDaniel Lezcano if (ret)
1432712616fSDaniel Lezcano return ret;
1441f2acc5aSJohn Crispin
1451f2acc5aSJohn Crispin clockevents_register_device(&systick.dev);
1461f2acc5aSJohn Crispin
1479475e90fSRob Herring pr_info("%pOFn: running - mult: %d, shift: %d\n",
1489475e90fSRob Herring np, systick.dev.mult, systick.dev.shift);
1492712616fSDaniel Lezcano
1502712616fSDaniel Lezcano return 0;
1511f2acc5aSJohn Crispin }
1521f2acc5aSJohn Crispin
15317273395SDaniel Lezcano TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
154